Refunds
dpay.pl lets you refund completed transactions. Both full and partial refunds are supported. You can issue refunds programmatically through the API or manually from the administration panel.
POST/api/v1/pbl/refundFull refund contract: parameters, checksum, and error codes.Full contract in the API Reference
Supported payment methods
| Method | Full refund | Partial refund | Multiple partial refunds |
|---|---|---|---|
| BLIK | Yes | Yes | Yes |
| BLIK BNPL | Yes | Yes | Yes |
| Bank transfers (PBL) | Yes | Yes | Yes |
| Payment cards (Visa / Mastercard) | Yes | Yes | Yes |
| MB WAY | Yes | Yes | Yes |
| PayPo | Yes | Yes | No - one refund only |
| Twisto | Yes | Yes | No - one refund only |
For PayPo and Twisto, you can issue exactly one refund for a transaction: either full or partial.
After the first refund, even a partial one, the transaction is marked as refunded and subsequent attempts
will fail with Transaction has already been refunded.
You can issue a refund within 180 days of the transaction date for payment cards, MB WAY, PayPo, and Twisto. For BLIK, the effective limit is 179 days. Bank transfers (PBL) have no refund time limit.
API endpoint
POST https://panel.dpay.pl/api/v1/pbl/refund
Content-Type: application/json
Request parameters
| Field | Type | Required | Description |
|---|---|---|---|
service | string | Yes | Service name from the panel |
transaction_id | string | Yes | Identifier of the transaction to refund |
value | string | No | Refund amount (for example, "15.00"). Omit it for a full refund |
reason | string | No | Refund description or reason, max. 255 characters. Note: if you send this field, it must be included in the checksum (see below) |
checksum | string | Yes | SHA-256 checksum |
Optional headers
| Header | Description |
|---|---|
DigitalPayments-PSP-Message-ID | Your own refund identifier (max. 24 characters; longer values are truncated). It determines the refund transfer title: dpay.pl zwrot {header value}. Without this header, the title is dpay.pl {transaction_id}. This identifier is also returned in the response's message field |
To issue a partial refund, provide the value parameter with an amount lower than the transaction amount. The amount must be a non-negative number (for example, "15.00").
Generating the checksum
The server verifies the checksum by concatenating the values of all body fields in the order in which
they appear in the JSON (excluding checksum), separated by |, with secret_hash appended
at the end. This means that:
- every field you send, including optional
valueandreason, must be included in the checksum, - the order of fields in the body must match the order of values in the checksum,
- omitting a sent field from the checksum, such as
reason, results in the HTTP 401 errorUnauthorized request.
For the formulas below, send the fields in the body in exactly the order shown.
Full refund
sha256({service}|{transaction_id}|{secret_hash})
Partial refund (with the value parameter)
sha256({service}|{transaction_id}|{value}|{secret_hash})
Refund with a reason (with the reason parameter)
For a body ordered as service, transaction_id, value, reason:
sha256({service}|{transaction_id}|{value}|{reason}|{secret_hash})
PHP
$service = 'abc123';
$transactionId = 'abc-def-123-456';
$secretHash = '9a8b7c6d5e4f3a2b1c0d';
// Full refund
$checksum = hash('sha256',
$service . '|' . $transactionId . '|' . $secretHash
);
// Partial refund
$value = '15.00';
$checksumPartial = hash('sha256',
$service . '|' . $transactionId . '|' . $value . '|' . $secretHash
);
JavaScript (Node.js)
const crypto = require('crypto');
const service = 'abc123';
const transactionId = 'abc-def-123-456';
const secretHash = '9a8b7c6d5e4f3a2b1c0d';
// Full refund
const checksum = crypto
.createHash('sha256')
.update(`${service}|${transactionId}|${secretHash}`)
.digest('hex');
// Partial refund
const value = '15.00';
const checksumPartial = crypto
.createHash('sha256')
.update(`${service}|${transactionId}|${value}|${secretHash}`)
.digest('hex');
Python
import hashlib
service = 'abc123'
transaction_id = 'abc-def-123-456'
secret_hash = '9a8b7c6d5e4f3a2b1c0d'
# Full refund
data = f'{service}|{transaction_id}|{secret_hash}'
checksum = hashlib.sha256(data.encode('utf-8')).hexdigest()
# Partial refund
value = '15.00'
data_partial = f'{service}|{transaction_id}|{value}|{secret_hash}'
checksum_partial = hashlib.sha256(data_partial.encode('utf-8')).hexdigest()
Request examples
Full refund: cURL
curl -X POST https://panel.dpay.pl/api/v1/pbl/refund \
-H "Content-Type: application/json" \
-d '{
"service": "abc123",
"transaction_id": "abc-def-123-456",
"checksum": "e3b0c44298fc1c149afb..."
}'
Partial refund: cURL
curl -X POST https://panel.dpay.pl/api/v1/pbl/refund \
-H "Content-Type: application/json" \
-d '{
"service": "abc123",
"transaction_id": "abc-def-123-456",
"value": "15.00",
"checksum": "a1b2c3d4e5f6..."
}'
PHP
<?php
$service = getenv('DPAY_SERVICE');
$secretHash = getenv('DPAY_SECRET_HASH');
$transactionId = 'abc-def-123-456';
$value = '15.00'; // null for a full refund
// Generate the checksum
if ($value !== null) {
$checksum = hash('sha256',
$service . '|' . $transactionId . '|' . $value . '|' . $secretHash
);
} else {
$checksum = hash('sha256',
$service . '|' . $transactionId . '|' . $secretHash
);
}
$payload = [
'service' => $service,
'transaction_id' => $transactionId,
'checksum' => $checksum,
];
if ($value !== null) {
$payload['value'] = $value;
}
$ch = curl_init('https://panel.dpay.pl/api/v1/pbl/refund');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode($payload),
CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
$result = json_decode($response, true);
if ($httpCode === 200 && $result['status'] === 'success') {
echo 'Refund completed successfully';
} else {
echo 'Refund error: ' . $result['message'];
}
API response
Success (HTTP 200)
{
"status": "success",
"refund": true,
"message": "dpay.pl abc-def-123-456"
}
The message field is the refund message identifier, which is used as the refund transfer title, rather than
a fixed response text. By default, it has the form dpay.pl {transaction_id}. If you sent the
DigitalPayments-PSP-Message-ID header, you receive dpay.pl zwrot {header value}.
Error
{
"status": "error",
"refund": false,
"message": "Transaction does not exist"
}
For a nonexistent transaction_id, the 404 response may use a simplified format: text
instead of a full JSON object with a message field. Do not rely solely on parsing the
message field; make your decision based on the HTTP status code.
Checking refund availability
Before issuing a refund, you can check whether it is available without performing the refund itself:
POST https://panel.dpay.pl/api/v1/pbl/check-refund-availability
Content-Type: application/json
/api/v1/pbl/check-refund-availabilityCheck refund availability without issuing the refund.Full contract in the API Reference
The parameters and checksum are identical to those used for a refund: service, transaction_id, and optionally
value, with the same checksum calculation rules. Example response:
{
"status": "success",
"refund": true,
"message": "Refund available"
}
When a refund is unavailable, you receive status: "error", refund: false, and a message explaining the reason
(the same messages listed in the error table below).
GET variants
Both endpoints also have GET variants with parameters in the path:
GET https://panel.dpay.pl/api/v1/pbl/refund/{service}/{transaction_id}/{checksum}
GET https://panel.dpay.pl/api/v1/pbl/check-refund-availability/{service}/{transaction_id}/{checksum}
We recommend the POST variants because they allow you to provide value and reason and do not leave the
checksum in intermediary access logs.
Multiple partial refunds
For methods that support multiple partial refunds (BLIK, cards, MB WAY, and PBL bank transfers), you can issue multiple refunds for a single transaction as long as their total does not exceed the original transaction amount.
dpay.pl tracks the total refunded amount in the refunded_amount field. Example:
| Operation | Amount | refunded_amount | Remaining |
|---|---|---|---|
| Transaction | 100.00 PLN | 0.00 | 100.00 |
| Refund #1 | 30.00 PLN | 30.00 | 70.00 |
| Refund #2 | 25.00 PLN | 55.00 | 45.00 |
| Refund #3 | 45.00 PLN | 100.00 | 0.00 |
Attempting to refund more than the transaction's remaining refundable amount results in an error.
Common errors
| HTTP | Message (message) | Cause |
|---|---|---|
| 401 | Unauthorized request | Invalid checksum. Check the field order, Secret Hash, and whether all sent fields, including value and reason, were included in the checksum |
| 404 | Transaction does not exist | The specified transaction_id does not exist |
| 402 | Transaction has not been paid | The transaction has not been paid; only paid transactions can be refunded |
| 410 | Transaction has already been refunded | The transaction has already been refunded (for PayPo/Twisto, this also applies after one partial refund) |
| 400 | Refund amount exceeds available refund amount | The refund amount exceeds the remaining refundable amount |
| 406 | Insufficient balance to process the refund | Insufficient balance to process the refund |
| 409 | Transaction already has a refund form submitted! | A refund form has already been submitted for this transaction |
| 400 | The operator of the indicated channel does not support refunds | The payment method or channel does not support refunds through this route, or the time limit has expired (BLIK) |
| 411 | Charge transactions cannot be refunded | Charge transactions cannot be refunded |
| 400 | Refund failed | The refund failed; try again or contact support |
Refunds in the administration panel
You can also issue a refund manually:
- Sign in to panel.dpay.pl.
- Go to the Transactions section.
- Find the transaction you want to refund using the filters or search.
- Click the transaction to open its details.
- Click Refund funds.
- Select the refund type: full or partial, if available for the payment method.
- For a partial refund, enter the amount.
- Confirm the refund.