Webhook по платежу
При смене статуса внешнего платежа сервис ставит в очередь HTTP POST с телом application/json на callbackUri из создания платежа. Ниже — единственный стабильный публичный контракт (корень + data). Структура не зависит от метода оплаты (СБП, карта и т.д.). Идентификатор платежа в data.id совпадает с data.id в ответах POST и GET статуса.
Корень объекта
| Поле | Тип | Обяз. | Описание |
|---|---|---|---|
| type | string | да | Тип события (см. блок «Поле type»). Сейчас всегда payment.status.updated. |
| eventId | string | да | Уникальный идентификатор одного доставленного события (например evt_ + ULID). Новое событие — новый eventId. |
| createdAt | string | да | Время формирования события, ISO 8601 с суффиксом Z (UTC). |
| data | object | да | Публичные бизнес-поля платежа (см. следующий блок). |
| id | string (UUID) | нет | Дубликат data.id на корне — для интеграций, читающих только верхний уровень. То же значение, что data.id. |
| order_id | string | нет | Дубликат data.orderId на корне (snake_case). |
Поле type — тип события
type задаёт семантику webhook: по нему удобно выбирать обработчик (как topic в event-driven системе), а не разбирать тело наугад.
payment.status.updated— изменение статуса внешнего платежа; полезная нагрузка вdata.
В будущем могут появиться другие значения type (например выплаты или споры). Неизвестные типы лучше логировать и пропускать без падения обработчика.
eventId и id — разные сущности
data.id(и корневойid) — стабильный идентификатор платежа (UUID). Один и тот же id может фигурировать в нескольких webhook подряд (каждый раз при смене статуса).eventId— уникальный идентификатор конкретного webhook-события. Используйте его для дедупликации доставки (повторная отправка того же события не должна обрабатываться дважды).
Для идемпотентности приёма уведомлений ориентируйтесь на eventId, а не на data.id в одиночку.
Жизненный цикл статусов
Альтернатива webhook для промежуточного опроса — GET статуса платежа (статусы в нижнем регистре). Для гарантированной доставки финала ориентируйтесь на webhook.
Типичная цепочка при успешной выдаче реквизита:
PENDING → COMPLETED / FAILED / CANCELLED
Если при создании реквизит подобрать нельзя — запись всё равно сохраняется, синхронный ответ содержит data.status: requisite_unavailable, а webhook приходит с REQUISITE_UNAVAILABLE (терминальный статус, без последующих переходов).
При апелляции возможны переходы через APPEAL и последующие события с тем же data.id.
- События могут приходить не в хронологическом порядке относительно времени в вашей системе.
- Возможны повторные доставки — используйте дедупликацию по
eventId(см. общие правила).
data — обязательный контракт
Внутри data всегда эти ключи (одинаковые для всех webhook); опционально extra, если передавался при создании:
| Поле | Тип | Обяз. | Описание |
|---|---|---|---|
| id | string (UUID) | да | Публичный идентификатор платежа — тот же UUID, что data.id в ответе POST / GET статуса. |
| orderId | string | да | Ваш orderId из запроса создания. |
| status | string | да | Текущий статус в UPPERCASE (см. список ниже). |
| amount | string | да | Сумма в виде строки — так проще сохранить точность без ошибок JSON number. Лишние нули в дробной части могут быть сняты. |
| currency | string | да | Валюта в UPPERCASE (например RUB). |
| extra | object | нет | Произвольные метаданные из запроса создания, если были переданы. |
meta (опционально)
Объект meta может отсутствовать. Если он есть, в нём могут быть вспомогательные поля (например reason, блок по апелляции). При REQUISITE_UNAVAILABLE в meta.reason часто приходит collect_requisites_not_available.
Важно: поле meta не входит в стабильный публичный контракт и не гарантируется в будущих версиях в том же виде. Не стройте на нём критическую бизнес-логику — опирайтесь на data.status и при необходимости уточняйте детали в поддержке.
Примеры JSON
Успешное завершение
{
"type": "payment.status.updated",
"eventId": "evt_01hz…",
"createdAt": "2026-04-20T12:00:00Z",
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"orderId": "demo-001",
"status": "COMPLETED",
"amount": "1000",
"currency": "RUB"
},
"id": "550e8400-e29b-41d4-a716-446655440000",
"order_id": "demo-001"
}Неуспех (пример)
Значение meta.reason иллюстративно; в проде может быть иной текст или код.
{
"type": "payment.status.updated",
"eventId": "evt_01hz…",
"createdAt": "2026-04-20T12:05:00Z",
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"orderId": "demo-001",
"status": "FAILED",
"amount": "13420",
"currency": "RUB"
},
"meta": {
"reason": "insufficient_funds"
}
}Реквизит не выдан (collect)
Соответствует синхронному ответу создания с data.status: requisite_unavailable и code: collect_requisites_not_available.
{
"type": "payment.status.updated",
"eventId": "evt_01hz…",
"createdAt": "2026-06-14T16:28:23Z",
"data": {
"id": "28ff765b-6395-4da7-9371-9371-23fcc877763c",
"orderId": "pp_463_02BAB86938A4",
"status": "REQUISITE_UNAVAILABLE",
"amount": "500",
"currency": "RUB"
},
"meta": {
"reason": "collect_requisites_not_available"
}
}Значения data.status
В webhook статус всегда в верхнем регистре (в GET статуса — lowercase):
PENDING— ожидает оплаты / обработки (реквизит выдан)COMPLETED— успешно завершёнFAILED— отказ / ошибкаCANCELLED— отменёнAPPEAL— открыта апелляцияREQUISITE_UNAVAILABLE— реквизит не выдан при создании (collect)
Терминальные (финальные) статусы: COMPLETED, FAILED, CANCELLED, REQUISITE_UNAVAILABLE. PENDING и APPEAL — как правило промежуточные (по апелляции возможны последующие события с тем же data.id).
Рекомендации по обработке
Минимальный рабочий алгоритм:
- Проверить подпись запроса (если включена для вашего endpoint).
- По
eventIdубедиться, что это событие ещё не обрабатывали. - Обновить локальное состояние платежа по
data.id/data.orderIdиdata.status. - Если статус терминальный — завершить пользовательский сценарий.
- Дедупликация доставки webhook: храните обработанные
eventId(они не повторяются для разных событий). - Сопоставление с вашей системой:
data.orderIdи/илиdata.id(или корневойid). - Решение по итогу оплаты: опирайтесь на
data.status; детали изmeta— только справочно. - Повторы и порядок доставки — в общих правилах Webhooks.
Выплаты — отдельный контур: см. Webhook по выплатам.