PAYATHON 2026

如何通过 HTTP 打开 alipay.trade.app.pay 支付签约页面

支付阿杰

结论

alipay.trade.app.pay 不能像普通 HTTP 页面一样直接在浏览器中打开。它返回的是支付宝 App SDK 所需的订单字符串,不是收银台 URL。即使把完整的网关请求拼成链接,再转成短链或二维码,也无法代替 App SDK 的调起流程。

如果要让用户扫码进入支付宝付款,并同时签约商家扣款,需要按场景选择接口:

  • 手机浏览器收银台:使用 alipay.trade.wap.pay,由服务端生成支付宝表单并完成跳转。
  • 电脑网页收银台:使用 alipay.trade.page.pay。
  • 纯二维码当面付:使用 alipay.trade.precreate 获取 qr_code。该接口通常用于扫码支付,并不意味着支持同时签约扣款。
  • 当前产品不支持在 WAP/Page 支付中传入签约参数:先通过 alipay.user.agreement.page.sign 完成签约,再发起支付。

能否通过 alipay.trade.wap.pay 或 alipay.trade.page.pay 一次完成支付和签约,取决于商户开通的扣款产品、签约场景,以及对应接口是否支持 agreement_sign_params。alipay.trade.app.pay 支持该字段,不代表其他支付接口也支持。

为什么当前链接不能直接使用

alipay.trade.app.pay 的标准调用流程如下:

商户服务端生成并签名 orderString
        ↓
商户服务端将 orderString 返回给 App
        ↓
App 调用支付宝客户端 SDK
        ↓
支付宝 App 展示支付/签约页面

生成的内容一般类似:

app_id=...&biz_content=...&charset=utf-8&method=alipay.trade.app.pay&sign=...&sign_type=RSA2&timestamp=...&version=1.0

这是一组 SDK 调用参数,不是浏览器页面地址。直接访问下面这样的地址:

https://openapi-sandbox.dl.alipaydev.com/gateway.do?...

只是在向开放平台网关提交 API 请求,不会自动得到适合浏览器展示的 App 支付页面。短链服务只能缩短 URL,不能改变接口本身的调用方式。

推荐方案一:使用 WAP 支付作为扫码落地页

如果客户使用手机扫码,可以让二维码指向商户自己的 HTTPS 地址:

https://pay.example.com/order/123

用户访问该地址后,商户服务端创建 alipay.trade.wap.pay 请求,并把支付宝返回的 HTML 表单直接输出给浏览器。浏览器随后会跳转到支付宝收银台。

Java 示例:

AlipayClient alipayClient = new DefaultAlipayClient(
    "https://openapi-sandbox.dl.alipaydev.com/gateway.do",
    appId,
    merchantPrivateKey,
    "json",
    "UTF-8",
    alipayPublicKey,
    "RSA2"
);

AlipayTradeWapPayRequest request = new AlipayTradeWapPayRequest();
request.setReturnUrl("https://pay.example.com/alipay/return");
request.setNotifyUrl("https://pay.example.com/alipay/notify");

JSONObject bizContent = new JSONObject();
bizContent.put("out_trade_no", "ORDER_20260909_001");
bizContent.put("subject", "test");
bizContent.put("total_amount", "0.01");
bizContent.put("product_code", "QUICK_WAP_WAY");

request.setBizContent(bizContent.toJSONString());

AlipayTradeWapPayResponse response = alipayClient.pageExecute(request);

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

// 将 response.getBody() 作为 text/html 返回给手机浏览器。
String paymentHtml = response.getBody();

控制器应以 HTML 类型返回内容,例如:

@GetMapping(
    value = "/order/{orderNo}",
    produces = MediaType.TEXT_HTML_VALUE
)
@ResponseBody
public String pay(@PathVariable String orderNo) throws Exception {
    return createAlipayWapPayForm(orderNo);
}

二维码中只需放入商户落地页 URL,不要直接写入包含签名和业务参数的支付宝网关请求。通过商户落地页,服务端也能检查订单状态、重新生成请求并阻止重复付款。

如何加入签约参数

只有同时满足以下条件,才可以尝试在支付请求中加入 agreement_sign_params:

  1. 商户已经开通对应的商家扣款产品。
  2. 当前签约场景和 personal_product_code 已由支付宝侧配置或认可。
  3. 所选支付接口的当前文档明确支持 agreement_sign_params。
  4. 沙盒环境支持正在测试的产品和场景。

数据结构示例如下:

