For anyone using Next.js you should check stripe documentation for your local webhook secret and this is my code for anyone that will need it or you could use the example from stripe github nextjs example
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
import type { NextApiRequest, NextApiResponse } from 'next'
import NextCors from 'nextjs-cors';
import { config as localConfig } from '../../config'
import Stripe from 'stripe';
type Data = {
name?: string,
error?: string,
message?: string,
status?: string,
}
export const config = {
api: {
bodyParser: false,
},
};
export default async function handler(
req: NextApiRequest,
res: NextApiResponse<Data>
) {
await NextCors(req, res, {
// Options
methods: ['GET', 'HEAD', 'PUT', 'PATCH', 'POST', 'DELETE'],
origin: '*',
});
const stripeInstance = new Stripe(localConfig.stripeSecretKey, { apiVersion: '2022-11-15' });
const endpointSecret = localConfig.stripeEndpointSecret
if (req.method === 'POST') {
// let event: Stripe.Event;
let event = req.body
// console.log(event)
// const clientReferenceId = req.body.data.object.client_reference_id
// console.log("clientReferenceId: ", clientReferenceId)
// Only verify the event if you have an endpoint secret defined.
// Otherwise use the basic event deserialized with JSON.parse
if (endpointSecret) {
// Get the signature sent by Stripe
const signature = req.headers['stripe-signature'] as string;
// console.log("signature: ", signature)
try {
const body = await buffer(req);
event = stripeInstance.webhooks.constructEvent(
body,
signature,
endpointSecret
);
} catch (err: any) {
console.log(`⚠️ Webhook signature verification failed.`, err.message);
// res.status(400).json({ error: err.message })
}
}
// Handle the event
switch (event.type) {
case 'payment_intent.succeeded':
const paymentIntent = event.data.object;
console.log(`PaymentIntent for ${paymentIntent.amount} was successful!`);
// Then define and call a method to handle the successful payment intent.
// handlePaymentIntentSucceeded(paymentIntent);
break;
case 'payment_method.attached':
const paymentMethod = event.data.object;
// Then define and call a method to handle the successful attachment of a PaymentMethod.
// handlePaymentMethodAttached(paymentMethod);
break;
case 'checkout.session.async_payment_failed':
const checkoutSessionAsyncPaymentFailed = event.data.object;
// Then define and call a function to handle the event checkout.session.async_payment_failed
break;
case 'checkout.session.async_payment_succeeded':
const checkoutSessionAsyncPaymentSucceeded = event.data.object;
// Then define and call a function to handle the event checkout.session.async_payment_succeeded
break;
case 'checkout.session.completed':
const checkoutSessionCompleted = event.data.object;
// Then define and call a function to handle the event checkout.session.completed
break;
case 'checkout.session.expired':
const checkoutSessionExpired = event.data.object;
// Then define and call a function to handle the event checkout.session.expired
break;
default:
// Unexpected event type
console.log(`Unhandled event type ${event.type}.`);
}
res.status(200).json({ status: "success", message: "Webhook received" })
} else {
res.status(405).json({ status: "error", message: "Method not allowd" })
}
}
const buffer = (req: NextApiRequest) => {
return new Promise<Buffer>((resolve, reject) => {
const chunks: Buffer[] = [];
req.on('data', (chunk: Buffer) => {
chunks.push(chunk);
});
req.on('end', () => {
resolve(Buffer.concat(chunks));
});
req.on('error', reject);
});
};