import Stripe from 'stripe'; import { headers } from 'next/headers'; import { NextResponse } from 'next/server'; import { db } from '@/lib/db'; import { stripe } from '@/lib/stripe'; import arcjet, { fixedWindow } from '@/lib/arcjet'; const aj = arcjet.withRule( fixedWindow({ mode: 'LIVE', max: 10, window: '60s', }) ); export async function POST(req: Request) { const decision = await aj.protect(req); if (decision.isDenied()) return new NextResponse( JSON.stringify({ error: 'Too many requests', reason: decision.reason }), { status: 429 } ); const body = await req.text(); const signature = (await headers()).get('Stripe-Signature') as string; let event: Stripe.Event; try { event = stripe.webhooks.constructEvent( body, signature, process.env.STRIPE_WEBHOOK_SECRET! ); } catch (error) { return new NextResponse(JSON.stringify(error), { status: 400 }); } const session = event.data.object as Stripe.Checkout.Session; if (event.type === 'checkout.session.completed') { const subscription = await stripe.subscriptions.retrieve( session.subscription as string ); if (!session?.metadata?.orgId) { return new NextResponse(JSON.stringify({ error: 'Missing orgId' }), { status: 400, }); } await db.orgSubscription.create({ data: { orgId: session.metadata.orgId, stripeSubscriptionId: subscription.id, stripeCustomerId: session.customer as string, stripePriceId: subscription.items.data[0].price.id, stripeCurrentPeriodEnd: new Date( subscription.current_period_end * 1000 ), }, }); } if (event.type === 'invoice.payment_succeeded') { const subscription = await stripe.subscriptions.retrieve( session.subscription as string ); await db.orgSubscription.update({ where: { stripeSubscriptionId: subscription.id, }, data: { stripePriceId: subscription.items.data[0].price.id, stripeCurrentPeriodEnd: new Date( subscription.current_period_end * 1000 ), }, }); } return NextResponse.json(null, { status: 200 }); }