stripe-node: Webhook validate signing error: No signature found matching the expected signature for payload. | express

Another ‘Webhook validate signing’ issue! Whoooohooo 🥳

Hi there! I am trying to built a Backend Server for a Web project with Google Cloud App Engine.

What do I want to achieve:

I want to verify all my received WebHooks from Stripe.

What is my Problem:

Currently I always get the same error message.

❌ 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

I tried lot’s of ways getting the raw body from the request and passing it to the stripe.webhooks.constructEvent Method. But all failed…

My Code to reproduce

app.post('/webhook', bodyParser.raw({type: 'application/json'}), (req, res) => {
  
  const raw = Buffer.from(JSON.stringify(req.body), 'base64').toString('utf8');
  console.log(raw);
  const sig = req.headers['stripe-signature'];
  const endpointSecret = 'whsec_*********';
  let event;

  try {
    event = stripe.webhooks.constructEvent(raw, sig, endpointSecret);
  } 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});
});

As you can see it is nearly identical to the code from the Stripe Docs.

I am not sure if App Engine, from Google Cloud, is parsing the request body before I can even touch it. My only reference point is that if I console.log out the complete request, the body part is always already parsed as a JSON, at least it seems like it.

This is what I get when I do console.log(req):

  { ... },
  body: {
    id: 'evt_1JcW8OKBPpELGMIU63zjXLPM',
    object: 'event',
    api_version: '2020-08-27',
    created: 1632319755,
    data: { object: [Object] },
    livemode: false,
    pending_webhooks: 1,
    request: { id: 'req_mjJqRWVdi4t0Hb', idempotency_key: null },
    type: 'invoice.paid'
},
{ ... }

What I have tried so far

I tried many things.

  • JSON.stringify(req.body) Result:
❌ 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
  • Buffer.from(req.body, 'base64').toString('utf8'); Result:
❌ 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
  • Buffer.from(JSON.stringify(req.body), 'base64').toString('utf8'); (same as in ‘My Code to reproduce’) Result:
❌ 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
  • I have also tried the ‘simple middleware’ idea from jlomas-stripe. But still: Result:
❌ 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
  • req.body.raw Result:
undefined
  • req.body.rawBody Result:
undefined
  • req.rawBody Result:
undefined

At the end, as you can see, I got very frustrated ☹️ …

Is this a Bug or is there a solution to fix it?

I have only tested it with the Stripe CLI.

Name Version
NodeJS v. 14
express v. 4.17.1
stripe v. 8.176.0

Thank you in advance for helping 😄 Greetings from Hamburg, Germany

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Reactions: 1
  • Comments: 20 (1 by maintainers)

Most upvoted comments

Hi, @MNorgren !

After a long debugging session with one very nice Stripe Discord Admin from the Stripe Dev Server the problem was actually very simple.

I did use a bodyParser like this: app.use(express.json()) and this middleware did converted the body into a JSON format before I could get the raw body.

And Stripe needs the raw body to verify the WebHook.

And so now what I have done is the following:

app.use((req, res, next) => {
  if (req.originalUrl === '/webhook') {
    next(); // Do nothing with the body because I need it in a raw state.
  } else {
    express.json()(req, res, next);  // ONLY do express.json() if the received request is NOT a WebHook from Stripe.
  }
});

Hope this helps! Greetings from Hamburg.

@Jonathan-Hofmann - Thank you, that also looks like it would work!

I also found a work-around by using the express.raw({type: 'application/json'}) as specified in the docs, but rather than using request.body, I had to use request.rawBody.

My express route ended up looking like this:

  app.post('/stripe/webhook', express.raw({type: 'application/json'}), asyncHandler(async (req: Request, res: Response, ) => {

    const stripeSignature = req.headers['stripe-signature'];
    if(stripeSignature == null) { throw new UnknownError('No stripe signature found!');  }

    const stripePayload = (req as any).rawBody || req.body;
    const stripeSecret = `whsec_*********`;
    const event = stripe.webhooks.constructEvent(stripePayload, stripeSignature?.toString(), stripeSecret);

    // Handle Event...
    const response = myService.handleEvent(event)
    res.send(response );
  }));

Hi, @MNorgren !

After a long debugging session with one very nice Stripe Discord Admin from the Stripe Dev Server the problem was actually very simple.

I did use a bodyParser like this: app.use(express.json()) and this middleware did converted the body into a JSON format before I could get the raw body.

And Stripe needs the raw body to verify the WebHook.

And so now what I have done is the following:

app.use((req, res, next) => {
  if (req.originalUrl === '/webhook') {
    next(); // Do nothing with the body because I need it in a raw state.
  } else {
    express.json()(req, res, next);  // ONLY do express.json() if the received request is NOT a WebHook from Stripe.
  }
});

Hope this helps! Greetings from Hamburg.

I love you, sincerely

HI all! I have a NestJS app and I am obtaning raw body at application level like this:

//main.ts
const app = await NestFactory.create(AppModule, { rawBody: true });

It works when I use my own credentials at test mode. But when I deploy the code to the production where I use another stripe account credentials with live mode, it throw this error when a payment is received: No signatures found matching the expected signature for payload

I couldn’t find any solution for days. Can you please help me?

Another alternative solution. Since we need to access only the raw request body we can alter the request object to have a raw body property. Since we are using json body parser we can set this in the middleware like this:

app.use(
  express.json({
    verify: (req, res, buf) => {
      req.rawBody = buf;
    },
    limit: '10kb',
  })
);
app.post('/webhook', (req,res,next)=>{
(req, res, next) => {
  const signature = req.headers['stripe-signature'];
  try {
    const event = stripe.webhooks.constructEvent(
      req.rawBody,
      signature,
      process.env.STRIPE_WEBHOOK_SECRET
    );

    if (event.type === 'checkout.session.completed') {
     // sth you want to run
    }

    res.status(200).json({ recived: true });
  } catch (err) {
    res.status(400).send(`Webhook error: ${err.message}`);
  }
}
});

Hi, @MNorgren !

After a long debugging session with one very nice Stripe Discord Admin from the Stripe Dev Server the problem was actually very simple.

I did use a bodyParser like this: app.use(express.json()) and this middleware did converted the body into a JSON format before I could get the raw body.

And Stripe needs the raw body to verify the WebHook.

And so now what I have done is the following:

app.use((req, res, next) => {
  if (req.originalUrl === '/webhook') {
    next(); // Do nothing with the body because I need it in a raw state.
  } else {
    express.json()(req, res, next);  // ONLY do express.json() if the received request is NOT a WebHook from Stripe.
  }
});

Hope this helps! Greetings from Hamburg.

u saved my life, thanks!!