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

Google Cloud-PlatformGoogle Cloud-FunctionsStripe Payments

Google Cloud-Platform Problem Overview


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

Google Cloud-Platform Solutions


Solution 1 - Google Cloud-Platform

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.

Solution 2 - Google Cloud-Platform

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 });
}

Solution 3 - Google Cloud-Platform

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

Solution 4 - Google Cloud-Platform

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. :)

Solution 5 - Google Cloud-Platform

2 things to note:

  1. pass req.rawBody instead of req.body to constructEvent
const event = stripe.webhooks.constructEvent(
        req.rawBody,
        sig,
        STRIPE_WEBHOOK_SECRET
      );
  1. Make sure you're using the correct webhook secret. It's unique per webhook url!

enter image description here

Solution 6 - Google Cloud-Platform

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

Solution 7 - Google Cloud-Platform

// Use JSON parser for all non-webhook routes
app.use(
  bodyParser.json({
    verify: (req, res, buf) => {
      const url = req.originalUrl;
      if (url.startsWith('/api/stripe/webhook')) {
        req.rawBody = buf.toString();
      }
    }
  })
);

The above code will look fine for the above answers. But even I was made one mistake. After put the same thing I got the same error.

Finally, I've figured it out if you're configured body-parser below the rawBody code then it'll work.

Like this

// Use JSON parser for all non-webhook routes
app.use(
  bodyParser.json({
    verify: (req, res, buf) => {
      const url = req.originalUrl;
      if (url.startsWith('/api/stripe/webhook')) {
        req.rawBody = buf.toString();
      }
    }
  })
);
// Setup express response and body parser configurations
app.use(express.json());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));

Hopefully, it'll help someone.

Solution 8 - Google Cloud-Platform

I was able to obtain data from one webhook but not from a second one: the problem was that the secret key I used was the same as the one used for the first webhook, but I found out that every webhook has a different key, that's way I got that same message.

Solution 9 - Google Cloud-Platform

This happened to me when sending a test webhook from the Stripe dashboard after I had renamed a firebase cloud function. All my other functions were working fine. Solved by re-setting in the terminal firebase functions:config:set stripe.webhook_signature="Your webhook signing secret" (if you're using that) and redeploying the functions firebase deploy --only functions

On a second occasion I solved the problem by rolling the stripe signature in the stripe dashboard.

Solution 10 - Google Cloud-Platform

Please use this script

app.use(
  bodyParser.json({
    verify: (req, res, buf) => {
      req.rawBody = buf;
    },
  })
);

Solution 11 - Google Cloud-Platform

AWS API Gateway + Lambda (Express.js CRUD) I'm using this for Stripe webhook endpoint and it works for me:

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

Solution 12 - Google Cloud-Platform

My fave was combining two of above great answers.

Then you can use req.rawbody when you construct the event.

Replace "webhook" with whatever route you wish you have a raw body for.

app.use(
  "/webhook",
  express.json({
    verify: (req, res, buf) => {
      req.rawBody = buf.toString();
    },
  })
);

BEFORE

app.use(express.json());

Works well if you are using routes and controllers.

Solution 13 - Google Cloud-Platform

To use raw body in express with a specific endpoint in a seperated middleware, my solution is just enabling router to use express.raw for the webhook endpoint. -node.js v12 -express.js v4.17.1

export const handleBodyRequestParsing = (router: Router): void => {
  router.use('/your_webhook_endpoint', express.raw({ type: '*/*' }))
  router.use(express.json({ limit: '100mb' }))
  router.use(express.urlencoded({ extended: true }))
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionZat42View Question on Stackoverflow
Solution 1 - Google Cloud-PlatformDoug StevensonView Answer on Stackoverflow
Solution 2 - Google Cloud-PlatformMatiasGView Answer on Stackoverflow
Solution 3 - Google Cloud-PlatformNitin KumarView Answer on Stackoverflow
Solution 4 - Google Cloud-PlatformFábio BC SouzaView Answer on Stackoverflow
Solution 5 - Google Cloud-PlatformGorvGoylView Answer on Stackoverflow
Solution 6 - Google Cloud-PlatformMuhammad ShahzadView Answer on Stackoverflow
Solution 7 - Google Cloud-PlatformMohamed JakkariyaView Answer on Stackoverflow
Solution 8 - Google Cloud-PlatformmarcolavView Answer on Stackoverflow
Solution 9 - Google Cloud-PlatformMarkView Answer on Stackoverflow
Solution 10 - Google Cloud-PlatformEng. GathechaView Answer on Stackoverflow
Solution 11 - Google Cloud-PlatformTomasz CzechowskiView Answer on Stackoverflow
Solution 12 - Google Cloud-PlatformOscar EkstrandView Answer on Stackoverflow
Solution 13 - Google Cloud-PlatformBarbarosView Answer on Stackoverflow