调用支付宝预授权冻结接口出现 unknow-error(系统繁忙)如何排查?
结论
这次返回的并不是文档中要求改用 sdkExecute 的旧版 SDK 响应,而是支付宝 v3 REST 网关返回的错误:
HTTP 500
{"code":"unknow-error","message":"系统繁忙"}
建议先检查两个明显的问题:
- 当前网关地址使用了
http://openapi.alipay.com,应改为:
https://openapi.alipay.com
- 请求报文末尾出现了:
: undefined
如果这不是日志工具的显示问题,而是实际发送的内容,说明请求对象的构造或序列化有异常。需要删除空字段名和 undefined 值,只发送接口定义中的有效参数。
使用 alipay-sdk-java-v3 调用 /v3/alipay/fund/auth/order/app/freeze 时,不应照搬旧版通用 SDK 中“遇到 isp.unknow-error 就改用 sdkExecute”的处理方法。
为什么不是同一种错误
文档中的错误属于支付宝旧版 OpenAPI 通用网关的业务响应:
{
"code": "20000",
"msg": "Service Currently Unavailable",
"sub_code": "isp.unknow-error",
"sub_msg": "系统繁忙"
}
当前收到的则是 v3 REST 接口的 HTTP 错误响应:
{
"code": "unknow-error",
"message": "系统繁忙"
}
两者有几处明显区别:
- 前者通常是在 HTTP 请求成功后返回的业务错误结构。
- 后者伴随 HTTP 500,属于网关或服务端异常响应。
sdkExecute用于在旧版 Java SDK 中生成 App 端调用参数,并不是处理所有“系统繁忙”错误的通用重试方法。
是否使用 sdkExecute,应根据当前 SDK 版本和接口文档规定的调用方式判断,不能只看“系统繁忙”这几个字。
建议的排查顺序
1. 将网关改为 HTTPS
当前记录中的地址是:
http://openapi.alipay.com/v3/alipay/fund/auth/order/app/freeze
应改为:
https://openapi.alipay.com/v3/alipay/fund/auth/order/app/freeze
还要检查配置文件、环境变量、反向代理和自定义 HTTP Client,避免 SDK 中配置了 HTTPS,代理层却又将其改写为 HTTP。
示意配置:
String gateway = "https://openapi.alipay.com";
不要自行拼接 HTTP 地址来覆盖 SDK 的默认网关。
2. 检查实际发送的 JSON
请求日志中出现了空字段名和 undefined:
: undefined
标准 JSON 不支持 JavaScript 风格的 undefined,实际报文中不应出现以下内容:
{
"": undefined
}
请求中只能包含有效字段。例如:
{
"deposit_product_mode": "POSTPAY_UNCERTAIN",
"post_payments": [
{
"name": "充电宝",
"description": "2元/小时;99元封顶"
}
],
"order_title": "XXX",
"amount": "99.00",
"pay_timeout": "2m",
"out_order_no": "300120240621103915XXXXXX",
"product_code": "PREAUTH_PAY",
"out_request_no": "300120240621103915XXXXXX"
}
Java 侧不要将未初始化的字段、空字符串字段或动态 Map 中的空键放入请求对象:
Map<String, Object> body = new HashMap<>();
body.put("deposit_product_mode", "POSTPAY_UNCERTAIN");
body.put("order_title", orderTitle);
body.put("amount", "99.00");
body.put("pay_timeout", "2m");
body.put("out_order_no", outOrderNo);
body.put("product_code", "PREAUTH_PAY");
body.put("out_request_no", outRequestNo);
body.entrySet().removeIf(entry ->
entry.getKey() == null
|| entry.getKey().isBlank()
|| entry.getValue() == null
);
如果使用 SDK 的强类型请求模型,优先通过模型提供的 setter 或 builder 赋值,不要再额外拼装未经校验的 Map。
3. 确认使用的是同一套 v3 调用方式
以下两套调用模式不能混用:
- v3 REST SDK 的请求模型、v3 路径和鉴权配置。
- 旧版通用 SDK 的
AlipayClient、通用网关参数和sdkExecute。
如果项目使用的是 v3 SDK,应通过该版本生成的 API 类调用接口。不同版本的类名和方法签名可能不同,请以项目当前依赖中的接口为准,不要直接复制旧版示例。
可以先检查依赖树,确认项目没有同时引入多个支付宝 SDK,同时留意间接依赖引起的版本冲突。Maven 项目可在本地执行:
mvn dependency:tree
重点查看是否同时存在旧版 alipay-sdk-java 和 alipay-sdk-java-v3,以及 Jackson、OkHttp、Apache HttpClient 等底层依赖是否被其他组件降级。
4. 核对环境和应用配置
以下配置必须属于同一个环境:
app_id- 支付宝公钥或支付宝证书
- 应用私钥
- 应用公钥证书
- 支付宝根证书
- 网关地址
- 产品签约环境
- 调用账号及授权关系
生产应用不能使用沙箱网关或沙箱密钥,沙箱应用也不能调用生产网关。
“小程序尚未上线”不一定会直接导致 HTTP 500。产品未签约、权限未开通或应用状态不符合要求时,通常会返回更明确的业务错误。不过,仍需前往支付宝开放平台确认预授权产品权限、应用状态和账号签约状态。
5. 记录完整的响应定位信息
排查时需要保留以下信息:
- 请求时间,精确到秒
app_id- 接口名称
out_order_noout_request_no- HTTP 状态码
- 响应体
- 响应头中的请求 ID、Trace ID 或网关流水号
- SDK 版本
- 网关地址
- 脱敏后的实际请求体
不要只记录 xxxxxx(隐藏)。敏感签名可以隐藏,但请求 ID 和 Trace ID 通常是支付宝技术支持定位网关日志的重要依据。
HTTP 500 的处理方式
HTTP 500 不能直接等同于“冻结失败”。服务端可能已经完成处理,只是在返回响应时发生异常,因此调用结果可能处于未知状态。
建议按以下流程处理:
- 保存本次请求的
out_order_no和out_request_no。 - 调用预授权订单查询接口,确认是否已经生成订单或完成冻结。
- 如果仍然查不到明确结果,再使用相同的幂等业务单号重试。
- 设置有限的重试次数并采用指数退避,避免连续高频请求。
示例:
int maxAttempts = 3;
long delayMillis = 500L;
for (int attempt = 1; attempt <= maxAttempts; attempt++) {
try {
FreezeResult result = callFreezeApi(request);
if (result.isSuccess()) {
return result;
}
if (!result.isRetryable()) {
throw new IllegalStateException(result.getCode());
}
} catch (HttpServerException ex) {
FreezeStatus status = queryByOutOrderNo(outOrderNo);
if (status.exists()) {
return status.toFreezeResult();
}
if (attempt == maxAttempts) {
throw ex;
}
Thread.sleep(delayMillis);
delayMillis *= 2;
}
}
其中,FreezeResult、FreezeStatus、callFreezeApi 和 queryByOutOrderNo 只是业务示意,需要替换为当前 SDK 中的实际类型和方法。
关于 sdkExecute
只有项目使用旧版通用 Java SDK,并且接口文档明确要求“由服务端生成供支付宝客户端拉起的参数字符串”时,才应采用类似写法:
AlipayFundAuthOrderAppFreezeResponse response =
alipayClient.sdkExecute(request);
String orderStr = response.getBody();
alipay-sdk-java-v3 的 REST 调用不能因为返回了 unknow-error 就改用这段代码,否则会混淆“服务端调用 v3 接口”和“生成客户端拉起参数”这两种不同流程。
最终处理建议
先将网关改为 HTTPS,并确认实际 JSON 中没有空字段和 undefined。之后统一 SDK 版本和调用模式,再核对应用权限、证书与环境配置。
如果再次出现 HTTP 500,不要马上更换业务单号并重复冻结。先使用原业务单号查询处理结果,再进行有限次数的重试。
修正请求后如果仍能稳定复现,请携带响应头中的请求 ID、请求时间、app_id、SDK 精确版本和脱敏报文,提交给支付宝开放平台技术支持定位。仅凭 unknow-error,还无法判断具体原因是网关瞬时故障、请求序列化异常,还是应用侧配置问题。
备注:内容仅供参考。
