Getting exception on webhook from Stripe
Asked Answered
S

2

4

I'm trying to set up a webhook from Stripe to handle the payment_intent.succeeded event, but I get an exception. This is my code from the Node backend (I have extracted all the relevant parts I hope. Let me know if anything else is needed):

const stripeWebHookSecret = 'whsec_WA0Rh4vAD3z0rMWy4kv2p6XXXXXXXXXX';

import express from 'express';
const app = express();
app.use(bodyParser.urlencoded({ extended:true }));
app.use(bodyParser.json());
app.use(session({ <some params here> }));

const openRouter = express.Router();

registerOpenPaymentRoutes(openRouter);

app.use('/open', openRouter);

And the implementation of registerOpenPaymentRoutes looks like this:

export const registerOpenPaymentRoutes = (router) => {
    router.post('/payment/intent/webhook', bodyParser.raw({type: 'application/json'}), (req, res) => {
        let signature = req.headers['stripe-signature'];
        try {
            let event = stripe.webhooks.constructEvent(req.body, signature, stripeWebHookSecret);
            switch(event.type){
                case 'payment_intent.succeeded':
                    let intent = event.data.object;
                    res.json({ message: 'Everything went smooth!', intent });
                default:
                    res.status(400).json({ error: 'Event type not supported' });
            }
        }
        catch (error){
            res.status(400).json({ message: `Wrong signature`, signature, body: req.body, error });
        }
    });
}

So far so good.When I fire a test webhook event from the Stripe dashboard, I hit the endpoint, but get the result from the catch block. The error message is as follows:

No signatures found matching the expected signature for payload. Are you passing the raw request body you received from Stripe? https://github.com/stripe/stripe-node#webhook-signing"

I'm returning the signature with the error message as well as you see above, and the signature looks like this:

"t=1557911017,v1=bebf499bcb35198b8bfaf22a68b8879574298f9f424e57ef292752e3ce21914d,v0=23402bb405bfd6bd2a13c310cfecda7ae1905609923d801fa4e8b872a4f82894"

As far as I understand from the documentation, what is needed to get the raw request body as mentioned are the bodyParser.raw({type: 'application/json'})argument to the router that I already have there.

Can anyone see the missing part?

Stephanus answered 15/5, 2019 at 9:10 Comment(0)
J
11

It's because you've already set your express app to use the bodyParser.json() middleware, which clashes with the bodyParser.raw() middleware you set up in your webhook route.

If you remove the app.use(bodyParser.json()); line your webhooks will work as intended, but then the rest of your routes won't have a parsed body, which isn't ideal.

I suggest adding a custom middleware to choose the bodyParser version based on route. Something like:

// only use the raw bodyParser for webhooks
app.use((req, res, next) => {
  if (req.originalUrl === '/payment/intent/webhook') {
    next();
  } else {
    bodyParser.json()(req, res, next);
  }
});

Then explicitly use the bodyParser.raw() middleware on the webhook routes.

Jobholder answered 15/5, 2019 at 9:42 Comment(3)
Worked like a charm! I thought that my bodyParser.raw() would override the bodyParser.json() for that request, but it didn't. Thanks a lot!Huey
It adds to the middleware queue rather than replacing. So first it would do the json encoding, then attempt raw, which leads to weird stuff happening and your signature being garbled/unusable.Jobholder
Does not work for me as of 2023.Stopoff
U
0

I came up with the very same problem, the answer above gave me an idea on how to solve it but didn't make the gig for me, so thanks for pointing me in the right direction.

I share the way it worked for me to bring the body raw without too much effort

I just add in my index.js after

app.use(bodyParser.urlencoded({ extended: false }));

this

app.use(
  bodyParser.json({ 
    verify: function (req, res, buf, encoding) {
      req.rawBody = buf;
    },
  })
);
app.use(
  bodyParser.urlencoded({
    extended: false,
    verify: function (req, res, buf, encoding) {
      req.rawBody = buf;
    },
  })
);

And then on the function where I needed a raw body I used:

router.post("/webhook",express.raw({ type: "application/json" }),async (req, res) => {
    const sig = req.headers["stripe-signature"];
    const body = req.rawBody; //here I got the raw body
});

And that was enough to pass the (in this case) stripe validation

Hope to be helpful to somebody!

Have a great coding!

Utta answered 8/9, 2022 at 16:7 Comment(2)
How would you implement your solution in TypeScript?Stopoff
well, you can use almost the same code, this is js and you want typescript, you need just to define the variable type. The implementation is the same, you need to include on your index.js or your server.js or your main file the bodyparser (you need to npm i the dependency body-parser) and then just include the app.use as I described just adding the type into the vars according to typescript. And finally on router.post you can see an example of how to implement it on an endpoint, in this case, an http post endpoint. Play around with the example and you will get it :)Utta

© 2022 - 2024 — McMap. All rights reserved.