PAYATHON 2026

支付宝扫码支付未弹出支付页面,仅显示 HTML 字符

支付小周

结论

通常不是响应头设置有误,而是支付接口的调用方式不对。

alipay.trade.wap.pay 返回一段 HTML 表单,需要由浏览器作为完整页面加载并执行。如果前端通过 Ajax、fetch 或 Axios 请求后端,响应只会作为字符串交给 JavaScript,并不会触发页面跳转。即使用 innerHTML 把内容插入页面,其中的 <script> 通常也不会执行。

示例使用了 alipay.trade.wap.pay 和 QUICK_WAP_WAY,对应的是手机网站支付,并非扫码支付。如果需要生成二维码,应改用预下单接口 alipay.trade.precreate。

为什么只显示 HTML 字符

一般有以下几种原因。

1. 前端通过 Ajax 请求支付接口

例如:

axios.post('/pay/alipay', data).then(response => {
  document.querySelector('#result').textContent = response.data;
});

这段代码会把后端返回的表单当作普通字符串显示。Content-Type: text/html 只说明响应内容的类型,无法让 Ajax 响应自动变成浏览器页面。

即使改成:

document.querySelector('#result').innerHTML = response.data;

浏览器通常也不会执行通过 innerHTML 动态插入的 <script>,所以下面的自动提交代码不会运行:

<script>document.forms[0].submit();</script>

2. 后端把 HTML 包装进了 JSON

如果接口最终返回:

{
  "code": 200,
  "data": "<form>...</form><script>...</script>"
}

它本质上仍是一个 JSON 接口,前端不会自动把 data 当作新页面执行。

如果项目使用了统一响应包装器、字符串转义器或全局返回值处理器,HTML 也可能被转成普通文本。

3. 使用了错误的支付产品

示例中的参数是:

method=alipay.trade.wap.pay
product_code=QUICK_WAP_WAY

这表示手机网站支付。正常流程是跳转到支付宝收银台,不会直接返回二维码图片,也不保证通过弹窗打开。

如果要在 PC 页面展示二维码供用户扫码,需要调用:

alipay.trade.precreate

然后使用响应中的 qr_code 生成二维码。

4. 表单地址前有多余空格

示例中 action 的开头多了一个空格:

action=" https://openapi-sandbox.dl.alipaydev.com/gateway.do..."

应删除这个空格:

action="https://openapi-sandbox.dl.alipaydev.com/gateway.do..."

部分浏览器可能会自动处理空白字符,但不要依赖这种行为。

手机网站支付的正确处理方式

让浏览器直接访问后端支付入口,再由后端原样返回支付宝 SDK 生成的完整表单。不要通过 Ajax 获取这段 HTML。

前端直接跳转

window.location.href = `/pay/alipay/wap?orderNo=${encodeURIComponent(orderNo)}`;

也可以用普通表单提交到后端:

<form method="post" action="/pay/alipay/wap">
  <input type="hidden" name="orderNo" value="70501111111S001111118">
  <button type="submit">立即支付</button>
</form>

这样,后端返回的 HTML 会作为新页面加载,其中的自动提交脚本才能正常执行。

Java 后端示例

以下示例假设 alipayClient.pageExecute(request).getBody() 返回的是支付宝 SDK 生成的完整表单:

@PostMapping("/pay/alipay/wap")
public void wapPay(
        @RequestParam String orderNo,
        HttpServletResponse response) throws Exception {

    AlipayTradeWapPayRequest request = new AlipayTradeWapPayRequest();

    AlipayTradeWapPayModel model = new AlipayTradeWapPayModel();
    model.setOutTradeNo(orderNo);
    model.setSubject("订单支付");
    model.setTotalAmount("9.00");
    model.setProductCode("QUICK_WAP_WAY");

    request.setBizModel(model);
    request.setReturnUrl("https://example.com/pay/alipay/return");
    request.setNotifyUrl("https://example.com/pay/alipay/notify");

    String form = alipayClient.pageExecute(request).getBody();

    response.setStatus(HttpServletResponse.SC_OK);
    response.setContentType("text/html;charset=UTF-8");
    response.getWriter().write(form);
    response.getWriter().flush();
}

这里需要把表单直接写入响应正文,不要再套一层统一 JSON 结果:

// 不推荐
return Result.success(form);

使用 Spring MVC 时,也可以直接返回 HTML:

@PostMapping(
    value = "/pay/alipay/wap",
    produces = "text/html;charset=UTF-8"
)
@ResponseBody
public String wapPay(@RequestParam String orderNo) throws Exception {
    AlipayTradeWapPayRequest request = new AlipayTradeWapPayRequest();

    AlipayTradeWapPayModel model = new AlipayTradeWapPayModel();
    model.setOutTradeNo(orderNo);
    model.setSubject("订单支付");
    model.setTotalAmount("9.00");
    model.setProductCode("QUICK_WAP_WAY");

    request.setBizModel(model);
    return alipayClient.pageExecute(request).getBody();
}

前提是项目中的统一响应处理器不会再次包装或转义这个字符串。

如果业务确实需要扫码支付

扫码支付不需要执行上述 WAP 表单,而应通过 alipay.trade.precreate 创建预下单。接口调用成功后会返回二维码内容,前端再把 qr_code 渲染成二维码。

流程如下:

前端创建订单
    → 后端调用 alipay.trade.precreate
    → 后端取得 qr_code
    → 前端生成二维码
    → 用户使用支付宝扫码
    → 后端接收异步通知或主动查询订单状态

Java 请求示例:

AlipayTradePrecreateRequest request = new AlipayTradePrecreateRequest();

AlipayTradePrecreateModel model = new AlipayTradePrecreateModel();
model.setOutTradeNo("70501111111S001111118");
model.setSubject("订单支付");
model.setTotalAmount("9.00");

request.setBizModel(model);
request.setNotifyUrl("https://example.com/pay/alipay/notify");

AlipayTradePrecreateResponse response =
        alipayClient.execute(request);

if (response.isSuccess()) {
    String qrCode = response.getQrCode();
    // 将 qrCode 返回给前端,由二维码组件进行渲染
}

qr_code 是二维码中包含的内容,不一定是可以直接展示的图片地址。前端还需要通过二维码组件生成图片或 Canvas。

排查步骤

可以按下面的顺序检查:

  1. 打开浏览器开发者工具的 Network 面板,查看支付请求的类型。如果显示为 fetch 或 xhr,当前请求就不会触发页面导航。

  2. 检查响应正文是否被包装成 JSON,以及 <、> 是否被转义成 &lt;、&gt;。

  3. 检查后端是否使用了统一响应包装、HTML 转义或字符串序列化处理。

  4. 确认实际响应头为:

    Content-Type: text/html;charset=UTF-8
  5. 删除 action 地址开头的空格。

  6. 确认业务需要的是手机网站支付还是二维码支付。前者使用 alipay.trade.wap.pay,后者使用 alipay.trade.precreate。

  7. 如果页面运行在 iframe、App WebView 或小程序 WebView 中,还要检查跳转限制、CSP、跨域策略和外部链接打开权限。

注意事项

不要使用 eval 执行返回内容,也不建议从 HTML 字符串中提取 <script> 再手动执行。这样容易引入安全问题,还可能破坏支付宝表单中的编码和签名参数。

支付是否成功也不能只看同步跳转页面。同步 return_url 主要用于用户页面跳转,最终订单状态应结合支付宝异步通知验签来判断,必要时再调用订单查询接口确认。

备注:内容仅供参考。