Topics covering webhook creation & management, event handling, Pub/Sub, and Eventbridge, in Shopify apps.
I'm unable to verify a Webhook in Node js.
I followed the solution proposed here but I cannot make it work:
I make sure that I use always the same SHOPIFY_WEBHOOK_SECRET.
But Received X-Shopify-Hmac-Sha256, and Calculated HMAC Hash never match.
Here is the script to create the webhook:
import fetch from 'node-fetch'; import dotenv from 'dotenv'; // Load environment variables from .env file dotenv.config(); const { SHOPIFY_STORE, SHOPIFY_API_KEY, SHOPIFY_API_PASSWORD, SHOPIFY_WEBHOOK_SECRET } = process.env; // Function to create a Shopify webhook const createShopifyWebhook = async () => { const url = `https://${SHOPIFY_STORE}/admin/api/2024-07/webhooks.json`; // Create the base64-encoded credentials for Basic Auth const credentials = Buffer.from(`${SHOPIFY_API_KEY}:${SHOPIFY_API_PASSWORD}`).toString('base64'); const webhookData = { webhook: { topic: 'orders/paid', // The event that triggers the webhook address: 'https://my-heroku-www/webhook/order-paid', // Your actual webhook URL format: 'json', // Webhook payload format secret: SHOPIFY_WEBHOOK_SECRET // Secret used to sign the webhook for HMAC verification } }; try { const response = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Basic ${credentials}`, // Basic Authentication header }, body: JSON.stringify(webhookData), // Send webhook data }); if (!response.ok) { throw new Error(`Error creating webhook: ${response.statusText}`); } const data = await response.json(); console.log('Webhook created successfully:', data); } catch (error) { console.error('Error creating webhook:', error); } }; createShopifyWebhook();
and here the webhook validation:
import express from 'express'; import bodyParser from 'body-parser'; import crypto from 'node:crypto'; import dotenv from 'dotenv'; dotenv.config(); const { SHOPIFY_WEBHOOK_SECRET } = process.env; const app = express(); const port = process.env.PORT || 3000; // Middleware to capture raw body for HMAC verification app.use(bodyParser.raw({ type: 'application/json' })); // HMAC Verification Middleware const verifyShopifyWebhook = (req, res, next) => { const hmacHeader = req.headers['x-shopify-hmac-sha256']; // Generate the HMAC using the raw body const calculatedHmac = crypto .createHmac('sha256', SHOPIFY_WEBHOOK_SECRET) .update(req.body, 'utf8', 'hex') .digest('base64'); console.log('Received X-Shopify-Hmac-Sha256:', hmacHeader); console.log('Calculated HMAC Hash:', calculatedHmac); const hmacBuffer = Buffer.from(hmacHeader, 'base64'); const calculatedHmacBuffer = Buffer.from(calculatedHmac, 'base64'); if (hmacBuffer.length !== calculatedHmacBuffer.length) { console.log('HMAC different lenghts'); return res.status(401).send("Couldn't verify incoming Webhook request!"); } if (!crypto.timingSafeEqual(hmacBuffer, calculatedHmacBuffer)) { console.log('HMAC comparison failed'); // <----------------- This is the output I get return res.status(401).send("Couldn't verify incoming Webhook request!"); } console.log('Webhook verification successful'); // Parse the raw body into JSON after verification req.body = JSON.parse(req.body.toString()); next(); // Proceed to the next middleware or route handler }; // Use the verification middleware before any route that needs verification app.post('/webhook/order-paid', verifyShopifyWebhook, (req, res) => { // Handle the verified webhook here const payload = req.body; console.log('Webhook Payload:', payload); res.status(200).send('Webhook processed successfully'); }); // Start the server app.listen(port, () => { console.log(`Server is running on port ${port}`); });
I always get "HMAC comparison failed"
I would need some help please, really don't know what is wrong...
Thank you very much
hi @rdiazroman
You can try it.
const bodyParser = require('body-parser')
// parse application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({ extended: false }))
// parse application/json
app.use(bodyParser.json())
const hmac = req.header("X-Shopify-Hmac-Sha256");
LOGGER.info(`hmac: ${hmac}`);
const genHash = crypto
.createHmac("sha256", process.env.APISECRET)
.update(JSON.stringify(req.body), "utf8", "hex")
.digest("base64");
console.log(genHash);
if (genHash !== hmac) {
return res.status(401).send("Couldn't verify incoming Webhook request!");
}