Generating checksums
A checksum is a security mechanism that ensures the integrity of data transmitted between your server and dpay.pl. Every API request requires a valid checksum generated using your Secret Hash.
How it works
A checksum is a SHA-256 hash generated from a concatenation of specific fields separated by a delimiter. dpay.pl verifies the checksum on its end - if it does not match the expected value, the request is rejected.
Secret Hash is a private key. Never expose it publicly, do not include it in frontend code, and do not commit it to a code repository. Store it exclusively in server environment variables.
Checksum formats for different endpoints
Payment registration (payments/register)
sha256({service}|{SecretHash}|{value}|{url_success}|{url_fail}|{url_ipn})
| Element | Description |
|---|---|
service | Service name from the panel |
SecretHash | Your private key |
value | Payment amount (e.g. "29.99") |
url_success | Success URL |
url_fail | Failure URL |
url_ipn | IPN notification URL |
Separator: | (pipe)
Full refund (pbl/refund)
sha256({service}|{transaction_id}|{secret_hash})
| Element | Description |
|---|---|
service | Service name |
transaction_id | Transaction ID to refund |
secret_hash | Your private key |
Separator: | (pipe)
Partial refund (pbl/refund with value parameter)
sha256({service}|{transaction_id}|{value}|{secret_hash})
| Element | Description |
|---|---|
service | Service name |
transaction_id | Transaction ID to refund |
value | Partial refund amount (e.g. "15.00") |
secret_hash | Your private key |
Separator: | (pipe)
When you include the value parameter in a refund request, you must include it in the checksum as well. Omitting value from the checksum will result in an Invalid checksum error.
Direct Carrier Billing (DCB)
sha256({GUID}|{SecretHash}|{value}|{url_success}|{url_fail}|{url_ipn})
| Element | Description |
|---|---|
GUID | Payment Point GUID identifier |
SecretHash | Your private key |
value | Amount in grosz (cents) (e.g. "1023") |
url_success | Success URL |
url_fail | Failure URL |
url_ipn | IPN notification URL |
Separator: | (pipe)
IPN signature verification
sha256({id}|{SecretHash}|{amount}|{email}|{type}|{attempt}|{version}|{custom})
| Element | Description |
|---|---|
id | Transaction ID |
SecretHash | Your private key |
amount | Transaction amount |
email | Customer email |
type | Notification type |
attempt | Delivery attempt number |
version | Protocol version |
custom | Custom data |
Separator: | (pipe)
Implementation examples
PHP
<?php
/**
* Generate checksum for payment registration
*/
function generatePaymentChecksum(
string $service,
string $secretHash,
string $value,
string $urlSuccess,
string $urlFail,
string $urlIpn
): string {
$data = implode('|', [
$service,
$secretHash,
$value,
$urlSuccess,
$urlFail,
$urlIpn,
]);
return hash('sha256', $data);
}
/**
* Generate checksum for refund
*/
function generateRefundChecksum(
string $service,
string $transactionId,
string $secretHash,
?string $value = null
): string {
if ($value !== null) {
$data = implode('|', [$service, $transactionId, $value, $secretHash]);
} else {
$data = implode('|', [$service, $transactionId, $secretHash]);
}
return hash('sha256', $data);
}
/**
* Generate checksum for DCB
*/
function generateDcbChecksum(
string $guid,
string $secretHash,
string $valueInGrosze,
string $urlSuccess,
string $urlFail,
string $urlIpn
): string {
$data = implode('|', [
$guid,
$secretHash,
$valueInGrosze,
$urlSuccess,
$urlFail,
$urlIpn,
]);
return hash('sha256', $data);
}
/**
* Verify IPN signature
*/
function verifyIpnSignature(array $data, string $secretHash): bool
{
$expected = hash('sha256',
$data['id'] . '|' . $secretHash . '|' . $data['amount'] . '|' . $data['email']
. '|' . $data['type'] . '|' . $data['attempt'] . '|' . $data['version'] . '|' . $data['custom']
);
return hash_equals($expected, $data['signature']);
}
// === Usage example ===
$service = getenv('DPAY_SERVICE');
$secretHash = getenv('DPAY_SECRET_HASH');
// Payment checksum
$checksum = generatePaymentChecksum(
$service,
$secretHash,
'29.99',
'https://myshop.com/success',
'https://myshop.com/failure',
'https://myshop.com/api/ipn'
);
echo $checksum; // e.g. "a1b2c3d4e5f6..."
JavaScript (Node.js)
const crypto = require('crypto');
/**
* Generate checksum for payment registration
*/
function generatePaymentChecksum(service, secretHash, value, urlSuccess, urlFail, urlIpn) {
const data = [service, secretHash, value, urlSuccess, urlFail, urlIpn].join('|');
return crypto.createHash('sha256').update(data).digest('hex');
}
/**
* Generate checksum for refund
*/
function generateRefundChecksum(service, transactionId, secretHash, value = null) {
const parts = value !== null
? [service, transactionId, value, secretHash]
: [service, transactionId, secretHash];
return crypto.createHash('sha256').update(parts.join('|')).digest('hex');
}
/**
* Generate checksum for DCB
*/
function generateDcbChecksum(guid, secretHash, valueInGrosze, urlSuccess, urlFail, urlIpn) {
const data = [guid, secretHash, valueInGrosze, urlSuccess, urlFail, urlIpn].join('|');
return crypto.createHash('sha256').update(data).digest('hex');
}
/**
* Verify IPN signature
*/
function verifyIpnSignature(data, secretHash) {
const raw = [
data.id, secretHash, data.amount, data.email,
data.type, data.attempt, data.version, data.custom,
].join('|');
const expected = crypto.createHash('sha256').update(raw).digest('hex');
return crypto.timingSafeEqual(
Buffer.from(expected, 'hex'),
Buffer.from(data.signature, 'hex')
);
}
// === Usage example ===
const service = process.env.DPAY_SERVICE;
const secretHash = process.env.DPAY_SECRET_HASH;
const checksum = generatePaymentChecksum(
service,
secretHash,
'29.99',
'https://myshop.com/success',
'https://myshop.com/failure',
'https://myshop.com/api/ipn'
);
console.log(checksum);
Python
import hashlib
import hmac
import os
def generate_payment_checksum(
service: str,
secret_hash: str,
value: str,
url_success: str,
url_fail: str,
url_ipn: str,
) -> str:
"""Generate checksum for payment registration."""
data = '|'.join([service, secret_hash, value, url_success, url_fail, url_ipn])
return hashlib.sha256(data.encode('utf-8')).hexdigest()
def generate_refund_checksum(
service: str,
transaction_id: str,
secret_hash: str,
value: str | None = None,
) -> str:
"""Generate checksum for refund (full or partial)."""
if value is not None:
data = '|'.join([service, transaction_id, value, secret_hash])
else:
data = '|'.join([service, transaction_id, secret_hash])
return hashlib.sha256(data.encode('utf-8')).hexdigest()
def generate_dcb_checksum(
guid: str,
secret_hash: str,
value_in_grosze: str,
url_success: str,
url_fail: str,
url_ipn: str,
) -> str:
"""Generate checksum for DCB."""
data = '|'.join([guid, secret_hash, value_in_grosze, url_success, url_fail, url_ipn])
return hashlib.sha256(data.encode('utf-8')).hexdigest()
def verify_ipn_signature(data: dict, secret_hash: str) -> bool:
"""Verify IPN signature."""
raw = '|'.join([
data['id'], secret_hash, data['amount'], data['email'],
data['type'], str(data['attempt']), data['version'], data['custom'],
])
expected = hashlib.sha256(raw.encode('utf-8')).hexdigest()
return hmac.compare_digest(expected, data['signature'])
# === Usage example ===
service = os.environ['DPAY_SERVICE']
secret_hash = os.environ['DPAY_SECRET_HASH']
checksum = generate_payment_checksum(
service,
secret_hash,
'29.99',
'https://myshop.com/success',
'https://myshop.com/failure',
'https://myshop.com/api/ipn',
)
print(checksum)
Common mistakes
1. Incorrect field order
Fields must be in exactly the order specified in the documentation. Swapping the order will produce a different checksum.
2. Extra whitespace
Make sure the field values do not contain extra spaces, newline characters or other whitespace:
// WRONG - extra space
$value = '29.99 ';
// CORRECT
$value = trim('29.99');
3. Character encoding
Use UTF-8 encoding. Special characters in URLs (e.g. non-ASCII characters) may produce different results depending on the encoding.
4. Timing-attack-safe comparison
When verifying the IPN signature, use a comparison method that is resistant to timing attacks:
// WRONG - vulnerable to timing attack
if ($expected === $data['signature']) { ... }
// CORRECT - resistant to timing attack
if (hash_equals($expected, $data['signature'])) { ... }
// CORRECT - Node.js
crypto.timingSafeEqual(
Buffer.from(expected, 'hex'),
Buffer.from(signature, 'hex')
);
# CORRECT - Python
import hmac
hmac.compare_digest(expected, signature)
Debugging checksums
If you receive an Invalid checksum error, follow these steps:
- Print the input data - check the exact value of each field
- Check the separator - use the
|(pipe) separator everywhere - Verify the Secret Hash - make sure you are using the current key from the panel
- Compare with an online tool - use any SHA-256 generator and compare the result with what your code produces
// Debugging - print data before hashing
$raw = $service . '|' . $secretHash . '|' . $value . '|' .
$urlSuccess . '|' . $urlFail . '|' . $urlIpn;
error_log('Checksum input: ' . $raw);
error_log('Checksum output: ' . hash('sha256', $raw));