Skip to content

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.

Updated 9 Sept 20263 min read
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.
Shell
curl -s https://developer.dukkan.one/contracts/v1/vectors/webhook-signing.json | head -c 400

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

  1. 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_grant gets exactly one re-read before the install is marked reconnect-required.
  2. Verify on raw bytes, then parse. Never re-serialise. Reject timestamps outside 5 minutes and bodies over 256 KB before touching the JSON.
  3. Bind to the signed install_id. The X-Dukkan-Install-Id header is a lookup hint; the envelope's install_id decides, and it must be the install whose secret verified.
  4. Dedupe on the signed id, not on the delivery header, and answer within the timeout before processing.
  5. Every write carries an Idempotency-Key, reused across your own retries; only reads and idempotent writes are retried on 429 and 5xx, honouring Retry-After.
  6. Money stays in integers. Scale by each value's own decimals; the only division is at display time (Money).
  7. 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.