import { NextRequest, NextResponse } from "next/server"; import { prisma } from "@/lib/db"; import { getSessionUser } from "@/lib/session"; import { getStripe, isBillingDevMock, isStripeConfigured } from "@/lib/stripe"; /** * Stripe Customer Portal update payment method, view invoices, cancel (per Dashboard config). * @see https://docs.stripe.com/customer-management/integrate-customer-portal */ export async function POST(req: NextRequest) { const user = await getSessionUser(); if (!user) { return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); } if (isBillingDevMock()) { return NextResponse.json( { error: "Billing portal is unavailable in mock mode." }, { status: 503 }, ); } if (!isStripeConfigured()) { return NextResponse.json( { error: "Payments are not configured yet. Set STRIPE_SECRET_KEY." }, { status: 503 }, ); } const dbUser = await prisma.user.findUniqueOrThrow({ where: { id: user.id }, select: { stripeCustomerId: true }, }); if (!dbUser.stripeCustomerId) { return NextResponse.json( { error: "No Stripe customer on this account. Complete a checkout first." }, { status: 400 }, ); } const origin = process.env.NEXTAUTH_URL || req.nextUrl.origin; const stripe = getStripe(); try { const session = await stripe.billingPortal.sessions.create({ customer: dbUser.stripeCustomerId, return_url: `${origin}/dashboard/settings`, }); return NextResponse.json({ url: session.url }); } catch (err) { const message = err instanceof Error ? err.message : "Could not open billing portal"; console.error("[stripe] billing portal failed", err); return NextResponse.json( { error: message.includes("No configuration") || message.includes("portal") ? "Billing portal is not configured in Stripe Dashboard yet. Enable it under Settings → Billing → Customer portal." : message, }, { status: 502 }, ); } }