Inquir Compute · вебхуки

Платформа повторов вебхуков: пережить повторные доставки провайдера

Stripe, GitHub и Slack повторяют неудачные вебхуки часами и днями. Нужны три слоя: отсечь подделки, подтвердить в лимите провайдера и повторить downstream-работу без дублирования эффектов. Inquir даёт идемпотентность, ретраи пайплайна и трассировки каждой доставки.

Обновлено: 2026-06-28

Суть ответа

Платформа повторов вебхуков: пережить повторные доставки провайдера. Функция вебхука проверяет подпись, пишет event ID провайдера в durable-хранилище, сразу возвращает 200 и ставит работу в пайплайн. Повторы провайдера упираются в idempotency-check и получают 200 без re-processing.

Когда подходит и когда нет

  • Вебхуки Stripe, GitHub, Slack, Shopify или HubSpot с агрессивными политиками повторных доставок
  • Downstream-работа может упасть уже после того, как провайдеру ответили 200

На что обратить внимание

  • Проверка «видел ли я этот event ID?» в памяти процесса не переживает рестарт и горизонтальное масштабирование. Нужны durable-ключи идемпотентности, записанные до любого побочного эффекта.
  • Повтор всего webhook-handler при сбое downstream снова гоняет верификацию подписи и рискует double-processing, если первая попытка частично успела.

Два вида повторов вебхуков — оба больно игнорировать

Повторы провайдера: Stripe до 72 часов при non-2xx или таймауте. GitHub — до 3 дней. Каждая redelivery — новый HTTP с тем же event ID; дубликаты нужно ловить до мутаций.

Ретраи downstream: хендлер уже ответил 200, но fulfillment API упал. Без платформы повторов это тихий сбой или ручной replay из логов. Ретраи шагов пайплайна решают это без повторного вызова со стороны провайдера.

Почему самописные ретраи ломаются в масштабе

Проверка «видел ли я этот event ID?» в памяти процесса не переживает рестарт и горизонтальное масштабирование. Нужны durable-ключи идемпотентности, записанные до любого побочного эффекта.

Повтор всего webhook-handler при сбое downstream снова гоняет верификацию подписи и рискует double-processing, если первая попытка частично успела.

Идемпотентный ingress + retriable-пайплайны

Функция вебхука проверяет подпись, пишет event ID провайдера в durable-хранилище, сразу возвращает 200 и ставит работу в пайплайн. Повторы провайдера упираются в idempotency-check и получают 200 без re-processing.

Шаги пайплайна повторяются независимо с exponential backoff. Трассировки показывают каждую доставку, каждый ретрай шага и итог — дежурный отвечает на вопрос «обработано ли событие?» за секунды.

Возможности платформы повторов вебхуков

Идемпотентность провайдера

Upsert event ID до мутаций. Дубликаты Stripe, GitHub, Slack — 200 без побочных эффектов.

Быстрый ACK под давлением таймаута

200 в окне Slack 3 с, лимите Stripe 30 с и ожиданиях GitHub — тяжёлое в пайплайнах.

Ретраи шагов downstream

Шаги пайплайна повторяют упавшие вызовы API, записи в БД и нотификации без повторного вызова ingress-функции.

Трассировки на доставку

Заголовки, тайминги, число ретраев и выходы шагов для каждой доставки — не чёрный ящик воркера.

Как собрать платформу повторов вебхуков на Inquir

Разделите повторные доставки провайдера (идемпотентность на ingress) и ретраи downstream (политика шагов пайплайна).

1

Проверить и записать event ID

HMAC по сырому body, upsert event ID провайдера, 200 — даже при duplicate delivery.

2

Поставить durable-работу

global.durable.startNew() с разобранным payload. HTTP завершается до начала downstream.

3

Повторять шаги, а не вебхук

Настройте политику ретраев на шагах пайплайна для downstream-сбоев. Завершённые шаги не перезапускаются при падении следующего.

Идемпотентный ingress вебхука с передачей в пайплайн

Повторные доставки провайдера упираются в проверку идемпотентности. Сбои downstream повторяются на уровне шага пайплайна.

webhooks/stripe-retry-safe.mjs
export async function handler(event) {
  const rawBody = event.body ?? '';
  if (!verifyStripeSignature(rawBody, event.headers['stripe-signature'])) {
    return { statusCode: 400, body: 'invalid signature' };
  }
  const evt = JSON.parse(rawBody);
  const isNew = await db.tryInsertWebhookEvent(evt.id, evt.type);
  if (!isNew) return { statusCode: 200, body: 'already processed' };
  await global.durable.startNew('stripe-fulfillment', undefined, {
    eventId: evt.id, type: evt.type, object: evt.data.object,
  });
  return { statusCode: 200, body: 'accepted' };
}

Когда нужна платформа повторов вебхуков

Когда это уместно

  • Вебхуки Stripe, GitHub, Slack, Shopify или HubSpot с агрессивными политиками повторных доставок
  • Downstream-работа может упасть уже после того, как провайдеру ответили 200

Когда лучше выбрать другое

  • Вебхуки уходят в чужую iPaaS без своей среды выполнения

Вопросы и ответы

Как долго провайдеры повторяют?

Stripe: до 72 ч с backoff. GitHub: до 3 дней. Slack ждёт ответ ~3 с. Закладывайте быстрый ACK и duplicate delivery.

Что, если шаг исчерпал ретраи?

Прогон пайплайна помечается failed с полными логами. Настройте алерт по failure rate и делайте ручной replay из истории по сохранённому payload.

Нужна отдельная dead-letter очередь?

Failed-прогоны пайплайна — DLQ с поиском в истории. Отдельную инфраструктуру DLQ поднимать не нужно.