# PPL CPL API — přehled služby Stateless multi-tenant proxy nad PPL CPL API (Create Package Label). Upstream: `https://api.dhl.com/ecs/ppl/myapi2` (production), `https://api-dev.dhl.com/ecs/ppl/myapi2` (test). Dokumentace PPL: https://ppl-cpl-api.apidog.io/ ## Autentizace Každý request nese hlavičky: - `X-Client-Id` — PPL ClientId (secret) - `X-Client-Secret` — PPL ClientSecret (secret) - `X-Environment` — `production` (default) nebo `test` - `Accept-Language` — volitelně, předává se do PPL (např. `cs-CZ`) Služba z údajů získá OAuth Bearer token (`POST {base}/login/getAccessToken`, grant `client_credentials`, scope `myapi2`) a **cachuje ho in-memory** pod SHA-256 hashem údajů. Důvod: PPL vydá max. 12 tokenů/min a token platí 30 minut. Token se obnovuje 60 s před expirací; na HTTP 401 z PPL se jednou obnoví a request se zopakuje. ## Limity PPL, které služba respektuje - min. 40 ms rozestup mezi requesty (globální throttle v procesu), - max. 1000 zásilek / 100 objednávek v jednom batchi, - batchId platí 30 dní, - pouze Latin znaky bez diakritiky — viz transliterace níže. ## Transliterace diakritiky CPL API odmítá texty s diakritikou. Těla `POST /shipments/batch`, `/shipments/create-and-wait`, `/orders/batch` a `/orders/create-and-wait` se defaultně rekurzivně transliterují přes unidecode (`Jiří Dvořák` → `Jiri Dvorak`). Chování: - query parametr `transliterate=true|false` na requestu má přednost, - jinak platí env `CPL_TRANSLITERATE` (default `true`), - generická `/proxy/...` NEtransliteruje nikdy (předává 1:1). ## Chybové odpovědi Jednotný JSON `{error, message, detail}`; `detail` obsahuje původní problem+json z PPL (typ, title, errors...). | Status | Význam | |---|---| | 400 | validační chyba (vstup zde nebo v PPL) | | 401 | chybějící X- hlavičky nebo PPL odmítlo údaje/token | | 403 | chybí oprávnění (role) k metodě v PPL | | 404 | batch/zásilka/objednávka neexistuje | | 429 | rate limit PPL | | 502 | výpadek / neočekávaná chyba PPL | Všechny chyby se logují (bez secrets) — žádná tichá selhání. ## Konfigurace (environment variables) | Proměnná | Default | Význam | |---|---|---| | `ROOT_PATH` | `""` | prefix za reverse proxy (`/apps/pplcplapi`) | | `CPL_DEFAULT_ENVIRONMENT` | `production` | prostředí bez hlavičky X-Environment | | `CPL_PRODUCTION_BASE_URL` / `CPL_TEST_BASE_URL` | viz výše | base URL upstreamu | | `CPL_OAUTH_SCOPE` | `myapi2` | OAuth scope | | `CPL_TRANSLITERATE` | `true` | default transliterace diakritiky | | `UPSTREAM_TIMEOUT_SECONDS` | `60` | timeout volání PPL | | `TOKEN_REFRESH_MARGIN_SECONDS` | `60` | předstih obnovy tokenu | | `MIN_REQUEST_INTERVAL_SECONDS` | `0.04` | rozestup requestů na PPL | | `BATCH_POLL_INTERVAL_SECONDS` | `1.0` | interval pollingu create-and-wait | | `BATCH_WAIT_TIMEOUT_SECONDS` | `30` | default timeout create-and-wait | | `LOG_LEVEL` | `INFO` | úroveň logování | Žádné secrets se nekonfigurují přes env — vše chodí per-request v hlavičkách. ## Struktura kódu ``` app/ main.py — FastAPI aplikace, root_path, popis pro Swagger config.py — env konfigurace credentials.py — X- hlavičky -> Credentials (dependency) token_cache.py — in-memory cache OAuth tokenů cpl_client.py — autentizovaný httpx klient, throttle, relay helpery transliterate.py — rekurzivní unidecode JSON těl errors.py — typované chyby + handlery routers/ meta.py — /health, /version shipments.py — zásilky, etikety, tracking, storno, celní dokumenty orders.py — objednávky svozu/přepravy codelists.py — číselníky, /version-information, /cpl-info lookups.py — /access-points, /address-whisper, /routing, /data/{guid} customer.py — /customer, /customer/addresses, /customer/number-range proxy.py — generická proxy /proxy/{cesta} ```