Skip to content

错误处理与重试

两套错误结构

标准 REST 错误

/api/v1/channel/*/api/v2/channel/* 使用 HTTP 状态码 + 统一 Body:

json
{
  "code": "CHANNEL_ORDER_NOT_FOUND",
  "message": "Channel order not found",
  "details": {}
}

code 是固定的字符串常量,程序里用它做分支判断message 是给人看的,仅供排查。

校验失败时 details.errors 列出具体字段:

json
{
  "code": "VALIDATION_ERROR",
  "message": "Request validation failed",
  "details": {
    "errors": [
      { "type": "value_error", "loc": ["body"], "msg": "at least one collection type is required" }
    ]
  }
}

业务码信封

/api/channel/data/* 的 HTTP 状态码始终是 200,结果在 success / code 里。 见获取数据

业务码

code含义处理
0 / 200成功——
60003正在采集15–30 秒后重试当前查询
60002失败或无可交付数据查询订单/产品的错误信息
10000任务不存在核对渠道编码与两类订单号
10001参数或输出范围错误按授权范围修正请求

60002 有两种含义

「采集失败」和「查询无数据」都是 60002msg 会给出具体原因 (如「查询无数据」「税务数据未匹配到已配置表单」「本任务未申请 PDF 报告」)。 需要区分时回到产品查询stateerror_code

HTTP 状态码

状态典型 code处理
401CHANNEL_AUTH_REQUIREDINVALID_CHANNEL_KEYCHANNEL_SIGNATURE_REQUIREDINVALID_CHANNEL_SIGNATURECHANNEL_TIMESTAMP_EXPIRED核对认证头、时钟与 canonical,见鉴权与签名
403CHANNEL_ORDER_FORBIDDENREPORT_NOT_ENTITLED渠道与订单不匹配,或未获定制报告授权
404CHANNEL_ORDER_NOT_FOUNDPRODUCT_NOT_REQUESTEDINVOICE_ORDER_NOT_FOUND核对订单号;确认本单申请过该产品
409EXTERNAL_ORDER_CONFLICTCHANNEL_REQUEST_REPLAYEDINVOICE_*_NOT_READY读 code 再决定,不要盲目重试
410RAW_ARTIFACT_EXPIREDHTML_REPORT_EXPIRED资源已过期,重新生成或联系平台
422VALIDATION_ERRORINVALID_PRODUCTINVALID_CALLBACK_URL修正请求

发票交付专属错误

code含义处理
INVOICE_DELIVERY_MODE_MISMATCH本单未申请该交付模式用申请时选择的模式取数
INVOICE_DATASET_NOT_READY发票快照未就绪等产品成功后重试
INVOICE_DATASET_EXPIRED快照已过期联系平台
INVOICE_CURSOR_INVALID游标不属于本数据集从头重新拉取
INVOICE_CURSOR_EXPIRED游标过期(24 小时)停止续拉并联系平台
INVOICE_EXPORT_NOT_FOUNDexportId 不属于本订单重新查询 package/status
INVOICE_EXPORT_NOT_READY分片包未就绪继续查询 package/status
INVOICE_EXPORT_EXPIRED分片包已过期联系平台

重试决策

mermaid
flowchart TD
  A[收到错误] --> B{哪一层}
  B -->|HTTP 401| C[修认证, 不要重试]
  B -->|HTTP 409| D[读 code]
  D -->|*_NOT_READY| E[等一段时间再重试]
  D -->|EXTERNAL_ORDER_CONFLICT| F[换订单号或改回原请求]
  D -->|CHANNEL_REQUEST_REPLAYED| G[换新 Nonce 重发]
  B -->|HTTP 422| H[修参数, 不要重试]
  B -->|业务码 60003| E
  B -->|业务码 60002| I[查产品状态定位原因]
  B -->|业务码 10001| J[按授权范围修正]

三条原则

场景做法
PROCESS60003继续等待同一任务,不要重新创建授权
平台系统重试沿用 authorization_codejob_idattempt_id 会变
1.5.3 税局会话失效、需重新录入凭据继续打开原 authorization_url 并使用 reenter=1;沿用 authorization_code / job_id,只滚动 attempt_id
平台明确创建新授权代次authorization_code 会变,按新代次继续处理

不要因部分质量警告重建订单

state=SUCCESSqualityStatus=WARNING / collectionResult=COMPLETED_WITH_WARNINGS 表示已有数据可用但覆盖不完整。保存 qualityIssuesdataQuality,按业务规则处理; 不要把它当成 60003 循环重试,也不要自动创建新的外部订单。

CONTACT_SUPPORT

遇到 CONTACT_SUPPORT 类错误码时停止自动重试, 保留错误码、external_order_idjob_idattempt_id 联系平台。

哪些请求可以放心重试

重试安全(术语叫「幂等」)的意思是:同一个请求重发多次,结果和只发一次一样, 不会产生重复数据或重复动作。

接口重试是否安全说明
创建授权✅ 请求体完全一致时任一要素变化 → 409
订单/产品查询只读
数据接口(Key 模式)只读
数据接口(签名模式)⚠️ 必须换新 Nonce复用 Nonce → 409 CHANNEL_REQUEST_REPLAYED
V2 游标⚠️ 换新 Nonce;同一 cursor 可重复请求入库时用 cursor 值去重
V2 分片包状态⚠️ 换新 Nonce;接口本身可安全重复调用会触发/推进准备流程
文件下载中断后可从断点继续

建议的重试节奏

失败后不要立刻重试:先等一小段时间,每失败一次就把等待时间拉长(术语叫「指数退避」)。

错误类型首次等待上限最多次数
60003 / *_NOT_READY20 秒5 分钟按业务超时(如 2 小时)
5xx / 网络超时2 秒起,每次翻倍60 秒5
401 / 422不重试————

轮询下限

状态查询间隔不得低于 15 秒。更密集的轮询不会让采集更快, 只会消耗渠道配额并可能触发限流。

建议留存的排查信息

无论走回调还是轮询,客户端都应把下面这些记录下来:

  • external_order_idauthorization_codejob_idattempt_id
  • 每次请求的 X-Channel-Nonce 与响应的 code / message
  • 回调的 event_idX-RPA-Delivery

排查时把这几项一起提供给平台,可以直接定位到具体采集尝试。

适配平台 1.5.3 · 接入指南、接口参考、字段字典与税局状态统一入口。