如何让支付二维码对应的预创建订单失效?
结论
要让支付二维码按时失效,建议在创建支付请求时设置 timeout_express。超过指定时间后,支付宝会自动关闭交易。
如果需要提前失效,要分情况处理:
- 成功调用
alipay.trade.precreate后,支付宝侧通常已经创建交易。如果交易状态为WAIT_BUYER_PAY,可以调用alipay.trade.close将其关闭。 alipay.trade.page.pay生成的是已签名的跳转请求。如果用户还没有打开支付页面,支付宝侧可能尚未创建交易,也就无法通过alipay.trade.close提前关闭。
已经签名并交给用户的二维码或跳转链接不能直接”撤回签名”。可行的做法是设置支付超时、停止展示支付入口,并在支付宝侧创建交易后将其关闭。
设置支付有效期
创建支付请求时,在业务参数中加入 timeout_express:
{
"out_trade_no": "ORDER_202609090001",
"total_amount": "99.00",
"subject": "测试订单",
"timeout_express": "10m"
}
常见的时间单位有:
m:分钟,例如10mh:小时,例如1hd:天,例如1d
允许设置的最小值、最大值和具体格式可能受接口产品及支付宝当前规则限制,请以实际接入文档和接口返回结果为准。
alipay.trade.precreate 示例
AlipayTradePrecreateRequest request = new AlipayTradePrecreateRequest();
JSONObject bizContent = new JSONObject();
bizContent.put("out_trade_no", "ORDER_202609090001");
bizContent.put("total_amount", "99.00");
bizContent.put("subject", "测试订单");
bizContent.put("timeout_express", "10m");
request.setBizContent(bizContent.toJSONString());
AlipayTradePrecreateResponse response = alipayClient.execute(request);
if (response.isSuccess()) {
String qrCode = response.getQrCode();
// 将 qrCode 生成二维码
} else {
// 记录 response.getCode()、response.getSubCode() 等信息
}
超过有效期后,即使用户仍保留二维码,也无法再完成这笔交易。二维码图片本身仍可识别,但对应交易已经关闭或超时。
alipay.trade.page.pay 示例
AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
request.setReturnUrl("https://example.com/pay/return");
request.setNotifyUrl("https://example.com/pay/notify");
JSONObject bizContent = new JSONObject();
bizContent.put("out_trade_no", "ORDER_202609090002");
bizContent.put("total_amount", "99.00");
bizContent.put("subject", "测试订单");
bizContent.put("product_code", "FAST_INSTANT_TRADE_PAY");
bizContent.put("timeout_express", "10m");
request.setBizContent(bizContent.toJSONString());
AlipayTradePagePayResponse response = alipayClient.pageExecute(request);
String paymentHtml = response.getBody();
必须在生成签名请求前将 timeout_express 写入业务参数。签名完成后再修改参数,会导致验签失败。
提前关闭预创建交易
如果 alipay.trade.precreate 已经调用成功,但用户尚未付款,可以通过商户订单号关闭交易:
AlipayTradeCloseRequest request = new AlipayTradeCloseRequest();
JSONObject bizContent = new JSONObject();
bizContent.put("out_trade_no", "ORDER_202609090001");
request.setBizContent(bizContent.toJSONString());
AlipayTradeCloseResponse response = alipayClient.execute(request);
if (response.isSuccess()) {
// 交易已关闭,原二维码不能再完成付款
} else {
// 根据 code、sub_code 判断交易不存在、状态不允许或其他失败原因
}
关闭前,可以先调用 alipay.trade.query 查询交易状态:
AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
JSONObject bizContent = new JSONObject();
bizContent.put("out_trade_no", "ORDER_202609090001");
request.setBizContent(bizContent.toJSONString());
AlipayTradeQueryResponse response = alipayClient.execute(request);
String tradeStatus = response.getTradeStatus();
只有仍处于待支付状态的交易才适合关闭。如果交易已经支付成功,alipay.trade.close 不能代替退款,需要根据业务规则调用退款接口。
为什么有时无法关闭
常见原因包括:
-
支付宝侧还没有创建交易
如果用户尚未访问
alipay.trade.page.pay生成的跳转页面,商户侧可能只有一份签名请求,支付宝侧还没有可关闭的交易记录。此时调用关闭接口,可能返回”交易不存在”一类结果。 -
交易状态已经变化
用户可能正在付款,也可能已经支付成功。关闭操作和支付操作存在并发竞争,不能认为关闭请求发出后一定会先于支付完成。
-
订单标识不正确
查询和关闭交易时,
out_trade_no必须与创建支付请求时完全一致。如果使用了新的商户订单号,支付宝无法将其与原交易关联。 -
将
alipay.trade.cancel当作通用失效接口alipay.trade.cancel与alipay.trade.close的用途和适用状态并不完全相同。需要终止待支付交易时,通常应调用alipay.trade.close,再根据接口返回判断是否关闭成功。
推荐处理流程
业务系统可以按以下流程控制支付状态:
- 每次支付尝试都使用唯一的
out_trade_no。 - 创建支付请求时,设置较短且合理的
timeout_express。 - 在本地订单中记录二维码的业务有效期。
- 用户取消支付后,立即停止展示二维码或禁用支付入口。
- 确认支付宝侧已经创建交易且交易仍处于待支付状态后,调用
alipay.trade.close。 - 如果关闭时交易已经支付成功,应转入支付成功或退款流程,不能只把本地订单改为”已取消”。
- 以支付宝异步通知和主动查询结果确定最终状态,并做好幂等处理。
在本地隐藏二维码,只能阻止用户继续从当前页面获取它,无法删除用户已经截图或保存的二维码。要真正阻止付款,仍需依靠支付宝的交易超时或交易关闭机制。
备注:内容仅供参考。
