import crypto from "node:crypto"; import { NextResponse } from "next/server"; import { z } from "zod"; import { createAdminSupabaseClient } from "@/lib/supabase/admin"; import { createServerSupabaseClient } from "@/lib/supabase/server"; import { epaySign } from "@/lib/epay/sign"; import { readEpayAvailability } from "@/lib/epay/availability"; import { epaySubmitUrl, readEpayConfig, EpayConfigurationError } from "@/lib/epay/config"; import { assertPublicGatewayUrl } from "@/lib/epay/gateway-policy"; export const runtime = "nodejs"; const schema = z.object({ packageId: z.string().uuid() }); export async function POST(request: Request) { try { const client = await createServerSupabaseClient(); const { data: { user } } = await client.auth.getUser(); if (!user) return NextResponse.json({ error: "请先登录" }, { status: 401 }); const parsed = schema.safeParse(await request.json().catch(() => null)); if (!parsed.success) return NextResponse.json({ error: "请选择有效套餐" }, { status: 400 }); const availability = await readEpayAvailability(); if (!availability.enabled) return NextResponse.json({ error: "在线支付暂未开放", code: "EPAY_DISABLED" }, { status: 403 }); const config = await readEpayConfig(); const submitUrl = epaySubmitUrl(config.gatewayUrl); await assertPublicGatewayUrl(submitUrl); const admin = createAdminSupabaseClient(); const { data: pack, error: packError } = await admin.from("payment_packages").select("id,name,price_cents,credits,enabled").eq("id", parsed.data.packageId).eq("enabled", true).maybeSingle(); if (packError || !pack) return NextResponse.json({ error: "套餐不存在或已下架" }, { status: 404 }); const orderNo = `JY${Date.now().toString(36)}${crypto.randomBytes(10).toString("hex")}`; const { error: orderError } = await admin.from("payment_orders").insert({ order_no: orderNo, user_id: user.id, package_id: pack.id, money_cents: pack.price_cents, credits: pack.credits }); if (orderError) return NextResponse.json({ error: "创建订单失败" }, { status: 500 }); const params = { money: (pack.price_cents / 100).toFixed(2), name: pack.name, notify_url: config.notifyUrl, out_trade_no: orderNo, pid: config.pid, return_url: config.returnUrl, sitename: config.siteName, type: "alipay", }; const signedParams = { ...params, sign: epaySign(params, config.key), sign_type: "MD5" }; const payUrl = new URL(submitUrl); for (const [name, value] of Object.entries(signedParams)) payUrl.searchParams.set(name, value); return NextResponse.json({ orderNo, payUrl: payUrl.toString(), qrCode: null }); } catch (error) { if (error instanceof EpayConfigurationError) return NextResponse.json({ error: "易支付尚未配置", code: "EPAY_NOT_CONFIGURED" }, { status: 503 }); return NextResponse.json({ error: "创建支付失败" }, { status: 500 }); } }