Przejdź do treści

Webhooks w payment gateway — retry policy, HMAC, DLQ. Production checklist

Webhook to najsłabszy punkt każdej integracji płatniczej. Zgubiony event = zamówienie bez wysyłki albo podwójna faktura. Pokazujemy, jak Payment Gateway gwarantuje at-least-once delivery i co musisz zrobić po swojej stronie.

Model dostarczania

Payment Gateway używa at-least-once delivery. Oznacza to: jeśli Twój endpoint zwróci 5xx/timeout, retry. Jeśli zwróci 2xx — jednorazowa dostawa zakończona.

Retry policy: 10 prób, exponential backoff: 5s, 15s, 1min, 5min, 15min, 1h, 3h, 6h, 12h, 24h. Łącznie ~46h okna retry.

Po 10 nieudanych próbach event ląduje w Dead Letter Queue i merchant dostaje alert email + Slack.

Weryfikacja HMAC — must have

Każdy webhook ma nagłówek X-PaymentOrchestrator-Signature = HMAC-SHA256(body, webhook_secret).

Przed przetworzeniem zawsze zweryfikuj: oblicz HMAC z raw body + porównaj. Użyj constant-time compare (np. crypto.timingSafeEqual w Node).

Bez weryfikacji ktoś może wysłać fałszywy payment.captured → Twój system zrealizuje zamówienie bez faktycznej płatności.

Idempotencja po Twojej stronie

At-least-once = czasami dostaniesz ten sam event dwa razy (np. Twój handler zwrócił 2xx, ale Payment Gateway nie zdążył odebrać ACK z sieci).

Każdy event ma unikalny event_id (UUID). Zapisz go w tabeli processed_events z UNIQUE constraint.

W handlerze: INSERT event_id. Jeśli duplicate key — skip. Jeśli OK — przetwarzaj biznes logikę w tej samej transakcji.

Timeout i async processing

Twój endpoint musi zwrócić 2xx w ciągu 5 sekund. Dłużej = retry.

Anti-pattern: synchroniczne przetwarzanie (email, fulfillment) w webhook handlerze. Tak nie.

Wzorzec: webhook → zapisz do kolejki (SQS, Redis, Postgres queue) → zwróć 200 → worker przetwarza async.

Replay i backfill

Każdy event jest dostępny przez API (GET /v1/events/:id) przez 30 dni.

W Payment Gateway panelu masz przycisk Replay — manualnie re-deliver konkretny event lub bulk (np. po incydencie po stronie Twojego API).

Filtrowanie po typie, dacie, statusie — w UI i przez API.

Testowanie lokalne

Webhook endpoint localhost nie jest dostępny publicznie. Użyj payorch listen --forward http://localhost:3000/webhook (nasz CLI) lub ngrok.

Sandbox pozwala triggerować dowolne eventy ręcznie: payment.captured, payment.failed, dispute.opened etc.

Zobacz pełną specyfikację webhooks

Umów rozmowę