Webhook-уведомления по выплатам
Подписанные уведомления об изменении статуса выплат
При каждом изменении статуса выплаты мы отправляем подписанный POST-запрос. Опрос GET /v1/payouts/{payoutId} — резервный способ, а не замена.
Адрес отправки уведомлений
Адрес определяется в следующем порядке:
callbackUrl, переданный в теле запросаPOST /v1/payouts: применяется только к этой выплате;callbackUrlиз настроек мерчанта;- если не задано ни одно из значений, уведомления по выплате не отправляются.
Адрес фиксируется в момент создания выплаты. Последующее изменение настроек мерчанта не переносит уведомления по уже созданной выплате на новый адрес.
Требования к callbackUrl
| Требование | Значение |
|---|---|
| Схема | http или https |
| Длина | до 2048 символов |
| Валидация | некорректный URL → 400 Validation failed |
Поле возвращается в объекте выплаты. Значение null означает, что используются настройки мерчанта.
Условия отправки уведомлений
Уведомление формируется при каждом изменении статуса, за тремя исключениями:
CREATED: уведомление не отправляется;COMPLETED: уведомление ставится в очередь в рамках транзакции завершения выплаты, когда известна итоговая комиссия;- ручная смена статуса выплаты
COMPLETEDиз-за ошибки на стороне банка: уведомление отправляется по запросу, обратитесь к вашему менеджеру.
Ваш менеджер также может повторно отправить уведомление вручную. Дублирующиеся записи исключаются по совокупности адреса, ID выплаты и статуса среди неотправленных уведомлений.
Формат уведомления
POST {callbackUrl}
Content-Type: application/json
X-Type: PAYOUT_UPDATE
X-Signature: HMAC-SHA512(тело запроса, API-токен) в формате hexТело
{
"id": "0198c3f1-2a4b-7c3d-9e0f-1a2b3c4d5e6f",
"status": "COMPLETED",
"amount": "1000.5",
"fee": "60",
"currency": "RUB",
"type": "C2C",
"note": null,
"externalId": "payout-123",
"merchantId": "0198c3f1-1111-7222-8333-444455556666",
"settlementCurrency": "USD",
"settlementAmount": "11.28",
"createdAt": "2026-07-29T10:00:00.000Z",
"completedAt": "2026-07-29T10:04:12.317Z"
}| Поле | Тип | Описание |
|---|---|---|
id | string (UUID) | ID выплаты |
status | string | Текущий статус, см. Статусы выплат |
amount | string | Сумма выплаты |
fee | string | Комиссия за выплату |
currency | string | Валюта выплаты |
type | string | Тип платежа |
note | string / null | Заметка к выплате |
externalId | string / null | ID выплаты в вашей системе |
merchantId | string (UUID) | ID вашего мерчанта |
settlementCurrency | string / null | Валюта расчётов |
settlementAmount | string / null | Сумма в валюте расчётов |
createdAt | string | Время создания |
completedAt | string / null | Время завершения |
callbackUrl не включается в тело: он определяет адрес доставки и не является частью состояния выплаты.
Проверка подписи
Каждое уведомление содержит заголовок X-Signature: HMAC-SHA512-хеш исходного тела запроса в формате hex, вычисленный с вашим API-токеном в качестве секрета. Та же схема используется для webhook-уведомлений по платежам.
import crypto from "crypto";
// Ваш API-токен используется как секрет для HMAC
const apiToken = process.env.API_TOKEN;
app.post("/webhooks/payments", (req, res) => {
const signature = req.headers["x-signature"];
const body = JSON.stringify(req.body);
// Считаем HMAC-SHA512 от тела запроса и сравниваем в HEX
const expected = crypto.createHmac("sha512", apiToken).update(body).digest("hex");
if (expected !== signature) {
return res.status(403).send("Invalid signature");
}
// Подпись верна — обрабатываем уведомление идемпотентно
const data = req.body;
if (data.status === "COMPLETED") {
console.log(`Заказ ${data.externalId} оплачен`);
}
res.status(200).send("OK");
});Если один endpoint получает уведомления и по платежам, и по выплатам, различайте их по заголовку X-Type: PAYOUT_UPDATE.
Политика повторных отправок
| Параметр | Значение |
|---|---|
| Количество попыток | до 30 |
| Интервал | 30s × 2^n, но не более 30 минут |
| Критерий успеха | HTTP-статус 200–399 |
Дополнительно применяется circuit breaker: адрес, стабильно возвращающий ошибки, временно исключается из доставки.
Рекомендации по работе с webhook-уведомлениями
- Всегда отвечайте кодом HTTP
200, чтобы подтвердить получение. Если ваш сервер не отвечает, мы повторяем отправку уведомления. - Проверяйте заголовок
X-Signatureперед обработкой тела запроса. - Обрабатывайте уведомления идемпотентно: одно и то же уведомление может быть доставлено несколько раз.
- Сопоставляйте уведомление с вашими записями по
externalId, а не по сумме. - Обрабатывайте все возможные значения статусов, включая неуспешные.