PAYATHON 2026

支付宝交易投诉列表接口6月1日起响应异常且未收到回调

支付小周

结论

code=20000、sub_code=isp.unknow-error 说明支付宝投诉列表接口没有完成正常的业务处理,返回的是服务端通用异常,不能理解为”没有投诉记录”。投诉详情接口仍可正常调用,因此问题更可能出在投诉列表的查询链路、应用权限、商户身份或投诉消息回调链路,而非整个支付宝网关不可用。

列表接口异常和回调中断的时间比较接近,建议先排查两类问题:

  1. 支付宝侧相关能力、权限或商户签约状态是否发生变化;
  2. 应用、公钥证书、商户身份、回调地址或消息接收配置是否在该时间点前后变更。

仅凭这组错误码无法判断具体原因,也不能直接认定是支付宝系统故障。请保留请求报文、响应报文以及支付宝网关返回的 trace_id,交由支付宝技术支持进一步定位。

原因分析

投诉列表接口返回系统级异常

本次返回内容为:

code: 20000
msg: Service Currently Unavailable
sub_code: isp.unknow-error
sub_msg: 系统繁忙

这类响应通常说明请求已经到达支付宝网关,但后端服务没有正常完成处理。可能的原因包括:

  • 投诉列表服务临时异常;
  • 应用没有对应接口权限,或权限状态发生变化;
  • 请求中的商户身份与应用授权关系不匹配;
  • 服务商代商户查询时,没有有效的应用授权;
  • 查询条件、时间范围或分页参数触发后端异常;
  • 接口能力调整后,原有调用方式已不符合当前要求。

投诉详情接口正常,只能说明网关通信、签名和部分投诉能力基本可用,不能证明列表查询权限及其后端链路也没有问题。两个接口可能采用不同的权限校验和服务链路。

回调中断不等于没有新投诉

5 月 31 日后没有收到投诉回调,可能存在两种情况:

  • 这段时间确实没有产生符合通知条件的新投诉;
  • 出现了新投诉,但消息订阅、商户授权或回调投递发生异常。

应先通过支付宝商家平台或其他可用渠道,确认 6 月 1 日之后是否有新增投诉。如果后台也没有新增投诉,未收到回调可能属于正常情况。如果后台有新增投诉,但系统没有收到通知,则需要单独排查回调链路。

排查步骤

1. 使用最小查询条件重新调用列表接口

先缩短查询时间范围,并使用较小的分页参数测试,避免一次查询过多数据。请求参数应以当前接口文档为准,不要参考其他接口自行增加字段。

测试时记录以下信息:

  • 请求时间;
  • 应用 app_id;
  • 实际查询的商户身份;
  • 请求参数,敏感字段需脱敏;
  • 完整响应;
  • HTTP 状态码;
  • 响应头或响应体中的 trace_id;
  • 使用的 SDK 及其版本;
  • 是否通过服务商模式代商户调用。

如果按任意单日或任意分页请求都稳定返回相同错误,问题通常不只是查询数据量过大。

2. 核对应用和商户身份

需要重点确认:

  • 当前使用的 app_id 是否还是原应用;
  • 接口请求是否切换过生产环境、沙箱环境或网关地址;
  • 应用私钥、支付宝公钥或证书是否更换;
  • 服务商代商户调用时,是否携带了正确且有效的授权信息;
  • 被查询商户是否确实是投诉所属商户;
  • 应用是否仍拥有 alipay.merchant.tradecomplain.batchquery 对应能力;
  • 商户签约、授权或产品开通状态是否发生变化。

如果 5 月 31 日前后做过应用发布、密钥轮换、证书更新、商户重新授权或配置迁移,应优先对比变更前后的配置。

3. 检查回调配置和接收日志

确认投诉消息的订阅关系及回调地址仍然有效,并检查以下内容:

  • 回调 URL 是否能从公网通过 HTTPS 访问;
  • 域名、证书、DNS、网关或防火墙是否在该日期前后变更;
  • 是否出现过 HTTP 301、302、403、404、499、500 或超时;
  • 反向代理、WAF、CDN 是否拦截了支付宝请求;
  • 应用是否在验签失败后直接丢弃消息;
  • 接收程序是否在解析异常时没有记录原始请求;
  • 回调处理成功后,是否按接口要求返回了正确的确认内容。

排查时不能只看业务数据库,还要检查负载均衡、Web 服务器、API 网关和应用层访问日志,确认支付宝请求究竟没有到达,还是到达后处理失败。

4. 验证回调签名和证书配置

如果使用支付宝公钥验签,需要确认当前配置的支付宝公钥与应用后台一致。如果使用证书模式,还要核对应用证书、支付宝公钥证书和支付宝根证书。

常见问题有:

  • 将应用公钥误当成支付宝公钥;
  • 密钥轮换后,回调服务器仍在使用旧公钥;
  • 回调参数在验签前被 URL 解码、重新排序或转换编码;
  • JSON、表单参数或原始字符串采用了错误的验签方式;
  • 验签失败时没有保存原始报文,导致后续无法复盘。

5. 确认支付宝后台是否存在新增投诉

这一步可以帮助区分”没有事件”和”事件未送达”:

  • 如果后台没有 6 月 1 日后的新增投诉,回调中断可能只是因为没有可通知的数据;
  • 如果后台有新增投诉,但没有收到回调,应重点检查订阅、授权和投递日志;
  • 如果后台有新增投诉,同时列表接口持续报错,应将两个问题一起提交给支付宝技术支持。

