Webhooks
The app receives six Shopify webhook topics, all mounted at/webhooks/* and verified with HMAC-SHA256 before processing.
Registered topics
The three GDPR topics are declared as compliance-topic subscriptions in
shopify.app.toml; the legacy privacy-compliance table is not used.
Processing pipeline
- Every route verifies the HMAC signature over the unparsed body before JSON parsing. The signature header must be the canonical base64 form of the SHA-256 digest, and the JSON root must be an object.
- Each delivery requires
X-Shopify-Webhook-Idand a route-matchingX-Shopify-Topic. The delivery ID is claimed atomically in D1; completed duplicates return200withduplicate: true, a concurrent live delivery returns retryable503, and an ID reused for a different topic or payload returns409. - Customer compliance deliveries require a valid positive customer ID before their receipt is claimed; subscription updates require an object envelope with a recognized status before the claim.
- Handlers return
200only after required verification and D1/KV work completes. A verifiedshop/redactwhose work is queued may return202. Handler failures mark the claim failed and return500so Shopify can retry.
Signature verification
Shopify sendsX-Shopify-Hmac-Sha256 (a base64 HMAC-SHA256 digest), X-Shopify-Topic, X-Shopify-Shop-Domain, X-Shopify-Api-Version, and X-Shopify-Webhook-Id. The Worker computes HMAC-SHA256 of the raw body with the app secret and compares it in constant time; a mismatch returns 401 before any receipt is claimed.
Handling summary
app/uninstalledverifies the signed domain and/or stored Shop GID, then probescurrentAppInstallationwith any current session. A still-installed result preserves all data; a revoked offline session triggers cleanup, while a revoked online (staff) session deletes only that session row.shop/updatenormalizes the payload domain and updatessessions.primary_domainmonotonically for the matching offline session.customers/data_requestreturns matching audit events, customer-linked retail workflow records with internal fields removed, and an ID-only customer object.customers/redactdeletes audit rows matching the validated customer ID.shop/redactpersists durable signed redaction work, guards against a live reinstall, and purges shop-scoped D1 and KV data.