17. júna 2026, Luboš Zápotočný
Webhooky sa opakujú. Vaša integrácia s tým musí rátať.
Platobné a platformové webhooky sa v okne na opakovanie doručujú aspoň raz, nie presne raz. Čo dvojité doručenie pokazí a ako ho idempotentné handlery absorbujú.
Prečítajte si dokumentáciu webhookov ktoréhokoľvek seriózneho
poskytovateľa a nájdete v tej či onej podobe to isté varovanie. Stripe
vám hovorí, aby ste si zaznamenávali ID spracovaných udalostí, pretože
endpoint môže tú istú udalosť dostať viac než raz. Adyen hovorí, aby
ste tú istú udalosť čakali dvakrát a duplikáty ošetrili. Shopify vám
dáva hlavičku X-Shopify-Webhook-Id, podľa ktorej sa dá
deduplikovať, a rovno uvádza, že doručenie nie je zaručené vôbec.
Všetky tri overené 13. júla 2026:
Stripe,
Adyen,
Shopify.
V rámci okna na opakovanie je kontrakt at-least-once, nie exactly-once. Ak váš endpoint zostane celé okno nedostupný, udalosť nepríde ani raz, a preto je pod všetkým ostatným záchytnou poistkou rekonciliačná úloha, ktorá si stav doťahuje z API poskytovateľa. Doručiť udalosť cez nespoľahlivú sieť presne raz sa nedá, takže poskytovatelia volia bezpečnú možnosť: keď si nie sú istí, či ste udalosť dostali, pošlú ju znova.
Väčšina integrácií je napísaná, akoby toto pravidlo neexistovalo.
Fungujú v deme, fungujú prvý mesiac, a potom niekde medzi
poskytovateľom a odpoveďou 200 nastane timeout a tá istá udalosť
order.paid príde dvakrát.
Čo dvojité doručenie naozaj pokazí
- Zákazník dostane dve potvrdenia objednávky.
- Zásoby sa odpočítajú dvakrát a začnú sa objavovať nesprávne „vypredané“.
- Faktúra sa vystaví dvakrát a účtovníctvo to nájde až o niekoľko mesiacov.
- Vernostný kredit sa pripíše dvakrát a nikto si to nevšimne, kým čísla neprestanú sedieť.
Poskytovateľ urobil to, čo má v dokumentácii. Handler sa spoliehal na záruku, ktorú poskytovateľ nikdy nedal.
Riešením je idempotencia
Idempotentný handler vyprodukuje rovnaký koncový stav bez ohľadu na to, či udalosť spracuje raz alebo päťkrát. Táto jediná vlastnosť absorbuje opakovania, manuálne replaye a celú triedu race conditions. Dosiahnuť ju je predovšetkým otázkou disciplíny:
- Každej udalosti dajte kľúč. Poskytovatelia posielajú ID udalosti; ak ho ten váš neposiela, odvoďte si ho zo stabilných polí. Spracované ID si zaznamenajte a duplikáty preskočte. Vynucujte to unique constraintom v databáze, nie aplikačným kódom: kontrola v aplikácii má race window, constraint nie.
- Uprednostnite absolútny stav pred relatívnymi zmenami. „Nastav status na zaplatené“ je prirodzene idempotentné. „Odpočítaj zo zásob 2“ nie je. Keď poskytovateľ posiela delty, prekladajte ich na upserty proti vlastnému záznamu udalosti, nie na nepodmienenú aritmetiku.
- Rátajte aj s prehodeným poradím. Opakovania prichádzajú aj mimo
poradia:
order.updatedmôže doraziť predorder.created. Čísla verzií alebo časové pečiatky na cieľovom zázname („aplikuj, iba ak je novšie“) zvládnu to, čo predpoklady o poradí nezvládnu.
Zvyšok checklistu
Idempotencia je jadro. Tri návyky okolo nej pokrývajú zvyšok:
- Potvrďte rýchlo, spracujte asynchrónne. Overte podpis, udalosť uložte, vráťte 200 a skutočnú prácu robte z fronty. Pomalé synchrónne handlery spôsobujú timeouty a timeouty spôsobujú práve tie opakovania, ktorých ste sa obávali.
- Autentifikujte každý webhook. Webhook endpoint je neautentifikovaná URL na verejnom internete, ktorá mení stav vášho biznisu. Väčšina poskytovateľov podpíše payload pomocou HMAC, ktorý si prepočítate a porovnáte. Niektorí payload neposielajú vôbec: notifikácia GoPay je HTTP GET s ID platby a smerodajný stav treba načítať späť z API GoPay, až potom podľa neho konať (GoPay, overené 13. júla 2026). Tak či onak, nikdy nekonajte na základe požiadavky v podobe, v akej prišla.
- Majte dead-letter queue s nástrojom na replay. Niektoré udalosti zlyhajú z dôvodov, ktoré opakovanie nevyrieši: zmazané SKU, diera v mapovaní. Odkladajte ich tam, upozorňujte na ne a umožnite prehratie jednej udalosti bez SSH na produkciu. Keď má poskytovateľ niekoľkohodinový výpadok, replay vám umožní zameškané udalosti dobehnúť bez vyhlasovania incidentu.
Jediný test pokryje všetko uvedené: vezmite včerajšie udalosti zo stagingu a každú z nich doručte dvakrát, v premiešanom poradí. Ak koncový stav sedí s produkciou, vaše handlery sú naozaj idempotentné.
Toto je štandard, podľa ktorého integrácie staviame. Je to väčšina toho, čo opisuje naša stránka o integráciách, a rovnaké uvažovanie sa objavuje v našom zákazkovom backendovom vývoji. A ak sa vaše čísla zásob už rozchádzajú, webhooková vrstva je jedným z prvých miest, kde hľadať.