Hash signature
Hash calculation
All hashes follow the same formula:
hash = sha1( md5( uppercase( concat_of_fields_in_required_order ) ) )
The list of fields differs per operation. Order matters: concatenate, do not delimit.
SHA256 hash mode
By default the inner digest is MD5, as shown above. If the Use SHA256 encryption algorithm for hash option is enabled for your protocol mapping (admin panel → Configuration → Protocol Mapping, per protocol / merchant), the inner digest is SHA256 instead: your request hash is validated with SHA256 and the callbacks you receive are signed with SHA256.
// default (MD5):
hash = sha1( md5( uppercase( concat_of_fields_in_required_order ) ) )
// "Use SHA256 encryption algorithm for hash" enabled:
hash = sha1( sha256( uppercase( concat_of_fields_in_required_order ) ) )
The field order is identical in both modes — only the inner hash function changes. Contact your administrator or account manager to confirm which mode is configured for your account.
Compute hashes on your server, never in the browser. The merchant password is the secret that authenticates your requests; exposing it in client-side code is a complete compromise.
Authentication signature
| Field | Source |
|---|---|
| 1 | order.number |
| 2 | order.amount |
| 3 | order.currency |
| 4 | order.description |
| 5 | password |
Capture / Refund signature
| Field | Source |
|---|---|
| 1 | payment_id |
| 2 | amount |
| 3 | password |
Void / Retry / Get-status-by-payment_id signature
| Field | Source |
|---|---|
| 1 | payment_id |
| 2 | password |
Get-status-by-order_id signature
| Field | Source |
|---|---|
| 1 | order_id |
| 2 | password |
Recurring signature
| Field | Source |
|---|---|
| 1 | recurring_init_trans_id |
| 2 | recurring_token |
| 3 | order.number |
| 4 | order.amount |
| 5 | order.description |
| 6 | password |
Callback signature (verification on your side)
| Field | Source |
|---|---|
| 1 | id (= payment_public_id) |
| 2 | order_number |
| 3 | order_amount |
| 4 | order_currency |
| 5 | order_description |
| 6 | password |
Customer-return signature (in success_url / cancel_url query parameters)
Same fields as callback signature, in the same order.
Reference implementations
Node.js
const crypto = require("crypto");
const md5 = s => crypto.createHash("md5").update(s).digest("hex");
const sha1 = s => crypto.createHash("sha1").update(s).digest("hex");
function makeHash(fields, password) {
const raw = (fields.join("") + password).toUpperCase();
return sha1(md5(raw));
}
// Auth
makeHash([order.number, order.amount, order.currency, order.description], password);
// Capture
makeHash([payment_id, amount], password);
// Verify callback (constant-time comparison)
function verifyCallback(body, password) {
const expected = makeHash(
[body.id, body.order_number, body.order_amount, body.order_currency, body.order_description],
password
);
return crypto.timingSafeEqual(Buffer.from(body.hash), Buffer.from(expected));
}
Python
import hashlib, hmac
def make_hash(fields: list[str], password: str) -> str:
raw = ("".join(fields) + password).upper()
return hashlib.sha1(hashlib.md5(raw.encode()).hexdigest().encode()).hexdigest()
# Auth
make_hash([order["number"], order["amount"], order["currency"], order["description"]], password)
# Verify callback (constant-time)
def verify_callback(body: dict, password: str) -> bool:
expected = make_hash(
[body["id"], body["order_number"], body["order_amount"], body["order_currency"], body["order_description"]],
password
)
return hmac.compare_digest(body["hash"], expected)
PHP
function make_hash(array $fields, string $password): string {
$raw = strtoupper(implode("", $fields) . $password);
return sha1(md5($raw));
}
// Auth
make_hash([$order["number"], $order["amount"], $order["currency"], $order["description"]], $password);
// Verify callback (constant-time)
function verify_callback(array $body, string $password): bool {
$expected = make_hash(
[$body["id"], $body["order_number"], $body["order_amount"], $body["order_currency"], $body["order_description"]],
$password
);
return hash_equals($body["hash"], $expected);
}
[!NOTE] About SHA-1 + MD5. This signature scheme is kept for backward compatibility with the existing merchant base. The merchant
passwordnever leaves your server in plaintext; it is only mixed into the hash input. Always use constant-time comparison (crypto.timingSafeEqual,hmac.compare_digest,hash_equals) when verifying callback signatures to prevent timing attacks.