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