Search code examples
node.jsstripe-paymentshapi.jscryptojsngrok

Stripe verify web-hook signature HMAC sha254 HAPI.js


I'm trying to verify a webhook sent by stripe in my hapi.js app. I've followed the instructions detailed here:

https://stripe.com/docs/webhooks/signatures

(I've obviously not posted my endpoint secret here :)

<!-- language: lang-js -->

const enpointSecret = ######;

const sig = _.fromPairs(request.headers["stripe-signature"].split(',')
.map(s => s.split('=')));
// produces object eg { t: '1111', v1: '111111..', v0: '...'} etc


const signed_payload = `${sig.t}.${JSON.stringify(request.payload)}`;

const hmac = crypto.createHmac('sha256', endpointSecret)
.update(signed_payload)
.digest('hex');

The generated hmac does NOT match the signature in the header (sig.v1). I can't figure out what I'm doing wrong...

I'm developing locally - and using ngrok, so that i can test my webhooks. Could this be an issue? Thanks


Solution

  • In Hapi 17, Following on from the comments - in Hapi 17, you must prevent the body from being parsed. I was dancing around the houses setting the output of the payload config... but you don't need to do this. Simple set payload.parse to false

    <!-- language: lang-js -->
    
        module.exports = {
            method: 'POST',
            path: '/api/webhook',
            config: {
                auth: false,
                payload: {
                 parse: false // the important bit
                },
            handler: async (request, h) => {
    

    I was then able to use the built in stripe method

    <!-- language: lang-js -->
    
        try {
           let event = stripe.webhooks.constructEvent( request.payload.toString(), request.headers["stripe-signature"], endpointSecret);
                console.log(event);
        }
        catch (err) {
           console.log(err)
        }
    

    The real credit should go to Karl Reid - so while I have posted this here, I have not marked it as the accepted answer.