PAYATHON 2026

手机 H5 如何携带上游商家信息并通过 scheme 拉起支付宝 App 支付?

支付老李

结论

手机 H5 支付需要由服务端调用 alipay.trade.wap.pay。支付宝生成收银台表单后,服务端将表单返回给前端并自动提交。支付宝会根据终端环境尝试拉起支付宝 App;如果无法拉起,通常会进入 H5 收银台。

常见的支付宝拉起 scheme 如下:

alipays://platformapi/startapp?appId=20000067&url={URL_ENCODED_CASHIER_URL}

其中,url 必须是经过 URL 编码的支付宝收银台地址。不要直接放入商户订单号,也不要在 scheme 中拼接自定义商家信息。

“上游商家信息”应通过支付接口支持的业务参数传递,并参与签名,不能放在 scheme 中。使用哪个字段取决于具体业务模式:

  • 服务商标识通常放在 extend_params.sys_service_provider_id 中,但对应的服务商模式必须已经开通。
  • 如果需要在异步通知中原样取回业务标识,可以使用 passback_params。
  • 收银台显示的商户主体取决于签约账号、app_id、卖家关系以及支付宝侧配置,无法通过 H5 参数随意修改。

推荐支付流程

完整流程如下:

  1. H5 将商品和订单信息提交给商户服务端。
  2. 服务端创建本地订单,并生成唯一的 out_trade_no。
  3. 服务端调用 alipay.trade.wap.pay。
  4. 将上游商家标识放入双方约定的接口字段。
  5. 使用商户私钥为整个请求签名。
  6. 服务端将支付宝 SDK 生成的表单返回给 H5。
  7. H5 自动提交表单,进入支付宝收银台。
  8. 支付宝通过 notify_url 异步通知支付结果。
  9. 服务端完成验签并核对订单信息,再更新订单状态。

支付结果必须以服务端收到的异步通知或主动查询结果为准,不能只看前端跳转页面。

服务端请求示例

以下是 Java SDK 的典型写法。不同 SDK 版本的方法签名可能有差异,请以项目实际使用的支付宝 SDK 为准。

AlipayClient alipayClient = new DefaultAlipayClient(
    "https://openapi.alipay.com/gateway.do",
    APP_ID,
    MERCHANT_PRIVATE_KEY,
    "json",
    "UTF-8",
    ALIPAY_PUBLIC_KEY,
    "RSA2"
);

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

Map<String, Object> extendParams = new HashMap<>();
extendParams.put("sys_service_provider_id", "2088XXXXXXXXXXXX");

Map<String, Object> bizContent = new HashMap<>();
bizContent.put("out_trade_no", "ORDER_202609090001");
bizContent.put("total_amount", "99.00");
bizContent.put("subject", "商品订单");
bizContent.put("product_code", "QUICK_WAP_WAY");
bizContent.put("quit_url", "https://m.example.com/order/ORDER_202609090001");
bizContent.put("passback_params", URLEncoder.encode(
    "upstream_merchant_id=M12345",
    StandardCharsets.UTF_8
));
bizContent.put("extend_params", extendParams);

request.setBizContent(new ObjectMapper().writeValueAsString(bizContent));

AlipayTradeWapPayResponse response = alipayClient.pageExecute(request);

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

// response.getBody() 通常是需要返回给浏览器并自动提交的 HTML 表单
String paymentForm = response.getBody();

服务端应将生成的表单直接作为 HTML 返回:

response.setContentType("text/html;charset=UTF-8");
response.getWriter().write(paymentForm);

商户私钥不能保存在浏览器端,也不要让前端自行生成支付宝签名。

上游商家信息如何传递

如果需要传递的只是商户自己的订单路由标识,例如上游商家编号,可以使用 passback_params:

{
  "out_trade_no": "ORDER_202609090001",
  "total_amount": "99.00",
  "subject": "商品订单",
  "product_code": "QUICK_WAP_WAY",
  "passback_params": "upstream_merchant_id%3DM12345"
}

支付宝通常会在异步通知中返回该字段。服务端收到后需要进行 URL 解码:

String passbackParams = URLDecoder.decode(
    request.getParameter("passback_params"),
    StandardCharsets.UTF_8
);

如果“上游商家”是指支付宝服务商体系中的服务商,则可能需要使用:

{
  "extend_params": {
    "sys_service_provider_id": "2088XXXXXXXXXXXX"
  }
}

这个字段并不是通用的自定义商户字段。只有签约产品、应用归属和服务商关系都符合要求时才应传入,也不能用其他商户的 PID 冒充服务商。

使用 scheme 拉起支付宝

如果已经取得合法的支付宝收银台 URL,并确认当前产品允许通过这种方式调用,可以按下面的方式构造 scheme:

function openAlipay(cashierUrl) {
  const scheme =
    "alipays://platformapi/startapp?appId=20000067&url=" +
    encodeURIComponent(cashierUrl);

  window.location.href = scheme;
}

例如:

alipays://platformapi/startapp?appId=20000067&url=https%3A%2F%2Fmclient.alipay.com%2F...

使用时要注意以下几点:

  • 必须对完整的收银台 URL 执行 encodeURIComponent。
  • 不要将未经签名的金额、卖家编号或订单号直接拼入收银台 URL。
  • 不要自行解析 SDK 返回的 HTML 表单来提取 URL,因为 SDK 的输出结构可能发生变化。
  • scheme 通常要在用户点击事件中触发,否则可能被浏览器拦截。
  • 微信、企业应用内置浏览器以及部分 iOS 或 Android WebView 可能会限制外部 App 拉起。
  • 如果无法拉起 App,需要保留 H5 收银台或浏览器打开提示作为降级方案。

实际项目中,建议优先提交 alipay.trade.wap.pay 返回的表单,由支付宝收银台处理 App 拉起和 H5 降级。只有支付宝为当前签约产品明确提供了收银台 URL,并允许通过 scheme 唤起时,才自行包装 alipays://。

异步通知处理

收到 notify_url 请求后,至少要完成验签:

boolean signVerified = AlipaySignature.rsaCheckV1(
    parameterMap,
    ALIPAY_PUBLIC_KEY,
    "UTF-8",
    "RSA2"
);

if (!signVerified) {
    throw new SecurityException("支付宝通知验签失败");
}

验签通过后,还需要核对以下信息:

  • app_id 是否属于当前应用。
  • out_trade_no 是否存在。
  • total_amount 是否与本地订单一致。
  • seller_id 是否为预期的收款主体。
  • 订单是否已经处理,避免重复通知。
  • trade_status 是否属于业务认可的成功状态。

处理完成后,按照支付宝协议返回:

success

注意事项

return_url 只是用户支付完成后的页面跳转地址,可能被关闭、拦截或重复访问,因此不能作为记账依据。

passback_params 适合传递简短的业务关联信息,但不要放入身份证号、手机号或密钥等敏感数据。更稳妥的做法是只传内部关联 ID,将完整信息保存在商户服务端。

如果业务涉及二级商户、间连商户、分账或受托收款,只传 sys_service_provider_id 通常无法满足要求。此时应根据已经签约的支付宝产品使用对应的二级商户或结算参数,不能通过普通 WAP 支付参数临时更改收款主体。

备注:内容仅供参考。