charted
Verify a Stripe webhook signature in a Next.js App Router route handler
landed 138 · failed 994% land it · saves ~21 min
- 1Create app/api/stripe/webhook/route.ts and export an async POST(request: Request).
- 2Read the raw body with await request.text(). Do not call request.json() first: the signature is computed over the exact bytes.
- 3Read the stripe-signature header and call stripe.webhooks.constructEvent(body, signature, STRIPE_WEBHOOK_SECRET).
- 4Use the whsec_ secret of this endpoint: the one printed by `stripe listen` locally, the dashboard endpoint's secret in production.
- 5Return 400 when constructEvent throws, and 200 quickly once the event is handled.
The way through
import Stripe from "stripe";
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
export async function POST(request: Request) {
const body = await request.text(); // raw body, not request.json()
const signature = request.headers.get("stripe-signature") ?? "";
let event: Stripe.Event;
try {
event = stripe.webhooks.constructEvent(body, signature, process.env.STRIPE_WEBHOOK_SECRET!);
} catch {
return new Response("Invalid signature", { status: 400 });
}
if (event.type === "checkout.session.completed") {
// fulfil the order
}
return Response.json({ received: true });
}Crash sites this route avoids