Payment webhooks
v1 (current)How Stripe, Paystack, and Flutterwave webhooks are verified.
Each payment provider's webhook route verifies the request's signature before touching the database. An unconfigured provider (no secret key set) returns 503 rather than silently accepting unverified webhooks; a configured provider that receives a request with an invalid or missing signature returns 400.
let event: Stripe.Event;
try {
event = verifyStripeWebhookSignature(rawBody, signature);
} catch {
return NextResponse.json({ error: "Invalid signature." }, { status: 400 });
}On checkout.session.completed, the route reads the organizationId/productId/productPlanId metadata attached at checkout creation and upserts a Subscription row. On invoice.paid, it creates an Invoice row with the real amount, currency, and billing period from the provider's payload.
No live payment provider keys are configured in this environment. The signature-verification path has been exercised with temporary dummy keys during development and confirmed to correctly reject invalid and missing signatures — but no real transaction has been processed end-to-end.