import { type NextRequest, NextResponse } from "next/server"; import { z } from "zod"; import { auth } from "@/app/(auth)/auth"; import { getPaidPlanAmountFen, getPaidPlanById, getWechatPayConfig, isPaidBillingPlanId, type PaidBillingPlanId, } from "@/lib/billing/plans"; import { createWechatNativeOrder, generateWechatOutTradeNo, WechatPayError } from "@/lib/billing/wechat"; import { upsertSystemSetting } from "@/lib/db/queries"; const schema = z.object({ planId: z.string(), }); function getBaseUrl(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 = schema.safeParse(await request.json()); if (!parsed.success || !isPaidBillingPlanId(parsed.data.planId)) { return NextResponse.json({ error: "Invalid plan ID" }, { status: 400 }); } const config = getWechatPayConfig(); if (!config) { return NextResponse.json({ error: "WeChat Pay is not configured" }, { status: 503 }); } const planId = parsed.data.planId as PaidBillingPlanId; const plan = getPaidPlanById(planId); if (!plan) { return NextResponse.json({ error: "Plan not found" }, { status: 400 }); } const outTradeNo = generateWechatOutTradeNo(); const amountFen = getPaidPlanAmountFen(planId); const notifyUrl = config.notifyUrl || new URL("/api/billing/wechat/notify", getBaseUrl(request)).toString(); const attachPayload = JSON.stringify({ planId, userId: session.user.id, email: session.user.email, }); const order = await createWechatNativeOrder({ appId: config.appId, mchId: config.mchId, privateKey: config.privateKey, serialNo: config.serialNo, description: `Lovart ${plan.name} Subscription`, outTradeNo, notifyUrl, amountFen, attach: attachPayload, }); await upsertSystemSetting({ key: `wechat:pay:order:${outTradeNo}`, value: { outTradeNo, channel: "wechat", status: "created", planId, userId: session.user.id, email: session.user.email, codeUrl: order.code_url, amountFen, createdAt: new Date().toISOString(), }, updatedBy: session.user.id, }); const qrCodeImageUrl = `https://api.qrserver.com/v1/create-qr-code/?size=260x260&data=${encodeURIComponent(order.code_url)}`; return NextResponse.json({ outTradeNo, codeUrl: order.code_url, qrCodeImageUrl, }); } catch (error) { if (error instanceof WechatPayError) { return NextResponse.json({ error: error.message }, { status: error.status }); } console.error("[Billing WeChat Checkout API] Error:", error); return NextResponse.json({ error: "Failed to create wechat pay order" }, { status: 500 }); } }