支付宝扫码支付未弹出支付页面,仅显示 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。
排查步骤
可以按下面的顺序检查:
-
打开浏览器开发者工具的 Network 面板,查看支付请求的类型。如果显示为
fetch或xhr,当前请求就不会触发页面导航。 -
检查响应正文是否被包装成 JSON,以及
<、>是否被转义成<、>。 -
检查后端是否使用了统一响应包装、HTML 转义或字符串序列化处理。
-
确认实际响应头为:
Content-Type: text/html;charset=UTF-8 -
删除
action地址开头的空格。 -
确认业务需要的是手机网站支付还是二维码支付。前者使用
alipay.trade.wap.pay,后者使用alipay.trade.precreate。 -
如果页面运行在 iframe、App WebView 或小程序 WebView 中,还要检查跳转限制、CSP、跨域策略和外部链接打开权限。
注意事项
不要使用 eval 执行返回内容,也不建议从 HTML 字符串中提取 <script> 再手动执行。这样容易引入安全问题,还可能破坏支付宝表单中的编码和签名参数。
支付是否成功也不能只看同步跳转页面。同步 return_url 主要用于用户页面跳转,最终订单状态应结合支付宝异步通知验签来判断,必要时再调用订单查询接口确认。
备注:内容仅供参考。
