PAYATHON 2026

如何让支付二维码对应的预创建订单失效?

支付阿杰

结论

要让支付二维码按时失效,建议在创建支付请求时设置 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:分钟,例如 10m
  • h:小时,例如 1h
  • d:天,例如 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 不能代替退款,需要根据业务规则调用退款接口。

为什么有时无法关闭

常见原因包括:

  1. 支付宝侧还没有创建交易

    如果用户尚未访问 alipay.trade.page.pay 生成的跳转页面,商户侧可能只有一份签名请求,支付宝侧还没有可关闭的交易记录。此时调用关闭接口,可能返回”交易不存在”一类结果。

  2. 交易状态已经变化

    用户可能正在付款,也可能已经支付成功。关闭操作和支付操作存在并发竞争,不能认为关闭请求发出后一定会先于支付完成。

  3. 订单标识不正确

    查询和关闭交易时,out_trade_no 必须与创建支付请求时完全一致。如果使用了新的商户订单号,支付宝无法将其与原交易关联。

  4. 将 alipay.trade.cancel 当作通用失效接口

    alipay.trade.cancel 与 alipay.trade.close 的用途和适用状态并不完全相同。需要终止待支付交易时,通常应调用 alipay.trade.close,再根据接口返回判断是否关闭成功。

推荐处理流程

业务系统可以按以下流程控制支付状态:

  1. 每次支付尝试都使用唯一的 out_trade_no。
  2. 创建支付请求时,设置较短且合理的 timeout_express。
  3. 在本地订单中记录二维码的业务有效期。
  4. 用户取消支付后,立即停止展示二维码或禁用支付入口。
  5. 确认支付宝侧已经创建交易且交易仍处于待支付状态后,调用 alipay.trade.close。
  6. 如果关闭时交易已经支付成功,应转入支付成功或退款流程,不能只把本地订单改为”已取消”。
  7. 以支付宝异步通知和主动查询结果确定最终状态,并做好幂等处理。

在本地隐藏二维码,只能阻止用户继续从当前页面获取它,无法删除用户已经截图或保存的二维码。要真正阻止付款,仍需依靠支付宝的交易超时或交易关闭机制。

备注:内容仅供参考。