import { NextRequest, NextResponse } from "next/server"; import { getApiUrlFromRequest, getPaymentConfig } from "@/config/services.config"; import { queryXorPayOrderStatus } from "@/app/lib/xorpayStatus"; import { queryZPayOrderStatus } from "@/app/lib/zpayStatus"; function resolvePayApiUrl(request: NextRequest): string { const config = getPaymentConfig(); const hostHeader = request.headers.get("host") || request.headers.get("x-forwarded-host"); return (getApiUrlFromRequest(hostHeader) || config.apiUrl).replace(/\/$/, ""); } /** * 支付状态查询:salonapi 优先,失败时直接向 zpay/xorpay 查询兜底 * 参考 nomadvip,保证本地无回调时也能检测到支付 */ export async function GET(request: NextRequest) { const orderId = request.nextUrl.searchParams.get("order_id"); if (!orderId) { return NextResponse.json( { ok: false, error: "missing order_id" }, { status: 400 } ); } const hostHeader = request.headers.get("host") || request.headers.get("x-forwarded-host"); const apiUrl = resolvePayApiUrl(request); const salonUrl = `${apiUrl}/salon/order_status?order_id=${encodeURIComponent(orderId)}`; // 并行查询 salonapi、xorpay、zpay,任一返回 paid 即立即返回,避免串行阻塞 const salonPaid = (async () => { try { const controller = new AbortController(); const timeout = setTimeout(() => controller.abort(), 3000); const res = await fetch(salonUrl, { cache: "no-store", signal: controller.signal, }).finally(() => clearTimeout(timeout)); const data = await res.json().catch(() => ({})); return !!data?.paid; } catch (error) { console.warn( "[pay/status] salonapi order_id=%s url=%s error=%s", orderId, salonUrl, error instanceof Error ? error.message : String(error) ); return false; } })(); const xorpayPaid = queryXorPayOrderStatus(orderId, 5000).then((r) => r?.paid ?? false); const zpayPaid = queryZPayOrderStatus(orderId, 5000).then((r) => r?.paid ?? false); const [salon, xorpay, zpay] = await Promise.all([salonPaid, xorpayPaid, zpayPaid]); if (salon || xorpay || zpay) { return NextResponse.json({ ok: true, paid: true }); } return NextResponse.json({ ok: true, paid: false }); }