Realtime synchronizace skladu: jak funguje pod kapotou
„Realtime sklad“ je v praxi řetěz událostí, front a opakování. Ukazujeme, jak ho postavit tak, aby neprodával zboží, které není, a na jaké limity narazíte u Shoptetu, Shopify, WooCommerce a Kauflandu.
Zákazník objedná na Kauflandu poslední kus. O deset minut později si stejný kus objedná jiný zákazník na e-shopu, protože e-shop o prodeji na marketplace ještě neví. Jednu objednávku musíte stornovat, zákazník je naštvaný a marketplace vám to započítá do metrik prodejce.
Tomu se říká overselling a jeho lék se jmenuje realtime synchronizace skladu. Proč na ní záleží z obchodního pohledu, jsme popsali v článku Realtime skladová synchronizace: proč na ní záleží. Tady jdeme pod kapotu: jaké mechanismy „realtime“ tvoří, kde se lámou a na jaké limity narazíte u konkrétních platforem.
Webhooky vs. polling
Existují dva způsoby, jak se systém dozví o změně skladu.
Polling znamená, že se integrace pravidelně ptá: „změnilo se něco?“. Typicky ji spouští cron každých pár minut a přes REST API stahuje zásoby nebo změny od posledního dotazu. Je jednoduchý a spolehlivý, protože nic nemůže „nepřijít“. Nevýhody: zpoždění až o délku intervalu a spotřeba limitů API i ve chvílích, kdy se nic neděje.
[Webhook](/kb/integrace/webhooky) otočí směr. Systém, ve kterém změna nastala, sám zavolá vaši URL a pošle informaci o události. Změna přijde během sekund a API se zbytečně nezatěžuje. Webhooky ale mají svá specifika, která musíte znát:
- Mohou přijít dvakrát. Shopify v dokumentaci výslovně uvádí, že stejný webhook můžete dostat víckrát, například po vypršení časového limitu. Pro rozpoznání duplicit posílá hlavičku X-Shopify-Webhook-Id.
- Mohou se opakovat se zpožděním. Když váš server neodpoví úspěšně, Shopify doručení opakuje, celkem 8krát během 4 hodin.
- Mohou se vypnout. WooCommerce po 5 po sobě jdoucích neúspěšných doručeních webhook deaktivuje a je potřeba ho znovu zapnout. Počet povolených selhání lze upravit filtrem woocommerce_max_webhook_delivery_failures.
- Nemusí obsahovat data. Shoptet u webhooku stock:movement posílá jen identifikátor skladu, kde došlo k pohybu, ne seznam změněných produktů. Konkrétní změny si integrace musí dotáhnout přes API. Shoptet navíc nabízí události stock:inStock, stock:soldOut a stock:minStockSupplyReached, které uvedl jako beta verzi.
Proto v praxi kombinujeme obojí: webhook jako rychlý spouštěč, polling v delším intervalu jako pojistka, která dožene, co webhook ztratil.
Fronta mezi událostí a zápisem
Webhook nikdy nezpracovávejte přímo v okamžiku přijetí. Správný postup:
- Přijměte webhook, ověřte podpis nebo původ a uložte ho do fronty zpráv.
- Okamžitě odpovězte úspěchem (HTTP 200). Odesílatel se tak nepokouší o opakování a nevypne vám webhook.
- Zpracování z fronty běží samostatně: dotáhne aktuální stav z API, spočítá dostupné množství a zapíše ho do cílových kanálů.
- Když cílový kanál neodpovídá nebo vrátí chybu limitu, zpráva se vrátí do fronty a zkusí se později.
Fronta taky vyhlazuje špičky. Hromadný import v ERP může během minuty vyvolat tisíce změn. Bez fronty byste je všechny poslali na marketplace najednou a narazili na limit.
Idempotence: stejná zpráva dvakrát nesmí nic rozbít
Protože webhooky i opakování z fronty mohou doručit stejnou zprávu víckrát, zpracování musí být idempotentní. Stejná operace provedená dvakrát má mít stejný výsledek jako jednou.
Prakticky:
- Zapisujte absolutní stav, ne rozdíl. „Dostupné množství je 7“ je bezpečné poslat dvakrát. „Odečti 1“ při dvojím doručení odečte dva kusy.
- Pamatujte si zpracované zprávy. Ukládejte identifikátor události (u Shopify X-Shopify-Webhook-Id) a duplicitu přeskočte.
- Hlídejte pořadí. Starší zpráva, která dorazí později, nesmí přepsat novější stav. Porovnávejte časové razítko nebo verzi záznamu.
- U vlastních API používejte klíč idempotence. Pro opakovatelné POST požadavky existuje návrh standardu IETF (Internet-Draft) pro hlavičku Idempotency-Key. Klient pošle s požadavkem unikátní klíč a server při opakování se stejným klíčem operaci neprovede podruhé.
Rezervace a overselling
Synchronizace změnu přenáší. Neřeší ale, kolik kusů je vlastně k dispozici. K tomu potřebujete model dostupnosti:
| Veličina | Význam |
|---|---|
| Fyzická zásoba | kusy, které leží ve skladu |
| Rezervováno | kusy v přijatých, ale neexpedovaných objednávkách ze všech kanálů |
| Pojistná rezerva | kusy, které záměrně nenabízíte, například na marketplace |
| Dostupné k prodeji | fyzická zásoba − rezervováno − pojistná rezerva |
Každá objednávka z libovolného kanálu musí okamžitě vytvořit rezervaci v jednom místě, typicky v ERP nebo v middleware. Teprve z dostupného množství se počítá, co se pošle do kanálů. Když rezervace vzniká až při importu objednávky do ERP třeba jednou za 15 minut, je to okno, ve kterém overselling vzniká.
U malých zásob pomáhá pojistná rezerva. Když máte poslední 2 kusy, na marketplace s pomalejší synchronizací můžete ukazovat 0 a prodávat je jen na e-shopu. Jak nastavit sklad napříč kanály obecně, popisujeme v článku Multichannel prodej: jak řídit sklad napříč kanály.
Rate limity: kolik toho platformy snesou
Realtime synchronizace naráží na limity API. Tady jsou konkrétní hodnoty z dokumentace k září 2026. Limity se mění, aktuální stav ověřte v dokumentaci platformy.
| Platforma | Limit | Co z toho plyne |
|---|---|---|
| Shoptet API | max. 50 souběžných spojení z jedné IP a 3 souběžná spojení na jeden token, navíc algoritmus leaky bucket | paralelizujte opatrně, při chybě 429 čekejte podle hlavičky Retry-After |
| Shopify GraphQL Admin API | 100 bodů za sekundu (standardní tarify), 200 u Advanced, 1 000 u Shopify Plus; cena dotazu se počítá podle jeho složitosti | posílejte jen potřebná pole, aktualizace skladu seskupujte |
| Kaufland Marketplace Seller API | 111 požadavků za sekundu na prodejce, podle Kauflandu jde o maximum | u velkých katalogů dávkujte a řaďte do fronty |
| WooCommerce | běží na vašem hostingu, propustnost závisí na serveru a konfiguraci | výkon API a úloh na pozadí je potřeba otestovat |
Shoptet k tomu v dokumentaci výslovně doporučuje odebírat webhooky a stahovat data jen při změně místo pravidelného dotazování. Stav limitu vrací v hlavičce X-RateLimit-Bucket-Filling u každé odpovědi.
Z pohledu návrhu to znamená: fronta s řízenou rychlostí odesílání pro každý kanál zvlášť, respektování Retry-After a posílání jen skutečných změn. Když se dostupné množství nezměnilo, nic neposílejte.
Jak na to prakticky: checklist realtime skladu
- Určete jeden zdroj pravdy pro dostupné množství (ERP, WMS, nebo middleware).
- Rezervaci vytvářejte hned při objednávce z libovolného kanálu.
- Změny zachytávejte webhooky, kontrolní polling nechte běžet v delším intervalu.
- Webhooky ukládejte do fronty a odpovídejte okamžitě.
- Zapisujte absolutní stav, deduplikujte podle ID události a hlídejte pořadí.
- Pro každý kanál nastavte řízenou rychlost podle jeho rate limitu.
- U posledních kusů používejte pojistnou rezervu pro pomalejší kanály.
- Monitorujte délku fronty, chyby 429 a rozdíly stavů mezi systémy.
Realtime synchronizaci skladu stavíme v rámci služby Automatizace a realtime synchronizace, napojení na účetní a skladové systémy v rámci ERP integrací. Když se rozhodujete, jestli realtime vůbec potřebujete, přečtěte si článek Cron joby vs. realtime a o praktickém využití webhooků Webhooky v ecommerce.
Potřebujete s tím pomoct?