Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
An example Node.js server that can verify a Shopify webhook's integrity. Run with `node index.js`.
const PORT = 3000;
const SECRET = 'APP_SHARED_SECRET';
var http = require('http'),
crypto = require('crypto'),
server;
function verifyShopifyHook(req) {
var digest = crypto.createHmac('SHA256', SECRET)
.update(new Buffer(req.body, 'utf8'))
.digest('base64');
return digest === req.headers['X-Shopify-Hmac-Sha256'];
}
function parseRequestBody(req, res) {
req.body = '';
req.on('data', function(chunk) {
req.body += chunk.toString('utf8');
});
req.on('end', function() {
handleRequest(req, res);
});
}
function handleRequest(req, res) {
if (verifyShopifyHook(req)) {
res.writeHead(200);
res.end('Verified webhook');
} else {
res.writeHead(401);
res.end('Unverified webhook');
}
}
server = http.createServer(parseRequestBody);
server.listen(PORT, function(){
console.log("Server listening on: http://localhost:%s", PORT);
});
@thierryskoda

This comment has been minimized.

Copy link

commented Oct 15, 2015

Are you sure this works ? I tried it and it doesn't work for me. I tried this:

console.log("here");
req.on('data', function(chunk) {
        console.log("test");
        req.body += chunk.toString('utf8');
    });

console.log("here"); get called but not the console.log("test");

@winsandymyint

This comment has been minimized.

Copy link

commented Oct 30, 2015

I tested that with both App's Secrete key and webhook verify key.
But it keeps showing me "Unverified webhook". :'(
epw_ohqgiv0

@winsandymyint

This comment has been minimized.

Copy link

commented Oct 30, 2015

Oh! Your code works!
my mistake. I forget to req with body when I test via postman. That's why.
Cool! Thank bro.

@andjosh

This comment has been minimized.

Copy link
Owner Author

commented Oct 31, 2015

@winsandymyint Glad it could help!
@thierryskoda Did you POST something in your request body?

@frehner

This comment has been minimized.

Copy link

commented Nov 4, 2016

The new buffer object (line 10) saved me; my version wasn't working on real Shopify orders (but was on test webhooks!) until I added that part in. Much thanks!

@martinliptak

This comment has been minimized.

Copy link

commented Jul 4, 2017

Thanks!

@jmortensen

This comment has been minimized.

Copy link

commented Jul 20, 2017

Thanks for making this. Another thing to keep in mind is that various middleware (e.g. body-parser) can alter the body and that can lead to being unable to verify the webhook. Make sure you have a reference to the raw body of what shopify sent so you can run that through your verification code.

@tiendq

This comment has been minimized.

Copy link

commented Nov 16, 2017

@andjosh great work, I don't know why Shopify documentation is so poor and it takes 2 different ways to verify HMAC.

@jmortensen and others: I got it worked well with Express and its middleware, you must use body-parser to get request.body, the key point is get it correctly :)

Use body-parser.text() even when Shopify sends you JSON data (application/json).

Replace line 10 with Buffer.from(request.body) since the constructor is deprecated.

@pritisolanki

This comment has been minimized.

Copy link

commented Apr 12, 2018

It is not working for me . The generated digest and received headers are different.. @tiendq can you please elaborate how to use body-parser.text() in above code ?

more details here - https://ecommerce.shopify.com/c/shopify-apis-and-technology/t/verify-shopify-webhook-integrity-node-express-js-512656

@Kushan-

This comment has been minimized.

Copy link

commented Jul 19, 2018

    let jsonString = JSON.stringify(req.body)
   
    let digest = crypto.createHmac("sha256", SECRET)
        .update(jsonString, 'utf8','hex')
        .digest('base64');
    console.log("digest->"+digest)

I hope this will help.

@mitchellporter

This comment has been minimized.

Copy link

commented Sep 9, 2018

@Kushan-'s solution works perfectly, while Shopify's own express middleware did not.. but they may be modifying the request somewhere... not sure, but this works.

@guillaumegarcia13

This comment has been minimized.

Copy link

commented Oct 3, 2018

+1 for @tiendq's solution
Somehow, using body-parser.json() blocked the processing of the request.

this.express = express()
    .use(rawBodyGetter)
    .use(bodyParser.text())
//  .use(bodyParser.json())
    .use(bodyParser.urlencoded({ extended: true }))
const checkWebHookIntegrity = async (req: any, res: any, next: any) => {
    let raw;

    try {
        raw = await rawBody(req);

        const digest = crypto.createHmac('sha256', CONFIG.SHOPIFY_WEBHOOK_SECRET)
                             .update(raw)
                             .digest('base64');
        if (digest === req.headers['x-shopify-hmac-sha256']) {
            req.body = JSON.parse(raw.toString('utf-8'));
            next();
        } else {
            console.log('Error with request', os.EOL, req.body);
            return res.status(401).send();
        }
    } catch(e) {
        console.log(e);
        return res.status(500).send();
    }
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.