Unverified Stripe webhooks in Next.js
ts import Stripe from "stripe";
Fix for Next.js
import Stripe from "stripe";
export async function POST(request: Request) {
const body = await request.text();
const signature = request.headers.get("stripe-signature");
const secret = process.env.STRIPE_WEBHOOK_SECRET;
if (!signature || !secret || !process.env.STRIPE_SECRET_KEY) {
return new Response("Misconfigured", { status: 500 });
}
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);
let event: Stripe.Event;
try {
event = stripe.webhooks.constructEvent(body, signature, secret);
} catch {
return new Response("Invalid signature", { status: 400 });
}
if (event.type === "checkout.session.completed") {
// Update the order from event.data, which Stripe signed.
}
return new Response("ok");
}
Use the method Stripe's current Node docs show if it differs from constructEvent. The requirement is the same: raw payload, header, endpoint secret, and no side effects when verification throws.
Ignore events you do not handle. Do not grant access from payment_intent.succeeded and also from the client after confirmCardPayment. The webhook is the source of truth. The browser is not. See the client sets the price or role.
The five-minute check, the other platforms, and the agent prompt are on the full guide. More Next.js fixes are on the Next.js page.