Shopify
Configure the webhook in Shopify
Shopify webhooks can be registered via the Admin UI or the Admin API.
Admin UI
- Shopify Admin → Settings → Notifications → scroll to Webhooks.
- Create webhook.
- Event: pick the topic (e.g.
orders/paid). - Format: JSON.
- URL:
https://splithook.com/e/{slug}. - Save.
The webhook secret is shown after creation — copy it immediately.
Admin API
curl -X POST https://{shop}.myshopify.com/admin/api/2024-04/webhooks.json \
-H "X-Shopify-Access-Token: {token}" \
-H "Content-Type: application/json" \
-d '{
"webhook": {
"topic": "orders/paid",
"address": "https://splithook.com/e/{slug}",
"format": "json"
}
}'
The shared secret is your app's client secret (not the access token).
Add the signing secret to Splithook
- Settings → Signing secrets → New.
- Provider: Shopify.
- Paste the webhook secret (shown in Admin → Notifications → Webhooks).
- In your destination, set Signing mode to Re-sign, select this secret.
Verify signatures in your handler
// Node.js
import crypto from 'crypto';
function verifyShopifyWebhook(rawBody: Buffer, hmacHeader: string, secret: string): boolean {
const computed = crypto
.createHmac('sha256', secret)
.update(rawBody)
.digest('base64');
return crypto.timingSafeEqual(Buffer.from(computed), Buffer.from(hmacHeader));
}
app.post('/webhooks/shopify', express.raw({ type: 'application/json' }), (req, res) => {
const hmac = req.headers['x-shopify-hmac-sha256'] as string;
if (!verifyShopifyWebhook(req.body, hmac, process.env.SHOPIFY_WEBHOOK_SECRET)) {
return res.status(401).send('Unauthorized');
}
// handle event...
res.sendStatus(200);
});
How Splithook re-signs Shopify events
Shopify's signature format:
x-shopify-hmac-sha256: abcABC123==
The value is base64(HMAC-SHA256(secret, body)). No timestamp. On replay, Splithook recomputes the HMAC and base64-encodes it.
Filtering by topic
Shopify sends the topic in X-Shopify-Topic (mapped to event_type):
event_type == 'orders/paid'
event_type == 'orders/cancelled'
event_type matches '^orders/'
event_type in ['products/create', 'products/update']