Designing a Production-Grade Webhook Receiver: Signature Verification, Retries and Idempotency
Why the Receiver Is the First Line of Defense
In order sync, refund notification and inventory-change scenarios, e-commerce platforms and OMS products (such as WDT or Jushuitan) actively push business events to a callback URL you register. Because that URL is exposed on the public internet, it faces three inherent risks:
- Forged requests — anyone who obtains the URL can POST a fake order straight into your ERP;
- Duplicate delivery — the platform retries when it does not receive a 2xx response, so the same event may arrive multiple times;
- Message loss — if your endpoint times out or is down, the platform exhausts its retries and the event is dropped.
A production-ready webhook receiver must address all three.
Layer 1: Signature Verification
The industry standard is an HMAC signature: the sender computes HMAC-SHA256 over the raw request body with a shared secret and places it in a header. GitHub's X-Hub-Signature-256 header (formatted as sha256=<hex>) is the canonical example. The receiver recomputes the digest and compares.
Two pitfalls matter most: always sign the raw body — if your framework has already parsed and re-serialized the JSON, key order and whitespace changes will break verification — and always compare digests with a constant-time function rather than === to avoid timing side channels. Some Chinese e-commerce ERPs use MD5 over sorted parameters instead; the principle is identical, only the algorithm differs.
Layer 2: Fast ACK + Async Processing
Platforms typically require a success response within 3–5 seconds or they treat the delivery as failed and retry. Never run business logic synchronously inside the request handler. Instead: verify the signature, persist the raw event together with its event ID (or push it onto a message queue), return 200 immediately, and let a background worker consume the event with retries and alerting.
Layer 3: Idempotent Consumption
Whether the platform retries or your worker retries, the same event can be consumed more than once. Consumers must use the platform-issued event ID / document number as an idempotency key: check a dedup table before processing and drop already-handled events, with a unique constraint on the business table as the final backstop.
On the Qeasy platform, all three layers — signature configuration, event staging and idempotent writes — are built into the webhook trigger, so integrators only need to configure the secret and field mappings to go live.