Øyvind Bråthen
Øyvind Bråthen

Reputation: 60724

Getting exception on webhook from Stripe

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?

Upvotes: 4

Views: 4236

Answers (2)

Tata Volador
Tata Volador

Reputation: 3

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!

Upvotes: 0

Paul Asjes
Paul Asjes

Reputation: 5857

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.

Upvotes: 11

Related Questions