{
  "out_trade_no": "ORDER_20260909_001",
  "subject": "test",
  "total_amount": "0.01",
  "product_code": "QUICK_WAP_WAY",
  "agreement_sign_params": {
    "product_code": "GENERAL_WITHHOLDING",
    "personal_product_code": "实际开通的产品码",
    "sign_scene": "实际配置的签约场景",
    "access_params": {
      "channel": "QRCODE"
    },
    "period_rule_params": {
      "period_type": "DAY",
      "period": 1,
      "execute_time": "实际有效日期",
      "single_amount": "0.01",
      "total_amount": "0.10",
      "total_payments": 2
    }
  }
}

这段示例只说明数据结构,不能证明 alipay.trade.wap.pay 一定接受这些字段。具体应以商户后台已开通产品对应的最新接口文档为准。如果 WAP/Page 接口文档没有列出 agreement_sign_params,就不要自行添加。网关可能忽略该字段,也可能直接返回参数错误。

推荐方案二:拆分为“先签约,后支付”

如果当前扣款产品只支持独立签约,可以采用下面的流程:

用户扫描商户二维码
    ↓
打开商户签约落地页
    ↓
服务端调用 alipay.user.agreement.page.sign
    ↓
用户在支付宝完成签约
    ↓
商户接收并验证异步通知
    ↓
确认协议状态有效
    ↓
发起 alipay.trade.wap.pay 或其他支付流程

独立签约所需字段会因产品类型而异,通常包括:

personal_product_code
sign_scene
external_agreement_no
access_params
period_rule_params
sign_notify_url

签约成功后,需要保存支付宝返回的协议号和协议状态。后续商家扣款必须使用产品规定的扣款接口及协议标识。首次签约成功不等于后续扣款授权流程已经全部完成。

如果业务必须让用户一次确认便同时完成支付和签约,需要先确认申请的产品是否提供“支付并签约”能力,以及该能力支持哪些支付渠道。没有对应产品权限时,更换参数或拼接 URL 都无法实现这一流程。

如果只需要扫码支付

如果业务不要求在同一页面完成扣款签约,可以调用 alipay.trade.precreate。接口调用成功后会返回二维码内容:

AlipayTradePrecreateRequest request = new AlipayTradePrecreateRequest();
request.setNotifyUrl("https://pay.example.com/alipay/notify");

JSONObject bizContent = new JSONObject();
bizContent.put("out_trade_no", "ORDER_20260909_002");
bizContent.put("subject", "test");
bizContent.put("total_amount", "0.01");

request.setBizContent(bizContent.toJSONString());

AlipayTradePrecreateResponse response = alipayClient.execute(request);

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

String qrCodeContent = response.getQrCode();

应使用返回的 qrCodeContent 生成二维码,不要自行拼装网关地址。至于 alipay.trade.precreate 能否与某种签约产品组合使用,仍要以该产品的开放能力说明为准。

当前参数还需要检查的问题

当前示例还有几处可能导致报错:

  • JSON 使用了单引号。实际传给接口的 biz_content 必须是合法 JSON,属性名和字符串都要使用双引号。
  • execute_time 是 2024-02-06,已经过期。周期扣款规则通常要求填写符合产品约束的有效执行日期。
  • sign_notify_url 的值末尾似乎有空格:"xxx "。需要清理空格,并替换成可从公网访问的 HTTPS 地址。
  • personal_product_code 不能随意填写为 auto_123,必须使用商户实际开通并配置的产品码。
  • sign_scene 必须与申请产品时配置的签约场景一致。
  • out_trade_no 应在商户系统内保持唯一,不建议长期使用固定值 123。
  • 金额字段建议使用字符串,例如 "0.01",避免浮点数序列化带来的精度问题。
  • return_url 只是同步跳转地址。订单和协议状态必须以验签后的异步通知或主动查询结果为准。
  • 沙盒不一定覆盖所有商家扣款产品。如果参数正确,但持续出现产品未开通、场景不支持或权限不足等提示,需要到正式应用中确认对应产品能力,不能直接据此判断生产环境也不可用。

在二维码浏览器场景中,优先使用“商户落地页 + alipay.trade.wap.pay”。如果该支付接口不支持当前扣款产品的一次性支付签约,就通过 alipay.user.agreement.page.sign 独立签约,再进入支付流程。alipay.trade.app.pay 则应留给已经集成支付宝客户端 SDK 的原生 App。

备注:内容仅供参考。