Files
Jyotisha/frontend/src/app/api/payment/epay/create/route.ts
T
linmeng f4e35974c6
Deploy staging to test server / deploy (push) Failing after 14m49s
Revert "merge: sync GitHub staging to Gitea"
This reverts commit a55c69115d, reversing
changes made to 02c9c9f3d6.
2026-07-30 14:38:17 +08:00

54 lines
2.9 KiB
TypeScript

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 });
}
}