diff --git a/app/api/pay/route.ts b/app/api/pay/route.ts index a102146..b3d62a9 100644 --- a/app/api/pay/route.ts +++ b/app/api/pay/route.ts @@ -15,6 +15,12 @@ function escapeAttr(s: string): string { .replace(/>/g, ">"); } +function buildErrorHtml(errorMsg: string): string { + const esc = (s: string) => + s.replace(/[&<>"']/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" })[c] || c); + return `支付失败

支付发起失败

${esc(errorMsg)}

返回首页重试

`; +} + function buildPayHtml( payUrl: string, params: Record, @@ -31,8 +37,9 @@ function buildPayHtml( .map(([k, v]) => ``) .join("\n"); const direct = options?.directToCashier; + // 微信内:显示加载提示,避免 visibility:hidden 导致部分 WebView 表单提交异常 return direct - ? `支付
${inputs}
` + ? `支付

正在跳转至微信支付...

${inputs}
` : ` @@ -120,6 +127,12 @@ export async function POST(request: NextRequest) { const device = String(body?.device || "pc"); if (!user_id) { + if (wantHtml) { + return new NextResponse(buildErrorHtml("缺少 user_id"), { + status: 400, + headers: { "Content-Type": "text/html; charset=utf-8" }, + }); + } return NextResponse.json({ ok: false, error: "缺少 user_id" }, { status: 400 }); } @@ -152,6 +165,12 @@ export async function POST(request: NextRequest) { ); if (!params || Object.keys(params).length === 0) { + if (wantHtml) { + return new NextResponse(buildErrorHtml("支付参数为空"), { + status: 500, + headers: { "Content-Type": "text/html; charset=utf-8" }, + }); + } return NextResponse.json({ ok: false, error: "支付参数为空" }, { status: 500 }); } @@ -262,13 +281,24 @@ export async function POST(request: NextRequest) { } if (redirectRes.status === 400 || redirectRes.status === 500) { - return NextResponse.json( - { ok: false, error: resData?.detail || resData?.msg || "支付跳转失败" }, - { status: redirectRes.status } - ); + const errMsg = resData?.detail || resData?.msg || "支付跳转失败"; + if (wantHtml) { + return new NextResponse(buildErrorHtml(errMsg), { + status: redirectRes.status, + headers: { "Content-Type": "text/html; charset=utf-8" }, + }); + } + return NextResponse.json({ ok: false, error: errMsg }, { status: redirectRes.status }); } - return NextResponse.json({ ok: false, error: "支付创建失败" }, { status: 500 }); + const createFailMsg = "支付创建失败"; + if (wantHtml) { + return new NextResponse(buildErrorHtml(createFailMsg), { + status: 500, + headers: { "Content-Type": "text/html; charset=utf-8" }, + }); + } + return NextResponse.json({ ok: false, error: createFailMsg }, { status: 500 }); } catch (e) { const err = e instanceof Error ? e : new Error(String(e)); const msg = err.message || ""; @@ -279,6 +309,15 @@ export async function POST(request: NextRequest) { ? "请求超时" : msg; console.error("Pay API error:", err); - return NextResponse.json({ ok: false, error: `支付请求失败: ${hint}` }, { status: 500 }); + const errorMsg = `支付请求失败: ${hint}`; + const ct = request.headers.get("content-type") || ""; + const isForm = ct.includes("form") || ct.includes("x-www-form-urlencoded"); + if (isForm) { + return new NextResponse(buildErrorHtml(errorMsg), { + status: 500, + headers: { "Content-Type": "text/html; charset=utf-8" }, + }); + } + return NextResponse.json({ ok: false, error: errorMsg }, { status: 500 }); } }