Wolt integrace — Technická dokumentace

Podrobný popis architektury, datových toků a implementačních detailů napojení na Wolt platformu.


Architektura

Komponenty

Komponenta
Technologie
Umístění
Účel

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):

  1. Worker přečte raw body requestu

  2. Dekóduje hex podpis z hlavičky wolt-signature na Uint8Array

  3. Importuje WOLT_WEBHOOK_SECRET jako HMAC klíč přes crypto.subtle.importKey

  4. Ověří podpis přes crypto.subtle.verify (timing-safe)

  5. 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:

  1. Počáteční získání: Při OAuth onboardingu (GET /wolt/redirect?code=...) se authorization code vymění za access + refresh token

  2. Úložiště: Tokeny se ukládají do Cloudflare KV pod klíčem wolt:tokens:<venue_id>

  3. 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:

  4. Venue ID z JWT: Access token je JWT — venue_id se extrahuje z payloadu (integration.venue_id)

KV klíčová struktura:


Webhook endpoint

POST /wolt/webhook/orders

Request (od Wolt)

Zpracování

  1. Parsování JSON z pre-read raw body (middleware)

  2. Ignorování jiných typů než order.notification

  3. Získání access tokenu z KV (s auto-refresh)

  4. Fetch kompletní objednávky z Wolt API: GET /v2/orders/{orderId}

  5. Routing dle status:

Status
Akce

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ý pickupCode

  • Vypočítá se reservedUntil:

    • Pokud Wolt poskytne pickup_eta → ETA + 30 minut

    • Jinak fallback: aktuální čas + 1 hodina

  • Vytvoří se Cart (status: in_process, channel: remote)

5. Rezervace inventory

Po vytvoření Cartu:

  1. Dotaz na cart items (s product ID) pro mapování

  2. Batch mutace — pro každý inventory item:

6. Potvrzení na Wolt

  • Standardní objednávka: PUT /orders/{id}/accept

  • Preorder: PUT /orders/{id}/confirm-preorder

  • Ihned poté: PUT /orders/{id}/ready (automat nevyžaduje přípravu)


Inventární rezervace

Datový model

Entita Inventory obsahuje pole pro rezervační systém:

Pole
Typ
Popis

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:

Pole
Typ
Popis

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.reservationSentAt se nastaví na aktuální čas

  • Vytvoří se MqttLog záznam (outbound)

Při chybě:

  • Cart.reservationSentAt se nastaví (aby se neposílalo opakovaně)

  • Cart.reservationError se 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

  1. Query na inventory kde status = reserved a reservedUntil < now (limit 100)

  2. Batch mutace — pro každý záznam:

    • statusavailable

    • reservedAtnull

    • reservedUntilnull

    • reservedBy → disconnect

  3. Log pro každý uvolněný záznam (SKU, automat, cart ID)

Scénáře

Scénář
Výsledek

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

Metoda
Endpoint
Účel
Autentizace

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]:

Log
Význam

[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

Log
Význam

[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