Wolt integrace — Technická dokumentace
Podrobný popis architektury, datových toků a implementačních detailů napojení na Wolt platformu.
Architektura
Komponenty
Worker
Cloudflare Workers + Hono
worker/src/wolt.ts
Zpracování webhooků, Wolt API volání, správa objednávek
Webhook middleware
Cloudflare Workers + Hono
worker/src/index.ts
HMAC-SHA256 ověření webhook podpisů
Cron worker
Cloudflare Cron Triggers
worker/src/scheduled/reservations.ts
Uvolnění prošlých rezervací
KV Store
Cloudflare KV
—
Bezpečné úložiště OAuth tokenů
MQTT Bridge
Node.js
docker/mqtt-bridge/src/services/reservation.service.ts
Polling pending rezervací, odesílání MQTT příkazů
Contember
GraphQL + PostgreSQL
api/model/
Datový model (Machine, Product, Inventory, Cart)
Autentizace
Webhook autentizace (Wolt → Worker)
Wolt podepisuje každý webhook request pomocí HMAC-SHA256. Podpis je v hlavičce wolt-signature jako hex string.
Implementace (worker/src/index.ts):
Worker přečte raw body requestu
Dekóduje hex podpis z hlavičky
wolt-signaturenaUint8ArrayImportuje
WOLT_WEBHOOK_SECRETjako HMAC klíč přescrypto.subtle.importKeyOvěří podpis přes
crypto.subtle.verify(timing-safe)Při nevalidním podpisu vrací
401 Unauthorized
API autentizace (Worker → Wolt)
Worker volá Wolt API s Bearer tokenem (OAuth 2.0 Authorization Code flow).
Token lifecycle:
Počáteční získání: Při OAuth onboardingu (
GET /wolt/redirect?code=...) se authorization code vymění za access + refresh tokenÚložiště: Tokeny se ukládají do Cloudflare KV pod klíčem
wolt:tokens:<venue_id>Auto-refresh: Při každém API volání se zkontroluje expirace. Pokud do expirace zbývá méně než 5 minut, provede se refresh:
Venue ID z JWT: Access token je JWT —
venue_idse extrahuje z payloadu (integration.venue_id)
KV klíčová struktura:
Webhook endpoint
POST /wolt/webhook/orders
Request (od Wolt)
Zpracování
Parsování JSON z pre-read raw body (middleware)
Ignorování jiných typů než
order.notificationZískání access tokenu z KV (s auto-refresh)
Fetch kompletní objednávky z Wolt API:
GET /v2/orders/{orderId}Routing dle
status:
CREATED
Zpracování objednávky (viz Order processing)
PRODUCTION
Log (objednávka se připravuje)
READY
Log (připravena k vyzvednutí)
DELIVERED
Log (doručena)
CANCELED
Log (zrušena)
Response
Vždy vrací 200 s { "received": true } — Wolt neočekává specifickou response.
Order processing (status = CREATED)
Hlavní business logika při přijetí nové objednávky.
Detailní postup
1. Nalezení automatu
Pokud automat neexistuje → objednávka se odmítne (PUT /orders/{id}/reject).
2. Kontrola dostupnosti
Pro každou položku objednávky se dotáže na produkt dle SKU a jeho dostupný inventory:
Inventory je řazeno
createdAt: asc(FIFO — nejstarší kusy se rezervují první)Kontroluje se, zda
availableCount >= requestedCount
3. Nedostatek zásob
Pokud jakýkoliv produkt není skladem:
Vytvoří se Cart se statusem
cancelled(pro evidenci)Objednávka se odmítne na Wolt API
4. Vytvoření objednávky
Pokud je vše skladem:
Vygeneruje se 6místný numerický
pickupCodeVypočítá se
reservedUntil:Pokud Wolt poskytne
pickup_eta→ ETA + 30 minutJinak fallback: aktuální čas + 1 hodina
Vytvoří se Cart (
status: in_process,channel: remote)
5. Rezervace inventory
Po vytvoření Cartu:
Dotaz na cart items (s product ID) pro mapování
Batch mutace — pro každý inventory item:
6. Potvrzení na Wolt
Standardní objednávka:
PUT /orders/{id}/acceptPreorder:
PUT /orders/{id}/confirm-preorderIhned poté:
PUT /orders/{id}/ready(automat nevyžaduje přípravu)
Inventární rezervace
Datový model
Entita Inventory obsahuje pole pro rezervační systém:
status
Enum
available, reserved, vended, removed, expired
reservedAt
DateTime
Kdy byla rezervace vytvořena
reservedUntil
DateTime
Do kdy rezervace platí
reservedBy
Relace → CartItem
Která položka objednávky drží rezervaci
Životní cyklus
FIFO princip
Při rezervaci se vybírají nejstarší dostupné kusy (orderBy: [{ createdAt: asc }]). Tím se minimalizuje riziko prošlé expirace u nevyzvednutých položek.
MQTT synchronizace do Android aplikace
Mechanismus
MQTT Bridge (Node.js service) každých 5 sekund polluje Contember na čekající rezervace:
ORDER_RESERVE příkaz
Po nalezení čekajících cartů se odešle MQTT příkaz na topic machines/<serialNumber>/commands:
command
String
Vždy ORDER_RESERVE
orderId
UUID
ID cartu v Contember
orderCode
String
6místný pickup code
reservedUntil
ISO 8601
Expirace rezervace
items[].sku
String
SKU produktu
items[].name
String
Název produktu
items[].quantity
Number
Objednaný počet kusů
items[].slots
String[]
Kódy slotů, kde jsou kusy rezervovány
Potvrzení odeslání
Po úspěšném MQTT publish:
Cart.reservationSentAtse nastaví na aktuální časVytvoří se MqttLog záznam (outbound)
Při chybě:
Cart.reservationSentAtse nastaví (aby se neposílalo opakovaně)Cart.reservationErrorse vyplní popisem chyby
Expirace rezervací (Cron Worker)
Cloudflare Cron Trigger běží každých 5 minut a uvolňuje prošlé rezervace.
Konfigurace
worker/wrangler.toml:
Postup
Query na inventory kde
status = reservedareservedUntil < now(limit 100)Batch mutace — pro každý záznam:
status→availablereservedAt→nullreservedUntil→nullreservedBy→ disconnect
Log pro každý uvolněný záznam (SKU, automat, cart ID)
Scénáře
Zákazník vyzvedne včas
Android pošle sale event → inventory přejde na vended (cron ho nechá)
Zákazník nevyzvedne
Cron uvolní reserved → available (produkt se vrátí do prodeje)
Wolt objednávka zrušena
Cron uvolní (pokud Wolt nestihne zrušit před expirací)
Datový model
Machine
Cart (objednávka)
CartItem (položka objednávky)
Inventory (skladová položka)
Wolt API endpointy
Base URL (development): https://pos-integration-service.development.dev.woltapi.com
Token URL: https://integrations-authentication-service.development.dev.woltapi.com/oauth2/token
POST
/oauth2/token
Výměna auth code / refresh tokenu
Basic (client_id:secret)
GET
/v2/orders/{orderId}
Kompletní detail objednávky
Bearer token
PUT
/orders/{orderId}/accept
Přijetí standardní objednávky
Bearer token
PUT
/orders/{orderId}/confirm-preorder
Potvrzení preorderu
Bearer token
PUT
/orders/{orderId}/reject
Odmítnutí objednávky
Bearer token
PUT
/orders/{orderId}/ready
Označení jako připravená k vyzvednutí
Bearer token
Logging a debugging
Cloudflare Workers (real-time logs)
Všechny operace se logují s prefixem [WOLT]:
[WOLT WEBHOOK RAW REQUEST]
Kompletní raw request (headers + body)
[WOLT WEBHOOK] Notification:
Parsovaná notifikace
[WOLT WEBHOOK] Full order:
Kompletní objednávka z Wolt API
[WOLT] Out of stock: SKU "..."
Produkt není skladem
[WOLT] Order #... accepted + ready
Úspěšné zpracování
[WOLT] Order #... rejected
Objednávka odmítnuta
[WOLT] Reserved N inventory items
Inventory rezervováno
[WOLT] Token refresh failed
Selhání obnovení OAuth tokenu
[CRON] Releasing N expired reservations
Cron uvolňuje prošlé rezervace
MQTT Bridge logs
[RESERVATION] Found N pending reservation(s)
Nalezeny čekající rezervace
[RESERVATION] ORDER_RESERVE sent for cart ...
MQTT příkaz odeslán
[RESERVATION] Cart ...: <error>
Chyba při odesílání
Contember (MqttLog)
Odchozí MQTT příkazy se ukládají do tabulky MqttLog s payload prefixem [outbound].
Soubory a struktura kódu
Last updated