Your Name 7432cfc890
Some checks failed
Lint / build (20) (push) Has been cancelled
Playwright Tests / test (push) Has been cancelled
feat: wire lovart pricing checkout and stripe webhook sync
2026-02-12 12:43:27 +08:00

74 lines
2.4 KiB
TypeScript

import { type NextRequest, NextResponse } from "next/server";
import { z } from "zod";
import { auth } from "@/app/(auth)/auth";
import { getStripePriceIdForPlan, isPaidBillingPlanId, type PaidBillingPlanId } from "@/lib/billing/plans";
import { createStripeCheckoutSession, StripeRequestError } from "@/lib/billing/stripe";
const checkoutSchema = z.object({
planId: z.string(),
});
function getExternalBase(request: NextRequest) {
return (
process.env.AUTH_URL ??
`${request.nextUrl.protocol}//${request.headers.get("host") ?? request.nextUrl.host}`
);
}
export async function POST(request: NextRequest) {
try {
const session = await auth();
if (!session?.user?.id || !session.user.email) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
if (session.user.type === "guest") {
return NextResponse.json({ error: "Guest user cannot checkout" }, { status: 403 });
}
const parsed = checkoutSchema.safeParse(await request.json());
if (!parsed.success || !isPaidBillingPlanId(parsed.data.planId)) {
return NextResponse.json({ error: "Invalid plan ID" }, { status: 400 });
}
const planId = parsed.data.planId as PaidBillingPlanId;
const stripeSecretKey = process.env.STRIPE_SECRET_KEY;
const priceId = getStripePriceIdForPlan(planId);
if (!stripeSecretKey || !priceId) {
return NextResponse.json(
{ error: "Billing is not configured. Missing Stripe keys or plan price ID." },
{ status: 503 }
);
}
const baseUrl = getExternalBase(request);
const successUrl = new URL(`/lovart/pricing?checkout=success&plan=${planId}`, baseUrl).toString();
const cancelUrl = new URL(`/lovart/pricing?checkout=cancelled&plan=${planId}`, baseUrl).toString();
const checkoutSession = await createStripeCheckoutSession({
secretKey: stripeSecretKey,
priceId,
userId: session.user.id,
email: session.user.email,
planId,
successUrl,
cancelUrl,
});
return NextResponse.json({
id: checkoutSession.id,
url: checkoutSession.url,
});
} catch (error) {
if (error instanceof StripeRequestError) {
return NextResponse.json({ error: error.message }, { status: error.status });
}
console.error("[Billing Checkout API] Error:", error);
return NextResponse.json({ error: "Failed to create checkout session" }, { status: 500 });
}
}