import { NextResponse } from "next/server"; import { stripeClient } from "../../../../lib/stripeClient"; export const runtime = "nodejs"; function logEvent(event: any) { const relatedId = event?.related_object?.id; const objectId = relatedId || event?.data?.object?.id; console.log( `[webhook] type=${event?.type} id=${event?.id} object_id=${objectId || "n/a"} livemode=${event?.livemode}` ); } export async function POST(req: Request) { const webhookSecret = process.env.STRIPE_BILLING_WEBHOOK_SECRET; if (!webhookSecret) { return NextResponse.json( { error: "Missing STRIPE_BILLING_WEBHOOK_SECRET." }, { status: 500 } ); } const signature = req.headers.get("stripe-signature"); if (!signature) { return NextResponse.json({ error: "Missing Stripe signature." }, { status: 400 }); } const rawBody = Buffer.from(await req.arrayBuffer()); try { // Try snapshot payload parsing first (standard webhook flow). try { const event = stripeClient.webhooks.constructEvent( rawBody, signature, webhookSecret ); logEvent(event); if ((event as any).type === "v1.billing.meter.error_report_triggered") { // Log only per requirements. } return NextResponse.json({ received: true }); } catch { // Fall through to thin payload handling. } const clientAny = stripeClient as any; if (typeof clientAny.parseThinEvent !== "function") { return NextResponse.json( { error: "Stripe SDK does not support parseThinEvent." }, { status: 500 } ); } const thinEvent = clientAny.parseThinEvent(rawBody, signature, webhookSecret); const fullEvent = await stripeClient.events.retrieve(thinEvent.id); logEvent(fullEvent); if ((fullEvent as any).type === "v1.billing.meter.error_report_triggered") { // Log only per requirements. } return NextResponse.json({ received: true }); } catch (err: any) { return NextResponse.json( { error: err?.message || "Webhook handler failed." }, { status: 400 } ); } }