Cloudflare Workers are a good fit for webhook handlers — cheap, fast, globally distributed, no servers to babysit. This tutorial wires up a worker that receives Shoppex webhook deliveries, verifies the signature, deduplicates by delivery ID, and reacts to the event.Documentation Index
Fetch the complete documentation index at: https://docs.shoppex.io/llms.txt
Use this file to discover all available pages before exploring further.
What you’ll have at the end
- A deployed Cloudflare Worker that listens for Shoppex events.
- HMAC-SHA256 signature verification against the V2 header.
- Idempotency using Cloudflare KV (so you can safely receive duplicates).
- A registered Shoppex webhook pointing at the worker.
Prerequisites
- A Cloudflare account with Workers enabled.
wranglerCLI installed (npm i -g wranglerorbun add -g wrangler).- A Shoppex shop with an API key that has
webhooks.write.
Step 1 — Create the worker
src/index.ts to edit.
Step 2 — Add a KV namespace for idempotency
Shoppex assigns a uniquedelivery_id to each delivery. To safely handle duplicates (which
can happen if you manually retry a webhook), record the IDs you’ve already processed and
short-circuit if you see one twice.
wrangler.toml:
Step 3 — Add the webhook secret
The webhook secret is what you’ll use to verify HMAC signatures. Add it as a wrangler secret (not in code, not inwrangler.toml):
Step 4 — Write the handler
Replacesrc/index.ts:
Step 5 — Test locally
http://localhost:8787. Send a POST request with fake headers
to confirm it returns 400 (missing headers) — then 401 (bad signature) when you send full
headers but wrong signature. That’s the right behavior.
Step 6 — Deploy
https://shoppex-webhook-worker.your-account.workers.dev.
Copy it.
Step 7 — Register the webhook in Shoppex
From your dashboard at Settings → Developer → Webhooks or via the API:secret. Save it immediately — Shoppex shows it only once.
Step 8 — Set the secret in the worker
Step 9 — Verify
Trigger a real event — make a purchase on your shop or use the dashboard’s “send test event” on the webhook configuration page. Check your worker logs:Event reference
Common events you’ll likely subscribe to:order:created— new order, not yet paid.order:paid— payment confirmed. This is the event 90% of integrations care about.order:cancelled,order:disputed— bad-path events worth knowing about.subscription:renewed,subscription:cancelled— recurring billing lifecycle.subscription:trial:started,:trial:ended— for nudge flows.product:stock— low-stock alert.
Retry behavior — important
If your worker returns a non-2xx or times out (30s limit), Shoppex automatically retries on an exponential backoff: 2 min, 4 min, 8 min, 16 min between attempts. After 5 total attempts (initial + 4 retries) the delivery is marked failed. You can manually re-queue any failed delivery from the dashboard or viaPOST /dev/v1/webhooks/logs/:id/retry. The same delivery_id is reused — that’s why
idempotency (the KV check above) matters.
Practical implications:
- Your worker should be reliable. Use a Worker (which is globally distributed) rather than a single-region origin server. Transient failures are forgiven automatically; sustained outages still get the delivery through if you recover within the ~30-minute retry window.
- Make handlers idempotent. Both automatic retries and your own manual retries will
re-send the same
delivery_id. The KV check above is what catches both cases. - Watch for failures. If a delivery exhausts all 5 attempts, it’s done — Shoppex won’t
try again. Monitor your worker logs (Cloudflare Analytics or
wrangler tail) and the Shoppex webhook-logs dashboard for failed entries you’ll need to manually re-queue once you’ve fixed the root cause.
Common pitfalls
- Parsing the body before verifying. You must use
request.text()to get the exact raw bytes Shoppex signed. If yourequest.json()first and re-serialize, the message no longer matches the signature. - Reading headers case-sensitively. The Fetch API in Workers gives you case-insensitive access, but be consistent.
- Forgetting the timestamp window. Without a 5-minute timestamp check, an attacker who captured one signed payload could replay it forever.
- Not enabling KV in production. Without idempotency, a manual retry double-processes the order. Worth the 5 minutes of KV setup.
Reference: Webhooks
Full event list, payload shapes, and the signature spec.