Appearance
Acknowledgement callback
For developers & integrators
With Acknowledgement mode: Asynchronous, an export is only complete when the receiving system calls back to confirm it processed the order. This page is the contract that system must implement.
Sequence
text
Shopware Receiving system (ERP)
│ POST <endpoint> │
│ { …mapped order…, │
│ "meta": { "correlationId": "c0ffee…" } │ ← written at the target's Correlation ID path
│ ─────────────────────────────────────────▶│
│ 2xx │
│ ◀─────────────────────────────────────────│ export state: awaiting_acknowledgement
│ │
│ …later, after processing… │
│ POST /kmh-order-export/acknowledge │
│ X-Kmh-Signature: <hmac> │
│ { "correlationId": "c0ffee…", │
│ "payloadHash": "<sha256>" } │
│ ◀─────────────────────────────────────────│
│ 200 {"status":"acknowledged"} │ export state: acknowledged
│ ─────────────────────────────────────────▶│If no valid callback arrives within the target's Acknowledgement timeout (default 3600 s), the export is marked failed.
Request
http
POST https://<shop-domain>/kmh-order-export/acknowledge
Content-Type: application/json
X-Kmh-Signature: 5d41402abc4b2a76b9719d911017c592…
{"correlationId":"0190f5b2c8f47c2a9d3a1b2c3d4e5f60","payloadHash":"410d13dabd50a65b4ef7e9c8a54317121345f7846791a0b539e7c4c4b46c501b"}| Part | Value |
|---|---|
correlationId | The value the plugin wrote into the payload at the target's Correlation ID path. |
payloadHash | The content hash of the payload you received — see below. |
X-Kmh-Signature | Lower-case hex HMAC-SHA256(raw request body, secret). The secret is the secret of the credential assigned to the target. A target without a credential secret cannot be acknowledged. |
Computing payloadHash
The plugin does not send the hash; the receiver computes it from the payload it received:
- Parse the received JSON.
- Remove the correlation ID field the plugin added (at the target's correlation path). The hash is taken before the ID is inserted, so re-exports with a new ID keep the same hash.
- Serialise canonically, the way PHP's
json_encode(…, JSON_UNESCAPED_UNICODE)does after sorting keys:- object keys sorted recursively, list order unchanged;
- no whitespace;
- non-ASCII characters not escaped;
- forward slashes escaped as
\/.
payloadHash= lower-case hex SHA-256 of that UTF-8 string.
python
import hashlib, hmac, json
def payload_hash(received: dict, correlation_path: str = "meta.correlationId") -> str:
data = json.loads(json.dumps(received)) # deep copy
*parents, leaf = correlation_path.split(".")
node = data
for key in parents:
node = node[key]
node.pop(leaf, None)
canonical = json.dumps(data, sort_keys=True, separators=(",", ":"), ensure_ascii=False)
canonical = canonical.replace("/", "\\/") # PHP escapes slashes by default
return hashlib.sha256(canonical.encode("utf-8")).hexdigest()
def signature(body: bytes, secret: str) -> str:
return hmac.new(secret.encode(), body, hashlib.sha256).hexdigest()Edge cases
If the correlation path created an otherwise empty object (e.g. meta exists only to hold the ID), remove that object too — it was not part of the hashed payload. Numbers must round-trip unchanged (100.0 stays 100.0). Test your implementation against a real export before going live: a wrong hash is answered 401.
This recipe was verified against the plugin's own ContentHasher. For XML targets the hash is still computed over the mapped data, not the XML text — convert the XML back to the same structure first, or use JSON for asynchronously acknowledged targets.
Responses
| Status | Body status | Meaning |
|---|---|---|
200 | acknowledged | Export settled. |
200 | stale | The hash belongs to an older revision of this export (the order was re-exported since). Nothing changes. |
200 | ignored | Signature and hash valid, but the export is not awaiting acknowledgement (already acknowledged, or failed/timed out). |
401 | invalid | Signature missing or wrong, no credential secret, or payloadHash missing. |
404 | not_found | Unknown or empty correlationId. |
The endpoint is idempotent: sending the same acknowledgement twice returns ignored the second time.
Example
bash
BODY='{"correlationId":"0190f5b2c8f47c2a9d3a1b2c3d4e5f60","payloadHash":"<hash>"}'
SIG=$(printf '%s' "$BODY" | openssl dgst -sha256 -hmac "$CREDENTIAL_SECRET" | sed 's/^.* //')
curl -i https://shop.example.com/kmh-order-export/acknowledge \
-H "Content-Type: application/json" -H "X-Kmh-Signature: $SIG" -d "$BODY"