PAYATHON 2026

alipay.trade.app.pay 是否会创建订单并通知 trade_closed

支付小周

结论

不需要先调用 alipay.trade.create。

alipay.trade.app.pay 可以直接创建交易并发起 App 支付。支付宝交易因超时未付款或被关闭后,异步通知中的 trade_status 可能是 TRADE_CLOSED。

商户不能只依赖异步通知确认订单状态。如果没有收到 TRADE_CLOSED,通常是因为交易尚未真正创建、关闭条件尚未触发、notify_url 配置有误或通知处理异常,并非缺少 alipay.trade.create。

为什么没有收到 TRADE_CLOSED

先看下面这行代码:

AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();

它只创建了一个 Java 请求对象,既没有向支付宝发送请求,也没有创建支付宝交易。

服务端还需要设置业务参数并调用支付宝客户端,例如:

AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request);

取得返回的 orderString 后,App 端还需要用它调起支付宝 SDK。完整执行支付链路后,才能根据 out_trade_no 查询支付宝侧是否已经存在这笔交易。

没有收到关闭通知,常见原因有:

  • 只创建了请求对象,没有真正调用支付宝接口。
  • 服务端取得了 orderString,但 App 没有调起支付宝 SDK,交易没有完整进入支付流程。
  • 订单没有设置较短的 timeout_express,目前仍在等待支付。
  • notify_url 未设置、无法通过公网访问,或者 HTTPS、域名解析、网关拦截等环节存在问题。
  • 通知已经到达,但验签失败,业务代码过滤了 TRADE_CLOSED,或者日志只记录支付成功通知。
  • 通知处理程序没有返回纯文本 success。支付宝重试后,系统仍未正确记录通知。
  • 商户只关闭了本地订单,没有调用支付宝关单接口。本地状态变化不会自动改变支付宝交易状态。
  • 异步通知延迟或最终未能成功送达,而商户系统也没有通过查询接口补偿。

推荐的支付流程

1. 直接调用 alipay.trade.app.pay

AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();

request.setNotifyUrl("https://example.com/alipay/notify");

AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
model.setOutTradeNo("ORDER_202609090001");
model.setSubject("商品订单");
model.setTotalAmount("99.00");
model.setProductCode("QUICK_MSECURITY_PAY");
model.setTimeoutExpress("30m");

request.setBizModel(model);

AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request);

if (response.isSuccess()) {
    String orderString = response.getBody();
    // 将 orderString 返回给 App,由 App 调起支付宝支付 SDK
} else {
    // 记录 response.getCode()、response.getSubCode() 等信息
}

具体的 SDK 类和可用字段可能随支付宝 Java SDK 版本而变化,应以项目实际使用版本的接口定义为准。

timeout_express 指定订单的可支付时间。到期后的状态变化未必精确到秒,因此不能用本地计时器代替支付宝交易状态查询。

2. 完整处理异步通知

通知处理不能只判断 TRADE_SUCCESS,还要识别 TRADE_CLOSED:

String tradeStatus = request.getParameter("trade_status");
String outTradeNo = request.getParameter("out_trade_no");
String tradeNo = request.getParameter("trade_no");

boolean signVerified = AlipaySignature.rsaCheckV1(
        requestParams,
        alipayPublicKey,
        charset,
        signType
);

if (!signVerified) {
    response.getWriter().print("failure");
    return;
}

// 还应校验 app_id、seller_id、out_trade_no、total_amount 等业务信息

switch (tradeStatus) {
    case "TRADE_SUCCESS":
    case "TRADE_FINISHED":
        // 幂等更新为支付成功
        break;

    case "TRADE_CLOSED":
        // 幂等更新为已关闭
        break;

    default:
        // 记录暂不处理的状态
        break;
}

response.getWriter().print("success");

处理异步通知时,必须先验签,再核对商户订单号、金额、收款方等业务信息。仅根据 trade_status 更新订单会带来安全风险。

3. 使用查询接口做状态补偿

不要一直等待 TRADE_CLOSED 通知。订单超过本地支付期限后,应调用 alipay.trade.query 查询支付宝侧的状态:

AlipayTradeQueryRequest queryRequest = new AlipayTradeQueryRequest();

AlipayTradeQueryModel queryModel = new AlipayTradeQueryModel();
queryModel.setOutTradeNo("ORDER_202609090001");

queryRequest.setBizModel(queryModel);

AlipayTradeQueryResponse queryResponse = alipayClient.execute(queryRequest);

if (queryResponse.isSuccess()) {
    String tradeStatus = queryResponse.getTradeStatus();

    if ("TRADE_CLOSED".equals(tradeStatus)) {
        // 幂等更新本地订单为已关闭
    } else if ("TRADE_SUCCESS".equals(tradeStatus)
            || "TRADE_FINISHED".equals(tradeStatus)) {
        // 幂等更新本地订单为支付成功
    }
}

如果商户决定主动关闭尚未付款的交易,可以调用 alipay.trade.close。关单前后都应通过查询接口确认最终状态,防止订单关闭与支付成功并发时出现错误覆盖。

是否可以先调用 alipay.trade.create

可以根据具体业务使用 alipay.trade.create 创建交易,但它不是 alipay.trade.app.pay 接收关闭通知的前置条件。

不要针对同一笔业务订单简单地先调用 alipay.trade.create,然后无条件调用 alipay.trade.app.pay。如果两个请求的交易标识、接口用途和业务流程没有设计好,可能造成重复下单、交易冲突或对账困难。

普通 App 支付直接使用 alipay.trade.app.pay 即可。实现时应注意:

  1. 正确设置 notify_url 和 timeout_express。
  2. 确认 App 已使用 orderString 调起支付宝 SDK。
  3. 在异步通知中同时处理支付成功和交易关闭状态。
  4. 对通知进行验签,并校验业务字段。
  5. 使用 alipay.trade.query 定时补偿。
  6. 需要主动关单时调用 alipay.trade.close。
  7. 所有状态更新都按 out_trade_no 做幂等处理。

备注:内容仅供参考。