La plupart des bugs de webhooks Shopify en local viennent d'une ligne : l'HMAC est encodé en base64, pas en hex. Si vous appelez .digest('hex') et comparez à X-Shopify-Hmac-Sha256, la vérification ne passera jamais — même avec le bon secret partagé.
Le piège base64
Stripe utilise hex. GitHub aussi (avec un préfixe sha256=). Shopify utilise base64. Trois providers, trois encodages. Votre première version du vérificateur va presque toujours copier un snippet d'un autre projet et échouer silencieusement.
const hmac = crypto
.createHmac('sha256', SHOPIFY_API_SECRET)
.update(rawBody)
.digest('base64');
const valid = crypto.timingSafeEqual(
Buffer.from(hmac),
Buffer.from(req.headers['x-shopify-hmac-sha256']),
);
Deux autres détails comptent. Le corps doit être les octets bruts, non parsés. Et la comparaison doit être à temps constant — l'égalité de chaînes fuite l'information octet par octet.
Pourquoi un tunnel simplifie tout
Vous pouvez aussi utiliser shopify app dev, qui inclut son propre tunnel. Ça marche. Mais ça encapsule votre serveur dans son processus, gère les logs à sa manière, et n'offre aucun bouton de rejeu. Pour du développement d'app sérieux, une URL publique stable plus un tunnel localhost avec capture de requêtes gagne plus de temps que la CLI n'en économise au setup.
npx portpreview 3000
Dev stores : la partie que Shopify survole
- Les dev stores envoient tous les événements webhooks. Création de commande, fulfillment, inventaire. Pas besoin de simuler — installez l'app sur un dev store et cliquez.
- Le secret webhook diffère par app et par canal. Si vous utilisez aussi EventBridge ou Pub/Sub, le comportement HMAC change. Ici on parle de livraison HTTPS classique.
Étapes
- Démarrez votre app localement (port 3000).
- Exécutez
npx portpreview 3000et copiez l'URL HTTPS. - Dans le dashboard Partners, ouvrez votre app et allez dans Configuration → Webhooks.
- Définissez l'endpoint sur
https://votre-tunnel.portpreview.dev/api/webhooks/shopify. - Installez l'app sur un dev store et déclenchez un événement.
- Inspectez la requête capturée. Corrigez. Rejouez.
Les erreurs récurrentes
Corps parsé avant vérification
Si app.use(express.json()) tourne avant votre route webhook, les octets bruts ont disparu au moment de vérifier. Montez un parser raw-body uniquement sur la route webhook.
Secrets confondus
Le secret de l'app Partners n'est pas le token de la Storefront API. L'HMAC webhook utilise le secret de l'app.
Test events trompeurs
Le bouton "Envoyer une notification de test" envoie un payload synthétique. La signature est réelle mais le corps est fixé. Pour un payload réaliste, déclenchez depuis le dev store.
Le rejeu n'est pas optionnel
Shopify retente les webhooks échoués jusqu'à 48 heures avec backoff exponentiel. Généreux, mais en itération vous ne voulez pas attendre. Capturez la première livraison dans l'historique du tunnel et rejouez à la demande.
Quand le local ne suffit plus
Les flux d'inscription de boutique, les webhooks GDPR et la facturation des abonnements sont plus faciles à valider en environnement déployé. Pour le reste — parsing, signature, logique métier — le local est plus rapide.
Pour la cryptographie sous-jacente, lisez notre guide vérification de signature. Rejoignez la waitlist PortPreview.