Stripe Webhook 400 error. Raw request body issues - node.js

I've been following Stripes documentation on setting up webhooks and testing locally through their Stripe CLI. I can successfully create events, but I receive a response of 400 POST.
2021-12-14 23:18:53 --> payment_intent.created [evt_3K6ox6LqfpEEJtVG0yhTBKDf]
2021-12-14 23:18:53 <-- [400] POST http://localhost:4242/webhook [evt_3K6ox6LqfpEEJtVG0yhTBKDf]
No matter what I do, I always receive a "No signatures found matching the expected signature for payload. Are you passing the raw request body you received from Stripe?" error. Looking through previous questions relating to this, the issue revolves around passing raw data.
I have doublechecked that my webhook secret matches that of the what the CLI gives me.
Here is the latest attempt:
// Use JSON parser for all non-webhook routes
app.use((req, res, next) => {
if (req.originalUrl === '/webhook') {
next();
} else {
express.json()(req, res, next);
}
});
// Stripe requires the raw body to construct the event
app.post('/webhook', express.raw({type: 'application/json'}), (req, res) => {
const sig = req.headers['stripe-signature'];
const rawBody = req.body
let event;
try {
event = stripe.webhooks.constructEvent( rawBody, sig, webhookSecret);
} catch (err) {
// On error, log and return the error message
console.log(`❌ Error message: ${err.message}`);
return res.status(400).send(`Webhook Error: ${err.message}`);
}
// Successfully constructed event
console.log('✅ Success:', event.id);
// Return a response to acknowledge receipt of the event
res.json({received: true});
});

It's a common issue and could depend on your setup and framework. If you are using Node, I recommend going through this Github issue and see if workarounds there work for you.
Lastly you can write into Stripe Support and their dev could help figure out what went wrong.

For 2022, change the app.use(express.json()) to this.
app.use(express.json({
limit: '5mb',
verify: (req, res, buf) => {
req.rawBody = buf.toString();
}
}));
Then you have to change the webhook code to use the rawBody variable that was added correctly and remove the middleware express.raw({type:'application/json'}) since that won't be needed once you have the rawBody variable added to the request anyways.
app.post('/webhook', (request, response) => {
const sig = request.headers['stripe-signature'];
let event;
try {
event = stripe.webhooks.constructEvent(request.rawBody, sig, endpointSecret); //#JA - Had to modify this to take the rawBody since this is what was needed.
} catch (err) {
response.status(400).send(`Webhook Error: ${err.message}`);
return;
}
// Handle the event
switch (event.type) {
case 'payment_intent.succeeded':
const paymentIntent = event.data.object;
// Then define and call a function to handle the event payment_intent.succeeded
break;
// ... handle other event types
default:
console.log(`Unhandled event type ${event.type}`);
}
// Return a 200 response to acknowledge receipt of the event
response.send();
});
The nice part about this fix is you can continue to use express.json() middleware to continue to parse the json from your API's as normal and none of your code should break :).

Related

Stripe API: Can´t use the raw body with express

So I´m trying to verify the transactions using Stripe and it needs the raw body to verify the request.
Stripe Docs on that topic: https://stripe.com/docs/webhooks/signatures
my code:
router.post(
"/stripe_webhook",
express.raw({ type: "application/json" }),
(request, response) => {
updateStripePaymentStatus(request, response);
}
);
exports.updateStripePaymentStatus = async (request, response) => {
const sig = request.headers["stripe-signature"];
let event;
try {
event = stripe.webhooks.constructEvent(request.body, sig, endpointSecret);
} catch (err) {
console.log(err);
response.status(400).send(`Webhook Error: ${err.message}`);
}
//Handle the event
switch (event.type) {
case "payment_intent.succeeded":
const paymentIntent = event.data.object;
const doc = await FileModel.findOne({ paymentId: paymentIntent.id });
doc.paymentStatus = paymentIntent.status;
doc.save();
break;
default:
// Unexpected event type
console.log(`Unhandled event type ${event.type}.`);
}
// Return a 200 response to acknowledge receipt of the event
response.send();
};
Error: message: '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',
The secret and and so on should work and all. But if I console.log the request.body it still returns a jason object instead of a string...
Any ideas what could be wrong here?
It could be a number of things. There's probably something in your stack that modifying the request body. Take a look at these issues for some clues: https://github.com/stripe/stripe-node/issues/341 and https://github.com/stripe/stripe-node/issues/356.

