当面付取消支付后订单仍为WAIT_BUYER_PAY是否正常?
结论
这是正常现象。
用户关闭支付弹窗或返回商家页面,只是中断了客户端的支付流程,不代表支付宝服务端已经关闭交易。只要订单尚未支付,商户没有主动关单,交易也没有超时关闭,调用 alipay.trade.query 时就可能返回:
WAIT_BUYER_PAY
这个状态表示交易已经创建,正在等待买家付款。
为什么订单没有自动取消
当面付通常包含三个相互独立的环节:
- 商户通过
alipay.trade.precreate等接口创建交易。 - 用户扫码进入支付宝收银台。
- 支付宝服务端根据实际付款结果更新交易状态。
关闭弹窗是用户端的操作。支付宝服务端通常无法据此判断用户只是暂时退出,还是已经放弃付款,所以不会立即关闭交易。
只要订单仍然有效,用户重新打开付款页面或再次扫描二维码,仍有可能完成付款。能否继续支付,取决于订单是否已经关闭、二维码是否有效,以及商户设置的超时规则。
建议的处理方式
1. 不要把关闭弹窗当作支付失败
前端收到“用户取消”“窗口关闭”或跳转中断后,不应直接把业务订单标记为支付失败。可以继续保留“待支付”状态,再由服务端查询实际交易状态。
各状态可以按以下方式处理:
trade_status | 建议处理 |
|---|---|
WAIT_BUYER_PAY | 保持待支付 |
TRADE_SUCCESS | 标记支付成功并履行业务 |
TRADE_FINISHED | 按已完成交易处理 |
TRADE_CLOSED | 标记订单已关闭 |
业务系统应以支付宝服务端的异步通知或主动查询结果为准,不能只依赖客户端返回的信息。
2. 用户明确取消订单时主动关闭交易
如果用户不是简单地关闭支付页面,而是在商户系统中明确点击了“取消订单”,商户服务端可以调用 alipay.trade.close,关闭尚未支付的交易。
下面是支付宝 Java SDK 的示意代码。实际类名和调用方式应以项目当前使用的 SDK 版本为准:
AlipayTradeCloseRequest request = new AlipayTradeCloseRequest();
AlipayTradeCloseModel model = new AlipayTradeCloseModel();
model.setOutTradeNo("YOUR_OUT_TRADE_NO");
request.setBizModel(model);
AlipayTradeCloseResponse response = alipayClient.execute(request);
if (response.isSuccess()) {
// 支付宝交易关闭成功
// 再将本地业务订单更新为已取消或已关闭
} else {
// 记录 code、subCode、msg、subMsg,进一步判断失败原因
System.out.println(response.getCode());
System.out.println(response.getSubCode());
System.out.println(response.getMsg());
System.out.println(response.getSubMsg());
}
关单前应先检查本地订单状态,并做好并发控制,避免用户付款和系统关单同时发生。
3. 由服务端定时检查超时订单
商户可以为业务订单设置支付期限。订单超过期限后,由服务端定时查询支付宝交易状态:
AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
AlipayTradeQueryModel model = new AlipayTradeQueryModel();
model.setOutTradeNo("YOUR_OUT_TRADE_NO");
request.setBizModel(model);
AlipayTradeQueryResponse response = alipayClient.execute(request);
if (response.isSuccess()) {
String tradeStatus = response.getTradeStatus();
if ("TRADE_SUCCESS".equals(tradeStatus)
|| "TRADE_FINISHED".equals(tradeStatus)) {
// 更新本地订单为支付成功
} else if ("WAIT_BUYER_PAY".equals(tradeStatus)) {
// 如果业务支付期限已过,可继续调用 alipay.trade.close
} else if ("TRADE_CLOSED".equals(tradeStatus)) {
// 更新本地订单为已关闭
}
}
超时订单可以按以下顺序处理:
- 判断本地订单是否已经超过支付期限。
- 调用
alipay.trade.query查询服务端状态。 - 如果已经支付,将本地订单更新为支付成功。
- 如果仍是
WAIT_BUYER_PAY,调用alipay.trade.close。 - 关单成功后,再关闭本地业务订单。
注意事项
以服务端结果为准
客户端的“取消支付”只能用于界面提示,不能代表最终交易状态。判断支付是否成功时,应以支付宝异步通知为主,并通过主动查询补偿可能遗漏的通知。
注意支付与关单并发
用户取消订单时,付款可能已经完成,只是异步通知还没有到达。如果关单接口调用失败,不能直接认定订单已经取消,应再次调用 alipay.trade.query 确认状态。
如果查询结果已经是 TRADE_SUCCESS,应按支付成功处理,不能再把订单改为已取消。
保证状态更新幂等
异步通知、主动查询和定时关单可能同时更新同一笔订单。更新数据库时应带上原状态条件,例如只允许:
UPDATE orders
SET status = 'PAID'
WHERE out_trade_no = ?
AND status = 'WAIT_PAY';
同一条支付通知也应进行幂等处理,避免重复发货、重复记账或重复增加权益。
区分业务订单和支付宝交易
商户页面显示“已取消”,不代表支付宝交易已经关闭。只有关单接口调用成功,或查询结果为 TRADE_CLOSED,才能确认支付宝侧的交易已经关闭。
因此,用户关闭支付弹窗后查询到 WAIT_BUYER_PAY 并非异常。如果业务要求订单取消后不能继续付款,商户服务端需要执行查询和关单流程。
备注:内容仅供参考。
