10

Pulling my hair out trying to pass a raw body for Stripe webhook on NextJS!.

Trying lots of solutions from everywhere and I cant seem to make it work.

Opening it up the devs with superpowers (of which I am still acquiring).

Error from Stripe Test:

No signatures found matching the expected signature for payload. Are you passing the raw request body you received from Stripe

My NextJS webhook endpoint test:

import { buffer } from 'micro';
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);

export default async function handler(req, res) {

    console.log("Payment intent")

    const event = req.body

    console.log(event)

    if (process.env.STRIPE_WEBHOOK_ENDPOINT_SECRET) {
        // Get the signature sent by Stripe
        const signature = req.headers['stripe-signature'];
        console.log(signature)
        try {
          event = stripe.webhooks.constructEvent(
            req.body,
            signature,
            process.env.STRIPE_WEBHOOK_ENDPOINT_SECRET
          );
        } catch (err) {
          console.log(`⚠️  Webhook signature verification failed.`, err.message);
          return res.sendStatus(400);
        }
      }

    console.log(event.type)

    // Handle the event
    switch (event.type) {
        case 'payment_intent.succeeded':
            console.log("Success!")
            break;
        default:
            console.log(`Unhandled event type ${event.type}`);
    }



}

Have also tried:

import { buffer } from 'micro';
import Cors from 'micro-cors';

const stripe = require("stripe")(process.env.STRIPE_SECRET_KEY);

const webhookSecret = process.env.STRIPE_WEBHOOK_ENDPOINT_SECRET_NEW;

// Stripe requires the raw body to construct the event.
export const config = {
  api: {
    bodyParser: false,
  },
};

const cors = Cors({
  allowMethods: ['POST', 'HEAD'],
});

const webhookHandler = async (req, res) => {

  if (req.method === 'POST') {
    const buf = await buffer(req);
    console.log(buf.toString())
    const sig = req.headers['stripe-signature'];
    console.log(process.env.STRIPE_SECRET_KEY)
    console.log(webhookSecret)
    console.log(sig)
    let event;

    try {
      event = stripe.webhooks.constructEvent(
        buf.toString(),
        sig,
        webhookSecret
      );
    } catch (err) {
      console.log(`❌ Error message: ${err.message}`);
      res.status(400).send(`Webhook Error: ${err.message}`);
      return;
    }


    if (event.type === 'payment_intent.succeeded') {
      const paymentIntent = event.data.object;
      console.log(` PaymentIntent status: ${paymentIntent.status}`);
    } else if (event.type === 'payment_intent.payment_failed') {
      const paymentIntent = event.data.object;
      console.log(
        `❌ Payment failed: ${paymentIntent.last_payment_error?.message}`
      );
    } else if (event.type === 'charge.succeeded') {
      const charge = event.data.object;
      console.log(` Charge id: ${charge.id}`);
    } else {
      console.warn(`‍♀️ Unhandled event type: ${event.type}`);
    }

    res.json({ received: true });
  } else {
    res.setHeader('Allow', 'POST');
    res.status(405).end('Method Not Allowed');
  }
};

export default cors(webhookHandler);
Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
SteveW
  • 337
  • 4
  • 11

3 Answers3

7

By default, NextJS parses the the request body based upon the incoming Content-Type in the headers. You would want to disable this [0] and then consume it as a stream using buffer.

The below code works for me :

export const config = {
  api: {
    bodyParser: false,
  },
};

import { buffer } from 'micro';
const stripe = require('stripe')(process.env.STRIPE_WEBHOOK_ENDPOINT_SECRET);