Why stripe webhook is returning 400 error and how to fix it?

I am trying to get the webhook working for stripe but constantly getting the following error: Webhook Error: 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 have followed their documentation and read through other SO users who had the same issue but still no success. Here is what I have in my code:
index.js
app.use('/webhooks/stripe', bodyParser.raw({type: "application/json"}), stripeWebhookRouter);
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json({
verify: function(req,res,buf) {
var url = req.originalUrl;
if (url.startsWith('/stripe-webhooks')) {
req.rawBody = buf.toString()
}
}
}));
And here is my stripe webhook file that listen on certain endpoint
stripeWebhook.js
let stripeSecretKey;
if(process.env.NODE_ENV === 'production'){
stripeSecretKey = process.env.STRIPE_WEBHOOK_SECRET_PRODUCTION;
}else{
stripeSecretKey = process.env.STRIPE_SK_TEST;
}
const stripe = require('stripe')(stripeSecretKey);
const stripeWebhookRouter = Router();
stripeWebhookRouter.post('/stripeWebhook', async (request, response) => {
console.log("Stripe webhook working")
const payload = request.body;
const sig = request.headers['stripe-signature'];
let event;
try {
event = await stripe.webhooks.constructEvent(payload, sig, stripeSecretKey);
console.log("Stripe event constructed ", event);
} catch (err) {
console.log(`❌ Error message: ${err.message}`);
return response.status(400).send(`Webhook Error: ${err.message}`);
}
// I have more code logic here, but it is irrelevant to this question
}
Am I setting up the bodyParser wrong for the Stripe router?
Okay fellow developers, I think I nailed the solution and if anyone faces similar issue please take look at my solution. I also strongly suggest that you have separate file for your router collection, because this will ensure that your index.js file is not clogged with bunch of routers.
So the issue appeared to be not being able to pass down raw incoming request to the stripe webhook endpoint. Here is my code change:
index.js
app.use('/webhooks/stripe', express.raw({type: "*/*"}), stripeWebhookRouter);
app.use((req, res, next) => {
if(req.originalUrl.startsWith('/stripe-webhooks')){
next();
}else{
express.json()(req, res, next);
}
})
and here us my stripeWebhook.js
const payload = request.body;
const sig = request.headers['stripe-signature'];
let event;
try {
event = await stripe.webhooks.constructEvent(payload, sig, stripeWebhookSecretKey);
console.log("✅ Stripe event constructed ", event);
} catch (err) {
console.log(`❌ Stripe event construction failed, error message: ${err.message}`);
return response.status(400).send(`Webhook Error: ${err.message}`);
}
Pay attention to how you parse incoming requests to your stripe webhook endpoint. Cheers!

Error occured Invalid payload provided. No JSON object could be decoded Coinbas

I having been battling this error for weeks now, read hundreds of resources that has just proven futtile and contacted coinbase support without getting any response from them. When I try testing the webhook from the coinbase Api I get this error "Remote server at 07b0a16c5735.ngrok.io returned an HTTP 400" and in my console I get "Error occured Invalid payload provided. No JSON object could be decoded"
I have attached screenshots of the errors I am encountering and this my code for the endpoint;
app.post(
"/endpoint", (request, response) => {
var event;
console.log(request.headers);
try {
event = Webhook.verifyEventBody(
request.rawBody,
request.headers["x-cc-webhook-signature"],
webhookSecret
);
} catch (error) {
console.log("Error occured", error.message);
return response.status(400).send("Webhook Error:" + error.message);
}
console.log("Success", event.id);
console.log(request.rawBody);
response.status(200).send("Signed Webhook Received: " + event.id);
}
);
I am currently using this line of code to parse the raw body to json:
function rawBody(req, res, next) {
req.setEncoding("utf8");
var data = "";
req.on("data", function (chunk) {
data += chunk;
});
req.on("end", function () {
req.rawBody = data;
next();
});
}
app.use(rawBody);
I tried using express.json() but supplemented it for the above line code when I was still getting the same error. This is the response I am expecting:
Success 00000000-0000-0000-0000-000000000000
{"id":"00000000-0000-0000-0000-000000000000","scheduled_for":"2018-01-01T00:40:00Z","attempt_number":1,"event":{"id":"00000000-0000-0000-0000-000000000000","resource":"event","type":"charge:confirmed","api_version":"2018-03-22","created_at":"2018-01-01T00:40:00Z","data":{"code":"AAAAAAAA","id":"00000000-0000-0000-0000-000000000000","resource":"charge","name":"The Sovereign Individual","description":"Mastering the Transition to the Information Age","hosted_url":"https://commerce.coinbase.com/charges/AAAAAAAA","created_at":"2018-01-01T00:00:00Z","confirmed_at":"2018-01-01T00:40:00Z","expires_at":"2018-01-01T01:00:00Z","support_email":"test#test.test","timeline":[{"time":"2018-01-01T00:00:00Z","status":"NEW"},{"status":"PENDING","payment":{"network":"ethereum","transaction_id":"0x0000000000000000000000000000000000000000000000000000000000000000"},"time":"2018-01-01T00:30:00Z"},{"status":"COMPLETED","payment":{"network":"ethereum","transaction_id":"0x0000000000000000000000000000000000000000000000000000000000000000"},"time":"2018-01-01T00:40:00Z"}],"metadata":{},"payment_threshold":{"overpayment_absolute_threshold":{"amount":"15.00","currency":"USD"},"overpayment_relative_threshold":"0.1","underpayment_absolute_threshold":{"amount":"5.00","currency":"USD"},"underpayment_relative_threshold":"0.1"},"pricing":{"local":{"amount":"100.00","currency":"USD"},"bitcoin":{"amount":"1.00000000","currency":"BTC"},"usdc":{"amount":"10.000000","currency":"USDC"},"bitcoincash":{"amount":"5.00000000","currency":"BCH"},"litecoin":{"amount":"2.00000000","currency":"LTC"}},"pricing_type":"fixed_price","payments":[{"network":"ethereum","transaction_id":"0x0000000000000000000000000000000000000000000000000000000000000000","status":"CONFIRMED","detected_at":"2018-01-01T00:30:00Z","value":{"local":{"amount":"100.0","currency":"USD"},"crypto":{"amount":"10.00","currency":"ETH"}},"block":{"height":100,"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","confirmations_accumulated":8,"confirmations_required":2}}],"addresses":{"bitcoin":"1000000000000000000000000000000000","usdc":"0x0000000000000000000000000000000000000000","litecoin":"3000000000000000000000000000000000","bitcoincash":"bitcoincash:000000000000000000000000000000000000000000"},"exchange_rates":{"BCH-USD":"1000.0","BTC-USD":"100.0","ETH-USD":"10.0","JPY-USD":"0.5","LTC-USD":"50.0","TST-USD":"0.5","BEER-USD":"0.1"}}}}
I had the same issue.
What worked for me was downgrading the version of the npm package that handles the payload within the webhook (coinbase-commerce-node) to the version 1.0.0.
I also implemented the webhook exactly as shown in the examples of the library:
https://github.com/coinbase/coinbase-commerce-node/tree/master/examples/webhook
You also need to make sure your server is properly configured to work with SSL. For example if you have the webhook implemented in Heroku, you can use heroku-ssl-redirect to force the usage of SSL.
Instead of sending the rawBody, you can use stringify object to verify the signature.
app.use(bodyParser.json());
try {
const event = Webhook.verifyEventBody(JSON.stringify(req.body), signature, webhookSecret);
if (event.type === 'charge:pending') {
// TODO
// user paid, but transaction not confirm on blockchain yet
}
if (event.type === 'charge:confirmed') {
// TODO
// all good, charge confirmed
}
if (event.type === 'charge:failed') {
// TODO
// charge failed or expired
}
res.send(`success ${event.id}`);
} catch (error) {
if (error) {
console.log(error);
}
res.status(400).send('failure!');
}

Stripe Webhook - Cannot read property 'type' of undefined

I just integrated the stripe webhook in node.
I am able to implement simple webhook endpoint and it is working absolutely fine.
But if I tried "Check webhook signature" I am getting the error: " Cannot read property 'type' of undefined"
My code looks just as provided by stripe:
app.post('/webhook', bodyParser.raw({type: 'application/json'}), (request, response) => {
const sig = request.headers['stripe-signature'];
let event;
try {
event = stripe.webhooks.constructEvent(request.body, sig, endpointSecret);
// event = request.body;
} catch (err) {
response.status(400).send(`Webhook Error: ${err.message}`);
}
// Handle the event
switch (event.type) {
case 'payment_intent.succeeded':
const paymentIntent = event.data.object;
console.log('PaymentIntent was successful!');
break;
default:
console.log(`Unhandled event type ${event.type}`);
// Unexpected event type
return response.status(400).end();
}
// Return a 200 response to acknowledge receipt of the event
response.json({ received: true });
});
I am not getting anything in the "event"
Please help me!!!
Try to use
bodyParser.raw({type: '*/*'})
https://github.com/stripe/stripe-node/issues/331
My issue was that I'm using NextJS and
I'm using Stripe to power the payment gateway of a Next.js app and got to the step to verify the webhook signing secret. I had some issues at first because Stripe expects the raw request body whereas Next.js parses the request body before it's passed to the handler.
https://maxkarlsson.dev/blog/2020/12/verify-stripe-webhook-signature-in-next-js-api-routes/

Stripe webhook not working in firebase function

I'm setting up a stripe webhook to fulfill my order with stripe after a payment while hosting it with firebase functions.
When developing on a localhost everything works fine until I publish it in a firebase function where it always sends me a [400] error.
Creating the payment intent works fine in the same firebase function but that one doesn't.
The error message I get is : 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
Code of my express backend :
app.post(
"/stripe/webhook",
bodyParser.raw({ type: "application/json" }),
async (request, response) => {
const payload = request.body;
const sig = request.headers["stripe-signature"];
let event;
try {
event = stripe.webhooks.constructEvent(payload, sig, endpointSecret);
} catch (err) {
return response.status(400).send(`Webhook Error: ${err.message}`);
}
// Handle the checkout.session.completed event
if (event.type === "checkout.session.completed") {
const session = event.data.object;
oldSession = await stripe.checkout.sessions.listLineItems(
session.id,
function (err, lineItems) {
fulfillOrder(session, lineItems);
}
);
}
response.status(200);
}
);
Anyone has an idea on how to make it work ? Thanks
I had the same problem but managed to find a solution that worked for me. First request the rawBody in a middleware using verify:
app.use(bodyParser.json({
verify: (req: any, res, buf, encoding) => {
// get rawBody
req.rawBody = buf.toString()
}
}))
Then parse the buffer as payload:
const payload = req.rawBody.toString()
request.body is a pre-parsed object of the request body and should not be used for verification. Instead you likely want request.rawBody which is a Buffer of the raw request body unparsed and unmodified. Try using that instead (you may need to convert it to a string) in your constructEvent call.

Resources