Webhooky v produkcii: podpis, duplicity a obnova po chybe
Ako oddeliť prijatie udalosti od jej spracovania, udržať kontrolu nad duplicitami a opraviť prenos bez opakovania obchodných účinkov.

Prijatá udalosť ešte nie je vybavená
Platobný systém oznámi úspešnú úhradu. Endpoint vráti HTTP 200 a až potom vloží úlohu do fronty. Medzi odpoveďou a zápisom sa proces ukončí. Poskytovateľ už dostal potvrdenie, ale objednávka čaká na spracovanie, ktoré sa nikdy nezačalo. V opačnom poradí, keď endpoint pred odpoveďou vykoná dlhý proces, môže poskytovateľ považovať pomalú odpoveď za zlyhanie a udalosť doručiť znova.
Príjem preto oddeľte od obchodného spracovania. V navrhovanom modeli endpoint overí požiadavku, trvalo uloží udalosť do databázovej schránky inbox a až po úspešnom commite pošle dohodnutú odpoveď. Samostatný worker následne vykoná prácu. Potvrdenie tu znamená „prevzali sme zodpovednosť za ďalšie spracovanie“, nie „objednávka už bola vybavená“.
Konkrétny limit odpovede určuje poskytovateľ. GitHub napríklad dokumentuje odpoveď 2XX do desiatich sekúnd a odporúča dlhšiu prácu presunúť do fronty. Iné rozhranie môže mať odlišný limit aj pravidlá opakovania. Návrh musí vychádzať z jeho dokumentácie, nie zo spoločnej konštanty pre všetky webhooky.
Overte podpis pred dôverou v obsah
Verejnú URL môže zavolať aj niekto mimo poskytovateľa. Pred obchodnou zmenou preto overte podpis podľa jeho protokolu, obmedzte veľkosť vstupu a skontrolujte podporovaný typ udalosti. Podpis overuje pôvod a integritu správy v rámci daného mechanizmu; nenahrádza pravidlá, podľa ktorých smie konkrétny účet meniť konkrétnu objednávku.
Stripe vyžaduje pri overovaní pôvodné telo požiadavky. Ak middleware najprv prečíta JSON a znovu ho serializuje, môže zmeniť bajty, nad ktorými vznikol podpis. Použite podporovanú knižnicu a uchovajte raw body pre overenie. Spravujte tajomstvo mimo kódu a rozlišujte testovacie a produkčné endpointy. Časovú toleranciu či rotáciu tajomstiev nastavte podľa poskytovateľa.
V tomto návrhu sa neplatne podpísaná správa nedostane do pracovného inboxu. Diagnostika si môže uložiť bezpečný dôvod odmietnutia a čas prijatia, nemá však vypisovať tajomstvo ani kompletný obsah platobných údajov. Test podpisu musí zahŕňať zmenu jediného bajtu aj cestu cez reálny middleware; úspech izolovanej knižničnej funkcie nestačí.
Inbox potrebuje stabilnú identitu udalosti
Rozlišujte udalosť a pokus o jej doručenie. Jeden obchodný záznam môže mať viac rôznych udalostí a jedna udalosť viac pokusov. Identifikátor objednávky preto nie je vhodným univerzálnym kľúčom na deduplikáciu. V navrhovanej schránke použite kombináciu vlastníka integrácie, zdroja a stabilného identifikátora udalosti podľa kontraktu poskytovateľa.
CloudEvents definuje identitu pomocou source a id. GitHub zachováva X-GitHub-Delivery aj pri požiadanej opätovnej dodávke. Tieto príklady ukazujú, prečo treba overiť rozsah a stabilitu konkrétneho identifikátora. Ak zdroj identitu neposkytuje, nemožno ju spoľahlivo nahradiť iba časom prijatia; návrh potrebuje pravidlá podľa jeho obchodného modelu.
Ukážková tabuľka uchováva payload, stav a počet pokusov. Primárny kľúč chráni identitu aj pri viacerých procesoch. Endpoint vloží platnú udalosť atomicky; pri konflikte potvrdí už existujúcu udalosť bez vytvorenia ďalšej úlohy. Ak rovnaké ID nesie iný obsah, zaznamená odchýlku a postupuje podľa pravidiel poskytovateľa. Retenciu payloadu aj záznamu identity treba dohodnúť samostatne.
CREATE TABLE webhook_inbox (
tenant_id bigint NOT NULL,
source text NOT NULL,
event_id text NOT NULL,
payload jsonb NOT NULL,
received_at timestamptz NOT NULL DEFAULT now(),
state text NOT NULL DEFAULT 'pending'
CHECK (state IN ('pending', 'processing', 'done', 'failed')),
attempts integer NOT NULL DEFAULT 0 CHECK (attempts >= 0),
next_attempt_at timestamptz NOT NULL DEFAULT now(),
PRIMARY KEY (tenant_id, source, event_id)
);Worker musí vedieť pokračovať po páde
Najjednoduchší worker pre krátke databázové účinky zamkne vybraný záznam, vykoná obchodnú zmenu a označí udalosť ako hotovú v jednej transakcii. Ak proces pred commitom spadne, transakcia sa vráti späť a udalosť zostane dostupná. Ak commit prebehne, ďalší worker uvidí hotový stav. Tým sa chráni konkrétna lokálna zmena; nevzniká tým všeobecná garancia doručenia „exactly once“.
Dlhšia práca môže používať rezerváciu s časom expirácie, teda lease. Jeho implementácia potrebuje vlastnú identitu vlastníka a pravidlo, kto smie výsledok potvrdiť po vypršaní rezervácie. Samotné nastavenie processing môže udalosť po páde zaseknúť navždy. Ukážkový inbox vyššie je základ schémy; pri lease režime treba doplniť tieto polia aj ochranu proti súbehu.
Prechodnú chybu worker naplánuje na neskorší pokus s odstupom. Chybu formátu či chýbajúce mapovanie odovzdá na kontrolu po dohodnutom limite. Operátor potrebuje vidieť pôvodnú udalosť, bezpečný dôvod zlyhania a už vykonané kroky. Oprava mapovania a riadené zopakovanie jednej udalosti bývajú užitočnejšie než ručné prehratie všetkého za celý deň.
Externý účinok prekračuje databázovú transakciu
Ak worker odošle správu do ERP a následne označí inbox ako hotový, môže spadnúť medzi týmito krokmi. Pri opakovaní odošle správu znova. Opačné poradie môže správu stratiť. Databázová transakcia nevie atomicky potvrdiť vlastný commit aj cudzie HTTP volanie bez osobitného distribuovaného protokolu.
Pre navrhovaný proces možno uložiť lokálnu obchodnú zmenu a položku outboxu v jednej transakcii. Ďalší proces odošle položku do cieľa a opakuje ju pod rovnakým identifikátorom operácie. AWS pri transactional outbox upozorňuje aj na možné duplicity pri publikovaní; príjemca stále potrebuje idempotentné spracovanie. Outbox presunie hranicu zodpovednosti, neodstráni ju.
Ak cieľ nepodporuje idempotenciu ani zistenie výsledku podľa stabilného identifikátora, po timeoute môže zostať nejasný stav. Vtedy treba zaviesť kontrolu v cieľovom systéme, schvaľovanie alebo iný obchodný postup. Automatické nekonečné opakovanie by mohlo vyrábať ďalšie rezervácie či dokumenty. Tento limit má byť známy ešte pred sľúbením úplnej automatizácie.
Poradie doručenia neurčuje stav objednávky
Stripe nezaručuje poradie doručenia udalostí. Pri inom poskytovateľovi ho overte osobitne. Predstavte si, že najprv príde „objednávka vybavená“ a až potom staršia správa „objednávka vytvorená“. Slepé prepísanie stavu posledným doručeným payloadom môže hotovú objednávku vrátiť na začiatok procesu.
Riešenie závisí od dostupného kontraktu. Ak zdroj poskytuje použiteľnú verziu záznamu, porovnávajte ju podľa jeho pravidiel. Ak poskytuje API aktuálneho stavu, udalosť môže byť podnetom na nové načítanie. Ak máte iba jednotlivé fakty, navrhnite povolené stavové prechody a riešenie chýbajúcej predchádzajúcej udalosti. Čas prijatia ani bežný timestamp automaticky neposkytujú spoľahlivé globálne poradie.
Oddelene riešte dve rôzne udalosti o tom istom obchodnom účinku. Deduplikácia event_id zachytí opakovanú dodávku, nemusí však zachytiť dva odlišné eventy, ktoré spúšťajú rovnaké vybavenie. Obchodné pravidlo môže vyžadovať ďalší unikátny identifikátor vybavenia alebo podmienený prechod stavu v databáze. Toto pravidlo treba odvodiť z významu udalostí, nie z podobnosti ich JSON dokumentov.
Prevádzka potrebuje dohľadateľné výnimky
Sledujte čas najstaršej čakajúcej udalosti, počet nevybavených záznamov a opakované chyby podľa zdroja. Samotný počet HTTP 200 nevypovedá o tom, či worker spracúva objednávky. Pre replay uchovajte potrebnú identitu aj po odstránení citlivého payloadu. Prístup k detailom a ručnému opakovaniu musí zodpovedať oprávneniam obsluhy.
Testovacia sada má kombinovať chyby príjmu, pády workerov a problematické obchodné prechody. Očakávaný výsledok určte pre každý scenár vopred. Pri strate odpovede po uložení inboxu má ďalšia dodávka potvrdiť ten istý záznam; pri strate spojenia pred uložením nesmie endpoint predstierať prevzatie zodpovednosti.
- Neplatný podpis alebo pozmenený payload nespustí obchodnú zmenu.
- Súbežné doručenie rovnakého event_id vytvorí jeden záznam inboxu.
- Pád pred commitom lokálnej zmeny umožní ďalší pokus bez čiastočného výsledku.
- Doručenie v opačnom poradí nevráti objednávku do neplatného staršieho stavu.
- Pád po vzdialenom úspechu preverí idempotenciu cieľa alebo dohodnutú ručnú kontrolu.
- Replay po oprave chyby zachová identitu udalosti a zaznamená rozhodnutie operátora.
Zdroje a dokumentácia
Pri implementácii skontrolujte dokumentáciu verzie, ktorú používate.
Od témy ku konkrétnemu riešeniu.
Súvisiaca realizácia: FaxCopy a.s.
