Skip to Content
One-time paymentsRequest signature

Request signature

The signature proves request authenticity. Two algorithm versions are supported: 3 (login-based) and 4 (bound to the request body).

Common steps

Inner hash
innerHash = lowercase(hmac_sha256(password, timestamp))
  • password — merchant secret key, stored in the project settings on the platform side.
  • login — the X-Login header value.
  • timestamp — the X-Timestamp header value.

password is a secret. It is never sent in the request, never logged and must not end up in client code: compute the signature only on the merchant server.

Version 3 — login-based

signature = lowercase(hmac_sha256(password, login + innerHash))
Example
password = "password" login = "login" timestamp = "2025-02-10T17:56:37Z" innerHash = lowercase(hmac_sha256("password", "2025-02-10T17:56:37Z")) signature = lowercase(hmac_sha256("password", "login" + innerHash)) → "5f9e4f4a7a805b898cac96a6c3e7a81dd0e06e200c291d51663e6f250b10853f"

Signature v3 implementation

LOGIN=demo_login PASSWORD=demo_password TS=$(date -u +%Y-%m-%dT%H:%M:%SZ) INNER=$(printf '%s' "$TS" \ | openssl dgst -sha256 -hmac "$PASSWORD" | awk '{print $NF}') SIG=$(printf '%s%s' "$LOGIN" "$INNER" \ | openssl dgst -sha256 -hmac "$PASSWORD" | awk '{print $NF}')

For version 4, requestPayload is added to the message — same function, but requestPayload + login + innerHash is signed.

Version 4 — bound to the request body

signature = lowercase(hmac_sha256(password, requestPayload + login + innerHash))

requestPayload is a deterministic concatenation of the significant request fields without separators:

Method and pathPayload
POST /invoices{order.merchant_order_id}{customerPayload}{order.price.amount}{order.price.currency}{contract.merchant_contract_id}{client_ip}
POST /invoices/{id}/authorize{invoice_id}{pin_code}{client_ip}
POST /invoices/{id}/cancel{invoice_id}{client_ip}
POST /invoices/{id}/refund{invoice_id}{client_ip}
GET /invoices/{id}{invoice_id}
GET /invoices?merchant_order_id=...{merchant_order_id}
POST /contracts{merchant_contract_id}{customerPayload}{pin_code}{client_ip}
POST /contracts/{id}/authorize{contract_id}{merchant_contract_id}{pin_code}{client_ip}
POST /contracts/{id}/cancel{contract_id}{merchant_contract_id}{client_ip}
POST /billing/check{customerPayload}

customerPayload depends on customer.type:

TypePayload
mobile_phone{msisdn}
undefined_bank_card, bank_card{source}{country}{merchant_customer_id}{email}
(other / none)(empty string)

Any missing field is substituted as an empty string. The invoice or contract ID is taken from the URL path.