Reputation: 60724
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
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
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