import { NextRequest, NextResponse } from 'next/server' import { auth } from '@/auth' import { stripe } from '@/lib/stripe' import { isBillingEnabled } from '@/lib/billing/stripe-prices' import { isCreditPackId, resolvePackPriceId, getCreditPack, } from '@/lib/billing/credit-packs' import { prisma } from '@/lib/prisma' import { z } from 'zod' const bodySchema = z.object({ packId: z.enum(['S', 'M', 'L']), }) export async function POST(req: NextRequest) { const session = await auth() if (!session?.user?.id || !session.user.email) { return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }) } if (!(await isBillingEnabled())) { return NextResponse.json({ error: 'Billing is not enabled' }, { status: 403 }) } const secret = process.env.STRIPE_SECRET_KEY if (!secret || secret === 'sk_test_placeholder') { return NextResponse.json( { error: 'Stripe is not configured (STRIPE_SECRET_KEY missing)' }, { status: 503 }, ) } const parsed = bodySchema.safeParse(await req.json()) if (!parsed.success || !isCreditPackId(parsed.data.packId)) { return NextResponse.json({ error: 'Invalid pack' }, { status: 400 }) } const packId = parsed.data.packId const pack = getCreditPack(packId) const userId = session.user.id const userEmail = session.user.email try { let priceId: string try { priceId = await resolvePackPriceId(packId) } catch (e) { console.error('[billing/create-pack-checkout] price resolve failed:', e) return NextResponse.json( { error: 'Credit pack price IDs not configured. Set them in Admin > Billing.' }, { status: 503 }, ) } if (priceId.startsWith('price_mock_')) { return NextResponse.json( { error: 'Credit pack price IDs not configured. Set them in Admin > Billing.' }, { status: 503 }, ) } const subscription = await prisma.subscription.findUnique({ where: { userId } }) let customerId = subscription?.stripeCustomerId ?? undefined if (customerId && customerId.startsWith('cus_mock')) { customerId = undefined } if (!customerId) { const customer = await stripe.customers.create({ email: userEmail, metadata: { userId }, }) customerId = customer.id await prisma.subscription.upsert({ where: { userId }, update: { stripeCustomerId: customerId }, create: { userId, stripeCustomerId: customerId, tier: 'BASIC', status: 'ACTIVE', currentPeriodStart: new Date(), currentPeriodEnd: new Date(Date.now() + 30 * 24 * 3600 * 1000), }, }) } const host = req.headers.get('x-forwarded-host') ?? req.headers.get('host') ?? 'localhost:3000' const proto = req.headers.get('x-forwarded-proto') ?? 'http' const origin = `${proto}://${host}` const checkoutSession = await stripe.checkout.sessions.create({ customer: customerId, mode: 'payment', line_items: [{ price: priceId, quantity: 1 }], success_url: `${origin}/settings/billing?session_id={CHECKOUT_SESSION_ID}&pack=1`, cancel_url: `${origin}/settings/billing?canceled=1`, metadata: { userId, type: 'credit_pack', packId, credits: String(pack.credits), }, payment_intent_data: { metadata: { userId, type: 'credit_pack', packId, credits: String(pack.credits), }, }, customer_update: { address: 'auto' }, allow_promotion_codes: true, }) if (!checkoutSession.url) { return NextResponse.json({ error: 'Checkout session has no URL' }, { status: 500 }) } return NextResponse.json({ url: checkoutSession.url, sessionId: checkoutSession.id, packId, credits: pack.credits, }) } catch (error) { console.error('[billing/create-pack-checkout]', error) const msg = error instanceof Error ? error.message : 'Failed to create pack checkout' return NextResponse.json({ error: msg }, { status: 500 }) } }