GH GambleHub

Webhooks: повторы и квитирование

1) Базовая модель доставки

At-least-once (по умолчанию): событие будет доставлено ≥1 раз. Гарантии ровно-один-раз достигаются идемпотентностью приемника.
Квитирование (ACK): только любая 2xx (обычно 200/204) от получателя означает успех. Все остальное трактуется как отказ и ведет к повтору.
Быстрый ACK: отвечайте 2xx после помещения события в свою очередь, а не после полной бизнес-обработки.

2) Формат событий и обязательные заголовки

Полезная нагрузка (пример)

json
{
"id": "evt_01HXYZ",
"type": "order. created",
"occurred_at": "2025-11-03T18:10:12Z",
"sequence": 128374,
"source": "orders",
"data": { "order_id": "o_123", "amount": "49. 90", "currency": "EUR" },
"schema_version": 1
}

Заголовки отправителя

`X-Webhook-Id: evt_01HXYZ` — уникальный ID события (используйте для дедупликации).
`X-Webhook-Seq: 128374` — монотонная последовательность (по подписке/теме).
`X-Signature: sha256=<base64(hmac_sha256(body, secret))>` — HMAC-подпись.
`X-Retry: 0,1,2...` — номер попытки.
`X-Webhook-Version: 1` — версионирование контракта.
(опционально) `Traceparent` — корреляция трасс.

Ответ получателя

2xx — успешно принято (дальше повторов по этому `id` не будет).
410 Gone — endpoint удален/неактивен → отправитель прекращает повторы и деактивирует подписку.
429/5xx/таймаут — отправитель повторяет по политике ретраев.

3) Политика повторов (retries)

Рекомендованная лестница backoff (+ jitter)

`1s, 3s, 10s, 30s, 2m, 10m, 30m, 2h, 6h, 24h` (останавливаемся после лимита, например 48–72 часов).

Правила:
  • Экспоненциальный backoff + случайный jitter (±20–30%) для избегания «стадного эффекта».
  • Кворум ошибок для временных сбоев (например, повтор, если 5xx или таймаут сети).
  • Respect 429: ставьте минимум `min(заголовок Retry-After, следующее окно backoff)`.

Таймауты и размеры

Таймаут соединения ≤ 3–5 сек; общий таймаут ответа ≤ 10 сек.
Размер тела по контракту (например, ≤ 256 KB), иначе 413 → логика «chunking» или «pull URL».

4) Идемпотентность и дедупликация

Идемпотентное применение: обработка повторов того же `id` должна возвращать тот же результат и не менять состояние повторно.
Дедуп-хранилище на стороне получателя: хранить `(X-Webhook-Id, processed_at, checksum)` с TTL ≥ окна ретраев (24–72 ч).
Композиционный ключ: если несколько топиков → `(subscription_id, event_id)`.

5) Порядок и «exactly-once эффекты»

Гарантировать строгий порядок сложно в распределенных системах. Используйте:
  • Partition by key: одно и то же логическое множество (например, `order_id`) всегда в один «канал» доставки.
  • Sequence: отклоняйте события со старым `X-Webhook-Seq` и ставьте их в «parking lot» до прихода недостающих.
Exactly-once эффекты достигаются через:
  • журнал примененных операций (outbox/inbox pattern),
  • транзакционное upsert по `event_id` в БД,
  • саги/компенсации для сложных процессов.

6) Решение ошибок по статус-кодам (таблица)

Код ответаЗначение для отправителяДействие
2xxACK полученСчитаем доставленным, останавливаем ретраи
4xx (кроме 410/429)Постоянная ошибка (payload/авторизация)Поставить в DLQ, уведомить интеграцию
410Endpoint удален/устарелОстановить ретраи, деактивировать подписку
408/429Временная перегрузка/таймаутПовтор по backoff/Jitter; учитывать `Retry-After`
5xxВременная ошибка сервераПовтор по backoff/Jitter
3xxНе используйте редиректы для вебхуковТрактовать как ошибка конфигурации

7) Безопасность канала

Подпись HMAC каждого сообщения; проверка на приемнике с «окном времени» (mitm и replay-атаки).
mTLS для чувствительных доменов (KYC/платежи).
IP allowlist исходящих адресов, TLS 1.2+, HSTS.
PII-минимизация: не отправляйте лишние персональные данные; маскируйте в логах.
Ротация секретов: два действующих ключа (active/next) и заголовок `X-Key-Id` для указания текущего.

8) Очереди, DLQ и реплей

События обязательно пишутся в выходную очередь/журнал на стороне отправителя (для надежного реплея).
При превышении максимума ретраев — событие уходит в DLQ (Dead Letter Queue) с причиной.
Replay API (для получателя/оператора): повторная отправка по `id`/диапазону времени/теме, с ограничением RPS и дополнительной подписью/авторизацией.

Пример Replay API (отправитель):

POST /v1/webhooks/replay
{ "subscription_id": "sub_123", "from": "2025-11-03T00:00:00Z", "to": "2025-11-03T12:00:00Z" }
→ 202 Accepted

9) Контракт и версия

