This commit is contained in:
eric
2026-03-16 09:32:35 -05:00
parent c54c236d1b
commit 14b0cd2429
21 changed files with 1058 additions and 706 deletions

View File

@@ -1,5 +1,7 @@
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();
@@ -8,6 +10,10 @@ function resolvePayApiUrl(request: NextRequest): string {
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) {
@@ -20,27 +26,36 @@ export async function GET(request: NextRequest) {
const hostHeader =
request.headers.get("host") || request.headers.get("x-forwarded-host");
const apiUrl = resolvePayApiUrl(request);
const url = `${apiUrl}/salon/order_status?order_id=${encodeURIComponent(orderId)}`;
const salonUrl = `${apiUrl}/salon/order_status?order_id=${encodeURIComponent(orderId)}`;
try {
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 3000);
const res = await fetch(url, {
cache: "no-store",
signal: controller.signal,
}).finally(() => clearTimeout(timeout));
const data = await res.json().catch(() => ({}));
const paid = !!data?.paid;
if (paid) {
return NextResponse.json({ ok: true, paid: true });
// 并行查询 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;
}
} catch (error) {
console.log(
"[pay/status] error order_id=%s error=%s",
orderId,
error instanceof Error ? error.message : String(error)
);
}
})();
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 });
}