PAYATHON 2026

订单码支付生成的二维码可以设置有效时间吗?

支付阿杰

结论

可以限制二维码的可支付时间,但要分清两种超时:

  • timeout_express、time_expire通常用于控制支付宝交易的付款期限。
  • 如果二维码首次扫码前尚未创建支付宝交易,调用关单接口会返回”交易不存在”。此时,无论设置交易超时参数还是调用关单接口,都不能让二维码失效。

因此,订单超时后只调用关闭交易接口并不可靠。要确保二维码过期后无法支付,需要设置支付宝接口支持的二维码有效期,同时在商户系统中严格校验订单状态和过期时间。

为什么未扫码时无法关单

部分订单码支付流程采用”扫码后建单”机制:

  1. 商户先生成订单二维码。
  2. 用户扫码后,支付宝侧才创建实际交易。
  3. 交易创建后,才能通过 out_trade_no 或 trade_no 查询和关闭交易。

如果用户一直没有扫码,支付宝侧可能没有对应的交易。此时调用关闭交易接口返回”交易不存在”,符合这一流程,但不代表二维码已经失效。

用户之后再次扫码,系统仍可能根据二维码内容创建新交易。这样就会出现商户订单已经超时,用户却仍能完成支付的情况。

正确处理方式

1. 设置二维码有效期参数

如果当前使用的订单码或预创建接口提供二维码有效期参数,应优先设置。部分支付宝当面付接口会区分以下字段:

  • qr_code_timeout_express:二维码本身的有效时间。
  • timeout_express:支付宝交易创建后的相对付款期限。
  • time_expire:支付宝交易的绝对过期时间。

不同产品、接口版本和签约能力支持的字段可能不同,应以当前接口文档中的请求参数为准。除了在本地 SDK 对象中设置字段,还要确认最终发给支付宝的请求 JSON 确实包含该字段。

例如,接口支持 qr_code_timeout_express 时,可以按类似方式设置请求:

{
  "out_trade_no": "ORDER_202609090001",
  "subject": "订单支付",
  "total_amount": "100.00",
  "qr_code_timeout_express": "10m",
  "timeout_express": "10m"
}

这样可以同时限制二维码的有效期和交易创建后的付款时间。具体取值格式、最小值和最大值,应以所调用接口的参数说明为准。

如果当前订单码支付接口没有提供二维码有效期字段,就不能用 timeout_express 代替二维码失效控制。

2. 商户系统必须校验订单状态

二维码有效期只能作为第一层控制。是否允许支付,最终应由商户订单状态决定。

扫码后创建交易或提交支付前,至少要校验:

  • 订单是否存在;
  • 订单是否仍处于待支付状态;
  • 当前时间是否早于商户订单的过期时间;
  • 订单金额是否与二维码及支付请求一致。

示例逻辑:

public PaymentResult createPayment(String outTradeNo) {
    Order order = orderService.findByOutTradeNo(outTradeNo);

    if (order == null) {
        throw new PaymentException("订单不存在");
    }

    if (order.getStatus() != OrderStatus.WAIT_PAY) {
        throw new PaymentException("订单当前状态不允许支付");
    }

    if (!Instant.now().isBefore(order.getExpireTime())) {
        orderService.markExpired(outTradeNo);
        throw new PaymentException("订单已过期,请重新下单");
    }

    return alipayService.createPayment(order);
}

过期校验必须在创建支付宝交易之前完成,否则用户扫码后仍可能生成一笔新的可支付交易。

3. 超时任务按交易是否存在分别处理

订单超时任务可以采用以下逻辑:

public void expireOrder(String outTradeNo) {
    Order order = orderService.lockByOutTradeNo(outTradeNo);

    if (order == null || order.getStatus() != OrderStatus.WAIT_PAY) {
        return;
    }

    orderService.markExpired(outTradeNo);

    AlipayTrade trade = alipayService.query(outTradeNo);
    if (trade != null) {
        alipayService.close(outTradeNo);
    }
}

建议按以下顺序处理:

  1. 先把商户订单改为已过期,阻止后续扫码建单。
  2. 再查询支付宝侧是否已有交易。
  3. 如果交易存在,调用关闭交易接口。
  4. 如果交易不存在,无需反复关单,但后续入口必须拒绝为该商户订单创建交易。

更新订单状态时,应使用数据库事务、条件更新或行锁,避免扫码支付与超时任务并发执行。

如果二维码直接由支付宝识别

如果二维码内容直接指向支付宝,扫码过程不经过商户服务,商户系统就无法在扫码时校验订单状态。此时只能依赖该支付产品提供的二维码有效期能力。

如果当前接口不支持二维码有效期,可以考虑以下方案:

  • 使用支持二维码有效期的预创建接口;
  • 生成指向商户中间页的动态二维码,由中间页校验订单后再发起支付;
  • 每次超时后生成新的订单号和二维码,不再复用旧订单;
  • 向支付宝技术支持确认当前签约产品是否支持二维码过期参数。

注意事项

  • 商户页面提示”订单超时”,不等于支付宝交易已经关闭。
  • timeout_express通常从支付宝交易实际创建时开始计算,不能自然覆盖”二维码生成后一直未扫码”的时间。
  • 调用关单接口返回”交易不存在”,表示支付宝侧尚无可关闭的交易,不代表二维码已经作废。
  • 收到支付异步通知后,仍要检查商户订单状态。如果业务上可能收到超时后的成功支付通知,需要制定退款或人工处理规则。
  • 使用 out_trade_no时必须保证唯一。过期订单不能复用原订单号重新发起支付。
  • 判断参数是否生效,应查看实际请求报文和所调用接口的响应。只检查 SDK 赋值代码,无法确认参数已经发送。

备注:内容仅供参考。