PAYATHON 2026

alipay.trade.wap.pay 能否返回支付链接?

支付老李

结论

alipay.trade.wap.pay 默认返回的是一段会自动提交的 HTML form,不是固定的支付链接。

如果业务方必须拿到一个可以直接打开的 URL,可以选择以下方案:

  1. 让浏览器直接渲染并提交支付宝 SDK 返回的 form。
  2. 在业务服务器上提供中转地址。用户打开该地址后,由服务器生成并输出支付表单。
  3. 如果当前版本的支付宝 SDK 明确支持通过 GET 方式执行页面类接口,可以使用 SDK 生成带签名参数的网关 URL。不过,要留意 URL 长度、参数泄露和 SDK 版本差异。

不能只提取表单的 action 属性作为支付链接,因为签名和业务参数通常放在表单字段中。单独访问 action 地址会缺少必要参数,无法正常发起支付。

为什么返回 form

alipay.trade.wap.pay 是页面跳转类接口。商户服务端生成签名后的支付请求,再让用户浏览器跳转到支付宝收银台。

支付宝 SDK 通常会把完整请求包装成类似下面的表单:

<form name="punchout_form"
      method="post"
      action="https://openapi.alipay.com/gateway.do">
    <input type="hidden" name="app_id" value="...">
    <input type="hidden" name="method" value="alipay.trade.wap.pay">
    <input type="hidden" name="biz_content" value="...">
    <input type="hidden" name="sign" value="...">
</form>
<script>
    document.forms[0].submit();
</script>

一笔完整的支付请求包含两部分:

  • action 中的支付宝网关地址;
  • 表单中的公共参数、业务参数和签名。

所以,https://openapi.alipay.com/gateway.do 只是网关地址,不能作为某一笔订单的支付链接。

推荐做法:直接输出并提交 form

后端调用 SDK 生成表单后,把表单内容作为 HTML 返回给浏览器。

下面是 Java 示例。具体类名和构造参数应以项目实际使用的支付宝 SDK 为准:

AlipayTradeWapPayRequest request = new AlipayTradeWapPayRequest();

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

AlipayTradeWapPayModel model = new AlipayTradeWapPayModel();
model.setOutTradeNo("ORDER_202609090001");
model.setTotalAmount("0.01");
model.setSubject("测试订单");
model.setProductCode("QUICK_WAP_WAY");

request.setBizModel(model);

AlipayTradeWapPayResponse response = alipayClient.pageExecute(request);

if (!response.isSuccess()) {
    throw new IllegalStateException(
        "创建支付宝支付请求失败:" + response.getSubMsg()
    );
}

String form = response.getBody();

Controller 可以直接返回这段 HTML:

@GetMapping(value = "/pay/alipay", produces = "text/html;charset=UTF-8")
@ResponseBody
public String pay() throws AlipayApiException {
    AlipayTradeWapPayRequest request = buildWapPayRequest();
    AlipayTradeWapPayResponse response = alipayClient.pageExecute(request);

    if (!response.isSuccess()) {
        throw new IllegalStateException(
            response.getSubCode() + ": " + response.getSubMsg()
        );
    }

    return response.getBody();
}

浏览器打开 /pay/alipay 后,会收到并自动提交该表单,然后进入支付宝收银台。

前端只能接收 URL 时的处理方式

如果 App、H5 页面或第三方系统要求后端返回 URL,可以返回商户自己的中转地址,不必把支付宝表单强行改造成链接:

{
  "payUrl": "https://merchant.example.com/pay/alipay?orderNo=ORDER_202609090001"
}

用户打开该地址后,商户服务器按以下流程处理:

  1. 根据 orderNo 查询订单;
  2. 校验订单状态、金额和当前用户;
  3. 调用 alipay.trade.wap.pay;
  4. 将 SDK 生成的表单作为 HTML 输出;
  5. 浏览器自动提交表单并跳转到支付宝。

这样既能满足前端只接收 URL 的要求,也能保留 POST 表单中的支付参数。

中转地址不能只凭订单号创建支付请求。服务端至少要验证订单归属和状态,必要时还应使用带有效期的随机令牌,防止他人枚举订单号。

使用 GET 支付地址

部分语言或版本的支付宝 SDK 可能为页面类接口提供 GET 执行方式。例如,某些 Java SDK 中有类似调用:

AlipayTradeWapPayResponse response =
    alipayClient.pageExecute(request, "GET");

String payUrl = response.getBody();

只有当前 SDK 的方法定义和文档明确支持这种用法时,才能把返回内容当作支付 URL。不同语言、不同版本的 SDK,其重载方法和返回格式可能并不一致。需要实际确认 response.getBody() 返回的是 URL 还是 HTML。

即使 SDK 支持 GET,也要考虑以下问题:

  • biz_content、签名等参数会出现在 URL 中;
  • URL 可能很长,容易超过浏览器、代理或二维码平台的长度限制;
  • 浏览器历史、访问日志或分析系统可能会记录该 URL;
  • 不要手工拼接参数。参数编码、排序和签名必须交给 SDK 完成。

如果无法确认当前 SDK 是否支持 GET,更稳妥的做法是使用商户中转地址输出 POST 表单。

注意事项

不要只提取 form 的 action

下面的处理方式是错误的:

String payUrl = "https://openapi.alipay.com/gateway.do";

这个网关地址没有携带订单信息和签名,无法代表具体的支付请求。

不要由前端自行拼接签名参数

商户私钥只能保存在服务端。前端不应参与签名,也不能接触私钥。

支付结果以异步通知为准

return_url 只用于支付完成后的页面跳转。用户可能关闭页面,也可能重复访问该地址。判断订单是否支付成功,应主要依据支付宝异步通知,并结合主动查询接口进行核实。

处理异步通知时,需要校验签名,并至少核对以下信息:

  • app_id
  • out_trade_no
  • trade_status
  • total_amount
  • 收款方身份信息

不要长期保存生成的表单或 URL

支付请求应在订单状态有效时动态生成。中转地址需要防范重复创建订单、越权访问和参数篡改。订单已经支付或关闭后,不应再让用户进入收银台。

备注:内容仅供参考。