2

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

Rob
  • 1,576
  • 3
  • 22
  • 52
  • 2
    `JSON.stringify` will mutate the request body. You need the generate the MAC on the exact, raw request body that you received from Stripe, or the signature won't match. Also, why not just use [stripe-node](https://github.com/stripe/stripe-node#webhook-signing)? – Karl Reid Jul 15 '18 at 11:40
  • 2
    More info [here](https://github.com/stripe/stripe-node/issues/331#issuecomment-314917167) – Karl Reid Jul 15 '18 at 11:55
  • Thanks. I tried stripe node - but it didnt work for this... trying to figure out how I can get the raw payload in hapi... setting options.payload.parse = false does nothing... – Rob Jul 15 '18 at 18:26

1 Answers1

11

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.

Rob
  • 1,576
  • 3
  • 22
  • 52
  • 2
    No worries, you should accept this, as it shows people how to get the request body in the format Stripe requires using hapi. – Karl Reid Jul 16 '18 at 18:25
  • Will do - thanks. I've got to wait another 15 hours before it will allow me. – Rob Jul 16 '18 at 19:37
  • I recive `Method get TypedArray.prototype.length called on incompatible receiver [object Object]` after `request.paylod.toString()`. Hapi 19, Node 14 – ChromeKK Jul 13 '21 at 07:29