PAYATHON 2026

调用支付宝预授权冻结接口出现 unknow-error(系统繁忙)如何排查?

支付小周

结论

这次返回的并不是文档中要求改用 sdkExecute 的旧版 SDK 响应,而是支付宝 v3 REST 网关返回的错误:

HTTP 500
{"code":"unknow-error","message":"系统繁忙"}

建议先检查两个明显的问题:

  1. 当前网关地址使用了 http://openapi.alipay.com,应改为:
https://openapi.alipay.com
  1. 请求报文末尾出现了:
: 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_no
  • out_request_no
  • HTTP 状态码
  • 响应体
  • 响应头中的请求 ID、Trace ID 或网关流水号
  • SDK 版本
  • 网关地址
  • 脱敏后的实际请求体

不要只记录 xxxxxx(隐藏)。敏感签名可以隐藏,但请求 ID 和 Trace ID 通常是支付宝技术支持定位网关日志的重要依据。

HTTP 500 的处理方式

HTTP 500 不能直接等同于“冻结失败”。服务端可能已经完成处理,只是在返回响应时发生异常,因此调用结果可能处于未知状态。

建议按以下流程处理:

  1. 保存本次请求的 out_order_no 和 out_request_no。
  2. 调用预授权订单查询接口,确认是否已经生成订单或完成冻结。
  3. 如果仍然查不到明确结果,再使用相同的幂等业务单号重试。
  4. 设置有限的重试次数并采用指数退避,避免连续高频请求。

示例:

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,还无法判断具体原因是网关瞬时故障、请求序列化异常,还是应用侧配置问题。

备注:内容仅供参考。