支付宝扫码打开H5页面后付款应接入哪种支付方式?
明确结论
用户扫描二维码进入 H5 页面,选择商品后使用支付宝付款,并不代表这笔支付自动属于“当面付”。具体应接入哪种产品,要看付款是如何发起的:
- H5 提交订单后跳转到支付宝收银台,通常应接入手机网站支付,对应接口为
alipay.trade.wap.pay。这个流程一般不需要获取用户的auth_code。 - 如果要在支付宝内置浏览器中直接通过支付宝 JSAPI 完成付款,应接入支付宝提供的 JSAPI 支付能力。服务端通常需要先取得用户的
user_id,因此可能要通过支付宝 OAuth 获取auth_code,再用它换取user_id。 - 经典的“当面付扫码支付”通常由商户调用
alipay.trade.precreate创建支付订单并生成付款二维码,再让用户扫码付款。普通的 H5 入口二维码并不是当面付的付款二维码。
支付宝开放平台可能调整产品名称和开放权限,接入时应以应用后台当前可签约的产品及相应文档为准。
推荐方案:手机网站支付
如果业务流程是:
- 用户使用支付宝扫描入口二维码;
- 在支付宝内置浏览器中打开 H5;
- 选择商品并提交订单;
- 跳转到支付宝收银台付款;
- 支付完成后返回 H5;
最直接的方案是接入手机网站支付,也就是 alipay.trade.wap.pay。
这种方案不需要事先识别支付宝用户,也不必为了完成支付而获取 auth_code。服务端创建业务订单后,调用支付接口生成跳转表单或支付地址,浏览器再进入支付宝收银台。
示意代码:
AlipayTradeWapPayRequest request = new AlipayTradeWapPayRequest();
request.setReturnUrl("https://example.com/pay/return");
request.setNotifyUrl("https://example.com/pay/notify");
request.setBizContent("""
{
"out_trade_no": "ORDER_202609090001",
"total_amount": "99.00",
"subject": "商品订单",
"product_code": "QUICK_WAP_WAY"
}
""");
AlipayTradeWapPayResponse response = alipayClient.pageExecute(request);
if (response.isSuccess()) {
String paymentForm = response.getBody();
// 将 paymentForm 返回给浏览器,由浏览器自动提交或直接输出。
}
前端收到支付表单后,应使用 SDK 返回的完整内容完成跳转,不要自行拼接支付宝网关参数。
需要原生支付体验时使用 JSAPI
如果不想跳转到普通 H5 收银台,而是要在支付宝客户端内直接唤起支付面板,可以采用 JSAPI 支付流程。
通常按以下步骤处理:
- 用户扫描入口二维码,进入支付宝内的 H5。
- H5 通过支付宝 OAuth 获取临时
auth_code。 - 服务端使用
auth_code换取用户user_id。 - 服务端创建支付订单,取得支付宝交易号
trade_no。 - H5 调用
AlipayJSBridge的tradePay。 - 服务端通过异步通知确认最终支付结果。
前端调用示例:
<script>
function ready(callback) {
if (window.AlipayJSBridge) {
callback();
} else {
document.addEventListener('AlipayJSBridgeReady', callback, false);
}
}
function pay(tradeNo) {
ready(function () {
AlipayJSBridge.call(
'tradePay',
{
tradeNO: tradeNo
},
function (result) {
console.log('tradePay result:', result);
// 此处结果只适合更新页面交互状态。
// 是否支付成功,应由服务端查询或异步通知确认。
}
);
});
}
</script>
这里的 tradeNO 指支付宝侧的交易号,不是商户自己的 out_trade_no,不要混用。
是否必须获取 auth_code
是否需要获取 auth_code,取决于采用的支付方式。
手机网站支付
调用 alipay.trade.wap.pay 时,通常不需要获取 auth_code。支付宝收银台会处理用户身份和付款账户。
如果业务还要识别支付宝用户,例如绑定会员或识别回访用户,则需要单独设计 OAuth 授权流程。这属于用户身份识别需求,并不是手机网站支付的必要步骤。
JSAPI 支付
如果创建交易的接口要求传入付款用户的 buyer_id,通常要先获取用户的 user_id。常见流程是:
- 将用户跳转到支付宝 OAuth 授权地址;
- 支付宝回跳业务页面,并携带临时
auth_code; - 服务端调用
alipay.system.oauth.token; - 从响应中取得
user_id; - 使用该
user_id创建交易。
auth_code 是一次性且短时有效的临时凭证,前端不应长期保存,也不能将其当作用户 ID。
如果只需要识别用户,不需要读取昵称、头像等信息,通常应优先使用基础或静默授权范围。具体授权范围是否可用、需要传入哪些参数,应以应用签约能力和当前官方文档为准。
为什么不建议直接认定为“当面付”
选择支付产品时,要看支付订单如何创建、付款如何完成,而不是用户是否扫描过二维码。
这两种二维码的用途不同:
- 入口二维码:内容通常是商户 H5 地址,例如
https://example.com/shop,只用于打开页面。 - 付款二维码:由支付订单生成,通常对应
alipay.trade.precreate返回的qr_code,用于直接发起某一笔交易。
用户先扫描入口二维码进入商城,选好商品后再跳转到支付宝收银台,这更符合手机网站支付流程。只有服务端先创建付款订单,再生成订单二维码供用户扫描时,才属于典型的当面付扫码支付模式。
服务端异步通知处理
无论使用手机网站支付还是 JSAPI 支付,都不能只根据前端回调认定付款成功。服务端必须验证支付宝发送的异步通知,至少包括:
- 验证通知签名;
- 校验
app_id; - 校验
out_trade_no; - 校验订单金额和币种;
- 检查交易状态;
- 保证订单更新具有幂等性;
- 成功处理后按协议返回确认内容。
伪代码示例:
@PostMapping("/pay/notify")
public String notify(HttpServletRequest request) {
Map<String, String> params = extractAlipayParams(request);
boolean valid = AlipaySignature.rsaCheckV1(
params,
alipayPublicKey,
"UTF-8",
"RSA2"
);
if (!valid) {
return "failure";
}
String outTradeNo = params.get("out_trade_no");
String tradeStatus = params.get("trade_status");
String totalAmount = params.get("total_amount");
Order order = orderService.findByOrderNo(outTradeNo);
if (order == null) {
return "failure";
}
if (!order.getAmount().toPlainString().equals(totalAmount)) {
return "failure";
}
if ("TRADE_SUCCESS".equals(tradeStatus)
|| "TRADE_FINISHED".equals(tradeStatus)) {
orderService.markPaidIdempotently(
outTradeNo,
params.get("trade_no")
);
}
return "success";
}
实际需要校验哪些字段,应结合所接产品的通知协议确定,不能只凭 trade_status 更新订单。
实施建议
如果业务不需要识别支付宝用户,也不要求原生唤起支付面板,优先使用 alipay.trade.wap.pay。它的流程较简单,也不需要处理 OAuth auth_code。
如果要在支付宝内置浏览器中结合会员识别和 JSAPI 支付,应先确认应用已经取得相应产品权限,再实现 OAuth、服务端创建交易以及 AlipayJSBridge.call('tradePay') 流程。
不要混淆“扫码进入页面”和“扫描付款二维码”。前者只是页面入口,仅凭这一点无法判断应该接入哪种支付产品。
备注:内容仅供参考。