6. 携带链路信息提交支付宝排查

isp.unknow-error 属于通用错误,调用方通常无法仅凭本地日志确定支付宝内部的失败节点。提交工单时建议提供:

  • 接口名称:alipay.merchant.tradecomplain.batchquery;
  • 首次异常时间及最近一次异常时间;
  • 精确到秒的请求时间;
  • app_id;
  • 商户 PID,公开交流时应脱敏;
  • 请求参数摘要;
  • 完整错误码;
  • trace_id;
  • 5 月 31 日前成功、6 月 1 日后失败的对比记录;
  • 投诉详情接口正常的请求记录;
  • 后台存在但未回调的投诉编号;
  • 回调 URL 和服务端访问日志摘要。

如果没有 trace_id,至少提供准确的请求时间、app_id 和商户 PID,方便支付宝按时间窗口检索服务端日志。

代码示例

下面以 PHP 为例,记录列表接口的完整诊断信息。接口参数和 SDK 类名需要根据实际使用的支付宝 SDK 及当前接口文档调整。

<?php

$requestTime = date('c');

try {
    // $client 为已经正确配置 app_id、私钥、支付宝公钥或证书的客户端
    // $request 为 alipay.merchant.tradecomplain.batchquery 对应的请求对象

    $response = $client->execute($request);

    $diagnostic = [
        'request_time' => $requestTime,
        'api' => 'alipay.merchant.tradecomplain.batchquery',
        'http_status' => $response->getHttpStatusCode() ?? null,
        'code' => $response->code ?? null,
        'msg' => $response->msg ?? null,
        'sub_code' => $response->sub_code ?? null,
        'sub_msg' => $response->sub_msg ?? null,
        'trace_id' => $response->trace_id ?? null,
    ];

    error_log(
        json_encode(
            $diagnostic,
            JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES
        )
    );

    if (($response->code ?? '') !== '10000') {
        throw new RuntimeException(
            sprintf(
                '支付宝投诉列表查询失败:code=%s, sub_code=%s, sub_msg=%s, trace_id=%s',
                $response->code ?? '',
                $response->sub_code ?? '',
                $response->sub_msg ?? '',
                $response->trace_id ?? ''
            )
        );
    }
} catch (Throwable $e) {
    error_log(json_encode([
        'request_time' => $requestTime,
        'api' => 'alipay.merchant.tradecomplain.batchquery',
        'exception' => get_class($e),
        'message' => $e->getMessage(),
    ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));

    throw $e;
}

回调接收端应先保存必要的原始信息,再进行验签和业务处理。日志中不要明文记录应用私钥、完整身份证号、手机号等敏感数据。

<?php

$receivedAt = date('c');
$rawBody = file_get_contents('php://input');
$params = $_POST;

error_log(json_encode([
    'received_at' => $receivedAt,
    'content_type' => $_SERVER['CONTENT_TYPE'] ?? '',
    'remote_addr' => $_SERVER['REMOTE_ADDR'] ?? '',
    'raw_body_length' => strlen($rawBody),
    'param_keys' => array_keys($params),
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));

try {
    // 按实际 SDK 的回调验签方法处理。
    // 验签数据格式必须与支付宝回调协议保持一致。
    $verified = verifyAlipayCallback($params, $rawBody);

    if (!$verified) {
        error_log('Alipay trade complain callback signature verification failed');
        http_response_code(400);
        exit('failure');
    }

    // 业务处理应具备幂等性,建议以通知 ID 或投诉单号防止重复入库。
    processTradeComplainCallback($params);

    exit('success');
} catch (Throwable $e) {
    error_log(json_encode([
        'received_at' => $receivedAt,
        'exception' => get_class($e),
        'message' => $e->getMessage(),
    ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));

    http_response_code(500);
    exit('failure');
}

示例中的 verifyAlipayCallback() 和 processTradeComplainCallback() 是占位函数,不能直接用于生产环境。具体验签字段、通知格式和成功响应内容必须以当前支付宝接口文档为准。

处理建议

如果接口只是偶尔返回 20000,可以进行有限次数的指数退避重试,例如分别间隔 1 秒、2 秒、4 秒,并设置最大重试次数。不要无间隔循环请求,以免加重故障或触发限流。

如果从 6 月 1 日起每次调用都稳定失败,重试通常解决不了问题。此时应尽快检查权限和授权状态,并向支付宝提供 trace_id。列表接口恢复前,可以在符合接口调用规则的情况下,使用已知投诉编号调用详情接口补查。但详情接口无法代替列表接口发现未知的新投诉。

注意事项

  • 不要将 code=20000 作为空列表处理,否则可能在没有告警的情况下漏掉投诉。
  • 详情接口正常,不代表可以排除列表接口权限问题或支付宝侧单独链路异常。
  • 不能只凭”没有收到回调”判断没有新投诉,需要与支付宝后台记录交叉核对。
  • 投诉回调应采用幂等处理,防止支付宝重试导致数据重复。
  • 查询任务应保存游标或时间窗口,接口恢复后补拉故障期间的数据。
  • 衔接查询时间范围时要考虑边界记录,补拉后按投诉编号去重。
  • 对外提供日志前,需要对 app_id、商户 PID、订单号、用户信息和签名内容进行脱敏。
  • 接口文档中的权限、参数和回调规则可能调整,最终以支付宝当前开放平台文档及技术支持反馈为准。

备注:内容仅供参考。