沙箱支付成功后返回 aop.ACQ.SYSTEM_ERROR 是什么原因?
明确结论
aop.ACQ.SYSTEM_ERROR 表示支付宝沙箱网关在处理请求或生成响应时发生了系统异常。它不是明确的“交易失败”结果,也不能用支付页面显示成功来判断最终交易状态。
沙箱页面提示支付成功,但接口返回 code: 20000,可能是支付流程已经完成,但同步响应阶段发生了异常。此时应通过交易查询接口或异步通知中的 trade_status 确认订单是否支付成功,不能直接根据当前返回值更新订单状态。
常见原因
1. 沙箱环境临时异常
支付宝沙箱主要用于接口联调,稳定性通常不及生产环境。如果支付、回跳、同步响应或交易数据写入过程中发生临时故障,可能会返回:
code: 20000
sub_code: aop.ACQ.SYSTEM_ERROR
sub_msg: 系统异常
遇到这种情况,可以稍后使用原商户订单号查询交易状态,不要立即创建新订单,以免重复支付。
2. 支付成功与同步返回属于不同环节
支付页面显示成功,只说明收银台操作已经完成。此后还要经过多个环节:
- 支付宝生成同步响应;
- 浏览器跳转到
return_url; - 支付宝向
notify_url发送异步通知; - 商户系统验签并更新订单;
- 商户主动查询交易状态。
后续任何一个环节发生异常,都可能出现“页面显示成功,但程序没有收到正常结果”的情况。
3. 请求参数或沙箱配置不一致
如果错误持续出现,还应检查以下内容:
- 请求是否发往沙箱网关;
app_id是否属于对应的沙箱应用;- 应用私钥与支付宝公钥是否配套;
- 签名方式是否一致,例如
RSA2; charset是否为utf-8;out_trade_no是否被重复使用;- 支付金额、商品标题等必填字段是否合法;
- SDK、网关地址和账号是否混入了生产环境配置。
配置有误时,接口通常会返回更明确的签名或参数错误。不过在沙箱环境中,不能因为收到 SYSTEM_ERROR 就排除配置问题。
解决步骤
第一步:保留原订单号并查询交易状态
使用支付请求中的 out_trade_no 调用 alipay.trade.query,重点检查:
trade_status
常见状态包括:
TRADE_SUCCESS:交易支付成功;TRADE_FINISHED:交易完成;WAIT_BUYER_PAY:等待买家付款;TRADE_CLOSED:交易关闭。
只有交易查询结果或验签通过的异步通知表明交易成功,才能将本地订单更新为已支付。
第二步:检查异步通知
如果配置了 notify_url,需要确认:
- 地址可以通过公网访问;
- 请求没有被登录验证、IP 白名单或 CSRF 校验拦截;
- 收到参数后执行了支付宝签名验证;
- 使用通知中的
app_id、out_trade_no和total_amount校验订单; - 处理成功后按接口要求返回
success。
支付宝可能重复发送异步通知,因此订单更新必须具备幂等性。
第三步:重试查询,不要重复支付
SYSTEM_ERROR 是系统级异常。可以对交易查询进行有限次数的延迟重试,例如等待数秒后再次查询。不要直接生成新的 out_trade_no,否则同一业务可能产生多笔交易。
第四步:检查沙箱配置
确认网关和密钥都来自同一套沙箱应用配置。沙箱请求不能使用生产环境的支付宝公钥,也不能将生产应用的 app_id 与沙箱账号混用。
如果同一请求能够稳定复现,应记录以下信息,以便排查:
app_id;out_trade_no;- 接口名称;
- 请求时间;
- 完整响应;
- SDK 日志或网关返回的请求标识。
记录日志时,不要输出应用私钥、用户密码或完整的敏感信息。
Python 处理示例
以下代码展示了基本处理思路。client 的初始化方式取决于实际使用的支付宝 SDK:
def handle_pay_response(client, pay_result, out_trade_no):
if (
pay_result.get("code") == "10000"
and pay_result.get("trade_status") in {"TRADE_SUCCESS", "TRADE_FINISHED"}
):
return {
"paid": True,
"source": "pay_response",
"result": pay_result,
}
if (
pay_result.get("code") == "20000"
or pay_result.get("sub_code") == "aop.ACQ.SYSTEM_ERROR"
):
query_result = client.execute(
"alipay.trade.query",
biz_content={
"out_trade_no": out_trade_no,
},
)
trade_status = query_result.get("trade_status")
if (
query_result.get("code") == "10000"
and trade_status in {"TRADE_SUCCESS", "TRADE_FINISHED"}
):
return {
"paid": True,
"source": "trade_query",
"result": query_result,
}
if trade_status == "WAIT_BUYER_PAY":
return {
"paid": False,
"pending": True,
"result": query_result,
}
return {
"paid": False,
"pending": False,
"result": query_result,
}
return {
"paid": False,
"pending": False,
"result": pay_result,
}
不同 SDK 的方法名称和返回结构可能不同,调用时应以当前 SDK 的接口定义为准。处理原则不变:收到 aop.ACQ.SYSTEM_ERROR 后,使用原 out_trade_no 查询真实的交易状态。
注意事项
返回数据中的以下字段:
buyer_pay_amount: 0.00
receipt_amount: 0.00
invoice_amount: 0.00
不能单独证明买家没有付款。它们可能只是异常响应中的默认值,并不代表有效的交易金额结果。
浏览器同步跳转也不能作为支付成功的唯一依据。可靠的订单状态应来自:
- 验签通过的异步通知;
alipay.trade.query的成功查询结果;- 对订单号、金额、应用 ID 和交易状态的完整校验。
如果查询结果为 TRADE_SUCCESS,可以按支付成功处理。如果持续查询不到交易,或者查询仍然返回系统异常,则更可能是沙箱服务异常或配置问题。此时应保留请求信息,稍后重试。
备注:内容仅供参考。
