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!
Related
This question is addressed ad nauseum on SO, but they all seem to tell people to use body-parser.
I'm trying to use the built-in parser in Express, rather than body-parser, since body-parser was re-added into Express 4.x so now they're the same thing.
However I'm getting an empty req.body with either multipart/form-data or x-www-urlencoded on both the HTML form and from Postman (resulting in "incorrect username or password" from passport, but that's a separate issue):
const express = require('express');
require('dotenv');
const passport = require('passport');
const mongoose = require('mongoose')
const User = require('../models/user');
passport.initialize();
// CHANGE: USE "createStrategy" INSTEAD OF "authenticate"
passport.use(User.createStrategy());
passport.serializeUser(User.serializeUser());
passport.deserializeUser(User.deserializeUser());
require('mongoose-type-url');
//connect to database
mongoose.connect(process.env.DATABASE_URL,{
useNewUrlParser:true,
useUnifiedTopology:true,
useFindAndModify: false,
useCreateIndex:true
}).then(() => {
console.log('Connected to Mongo DB');
}).catch(err => {
console.log('Mongoose error: ',err);
});
const app = express();
/* This is where npx express-generator automatically created the 'app.use'
lines for the input parsers, both for json and urlencoded.
I haven't touched this, it should be working just fine */
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(require('cookie-parser')());
app.use(require('express-session')({
secret:"more things in the world",
resave: false,
saveUninitialized: true
}));
app.use(passport.initialize());
app.use(passport.session());
app.post('/login', testPostLogin);
// POST /login
async function testPostLogin(req, res, next) {
const {username, password } = req.body;
const { user, error } = await User.authenticate()(username, password);
if(!user && error) {
return next(error);
}
req.login(user, function (err) {
if(err){
return next(err);
}
req.session.success = `Welcome back, ${username}`;
/*if they came to the login page from somewhere that called 'isLoggedIn',
send them back there after logging in, then remove the hook to do that */
const redirectUrl = req.session.redirectTo || '/';
delete req.session.redirectTo;
res.redirect(redirectUrl);
});
}
let port = process.env.PORT;
if (port == null || port == "") {
port = 8080;
}
app.listen(port, () => {
console.log("server has started, listening on port "+port);
});
Postman curl:
curl --location --request POST 'http://127.0.0.1:8080/login' \
--header 'Cookie: connect.sid=s%3A0aRKbFHVGSiUk-53HCZjuHKqYFAHIV7j.H9SDAJEDwY4rla2K%2BQ7ejiIJCMekMvhyeU8IH1sIh%2BE' \
--form 'username="bob"' \
--form 'password="password"'
Am I missing something stupidly obvious in my setup that is preventing express from parsing the body of the POST request?
UPDATE 2021-01-26 16:04 CST: I tried subbing in body-parser just to see what would happen, and still zilch. So is the issue that the middleware isn't being called on the route? Does that mean there's something wrong in my order of requirements?
if you read body-parser documentation it's not possible, you should use third party module
This does not handle multipart bodies, due to their complex and typically large nature. For multipart bodies, you may be interested in the following modules:
busboy and connect-busboy, multiparty and connect-multiparty, formidable,
multer
If your form isn't sending any actual multipart information, only text, the express.urlencoded() should be able to parse it if sent with application/x-www-form-urlencoded headers
Besides the solutions with formidable, there is another module which I have been using in my recent projects since 2019. The module express-form-data can be easily declared in your server file like:
const formData = require('express-form-data');
app.use(formData.parse());
async function testPostLogin(req, res, next) {
const {username, password } = req.body;
}
The above is going to work with x-www-form-urlencoded and json but it will NOT work with any multipart. form-data is also multipart with the header multipart/form-data.
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}));
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 am not able to get the data in the http post method of express nodejs.I am posting data from angular2. When i inspect in the network section of chrome, the payload is visible but the same data is received blank at app.post method.Please help me.
angular2 code
this.headers = new Headers();
this.headers.append('Content-Type', 'x-www-form-urlencoded');
let body = JSON.stringify({name:"Lionel Messi"});
return this.http
.post('http://localhost:8081/save',body
,this.headers);
}
nodejs code
var bodyParser = require("body-parser");
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.post('/save', function (req, res) {
console.log("Got a POST request for the homepage");
console.log(req.body);// output - {}
res.send('Hello POST');
})
Network Section in Chrome....payload is proper
alert method in node.js will not work . You need to use console.log("Hello");
Second thing is to get body data , use req.body.name
My way of writing code is like below and it works
$http({
method: 'POST',
url: 'http://localhost:8081/save',
data: {name:"Lionel Messi"}
})
.success(function(data) {
return data
})
.error(function(error) {
// handle error
});
Other way you can try is:
$http.post('http://localhost:8081/save', {name:"Lionel Messi"})
.then(function(data) {return data})
.catch(function() {console.log("Error Occured");});
You can do it like this-
Suppose you have sent username and password from your browser by post method.
app.post("/ url,function(request,response)
{ var username=request.body.username;
var password=request.body.password;})
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/