PAYATHON 2026

手机网站支付接口2.0无法唤起支付宝并跳转白屏

支付老李

结论

白屏并不是支付宝无法唤起,而是支付跳转地址中混入了引号和转义符。

实际地址以:

https://pay.ruixuezu.com/%22https://openapi.alipay.com/gateway.do...

开头。这里的 %22 代表双引号 ",说明浏览器收到的内容很可能是:

"https://openapi.alipay.com/gateway.do?\"biz_content\"=...

这个地址没有以合法的 https:// 开头,因此浏览器会把它当作当前站点下的相对路径,最终跳转到 pay.ruixuezu.com/"https://... 并显示白屏。

常见原因

这类问题通常是支付参数在服务端和前端之间被重复序列化,或者前端直接把 JSON 字符串当作 URL 使用。例如:

window.location.href = JSON.stringify(payUrl);

另一种情况是服务端返回支付地址时手动添加了引号:

return "\"" + payUrl + "\"";

地址中的:

%5C%22biz_content%5C%22

解码后是:

\"biz_content\"

这说明参数中也混入了 JSON 转义字符。支付宝网关的参数名应该是 biz_content,不能带双引号或反斜杠。

解决步骤

1. 检查服务端生成的原始内容

服务端向前端返回内容之前,先检查支付地址或支付表单的第一个字符。

正确的 URL 类似:

https://openapi.alipay.com/gateway.do?app_id=...&biz_content=...

下面这些形式都是错误的:

"https://openapi.alipay.com/gateway.do?..."
\"https://openapi.alipay.com/gateway.do?...\"

如果使用支付宝官方 SDK,不要再手动替换双引号、添加反斜杠,也不要对完整结果做二次 URL 编码。

2. 不要重复序列化 URL

错误写法:

const payUrl = JSON.stringify(response.data);
window.location.href = payUrl;

正确写法:

const payUrl = response.data.payUrl;
window.location.href = payUrl;

如果接口直接返回纯文本 URL,可以这样读取:

const response = await fetch("/api/pay");
const payUrl = await response.text();

window.location.href = payUrl.trim();

如果服务端返回 JSON,应使用清晰的数据结构:

{
  "payUrl": "https://openapi.alipay.com/gateway.do?app_id=..."
}

不要返回已经包含额外引号的内容:

{
  "payUrl": "\"https://openapi.alipay.com/gateway.do?app_id=...\""
}

3. 直接输出 SDK 返回的支付表单

手机网站支付接口通常可以通过支付宝 SDK 生成自动提交的 HTML 表单。以 Java 为例:

AlipayTradeWapPayRequest request = new AlipayTradeWapPayRequest();
request.setReturnUrl(returnUrl);
request.setNotifyUrl(notifyUrl);
request.setBizContent(bizContent);

AlipayTradeWapPayResponse response = alipayClient.pageExecute(request);

if (response.isSuccess()) {
    String form = response.getBody();

    httpServletResponse.setContentType("text/html;charset=UTF-8");
    httpServletResponse.getWriter().write(form);
    httpServletResponse.getWriter().flush();
} else {
    throw new IllegalStateException(
        response.getCode() + ": " + response.getSubMsg()
    );
}

当 response.getBody() 返回 HTML 表单时,应将它作为 HTML 响应直接写回浏览器。不能先用 JSON 包装,再把整段内容赋给 location.href。

前端也不能用下面的方式处理表单:

window.location.href = response.data.form;

如果业务必须通过 AJAX 获取表单,可以把表单写入页面后提交:

const formHtml = response.data.form;

const container = document.createElement("div");
container.innerHTML = formHtml;
document.body.appendChild(container);

const form = container.querySelector("form");
if (!form) {
  throw new Error("支付响应中未找到 form");
}

form.submit();

更稳妥的做法是让浏览器直接访问服务端支付入口,再由服务端输出 SDK 生成的 HTML。

4. 检查域名是否被错误拼接

不要把支付宝地址拼接到本站域名后面:

window.location.href = location.origin + "/" + payUrl;

应直接使用支付宝返回的绝对地址:

window.location.href = payUrl;

跳转前还可以做一次基本校验:

const payUrl = response.data.payUrl.trim();
const url = new URL(payUrl);

if (url.protocol !== "https:" ||
    url.hostname !== "openapi.alipay.com") {
  throw new Error("支付跳转地址不合法");
}

window.location.assign(url.toString());

排查方法

打开浏览器开发者工具,在 Network 面板中依次检查:

  1. 服务端支付接口返回的原始内容;
  2. 前端解析后的 payUrl 或 form;
  3. 执行跳转前的最终字符串;
  4. 字符串中是否出现多余的 ", \", %22 或 %5C%22;
  5. 接口返回的究竟是 URL、HTML 表单,还是包含字段的 JSON。

跳转前可以临时输出:

console.log("payUrl raw:", response.data.payUrl);
console.log("payUrl json:", JSON.stringify(response.data.payUrl));

如果第二行显示:

"\"https://openapi.alipay.com/gateway.do?...\""

说明字符串内部已经带有多余的引号。此时应回到服务端修正,不要继续在前端调用 replace() 补救。

注意事项

  • biz_content 本身是 JSON 字符串,但提交为网关参数时,参数名不能写成 "biz_content" 或 \"biz_content\"。
  • 支付参数的编码和签名应交给支付宝 SDK 处理。签名完成后再修改、解码或重新拼接参数,可能导致验签失败。
  • 不建议长期使用 replaceAll("\\\"", "\"") 或截取首尾字符来规避问题,这样只会掩盖重复序列化的根因。
  • return_url 只处理用户支付后的同步跳转。支付结果应以服务端收到的 notify_url 异步通知和验签结果为准。
  • 如果地址修正后可以进入支付宝页面,但仍无法拉起客户端,再检查浏览器环境、支付宝客户端、HTTPS、应用配置以及 product_code=QUICK_WAP_WAY。这些是后续问题,与当前由地址异常引起的白屏无关。

备注:内容仅供参考。