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.