I am new to full stack development. I am having an issue with using a webhook for the first time, specifically one from a stripe checkout session. From my understanding, stripe requires the raw body of the request to properly decode the signature. My backend uses a few middlewares that I believe are getting in the way of the raw body parser from doing it's job. I understand that I need to apply a form of if/else to allow my backend to decide which middleware/s to use, but I am unsure of how to properly do it. Here is my code:
// only use the raw bodyParser for webhooks
app.use((req, res, next) => {
if (req.originalUrl === '/webhook') {
next();
} else {
cors();
express.json({limit: '50mb'})
express.urlencoded({limit: '50mb', extended: true})
}
});
//app.use(cors());
//app.use(express.json({limit: '50mb'}));
//app.use(express.urlencoded({limit: '50mb', extended: true}));
The commented out app.use statements are my middlewares that were properly working before I attempted to get my webhook working. This code does not work, it is just my attempt to apply the middlewares properly. The following is my post route for the webhook, which is just a copied version of the docs from stripe, which I will edit once I successfully receive the webhook:
// Stripe requires the raw body to construct the event
app.post('/webhook', bodyParser.raw({type: 'application/json'}), (req, res) => {
const sig = req.headers['stripe-signature'];
let event;
try {
event = stripe.webhooks.constructEvent(req.body, 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});
});
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
It appears that after hours of messing around, I solved my own question just minutes after finally deciding to post it here! But of course....lol
My solution that worked:
// only use the raw bodyParser for webhooks
app.use((req, res, next) => {
if (req.originalUrl === '/webhook') {
next();
} else {
express.json({limit: '50mb'})(req, res, next);
}
});
app.use(cors());
//app.use(express.json({limit: '50mb'}));
app.use(express.urlencoded({limit: '50mb', extended: true}));
Related
I am using the code provide by Stripe to test a webhook. The Stripe secret and the endpoint secret have been triple checked.
Stripe version: 6.19
Body-Parser: 1.19
When I test webhook on the Stripe dashboard I get the result: (Test webhook error: 400) No signatures found matching the expected signature for payload. Are you passing the raw request body you received from Stripe?
Any help would be appreciated.
var bodyParser - require('body-parser');
// Using Express
const app = require('express')();
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
// Set your secret key: remember to change this to your live secret key in production
// See your keys here: https://dashboard.stripe.com/account/apikeys
const stripe = require('stripe')('sk_test_VPw...');
// Find your endpoint's secret in your Dashboard's webhook settings
const endpointSecret = 'whsec_...';
// Use body-parser to retrieve the raw body as a buffer
const bodyParser = require('body-parser');
// Match the raw body to content type application/json
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); //NOT WORKING!
} 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;
// Fulfill the purchase...
handleCheckoutSession(session);
}
// Return a response to acknowledge receipt of the event
response.json({received: true});
});
Usually this is due to something on your side parsing or modifying the raw request string before the signature is checked(so the signature is computed against a modified string, not the exact one Stripe sent). In this case it looks like the JSON express middleware is doing that:
app.use(express.json());.
Stripe has an example of using a raw bodyParser middleware on the webhook endpoint instead so that your code gets the raw string that's required :
// 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'];
let event;
try {
event = stripe.webhooks.constructEvent(req.body, 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});
});
One liner plus no deprecated bodyParser. Make sure to define your endpoint's parser before the generic one, aka express.json().
app.use('/stripe/webhook', express.raw({type: "*/*"}))
app.use(express.json())
In addition to everything, check whsec_
How to get both parsed body and raw body in Express:
app.use(bodyParser.json({
verify: (req, res, buf) => {
req.rawBody = buf
}
}))
Thanks to:
https://flaviocopes.com/express-get-raw-body/
For those working with NextJS. Here is a solution I bumped on Reddit by one #
u/SiMFiCysed
https://www.reddit.com/user/SiMFiCysed/
One other thing that could be going wrong (which was giving me the error) is that the production webhook key is being used with a test request, or vice versa.
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?
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.
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!
I'm trying to write an express middleware, where I log certain req and res headers and write it to an external database.
Part of this is throwing a custom error handler, where I can write my err.stack, err.message, req.body, req.params and req.url to an external file or application.
I tried making my own errorhandler and call it last in app.use() hierarchy. In my routes I throw a new error, and in my custom error handler, I try to log the error and the req.body. However, nothing gets logged except for the error.
app.js
app.use(responseTime());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use(cookieParser());
app.use(multilogger({extended: false, development: true, interval: 5000}));
app.use(multiError);
app.use('/', indexRouter);
random route
router.post("/", function(req, res, next) {
return next(new Error("Oh no!"));
});
Custom Error
module.exports = function (err, req, res, next) {
console.error(err.message); //only this gets logged
console.error(req.body); // this doesn't appear
next(err);
};
I want to destructure the req object of my API Call inside my custom error handler so I can do other things with it.
Thanks in advance!
You're experiencing this because Express evaluates all middleware/routes in the order they have been added to Express. Calling next() literally means, "go to the next possible handler that matches this request". In most cases there is only one route that matches a requested URI, that means the next possible handler would be a handler that has no route specified.
In Express, you define the Default Error Handler & 404 Handler without routes, after all other middleware/routes. The 404 Handler should always be the last route inside your Express App.
The Default Error Handler is also a special middleware because it accepts 4 parameters, the first being the error. Its important to check if the error is defined because Express will invoke the Default Error Handler if no route is matched since it doesn't have a route specified. That is why the 404 Handler always goes after Default Error Handler since it is the catch all for any requests that didn't match a route and there would be no error in those cases.
Read more about Writing Middleware, Using Middleware & Error Handling in Express.
const express = require('express')
const bodyParser = require('body-parser')
const PORT = process.env.PORT || 1337
// not sure where this is in your code based on question info
const indexRouter = require('./indexRouter')
const app = express()
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({ extended: true }))
// Your Router
app.use('/', indexRouter)
// Default Error Handler
app.use((err, req, res, next) => {
// This is necessary to detect any unmatched routes that should be a 404
if (!err) {
return next()
}
// Handle Error and Respond
})
// 404 Handler, No Route Defined Matched the Requested Route
app.use((req, res) => res.sendStatus(404))
app.listen(PORT, () => console.log(`Listening on ${Port}`))
I have two endpoints in a node js app:
app.get('search', myGetController);
app.post('add', myPostController);
For simplicity, let's assume both services have only the following code:
exports.myGetController = function(req, res) {
res.status(404).json({ error: "not found" });
};
I want to have a middleware that is executed after the processing of the controllers, but before they are sent to the browser, so I can add a header based on the body of the response.
// process all responses to add ed25519
app.use(function(req, res, next) {
res.setHeader('CharCount', [How To get Body content]);
next();
});
I have two questions:
First of all, I would like to have all my controllers pass by that middleware after processing.
Second, I would like to access the body content so I can generate a header based on its content.
UPDATE
I have tried the suggested answer someone posted, and it is not working, or I am missing something.
This is what I have (before setting up my routes):
app.use(function(req, res, next) {
const oldResJson = res.json;
res.json = function(body) {
res.setHeader('myToken', generateHeaderBasedOnBody(oldResJson));
oldResJson.call(res, body);
}
next();
});
The response that is being passed to my method is an empty string, even though the response sent by the service is not empty. Am I doing this in the wrong place, or what am I missing?
One solution for this issue would be to override the res.json function like so:
// process all responses to add ed25519
app.use(function(req, res, next) {
const oldResJson = res.json;
res.json = function(body) {
res.setHeader('CharCount', /* Use body here */);
oldResJson.call(res, body);
}
next();
});
By doing this, you don't even need to change your controllers.
I'am trying to handle http post message from Mailgun bounce webhook. When sending it to Mailgun's Postbin service all data is found of course. But I'm now sending that POST to my localhost server for development purposes and all I get is empty json array. I use Test Webhook.
Intent is to keep this simple as possible besides our main service. That for I started using nodejs/expressjs to create stand alone webservice to work as relay to receive POST messages of email bounces from Mailgun and inform admins about bounced email addresses.
Now I can't figure why I don't get the same data as is visible in Postbin.
var express = require('express');
var app = express();
var bodyParser = require('body-parser');
var mailgun = require('mailgun-js')({apiKey: 'key-...', domain: 'mymailgundomain.com'});
app.use(bodyParser.urlencoded({
extended: true
}));
function router(app) {
app.post('/webhooks/*', function (req, res, next) {
var body = req.body;
if (!mailgun.validateWebhook(body.timestamp, body.token, body.signature)) {
console.error('Request came, but not from Mailgun');
res.send({ error: { message: 'Invalid signature. Are you even Mailgun?' } });
return;
}
next();
});
app.post('/webhooks/mailgun/', function (req, res) {
// actually handle request here
console.log("got post message");
res.send("ok 200");
});
}
app.listen(5000, function(){
router(app);
console.log("listening post in port 5000");
});
I'm running this from Mailgun's Test Webhook using url like http://mylocalhostwithpublicip.com:5000/webhooks/mailgun
Code structure is copied from https://github.com/1lobby/mailgun-js. Probably I'm missing something fundamental here as I can't figure it out myself.
The reason you're not seeing req.body populated is because the body-parser module does not support multipart/form-data requests. For those kinds of requests you need a different module such as multer, busboy/connect-busboy, multiparty, or formidable.
If your content-type (shown by logging console.dir(req.headers['content-type'])) is 'application/x-www-form-urlencoded', and you're using body-parser, try adding the following line:
bodyParser = require('body-parser')
app.use(bodyParser.urlencoded({ extended: false }))
to make it work with multer, you can use .any() (version 1.1.0)
for me it worked like this: (assuming multer is included and declared as "multer")
post('/track', multer.any(),function(req, res){
//if body is a string, parse the json
var data=(typeof req.body=='string')?JSON.parse(req.body):req.body;
//if data is an object but you can't verify if a field exists with hasOwnProperty, force conversion with JSON
if(typeof data=='object' && typeof data.hasOwnProperty=='undefined')
data=JSON.parse(JSON.stringify(data));
//data is your object
});
var multer = require('multer');
var msg = multer();
post('/track', msg.any(), function(req, res){
console.log(req.body);
}
I make a custom parser for get data in req.body when the Content-type = 'multipart/alternative'
https://github.com/josemadev/Multiparser/