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 即可。实现时应注意:
- 正确设置
notify_url和timeout_express。 - 确认 App 已使用
orderString调起支付宝 SDK。 - 在异步通知中同时处理支付成功和交易关闭状态。
- 对通知进行验签,并校验业务字段。
- 使用
alipay.trade.query定时补偿。 - 需要主动关单时调用
alipay.trade.close。 - 所有状态更新都按
out_trade_no做幂等处理。
备注:内容仅供参考。
