Files
2026-02-10 01:14:19 +00:00

73 lines
2.1 KiB
TypeScript

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 }
);
}
}