export default async function handler(req, res) {

    let event;

    if (process.env.STRIPE_WEBHOOK_ENDPOINT_SECRET) {
        // Get the signature sent by Stripe
        const signature = req.headers['stripe-signature'];
        const buf = await buffer(req);

        try {
          event = stripe.webhooks.constructEvent(
            buf,
            signature,
            process.env.STRIPE_WEBHOOK_ENDPOINT_SECRET
          );
...

[0] https://nextjs.org/docs/api-routes/api-middlewares#custom-config

alex
  • 1,923
  • 11
  • 9
  • 1
    Sorry. I actually had the bodyParser set to false. Was not in the code I presented. And I have attempted to use micro buffer too. Still the same outcome. – SteveW Nov 15 '21 at 08:15
  • 1
    So I create the webhook in the Stripe dashboard. It hits my API point. I click on 'Signing Secret' in the webhook to get the secret 'whsec_...' This is all done under Stripe testing. Not Live. So the req.body is at the endpoint, the stripe signatures in the header and my webhook secret but I just cant get passed stripe.webhooks.constructEvent() – SteveW Nov 15 '21 at 08:21
  • 1
    Update question with new code I am trying. Same result. – SteveW Nov 15 '21 at 08:29
  • i've tried running the second example you have and it works for me. The next thing you would probably want to check is your secret key and webhook. I'd suggest logging them in your console just before the `stripe.webhooks.constructEvent` method is run to make sure that they match up. i.e. the secret key should look like sk_test_.... and should be from the same Stripe account as where you're retrieving the webhook secret – alex Nov 15 '21 at 08:48
  • I have recreated my hook and I get req.body as undefined. Everything else is fine. – SteveW Nov 15 '21 at 09:22
  • I have updated the second webhook code with my logging. My secret key is the one I use throughout my app. Never had issues with it. Can create PaymentIntents with it that trigger this webhook. I have triple checked the webhook key (copy and pasted) And I get Stripe signature in the header Yet still not working. And yet you had no problems? – SteveW Nov 15 '21 at 09:58
  • How are you sending webhooks events to your webhook server? Are you using the Stripe CLI? If you're using the Stripe CLI, you would want to use the CLI webhook secret instead. – alex Nov 15 '21 at 10:28
  • No am I sending a webhook configured in the dashboard - in test mode. – SteveW Nov 15 '21 at 11:01
  • i'm all out of ideas unfortunately, maybe someone else may have an answer – alex Nov 15 '21 at 12:47
  • 3
    Is there a way to do this without the "micro" package? I'm reluctant to pull in another dependency just for this ... – jameshfisher Apr 26 '22 at 18:40
2

To solve this, I disabled the NextJS body parser by exporting the config object in the same api route file:

export const config = {
  api: {
    bodyParser: false,
  },
};

Then I imported the raw-body package so I can convert the request stream into a raw buffer:

var getRawBody = require('raw-body')

Finally I used the raw-body package via promise interface:

if (endpointSecret) {
  const signature = request.headers['stripe-signature'];
  console.log("sig", signature);
  getRawBody(request)
    .then(function (buf) {
      rawBody = buf;
      event = stripe.webhooks.constructEvent(
        rawBody,
        signature,
        endpointSecret
      );
      let subscription;
      let status;
      // Handle the event
      switch (event.type) {
        case 'customer.subscription.trial_will_end':
          subscription = event.data.object;
        
        default:
          // Unexpected event type
          console.log(`Unhandled event type ${event.type}.`);
      }
      // Return a 200 response to acknowledge receipt of the event
      return response.status(200);
    })
  .catch(function (err) {
    console.log(`⚠️  Webhook signature verification failed.`, err.message);
    return response.status(500);
  })
} else {
  console.log("Missing endpoint secret");
  return response.status(500);
}

This worked for me. Hope it can help you out.

philip_kobernik
  • 426
  • 5
  • 7
0

I understand that this question may be old, but for anyone who comes across it in the future, I want to share that I have successfully integrated Stripe into my project using webhooks and the new /app router.

I have documented the entire step-by-step tutorial in the README.md file.

Feel free to check out the repository at the following link: https://github.com/BastidaNicolas/nextauth-prisma-stripe

Knee-co
  • 333
  • 2
  • 9