The contract for other languages
Everything a PHP, Python or Go implementation needs to talk to Dukkan exactly like the official SDK: the OpenAPI document, the test vectors, the envelope and the config schema.
On this page
This page is for anyone implementing Dukkan support outside Node: a PHP package, a Python client, a Go service. By the end you know which four artifacts pin the contract, where each is published, and how to prove an implementation matches the official SDK byte for byte.
The official @dukkan.one/app-sdk is generated and tested from these same artifacts, so an implementation that reproduces them behaves identically. Nothing here is Node-specific.
The four artifacts#
| Artifact | Published at | Pins |
|---|---|---|
| OpenAPI document | /platform-api-v1.yaml |
Every operation, schema, error, the topic list with required scopes (x-dukkan-topic-scopes), every topic's payload schema (x-dukkan-topic-payloads), the order status matrix (x-dukkan-order-status-transitions), and the dated revision (x-dukkan-api-version). |
| Test vectors | /contracts/v1/index.json |
Expected values for signing, PKCE, canonical JSON, the envelope and the token endpoint's error semantics. |
| Config schema | /contracts/v1/dukkan.app.schema.json |
JSON Schema of dukkan.app.toml, so a CLI in any language validates the same file. |
| Envelope | Webhooks and the envelope.json vector |
The signed body every delivery shares. |
index.json lists each file with its sha256 and the API revision it belongs to. Pin those hashes in your repository; a change is a reviewed contract change on Dukkan's side and shows up in the changelog.
Test vectors#
Each vector file is a set of inputs with the value the platform computes. An implementation is correct when every expected value matches exactly.
| File | What it proves |
|---|---|
webhook-signing.json |
hex(HMAC-SHA256(secret, timestamp + "." + body)) over several bodies, including non-ASCII and whitespace-sensitive ones, plus the exact header names. |
pkce.json |
S256 challenges for given verifiers and the verifier alphabet. |
canonical-json.json |
Key-sorted, whitespace-free serialisation at every depth and the sha256 the platform compares when the same Idempotency-Key arrives with a different body. |
envelope.json |
A production envelope and a test: true envelope, serialised, plus envelopes that must be rejected (wrong api_version, missing install_id, bad sequence). |
oauth-errors.json |
The token endpoint's error codes, the 30 second refresh-reuse leeway, and the fields a success carries. |
idempotency-key.json |
The deterministic Idempotency-Key derivation (namespace-sha256(JSON list of namespace and parts)), so a job moved between languages keeps replaying the same recorded write. |
curl -s https://developer.dukkan.one/contracts/v1/vectors/webhook-signing.json | head -c 400The official SDK runs these vectors in its own test suite; a port should do the same in CI so a drift on either side is caught before a merchant sees it.
Behaviour the vectors cannot pin#
Some rules are about sequencing, not values. A faithful port implements all of them:
- Refresh under a lock. The platform revokes an install when a rotated refresh token is presented again. Serialise refreshes per install across processes; re-read the stored token inside the lock and skip the refresh when a sibling already rotated. An
invalid_grantgets exactly one re-read before the install is marked reconnect-required. - Verify on raw bytes, then parse. Never re-serialise. Reject timestamps outside 5 minutes and bodies over 256 KB before touching the JSON.
- Bind to the signed
install_id. TheX-Dukkan-Install-Idheader is a lookup hint; the envelope'sinstall_iddecides, and it must be the install whose secret verified. - Dedupe on the signed
id, not on the delivery header, and answer within the timeout before processing. - Every write carries an
Idempotency-Key, reused across your own retries; only reads and idempotent writes are retried on429and5xx, honouringRetry-After. - Money stays in integers. Scale by each value's own
decimals; the only division is at display time (Money). - Additive versioning. Unknown fields and unknown topics must not fail parsing (API reference).
Generating a client#
The OpenAPI document declares an operationId for every operation, so any generator produces stable method names (listOrders, updateOrderStatus, upsertWebhookSubscription). The official SDK's typed client is generated from the same ids; the SDK reference shows the resulting surface, which a port can mirror one to one.
Next steps#
- Webhooks for the envelope and delivery semantics in prose.
- Authorization for the two grants and the error table.
- Support to tell us about a port; we link community packages from this page.