APP支付接口报错ALIN42281,如何解决?
结论
ALIN42281 表示当前支付宝订单已处于“不允许继续付款”的状态。常见原因是订单已经关闭、支付或退款,也可能是商户重复使用了已经结束交易的 out_trade_no。
收到这个错误后,不要反复调用 APP 支付接口。先查询原订单状态,再根据结果处理:
- 已支付:按支付成功处理,避免重复下单或扣款。
- 待支付:检查订单是否超时、金额是否一致,并确认有无并发支付请求。
- 已关闭或已退款:生成新的商户订单号,重新发起支付。
- 查询不到:核对应用、运行环境、商户身份和订单号。确认没有支付成功记录后,再创建新订单。
错误原因
APP 支付通常通过 alipay.trade.app.pay 创建支付请求,out_trade_no 是商户侧订单号。支付宝通过该订单号识别交易,已经进入终态的交易不能重新作为新交易付款。
以下情况容易触发该错误:
- 用户取消订单或订单超时后,商户仍使用原
out_trade_no发起支付。 - 商户已经调用关单接口,但客户端仍使用旧支付参数唤起支付宝。
- 订单已经支付成功,但商户没有正确处理同步返回或异步通知,随后再次尝试支付。
- 订单已经退款,业务系统却将其恢复为“待支付”,并继续使用原交易号。
- 用户多次点击支付,产生并发请求,导致商户订单状态与支付宝交易状态不同步。
- 测试环境与生产环境混用,或者查询订单时使用了不同的
app_id、商户身份或网关环境。
ALIN42281 只说明该交易当前不能继续付款。仅凭这个错误码,无法确定订单究竟是已关闭、已支付还是处于其他状态。最终状态应通过订单查询接口和异步通知确认。
排查步骤
1. 确认本次请求使用的商户订单号
记录调用 alipay.trade.app.pay 时提交的这些字段:
app_id
method
out_trade_no
total_amount
subject
notify_url
timestamp
重点检查 out_trade_no:
- 是否曾被其他业务订单使用;
- 失败重试时是否一直复用;
- 是否对应已经取消、关闭或退款的订单;
- 是否有多个请求同时操作同一个订单;
- 数据库中的订单状态是否与支付宝侧一致。
不能只检查客户端传入的数据。支付金额、订单号和商品信息应以服务端保存的订单记录为准。
2. 查询支付宝侧交易状态
可以通过 alipay.trade.query 查询原订单。下面的示例只保留核心逻辑,SDK 初始化方式和具体异常类型应根据项目实际使用的支付宝开放平台 SDK 调整。
AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
AlipayTradeQueryModel model = new AlipayTradeQueryModel();
model.setOutTradeNo(outTradeNo);
request.setBizModel(model);
AlipayTradeQueryResponse response = alipayClient.execute(request);
if (response.isSuccess()) {
String tradeStatus = response.getTradeStatus();
String tradeNo = response.getTradeNo();
System.out.println("tradeNo=" + tradeNo);
System.out.println("tradeStatus=" + tradeStatus);
} else {
System.out.println("code=" + response.getCode());
System.out.println("subCode=" + response.getSubCode());
System.out.println("subMsg=" + response.getSubMsg());
}
常见交易状态可按下表处理:
| 交易状态 | 含义 | 建议处理 |
|---|---|---|
WAIT_BUYER_PAY | 等待买家付款 | 检查是否超时、金额是否一致,以及是否发生并发请求 |
TRADE_SUCCESS | 支付成功 | 更新商户订单,不要重新发起支付 |
TRADE_FINISHED | 交易结束 | 不允许再次支付,按已完成交易处理 |
TRADE_CLOSED | 交易关闭 | 使用新的 out_trade_no 创建新订单 |
| 查询不到交易 | 支付宝侧无对应记录或查询条件不匹配 | 核对环境、应用和订单号后,再决定是否重新下单 |
不同产品和接入模式返回的字段可能不同,应以实际接口响应及所用 SDK 的文档为准。
3. 核对商户本地订单状态
同时检查数据库中的订单记录,例如:
SELECT
order_no,
payment_status,
total_amount,
alipay_trade_no,
closed_at,
paid_at,
refunded_at
FROM payment_order
WHERE order_no = '实际的商户订单号';
重点查看是否存在以下矛盾:
- 本地状态是“待支付”,支付宝状态是
TRADE_SUCCESS; - 本地状态是“待支付”,支付宝状态是
TRADE_CLOSED; - 本地订单金额与发起支付时的
total_amount不一致; - 同一个
out_trade_no对应多条业务订单; - 支付成功的异步通知已经到达,但处理失败或事务发生回滚;
- 定时关单任务已经关闭交易,客户端却仍保留旧支付参数。
4. 根据交易状态处理
原订单已经支付
如果查询结果是 TRADE_SUCCESS 或 TRADE_FINISHED,不要重新下单。校验支付信息后,将商户订单更新为支付成功。
至少需要校验:
app_id是否匹配;out_trade_no是否匹配;total_amount是否与商户订单金额一致;- 收款方身份是否符合预期;
- 通知签名是否验证成功。
订单状态更新必须支持幂等处理,例如:
UPDATE payment_order
SET
payment_status = 'PAID',
alipay_trade_no = :trade_no,
paid_at = :paid_at
WHERE order_no = :order_no
AND payment_status = 'PENDING';
如果更新行数为 0,应查询订单当前状态,不能直接判定处理失败。异步通知可能重复发送,也可能与主动查询同时执行。
原订单已经关闭
如果状态是 TRADE_CLOSED,原支付宝交易无法恢复付款。业务系统需要创建新的支付订单,并生成全新的 out_trade_no。
String newOutTradeNo = orderNoGenerator.next();
AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
model.setOutTradeNo(newOutTradeNo);
model.setTotalAmount(totalAmount.toPlainString());
model.setSubject(subject);
model.setProductCode("QUICK_MSECURITY_PAY");
AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
request.setBizModel(model);
request.setNotifyUrl(notifyUrl);
AlipayTradeAppPayResponse response =
alipayClient.sdkExecute(request);
if (!response.isSuccess()) {
throw new IllegalStateException(
"创建支付失败: " + response.getSubCode() + " " + response.getSubMsg()
);
}
String orderString = response.getBody();
生成新订单号后,还要记录新旧支付单之间的关系,方便审计和问题排查:
业务订单号:BUSINESS_10001
旧支付单号:PAY_10001_01
新支付单号:PAY_10001_02
不能只修改本地状态,然后继续使用旧的 out_trade_no。
原订单仍在等待付款
如果状态是 WAIT_BUYER_PAY,通常不应立即创建多个新交易。先检查:
- 支付参数是否已经过期;
- 商户是否设置了超时时间;
- 订单是否正在由关单任务处理;
- 客户端是否重复提交;
- 同一订单的金额、标题等数据是否发生变化。
如果业务决定放弃原交易,应先确认订单尚未支付,再关闭旧交易,并使用新的 out_trade_no 下单。关单操作可能与支付同时发生,因此关闭前后都要再次查询状态,避免把已经支付成功的订单误判为失败。
推荐的服务端处理流程
收到支付请求
↓
查询商户订单
↓
订单已支付?——是→ 返回支付成功状态
↓否
是否存在历史支付单?
↓
查询支付宝交易状态
↓
已支付 → 更新本地状态并返回成功
待支付 → 返回现有支付参数,或按业务规则处理
已关闭/已结束 → 创建新支付单号并重新下单
查询异常 → 暂停自动重试,记录响应并人工或定时核查
支付接口不能把“重新点击支付”直接处理成“原样重放上一次请求”。业务订单号和支付单号应分开管理。一个业务订单可以因超时产生多个支付单,但每个支付单都必须使用唯一的 out_trade_no。
注意事项
- 不要只根据 APP 的同步返回结果更新支付状态。同步返回可能因网络问题或用户关闭页面而丢失,支付结果应以验签通过的异步通知和主动查询为准。
- 收到
ALIN42281后不要无限重试。重复提交同一个已关闭订单无法恢复交易。 - 查询原订单前不要直接创建新订单,否则可能出现原订单已经支付,新订单又被支付一次的情况。
out_trade_no应全局唯一并保存到数据库,不要只靠可能重复的时间戳或客户端随机值保证唯一性。- 创建支付、处理异步通知、主动查询和执行关单时,都要做好并发控制与幂等处理。
- 日志中应保留
out_trade_no、支付宝trade_no、接口code、sub_code、sub_msg和请求时间,但不能记录私钥、完整签名材料或用户敏感信息。 - 如果查询结果与错误提示无法对应,应向支付宝技术支持提供脱敏后的请求参数、响应字段、订单号和发生时间。不要只提供
ALIN42281,单独的错误码通常不足以判断具体订单状态。
备注:内容仅供参考。
