Appearance
Callbacks
When creating a payment or payout, you can specify a callback_url. As soon as the transaction reaches a new status you will receive a notification at the specified url.
Same flat format as the API
Callback bodies use the same flat format as the REST responses: amount and currency are separate top-level fields, payment_method is the method type as a plain string, method details arrive as payment_method_* keys, and a failure is reported as error_code / error_message.
There are no nested objects anywhere in the body, so the parser you use for GET /v1/payment/ responses works unchanged for callbacks.
Payment callbacks
json
{
"status": "completed",
"transaction_id": "703ea600-f5bc-4ef0-b9b6-7af6000abf7e",
"amount": "1.0",
"currency": "RUB",
"redirect_url": "https://pay.wirekassa.com/703ea600-f5bc-4ef0-b9b6-7af6000abf7e/",
"channel": "2bad7e2d-15b1-4078-b5ee-01ff2e4491b3",
"external_id": "external-id",
"payment_method": "card",
"payment_method_card_mask": "400000****0002",
"payment_method_card_type": "VISA",
"create_date": "2025-11-24T20:10:58.065252+00:00",
"update_date": "2025-11-24T20:12:41.771903+00:00"
}Keys whose value is null are omitted. Possible values of status are processing, completed, failed and mispaid.
Body fields
| Field | Description |
|---|---|
status | processing, completed, failed or mispaid |
transaction_id | Wirekassa transaction identifier |
amount | Requested amount in major units |
currency | ISO 4217 currency code |
received_amount | Amount actually received; present when it differs from amount |
redirect_url | Hosted checkout page for this transaction |
channel | Merchant channel the transaction belongs to |
external_id | Identifier you supplied when creating the transaction |
payment_method | Method type, e.g. card, sbp, cross_border |
payment_method_<field> | Method details; the exact set depends on the method |
create_date | ISO 8601 creation timestamp |
update_date | ISO 8601 timestamp of the last status change |
error_code | Present only when status is failed |
error_message | Human-readable reason; present only when status is failed |
Partially paid transactions
For a partially paid transaction the paid sum arrives in received_amount. See Mispaid status.
json
{
"status": "mispaid",
"transaction_id": "703ea600-f5bc-4ef0-b9b6-7af6000abf7e",
"amount": "111.0",
"currency": "RUB",
"received_amount": "100.0",
"redirect_url": "https://pay.wirekassa.com/703ea600-f5bc-4ef0-b9b6-7af6000abf7e/",
"channel": "2bad7e2d-15b1-4078-b5ee-01ff2e4491b3",
"external_id": "external-id",
"payment_method": "sbp",
"create_date": "2025-11-24T20:10:58.065252+00:00",
"update_date": "2025-11-24T20:12:41.771903+00:00"
}Declined transactions
For a declined transaction the reason arrives in two flat fields:
json
{
"status": "failed",
"transaction_id": "703ea600-f5bc-4ef0-b9b6-7af6000abf7e",
"amount": "1.0",
"currency": "RUB",
"redirect_url": "https://pay.wirekassa.com/703ea600-f5bc-4ef0-b9b6-7af6000abf7e/",
"channel": "2bad7e2d-15b1-4078-b5ee-01ff2e4491b3",
"external_id": "external-id",
"payment_method": "card",
"error_code": "PROVIDER_PROCESSING_FAILED",
"error_message": "Payment provider returned an error.",
"create_date": "2025-11-24T20:10:58.065252+00:00",
"update_date": "2025-11-24T20:12:41.771903+00:00"
}Payout callbacks
Payouts use the same notification body as payments. A payout callback is delivered once the payout reaches a final status (completed or failed), to the callback_url supplied when it was created.
Because both are rendered by the same schema, a payout callback carries the payment-shaped body: the method type arrives under payment_method, not payout_method, and a redirect_url is present even though it has no meaning for a payout. Match the notification to your record by transaction_id or external_id.
json
{
"status": "completed",
"transaction_id": "b5cff6ed-f3f0-46df-a390-2ffdb11430f4",
"amount": "2500.0",
"currency": "RUB",
"redirect_url": "https://pay.wirekassa.com/b5cff6ed-f3f0-46df-a390-2ffdb11430f4/",
"channel": "b96d5b92-8f34-4b12-9b16-5f047b57c901",
"external_id": "ORDER-49283",
"payment_method": "card",
"create_date": "2025-11-24T20:10:58.065252+00:00",
"update_date": "2025-11-24T20:14:12.882104+00:00"
}A declined payout reports the reason in the same two fields:
json
{
"status": "failed",
"transaction_id": "b5cff6ed-f3f0-46df-a390-2ffdb11430f4",
"amount": "2500.0",
"currency": "RUB",
"redirect_url": "https://pay.wirekassa.com/b5cff6ed-f3f0-46df-a390-2ffdb11430f4/",
"channel": "b96d5b92-8f34-4b12-9b16-5f047b57c901",
"external_id": "ORDER-49283",
"payment_method": "card",
"error_code": "DECLINED_GENERIC",
"error_message": "The payout was declined.",
"create_date": "2025-11-24T20:10:58.065252+00:00",
"update_date": "2025-11-24T20:14:12.882104+00:00"
}Verifying the signature
The request will also contain the following headers, which you can use to make sure that the callback is genuine:
json
{
"Content-Type": "application/json",
"X-API-KEY": "key",
"X-TIMESTAMP": "1764015058.078768",
"X-SIGNATURE": "ac22c7d3ea3bb8d304167cacc8d2b7486803242c8c5a35904a632e939496eb79"
}An example of a authenticity check function in Python:
python
import hmac
import hashlib
import json
def verify_signature(
secret_key: str,
raw_body: str,
timestamp: str, # X-TIMESTAMP
received_signature: str # X-SIGNATURE
) -> bool:
message = f"{timestamp}{raw_body}"
expected_signature = hmac.new(
secret_key.encode(),
message.encode(),
hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected_signature, received_signature)Note: For verification, use the secret key corresponding to the public key from the X-API-KEY header. The secret key is known only to you and our system.
Sign the raw body exactly as received — the signature is computed over the transmitted bytes, so re-serialising the JSON before checking it will not match.
If the verification is successful, send the 200 code in response to confirm receipt of the callback. A non-2xx response is treated as a delivery failure and the callback is retried with exponential backoff.