支付宝APP扫码进入H5缴费应选择哪种支付产品?
结论
这种场景通常应接入支付宝的手机网站支付(WAP 支付),对应接口是:
alipay.trade.wap.pay
二维码只是 H5 页面的入口。用户扫码进入页面,输入车牌号、确认金额并发起支付,实际交易仍属于移动端网页支付,不是“当面付”中的用户付款码支付。
为什么选择手机网站支付
选择支付产品时,主要看用户在哪里确认订单、发起付款,不能只看流程中有没有二维码:
- 二维码中存放的是停车缴费 H5 页面的 URL。
- 用户进入 H5 后输入车牌号、查询费用并确认订单。
- 服务端根据订单动态确定支付金额。
- H5 页面调用支付宝收银台完成付款。
这里的二维码只负责打开页面,后续支付应按手机网站支付接入。
“当面付”更适合商家展示收款码、用户出示付款码,或收银设备扫码等线下收银场景。如果二维码只是业务页面的入口,就不应因为存在扫码动作而选择当面付。
接入流程
1. 开通手机网站支付能力
先在支付宝开放平台创建应用,再按平台当前要求完成应用签约、密钥配置和上线审核。
通常需要保存和配置以下内容:
- 应用
app_id - 应用私钥
- 支付宝公钥或支付宝公钥证书
- 支付异步通知地址
notify_url - 支付完成后的页面跳转地址
return_url
开放平台可能调整签约条件和配置入口,具体应以应用后台当前显示的产品能力为准。
2. 二维码指向业务 H5 页面
二维码中可以放入类似下面的 HTTPS 地址:
https://pay.example.com/parking
用户使用支付宝 APP 扫码进入页面后,输入车牌号并查询待缴费用。
不要将订单金额等关键交易信息直接写入二维码,也不要信任前端提交的金额。服务端应根据车牌号、停车记录和计费规则生成订单。
3. 服务端创建支付请求
服务端确认订单后,调用 alipay.trade.wap.pay。下面是使用支付宝 Java SDK 的示意代码:
AlipayClient alipayClient = new DefaultAlipayClient(
"https://openapi.alipay.com/gateway.do",
appId,
merchantPrivateKey,
"json",
"UTF-8",
alipayPublicKey,
"RSA2"
);
AlipayTradeWapPayRequest request = new AlipayTradeWapPayRequest();
request.setNotifyUrl("https://pay.example.com/alipay/notify");
request.setReturnUrl("https://pay.example.com/payment/result");
AlipayTradeWapPayModel model = new AlipayTradeWapPayModel();
model.setOutTradeNo(orderNo);
model.setTotalAmount(totalAmount);
model.setSubject("停车缴费");
model.setProductCode("QUICK_WAP_WAY");
request.setBizModel(model);
AlipayTradeWapPayResponse response = alipayClient.pageExecute(request);
if (!response.isSuccess()) {
throw new IllegalStateException(
"创建支付宝支付请求失败:" + response.getSubMsg()
);
}
String paymentForm = response.getBody();
response.getBody() 通常是支付宝返回的自动提交表单。服务端可以将其作为 HTML 响应返回给浏览器,让页面跳转到支付宝收银台。
这段代码只展示核心调用方式。支付宝 SDK 的不同版本可能在类结构或证书配置方式上有所差异,实际接入时应以所用版本的接口定义为准。
4. 处理异步通知
支付结果必须以支付宝服务端发送的异步通知为准,不能只依赖 return_url 页面显示的结果。
收到通知后,应完成以下处理:
- 验证支付宝签名。
- 核对
app_id和商户订单号out_trade_no。 - 核对金额
total_amount。 - 检查交易状态,例如
TRADE_SUCCESS。 - 以幂等方式更新停车订单。
- 处理成功后,按支付宝要求返回成功响应。
示意逻辑如下:
Map<String, String> params = extractRequestParams(request);
boolean valid = AlipaySignature.rsaCheckV1(
params,
alipayPublicKey,
"UTF-8",
"RSA2"
);
if (!valid) {
response.getWriter().write("failure");
return;
}
String outTradeNo = params.get("out_trade_no");
String tradeStatus = params.get("trade_status");
String totalAmount = params.get("total_amount");
ParkingOrder order = orderService.findByOrderNo(outTradeNo);
if (order == null || !order.getAmount().toPlainString().equals(totalAmount)) {
response.getWriter().write("failure");
return;
}
if ("TRADE_SUCCESS".equals(tradeStatus)
|| "TRADE_FINISHED".equals(tradeStatus)) {
orderService.markPaidIdempotently(
outTradeNo,
params.get("trade_no")
);
}
response.getWriter().write("success");
生产环境中,还应根据支付宝当前文档核验通知里的商户身份、交易归属等字段。
特殊情况
如果 H5 页面明确运行在支付宝客户端容器中,并希望通过支付宝 JSAPI 直接唤起支付,也可以评估 JSAPI 支付方案。一般由服务端创建交易,再由页面调用支付宝提供的客户端支付能力。
JSAPI 支付对页面运行环境、授权方式和产品权限还有额外要求。如果页面需要同时兼容普通手机浏览器,手机网站支付通常更合适。
注意事项
- 二维码应使用可信的 HTTPS 域名,不要使用随时可能变化的临时地址。
- 订单号
out_trade_no必须由商户系统生成,并保证唯一。 - 金额必须由服务端计算,不能直接采用前端传入的值。
return_url只用于改善用户体验,不能证明订单已经支付成功。- 异步通知可能重复发送,因此订单更新必须具备幂等性。
- 确认支付成功后,再执行抬杆、放行或销账等业务操作。
- 如果放行前需要即时确认,可以结合异步通知和主动查单处理,但不能只根据前端回跳结果放行。
- 如果二维码不是业务 H5 地址,而是支付宝预下单生成的收款二维码,则应按照当面付等线下支付场景重新评估。
备注:内容仅供参考。
