支付宝交易投诉列表接口6月1日起响应异常且未收到回调
结论
code=20000、sub_code=isp.unknow-error 说明支付宝投诉列表接口没有完成正常的业务处理,返回的是服务端通用异常,不能理解为”没有投诉记录”。投诉详情接口仍可正常调用,因此问题更可能出在投诉列表的查询链路、应用权限、商户身份或投诉消息回调链路,而非整个支付宝网关不可用。
列表接口异常和回调中断的时间比较接近,建议先排查两类问题:
- 支付宝侧相关能力、权限或商户签约状态是否发生变化;
- 应用、公钥证书、商户身份、回调地址或消息接收配置是否在该时间点前后变更。
仅凭这组错误码无法判断具体原因,也不能直接认定是支付宝系统故障。请保留请求报文、响应报文以及支付宝网关返回的 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、订单号、用户信息和签名内容进行脱敏。 - 接口文档中的权限、参数和回调规则可能调整,最终以支付宝当前开放平台文档及技术支持反馈为准。
备注:内容仅供参考。
