Shopify signs webhooks with X-Shopify-Hmac-Sha256, a base64 HMAC-SHA256 of the raw body using your app's client secret. There is no timestamp, so an exact replay of a captured Shopify webhook still carries a valid signature.
Receive Shopify webhooks on localhost
hwcli listen shop-events --forward :3000/webhooks/shopify --include-sensitive --append-path
Register the endpoint URL for each topic in your app's configuration or with the Admin API. With --append-path, a URL like …/<endpoint>/orders/create is delivered to localhost:3000/webhooks/shopify/orders/create. HookWatcher recognizes Shopify requests from their X-Shopify-Topic, X-Shopify-Shop-Domain and X-Shopify-Webhook-Id headers and labels them with the topic.
Replay orders
hwcli replay evt_7be20c11 # exact, signature still valid hwcli replay evt_7be20c11 --duplicate 3 # Shopify retries up to 8 times hwcli replay evt_c3 evt_a1 --target :3000/webhooks/shopify/orders/updated
Modifying the body invalidates the HMAC. HookWatcher can generate test signatures for Stripe and GitHub secrets, but not yet for Shopify, so test modified Shopify payloads with verification against a test secret, or use them to check your rejection path.
Things to test for Shopify
- Duplicates: key on
X-Shopify-Webhook-Id, which stays the same across retries of one delivery. - Ordering:
orders/updatedcan arrive beforeorders/create. Compareupdated_atbefore overwriting. - Response time: Shopify expects a response within 5 seconds. Every replay records how long your handler took.