Skip to main content

Webhooks

Webhooks let you receive real-time notifications about payment status changes.

Setup

When creating a payment, specify:

  • notify_url — URL to receive notifications
  • notification_token — token for validation (optional)

Webhook Headers

HeaderDescription
Content-Typeapplication/json
X-Webhook-EventEvent type (payment.status_changed)
X-SignatureHMAC-SHA256 signature of the request body
X-Notification-TokenYour token from notification_token (if it was provided)
Validation via Notification Token

If you passed a notification_token when creating the payment, it is returned in the X-Notification-Token header. Compare it with the expected value for quick validation without computing a signature.

Webhook Format

{
"event": "payment.status_changed",
"paymentId": "550e8400-e29b-41d4-a716-446655440000",
"ext_id": "order_12345",
"status": "COMPLETED",
"amount": 5000,
"currency": "RUB",
"timestamp": "2026-01-30T12:22:00Z"
}

Events

EventStatusDescription
payment.status_changedCOMPLETEDPayment successfully completed
payment.status_changedCANCELLEDPayment cancelled
payment.status_changedCOMPLETED_APPEALDispute approved
payment.status_changedCANCELLED_APPEALDispute rejected
payment.status_changedDISPUTEDispute opened on the payment

Signature Verification

The X-Signature header contains the HMAC-SHA256 signature of the request body (hex).

JavaScript

const crypto = require('crypto');

function verifyWebhook(body, signature, secretKey) {
const expectedSignature = crypto
.createHmac('sha256', secretKey)
.update(JSON.stringify(body))
.digest('hex');

return signature === expectedSignature;
}

app.post('/webhook', (req, res) => {
// Option 1: Validate via notification_token (fast)
const token = req.headers['x-notification-token'];
if (token && token === expectedToken) {
// Validation passed
}

// Option 2: Validate via signature (reliable)
const signature = req.headers['x-signature'];
if (!verifyWebhook(req.body, signature, secretKey)) {
return res.status(401).send('Invalid signature');
}

const { event, paymentId, status } = req.body;
console.log(`${event}: Payment ${paymentId} is ${status}`);

res.status(200).send('OK');
});

Python

import hmac
import hashlib
import json

def verify_webhook(body, signature, secret_key):
expected_signature = hmac.new(
secret_key.encode(),
json.dumps(body).encode(),
hashlib.sha256
).hexdigest()

return signature == expected_signature

PHP

function verifyWebhook($body, $signature, $secretKey) {
$expectedSignature = hash_hmac(
'sha256',
json_encode($body),
$secretKey
);

return hash_equals($expectedSignature, $signature);
}

Server Requirements

  1. HTTP 200 OK — a webhook is considered delivered when HTTP 200 is received
  2. Timeout — the response must be received within 10 seconds
  3. Idempotency — handle duplicate deliveries correctly

Retry Policy

On failed delivery, the system will retry:

AttemptDelay
1Immediately
260 seconds
360 seconds

After 3 failed attempts, the webhook is marked as undelivered.