Skip to content

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

FieldDescription
statusprocessing, completed, failed or mispaid
transaction_idWirekassa transaction identifier
amountRequested amount in major units
currencyISO 4217 currency code
received_amountAmount actually received; present when it differs from amount
redirect_urlHosted checkout page for this transaction
channelMerchant channel the transaction belongs to
external_idIdentifier you supplied when creating the transaction
payment_methodMethod type, e.g. card, sbp, cross_border
payment_method_<field>Method details; the exact set depends on the method
create_dateISO 8601 creation timestamp
update_dateISO 8601 timestamp of the last status change
error_codePresent only when status is failed
error_messageHuman-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.