Перейти к содержимому
ZeroKYC Pay

Справочник вебхуков

Полный каталог событий с точными payload, заголовки и семантика доставки, лесенка ретраев 1м→12ч и правила идемпотентности.

Всё о получении событий ZeroKYC Pay: каталог событий, точная форма payload, заголовки доставки, лесенка ретраев и правила идемпотентности.

Webhook-эндпоинты настраиваются в кабинете в разделе Вебхуки: URL плюс секрет эндпоинта (whsec_…). Каждый активный эндпоинт получает все события; конкретный инвойс может переопределить URL полем webhook_url при создании.

События

СобытиеКогда срабатывает
payment.detectedНа цепи найдена транзакция, совпадающая с одним из вариантов инвойса (мемпул или ранние подтверждения)
payment.confirmedДостигнуто нужное число подтверждений — сигнал «отгружать товар»
payment.underpaidПришёл платёж ниже приемлемого диапазона; инвойс становится underpaid и требует вашего решения
invoice.expiredTTL истёк без валидного платежа
invoice.canceledИнвойс отменён через POST /v1/invoices/{id}/cancel до оплаты
webhook.testВы нажали Отправить тестовое событие в кабинете — подписанная smoke-проверка обработчика

invoice.created существует только в журнале событий (GET /v1/events), вебхуком он не доставляется.

Мультивариантные инвойсы (payment_currency=any) подтверждаются по первому валидному платежу; остальные варианты закрываются автоматически.

Payload

Каждая доставка — JSON-конверт: id события, type, created_at и объект data, специфичный для события:

{
  "id": "evt_5c1a...",
  "type": "payment.confirmed",
  "created_at": "2026-09-06T14:02:33Z",
  "data": {
    "invoice_id": "inv_9f2k...",
    "order_id": "order-1042",
    "amount": "49.00",
    "base_currency": "USD",
    "option": {
      "asset": "TON",
      "network": "ton",
      "amount_crypto": "9.81",
      "paid_amount": "9.81"
    },
    "tx": {
      "hash": "9d24...",
      "confirmations": 1,
      "explorer_url": "https://tonviewer.com/transaction/9d24...",
      "output_index": 0
    },
    "metadata": { "customer_id": "cust_8821" }
  }
}

У payment.detected, payment.confirmed и payment.underpaid структура идентична. По полям:

  • id глобально уникален для события — сохраняйте его и дедуплицируйте (доставка at-least-once).
  • order_id и metadata повторяют то, что вы отправили при создании инвойса; order_id — ваш ключ соединения.
  • amount — сумма инвойса в base_currency; крипто-цифры живут в option (amount_crypto = к оплате, paid_amount = фактически наблюдается на цепи).
  • tx.explorer_url ведёт на транзакцию в эксплорере актива; output_index различает UTXO/log-выходы.
  • Переплаты подтверждаются штатно: option.paid_amount — наблюдаемая правда.

События жизненного цикла компактнее:

{
  "id": "evt_7b2c...",
  "type": "invoice.expired",
  "created_at": "2026-09-06T20:20:11Z",
  "data": {
    "invoice_id": "inv_9f2k...",
    "order_id": "order-1042",
    "amount": "49.00",
    "base_currency": "USD"
  }
}

invoice.canceled несёт только invoice_id и order_id; webhook.test — endpoint_id, url и строку message.

Заголовки доставки

ЗаголовокЗначение
Content-Typeapplication/json
User-AgentZeroKYC-Pay-Webhooks/1.0
X-ZKP-Signaturet=<unix seconds>,v1=<hex hmac-sha256>
X-ZKP-Event-IdТот же id, что в payload — дедупликация без парсинга тела
X-ZKP-Event-TypeТот же type — маршрутизация без парсинга тела

Подпись — HMAC-SHA256 по строке {timestamp}.{raw_body} секретом эндпоинта; окно таймстемпа — ±300 секунд. Проверяйте до доверия — полные примеры Node/PHP/Python в проверке HMAC.

Семантика доставки и ретраи

  • Успех = любой 2xx в течение 10 секунд. Всё остальное — 4xx, 5xx, таймаут, ошибка TLS — планирование ретрая.
  • Редиректы не преследуются. 3xx считается провалом: указывайте конечный URL эндпоинта, а не ссылку-редирект.
  • Лесенка ретраев:
ПопыткаЗадержка
1сразу
21 минута
35 минут
430 минут
52 часа
612 часов → затем failed
  • Когда лесенка исчерпана, доставка помечается failed, в кабинет приходит уведомление. Ручной retry (Вебхуки → журнал доставок или по инвойсу) сбрасывает счётчик попыток и срабатывает сразу.
  • Секрет и URL эндпоинта ротируются в разделе Вебхуки в любой момент; кнопка Отправить тестовое событие доставляет подписанный webhook.test — проверьте новую конфигурацию end-to-end.

Сверка (reconciliation)

Обработчики должны быть идемпотентны, а API даёт два способа перепроверки:

  1. GET /v1/invoices/{id} всегда возвращает авторитетное состояние инвойса — считайте вебхуки подсказкой, а не истиной в последней инстанции.
  2. GET /v1/events?limit=100 проигрывает журнал событий (включая типы только-для-журнала вроде invoice.created), с фильтрами type и created_after и курсорной пагинацией — используйте его для восстановления после простоя.

Rescan-задача перепроверяет все открытые инвойсы каждые 3 минуты, поэтому пропущенные в реальном времени события цепи самолечатся: payment.confirmed может прийти заметно позже самой транзакции.

Чек-лист потребителя

  1. Проверяйте HMAC из X-ZKP-Signature и окно ±300 с, прежде чем трогать payload.
  2. Дедуплицируйте по event.id (или X-ZKP-Event-Id) — одно событие может прийти больше раза.
  3. Исполняйте заказ только по payment.confirmed; payment.detected — для раннего фидбэка покупателю, payment.underpaid — для ручного разбора.
  4. Отвечайте 2xx быстро; тяжёлую работу выносите в фон.