Версионируйте событие (поле `schema_version`) и транспорт (`X-Webhook-Version`).
Добавляйте поля только как опциональные; при удалении — минорная миграция и переходный период (dual-write).
Документируйте типы событий, примеры, схемы (JSON Schema), коды ошибок.

10) Наблюдаемость и SLO

Ключевые метрики отправителя:
  • `delivery_success_rate` (2xx/все попытки), `first_attempt_success_rate`
  • `retries_total`, `max_retry_age_seconds`, `dlq_count`
  • `latency_p50/p95` (occurred_at → ack_received_at)
Ключевые метрики получателя:
  • `ack_latency` (receive → 2xx), `processing_latency` (enqueue → done)
  • `duplicates_total`, `invalid_signature_total`, `out_of_order_total`
SLO примеры:

99.9% событий получают первый ACK ≤ 60 сек (28d).

  • DLQ ≤ 0.1% от общего числа; реплей DLQ ≤ 24 ч.

11) Тайминг и разрывы сети

Используйте UTC в полях времени; синхронизируйте NTP.
Отправляйте `occurred_at` и фиксируйте `delivered_at`, чтобы считать лаг.
При длительных разрывах сеть/endpoint → накапливайте в очереди, ограничивайте рост (backpressure + квоты).

12) Рекомендованные лимиты и гигиена

RPS на подписку (например, 50 RPS, burst 100) + параллелизм (например, 10).
Макс. тело: 64–256 KB; для большего — «notification + URL» и подпись на скачивание.
Имена событий в `snake.case` или `dot.type` (`order.created`).
Строгая идемпотентность write-операций приемника.

13) Примеры: отправитель и получатель

13.1 Отправитель (псевдокод)

python def send_event(event, attempt=0):
body = json. dumps(event)
sig = hmac_sha256_base64(body, secret)
headers = {
"X-Webhook-Id": event["id"],
"X-Webhook-Seq": str(event["sequence"]),
"X-Retry": str(attempt),
"X-Signature": f"sha256={sig}",
"Content-Type": "application/json"
}
res = http. post(endpoint, body, headers, timeout=10)
if 200 <= res. status < 300:
mark_delivered(event["id"])
elif res. status == 410:
deactivate_subscription()
else:
schedule_retry(event, attempt+1) # backoff + jitter, respect 429 Retry-After

13.2 Получатель (псевдокод)

python
@app. post("/webhooks")
def handle():
body  = request. data headers = request. headers assert verify_hmac(body, headers["X-Signature"], secret)
evt_id = headers["X-Webhook-Id"]
if dedup_store. exists(evt_id):
return, "" 204 enqueue_for_processing (body) # fast path. dedup_store put(evt_id, ttl=723600)
return, "" 202 # or 204

14) Тестирование и хаос-практики

Негативные кейсы: невалидная подпись, 429/5xx, таймаут, 410, большие payload’ы.
Поведенческие: out-of-order, дубликаты, задержки 1–10 минут, разрыв на 24 часа.
Нагрузочные: burst 10×; проверьте backpressure и устойчивость DLQ.
Контракты: JSON Schema, обязательные заголовки, стабильные типы событий.

15) Чек-лист внедрения

  • 2xx = ACK, и быстрый возврат после enqueue
  • Экспоненциальный backoff + jitter, уважение `Retry-After`
  • Идемпотентность приемника и дедуп по `X-Webhook-Id` (TTL ≥ ретраи)
  • Подписи HMAC, ротация секретов, optional mTLS
  • DLQ + Replay API, мониторинг и алерты
  • Ограничения: таймауты, RPS, размер тела
  • Порядок: partition by key или `sequence` + «parking lot»
  • Документация: схемы, примеры, кодовки ошибок, версии
  • Хаос-тесты: задержки, дубли, отказ сети, длительный replay

16) Мини-FAQ

Нужно ли всегда отвечать 200?
Любая 2xx засчитывается как успех. 202/204 — нормальная практика для «принято в очередь».

Можно ли остановить повторы?
Да, ответом 410 и/или через консоль/API отправителя (отключение подписки).

Как быть с большими payload’ами?
Отправляйте «уведомление + secure URL», подпишите запрос на скачивание и установите TTL.

Как обеспечить порядок?
Partition by key + `sequence`; при расхождении — «parking lot» и переигрывание.

Итог

Надежные вебхуки — это четкая семантика ACK (2xx), разумные повторы с backoff+jitter, строгая идемпотентность и дедупликация, грамотная безопасность (HMAC/mTLS), очереди + DLQ + реплей, и прозрачная наблюдаемость. Зафиксируйте контракт, введите лимиты и метрики, регулярно прогоняйте хаос-сценарии — и ваши интеграции перестанут «сыпаться» при первых же сбоях.

Contact

Свяжитесь с нами

Обращайтесь по любым вопросам или за поддержкой.Мы всегда готовы помочь!

Начать интеграцию

Email — обязателен. Telegram или WhatsApp — по желанию.

Ваше имя необязательно
Email необязательно
Тема необязательно
Сообщение необязательно
Telegram необязательно
@
Если укажете Telegram — мы ответим и там, в дополнение к Email.
WhatsApp необязательно
Формат: +код страны и номер (например, +380XXXXXXXXX).

Нажимая кнопку, вы соглашаетесь на обработку данных.