Když integrace potřebuje vědět o nové objednávce, jsou dvě cesty: ptát se API dokola, nebo si nechat poslat zprávu, když se něco stane. CMS umí obojí, ale udržitelné je jen jedno.
Tenhle text je pro vývojáře. Pokud hledáte hotové řešení, začněte radši na stránce Notify.
Proč ne polling
Stahovat seznam objednávek každou minutu znamená spotřebovat limit požadavků na otázku, na kterou je odpověď skoro vždycky „nic nového”. U jednoho e-shopu to projde. U dvou set e-shopů je to dvě stě zbytečných volání za minutu — a ve chvíli, kdy objednávka opravdu přijde, máte v horším případě minutu zpoždění.
Webhook otočí směr: platforma zavolá vás.
Registrace
POST /api/webhooks s polem data, kde každá položka má event a url.
Najednou lze zaregistrovat nejvýše 50 webhooků a URL smí mít nejvýše 2000
znaků.
Důležité omezení: jedna URL na jednu událost. Pokus zaregistrovat tutéž
událost podruhé skončí chybou 422 Webhook already exists for this event, takže
registrace musí být idempotentní už u vás — nejdřív GET /api/webhooks, pak
doplnit jen to, co chybí.
Některé události se navzájem vylučují. order:update nelze registrovat spolu
s order:cancel a customer:update spolu s customer:disableOrders nebo
customer:enableOrders; kombinace vrátí 409.
Co přijde
Notifikace je krátký JSON:
{
"eshopId": 222651,
"event": "order:create",
"eventCreated": "2019-01-08T15:13:39+0100",
"eventInstance": "2018000057"
}
eventInstance je identifikátor entity — kód objednávky, GUID produktu,
u hromadných událostí seznam. Tělo entity v notifikaci není. Parametr
sendPayload: "full" existuje, ale platí jen pro události označené jako
payload supported, a v aktuálním OpenAPI taková událost není ani jedna. Detail
objednávky si tedy vždycky dočtete samostatným voláním.
Podpis
Každá notifikace může nést podpisovou hlavičku —
hash_hmac('sha1', rawBody, signatureKey) v hexadecimálním tvaru. Klíč získáte
přes POST /api/webhooks/renew-signature-key, platí per doplněk × e-shop
a každé zavolání vygeneruje nový.
Háček: dokud tenhle endpoint nezavoláte, notifikace podpis nemají. Volejte ho tedy hned po instalaci a klíč si uložte šifrovaně. A ověřujte podpis nad surovým tělem požadavku, ne nad tím, co vypadne z JSON parseru — jakékoli přeskládání objektu podpis rozbije.
Nepodepsanou notifikaci odmítněte. Fail closed, ne fail open.
Čtyři sekundy
Na notifikaci je třeba odpovědět 200 do 4 sekund. Pokud se to nestihne,
CMS to zkusí znovu každých 15 minut, nejvýše třikrát, a pak notifikaci
označí jako inactive — čili je nenávratně pryč. Sedmidenní historii si
prohlédnete přes GET /api/webhooks/notifications.
Z toho plyne jediná možná architektura handleru:
- ověř podpis,
- ulož surovou událost,
- zařaď úlohu do fronty,
- vrať 200.
Nic víc. Žádné volání API vašeho CMS, žádné skládání zprávy, žádné odesílání do Slacku — to všechno patří do workeru. Cíl pod 500 ms, ne „vejdeme se do čtyř sekund”.
Idempotence
CMS neposílá žádné delivery ID. Když tedy notifikace dorazí podruhé — a při opakování dorazí — nemáte podle čeho poznat, že jde o tutéž. Klíč si musíte poskládat sami z toho, co ve zprávě je:
(doplněk, eshopId, event, eventCreated, eventInstance)
Tahle pětice je stabilní napříč opakováními. Uložte ji s unikátním indexem a druhý zápis potichu zahoďte.
Rate limit
API má leaky bucket velikosti 200 kapek s odtokem 10 za sekundu, tedy 600 požadavků za minutu, počítáno per doplněk × e-shop. Jiný doplněk téhož e-shopu má vlastní bucket, takže si navzájem nekonkurujete.
Hlavička X-RateLimit-Bucket-Filling: 130/200 chodí vždycky, Retry-After jen
při plném bucketu — a je to HTTP-date v GMT, ne počet sekund. Kdo ho parsuje
jako číslo, dostane NaN a začne točit requesty naprázdno.
Shrnutí
Webhooky v CMS jsou přímočaré, ale mají čtyři místa, kde se integrace láme:
chybějící podpisový klíč, dlouhý handler, chybějící idempotence a špatně
parsovaný Retry-After. Když tyhle čtyři věci sedí, zbytek je už jen skládání
zprávy.
Přesně tohle řeší sdílený CMS kit v našem monorepu a stojí na tom Notify.