Stripe Error: No signatures found matching the expected signature for payload

I have a stripe webhook that call a Firebase function. In this function I need to verify that this request comes from Stripe servers. Here is the code :

const functions = require('firebase-functions');
const bodyParser = require('body-parser');
const stripe = require("stripe")("sk_test_****");
const endpointSecret = 'whsec_****';
const app = require('express')();

app.use(bodyParser.json({
    verify: function (req, res, buf) {
        var url = req.originalUrl;
        if (url.startsWith('/webhook')) {
            req.rawBody = buf.toString()
        }
    }
}));

app.post('/webhook/example', (req, res) => {
    let sig = req.headers["stripe-signature"];

    try {
        console.log(req.bodyRaw)
        let event = stripe.webhooks.constructEvent(req.body, sig, endpointSecret);
        console.log(event);
        res.status(200).end()

        // Do something with event
    }
    catch (err) {
        console.log(err);
        res.status(400).end()
    }
});

exports.app = functions.https.onRequest(app);

As mentioned in Stripe Documentation, I have to use raw body to perform this security check.

I have tried with my current code and with :

app.use(require('body-parser').raw({type: '*/*'}));

But I always get this error :

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

Cloud Functions automatically parses body content of known types. If you're getting JSON, then it's already parsed and available to you in req.body. You shouldn't need to add other body parsing middleware.

If you need to process the raw data, you should use req.rawBody, but I don't think you'll need to do that here.


Here is code which is working for me:

app.use(bodyParser.json({
  verify: function (req, res, buf) {
    var url = req.originalUrl;
    if (url.startsWith('/stripe')) {
       req.rawBody = buf.toString();
    }
  }
}));

And then pass the req.rawBody for verification

stripe.checkWebHook(req.rawBody, signature);

Reference: https://github.com/stripe/stripe-node/issues/341


Here is what is working for me:

add this line:

app.use('/api/subs/stripe-webhook', bodyParser.raw({type: "*/*"}))

(The first argument specifies which route we should use the raw body parser on. See the app.use() reference doc.)

just before this line:

app.use(bodyParser.json());

(it doesn't affect all your operation, just this: '/api/subs/stripe-webhook')

Note: If you are using Express 4.16+ you can replace bodyParser by express:

app.use('/api/subs/stripe-webhook', express.raw({type: "*/*"}));
app.use(express.json());

Then:

const endpointSecret = 'whsec_........'

const stripeWebhook = async (req, res) => {
    const sig = req.headers['stripe-signature'];

    let eventSecure = {}
    try {
        eventSecure = stripe.webhooks.constructEvent(req.body, sig, endpointSecret);
        //console.log('eventSecure :', eventSecure);
    }
    catch (err) {
        console.log('err.message :', err.message);
        res.status(400).send(`Webhook Secure Error: ${err.message}`)
        return
    }
    res.status(200).send({ received: true });
}

2021 - Solution

I faced that error, and after a lot research I could not figure out the problem easily, but finally I could do it based in my architecture below:

//App.js

this.server.use((req, res, next) => {
  if (req.originalUrl.startsWith('/webhook')) {
    next();
  } else {
    express.json()(req, res, next);
  }
});
//routes.js

routes.post(
  '/webhook-payment-intent-update',
  bodyParser.raw({ type: 'application/json' }),

  //your stripe logic (Im using a controller, but wherever)
  (req, res) => {
    stripe.webhooks.constructEvent(...)
  }
)

Two big warnings to pay attention:

  • Make sure to send the req.headers['stripe-signature']
  • Make sure that your endpointSecret is right, if not it will still saying the same error

Tips:

  • Test it locally by installing the Stripe CLI: https://stripe.com/docs/webhooks/test

  • Verify your key on stripe dashboard or you can also make sure if you have the right key by verifying you stripe log as below:

webhook secret key example

I hope it helps you. :)


It is late but will help others

Github answer

    const payload = req.body
    const sig = req.headers['stripe-signature']
    const payloadString = JSON.stringify(payload, null, 2);
    const secret = 'webhook_secret';
    const header = stripe.webhooks.generateTestHeaderString({
            payload: payloadString,
            secret,
    });
    
     let event;
     try {
          event = stripe.webhooks.constructEvent(payloadString, header, secret);
    
     } catch (err) {
            console.log(`Webhook Error: ${err.message}`)
            return res.status(400).send(`Webhook Error: ${err.message}`);
     }

       switch (event.type) {
           case 'checkout.session.completed': {
       ......
    enter code here