protopays · API

Webhook по платежу

При смене статуса внешнего платежа сервис ставит в очередь HTTP POST с телом application/json на callbackUri из создания платежа. Ниже — единственный стабильный публичный контракт (корень + data). Структура не зависит от метода оплаты (СБП, карта и т.д.). Идентификатор платежа в data.id совпадает с data.id в ответах POST и GET статуса.

Корень объекта

ПолеТипОбяз.Описание
typestringдаТип события (см. блок «Поле type»). Сейчас всегда payment.status.updated.
eventIdstringдаУникальный идентификатор одного доставленного события (например evt_ + ULID). Новое событие — новый eventId.
createdAtstringдаВремя формирования события, ISO 8601 с суффиксом Z (UTC).
dataobjectдаПубличные бизнес-поля платежа (см. следующий блок).
idstring (UUID)нетДубликат data.id на корне — для интеграций, читающих только верхний уровень. То же значение, что data.id.
order_idstringнетДубликат 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, если передавался при создании:

ПолеТипОбяз.Описание
idstring (UUID)даПубличный идентификатор платежа — тот же UUID, что data.id в ответе POST / GET статуса.
orderIdstringдаВаш orderId из запроса создания.
statusstringдаТекущий статус в UPPERCASE (см. список ниже).
amountstringдаСумма в виде строки — так проще сохранить точность без ошибок JSON number. Лишние нули в дробной части могут быть сняты.
currencystringдаВалюта в UPPERCASE (например RUB).
extraobjectнетПроизвольные метаданные из запроса создания, если были переданы.

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).

Рекомендации по обработке

Минимальный рабочий алгоритм:

  1. Проверить подпись запроса (если включена для вашего endpoint).
  2. По eventId убедиться, что это событие ещё не обрабатывали.
  3. Обновить локальное состояние платежа по data.id / data.orderId и data.status.
  4. Если статус терминальный — завершить пользовательский сценарий.
  • Дедупликация доставки webhook: храните обработанные eventId (они не повторяются для разных событий).
  • Сопоставление с вашей системой: data.orderId и/или data.id (или корневой id).
  • Решение по итогу оплаты: опирайтесь на data.status; детали из meta — только справочно.
  • Повторы и порядок доставки — в общих правилах Webhooks.

Выплаты — отдельный контур: см. Webhook по выплатам.