错误码
错误使用 { ok: false, error: { code, message, field?, meta?, request_id } } 和正确 HTTP 状态,绝不返回 200。/v1 与嵌入渠道的错误列表与后端注册表同步。
错误响应结构
http
HTTP/1.1 402 Payment Required
Content-Type: application/json
X-Request-Id: 018f2b00-0000-7000-8000-0000000000ff
{
"ok": false,
"error": {
"code": "wallet.insufficient_balance",
"message": "Số dư ví không đủ để thực hiện giao dịch.",
"field": null,
"meta": null,
"request_id": "018f2b00-0000-7000-8000-0000000000ff"
}
}为何使用 domain.reason 而非 0000
HTTP 原生: 代理、监控和 REST 客户端已支持标准 HTTP 状态。
自解释: wallet.insufficient_balance 无需查表即可理解,新增代码也不会破坏现有逻辑。
旧系统需要数字?不可变代码可作为内部翻译表的稳定键。
错误处理的 4 条建议
先判断 HTTP 状态,再看错误码
HTTP 状态用于重试、告警和熔断,error.code 用于用户提示。状态码比具体错误码更稳定。
未知错误码不应导致流程崩溃
遇到未知代码时按 HTTP 状态处理,记录原始代码和 request_id,展示默认提示,避免未捕获异常。
始终记录 request_id
每个响应及错误中都有 X-Request-Id,是技术支持排查请求的关键标识。
遵守 429 和 503 的 Retry-After
请读取 Retry-After,不要硬编码延迟。429 还包含 X-RateLimit-Limit / Remaining / Reset。
代码 ↔ HTTP 对照表
按域分组,结合 API 参考查看各接口可能返回的错误分支。
显示 91 / 91 个代码
认证、密钥与限额 partner.*
| code | HTTP | 含义 | 可以重试? |
|---|---|---|---|
partner.invalid_key | 401 | API 密钥无效。 | 否:原样重试仍会失败 |
partner.invalid_signature | 401 | 请求签名无效。 | 否:原样重试仍会失败 |
partner.timestamp_expired | 401 | 请求时间戳与服务器时间相差过大。 | 否:原样重试仍会失败 |
partner.nonce_reused | 401 | 此 nonce 已被使用。 | 否:原样重试仍会失败 |
partner.ip_not_allowed | 403 | 此 IP 地址不在允许列表中。 | 否:原样重试仍会失败 |
partner.scope_denied | 403 | 此 API 密钥无权调用该端点。 | 否:原样重试仍会失败 |
partner.key_suspended | 403 | 此 API 密钥目前已暂停。 | 否:原样重试仍会失败 |
partner.key_revoked | 403 | 此 API 密钥已被撤销。 | 否:原样重试仍会失败 |
partner.endpoint_disabled | 503 | 此端点组暂时停止服务。 | 是:可原样重试 |
partner.rate_limited | 429 | 请求频率已超限,请稍后重试。 | 是:可原样重试 |
partner.package_not_visible | 404 | 您的密钥无法购买此套餐代码。 | 否:原样重试仍会失败 |
partner.daily_quota_exceeded | 429 | 已超过每日请求配额,请明天重试。 | 是:可原样重试 |
partner.wrong_host | 403 | 此密钥不适用于当前主机。`tke_test_` 须调用 `https://api-testing.tongkhoesim.com`,`tke_live_` 须调用 `https://api.tongkhoesim.com`。 | 否:原样重试仍会失败 |
API 通用错误 internal.*
| code | HTTP | 含义 | 可以重试? |
|---|---|---|---|
internal.validation_failed | 400 | 提交的数据无效。 | 否:原样重试仍会失败 |
internal.invalid_filter | 400 | 筛选条件无效。 | 否:原样重试仍会失败 |
internal.invalid_sort_field | 400 | 排序字段无效。 | 否:原样重试仍会失败 |
internal.unauthenticated | 401 | 您尚未登录或会话已过期。 | 否:原样重试仍会失败 |
internal.forbidden | 403 | 您无权执行此操作。 | 否:原样重试仍会失败 |
internal.not_found | 404 | 找不到请求的资源。 | 否:原样重试仍会失败 |
internal.route_not_found | 404 | 此路径不存在。请对照文档检查请求方法和路径。 | 否:原样重试仍会失败 |
internal.method_not_allowed | 405 | 不支持此 HTTP 方法。 | 否:原样重试仍会失败 |
internal.conflict | 409 | 状态冲突,无法完成操作。 | 否:原样重试仍会失败 |
internal.unsupported_media_type | 415 | 不支持此内容类型。 | 否:原样重试仍会失败 |
internal.rate_limited | 429 | 请求过于频繁,请稍后重试。 | 是:可原样重试 |
internal.server_error | 500 | 系统发生错误,请稍后重试。 | 是:可原样重试 |
internal.service_unavailable | 503 | 服务暂时不可用,请稍后重试。 | 是:可原样重试 |
internal.limit_exceeded | 422 | 已达到允许的上限,请删除部分项目后重试。 | 否:原样重试仍会失败 |
internal.money_frozen | 503 | 系统为核对账本暂时暂停资金交易,请稍后重试。 | 是:可原样重试 |
套餐目录 catalog.*
| code | HTTP | 含义 | 可以重试? |
|---|---|---|---|
catalog.cursor_mismatch | 400 | 分页游标与当前筛选条件不符,请去掉 cursor 参数并从第一页重新查询。 | 否:原样重试仍会失败 |
报价与订单 order.*
| code | HTTP | 含义 | 可以重试? |
|---|---|---|---|
order.quote_not_found | 404 | 找不到报价。 | 否:原样重试仍会失败 |
order.quote_expired | 409 | 此报价已过期,请重新创建。 | 否:原样重试仍会失败 |
order.quote_already_committed | 409 | 此报价已确认为订单。 | 否:原样重试仍会失败 |
order.package_not_allowed | 400 | 此套餐不存在或不可购买。 | 否:原样重试仍会失败 |
order.not_deliverable | 409 | 此订单尚未完成,无法执行此操作。 | 否:原样重试仍会失败 |
order.quantity_limit_exceeded | 400 | eSIM 数量超过单笔订单上限。 | 否:原样重试仍会失败 |
order.fulfillment_config_changed | 409 | 套餐交付配置已更改,请重新创建报价。 | 否:原样重试仍会失败 |
order.refund_suggestion_pending | 409 | 此订单存在待批准的退款建议,请先处理。 | 否:原样重试仍会失败 |
eSIM esim.*
| code | HTTP | 含义 | 可以重试? |
|---|---|---|---|
esim.out_of_stock | 409 | 此套餐库存不足。 | 否:原样重试仍会失败 |
esim.not_cancellable | 409 | 此 eSIM 当前状态不可取消。 | 否:原样重试仍会失败 |
esim.lpa_not_available | 409 | 此 eSIM 尚无 LPA 代码(尚未激活或注册)。 | 否:原样重试仍会失败 |
esim.not_eligible | 409 | 此 eSIM 不符合加购条件。 | 否:原样重试仍会失败 |
esim.not_activated | 409 | 此 eSIM 尚未安装,请安装并启用后再加购。 | 否:原样重试仍会失败 |
esim.topup_not_supported | 409 | 此套餐不支持加购。 | 否:原样重试仍会失败 |
esim.topup_provider_mismatch | 409 | 此加购套餐与 eSIM 所属供应商不同,请选择该 eSIM 对应的加购套餐。 | 否:原样重试仍会失败 |
esim.refund_not_eligible | 409 | 此 eSIM 不符合退款申请条件。 | 否:原样重试仍会失败 |
esim.refund_request_pending | 409 | 此 eSIM 已有待处理的退款申请。 | 否:原样重试仍会失败 |
esim.suspended | 409 | 此 eSIM 已暂停,请先恢复再操作。 | 否:原样重试仍会失败 |
钱包 wallet.*
| code | HTTP | 含义 | 可以重试? |
|---|---|---|---|
wallet.frozen | 409 | 此钱包已冻结,无法支出。 | 否:原样重试仍会失败 |
wallet.velocity_hold | 409 | 此钱包已暂停以待审核,请联系支持人员。 | 否:原样重试仍会失败 |
wallet.insufficient_balance | 402 | 钱包余额不足,无法完成此交易。 | 否:原样重试仍会失败 |
wallet.deleted | 409 | 目标钱包无效(已删除)。 | 否:原样重试仍会失败 |
wallet.velocity_exceeded | 429 | 短时间内交易超出限额,钱包已暂停以待审核。 | 否:原样重试仍会失败 |
退款申请 refund.*
| code | HTTP | 含义 | 可以重试? |
|---|---|---|---|
refund.note_required | 400 | 请填写拒绝退款的原因。 | 否:原样重试仍会失败 |
售价 pricing.*
| code | HTTP | 含义 | 可以重试? |
|---|---|---|---|
pricing.no_price_for_buyer | 409 | 此套餐尚未为您配置销售价格。 | 否:原样重试仍会失败 |
eSIM 供应商 provider.*
| code | HTTP | 含义 | 可以重试? |
|---|---|---|---|
provider.disabled | 409 | 此套餐的供应商已停用,无法销售。 | 否:原样重试仍会失败 |
provider.api_group_disabled | 409 | 此供应商的服务组目前已停用。 | 否:原样重试仍会失败 |
provider.not_registered | 500 | 此供应商尚未配置连接器。 | 是:可原样重试 |
provider.unsupported | 400 | 此供应商不支持该操作。 | 否:原样重试仍会失败 |
provider.timeout | 504 | 供应商响应超时,请稍后重试。 | 是:可原样重试 |
provider.auth_failed | 502 | 供应商身份验证失败。 | 是:可原样重试 |
provider.rate_limited | 429 | 供应商正在限制请求频率,请稍后重试。 | 是:可原样重试 |
provider.malformed_response | 502 | 供应商返回了无效数据。 | 是:可原样重试 |
provider.call_failed | 502 | 调用供应商失败。 | 是:可原样重试 |
provider.out_of_stock | 409 | 供应商处此套餐已售罄。 | 否:原样重试仍会失败 |
provider.msisdn_taken | 409 | 此 MSISDN 在供应商处已属于其他用户。 | 否:原样重试仍会失败 |
provider.msisdn_exhausted | 409 | 供应商已无可用 MSISDN。 | 否:原样重试仍会失败 |
Idempotency idempotency.*
| code | HTTP | 含义 | 可以重试? |
|---|---|---|---|
idempotency.conflict | 409 | 此 Idempotency-Key 已用于其他内容。 | 否:原样重试仍会失败 |
供应商能力 capability.*
| code | HTTP | 含义 | 可以重试? |
|---|---|---|---|
capability.not_supported | 422 | 此套餐不支持请求的功能。 | 否:原样重试仍会失败 |
测试模式 / 模拟 testmode.*
| code | HTTP | 含义 | 可以重试? |
|---|---|---|---|
testmode.live_key_forbidden | 403 | 此端点仅支持在 api-testing 上使用测试密钥(`tke_test_`)。请改用同一组织的测试密钥。 | 否:原样重试仍会失败 |
testmode.not_test_item | 422 | 此 eSIM 为真实数据,测试模式只能操作测试 eSIM。如需取消真实订单,请提交退款审核。 | 否:原样重试仍会失败 |
testmode.invalid_scenario | 422 | 模拟场景无效。请至少发送 `data_used_mb`、`status`、`expire_now`、`throttled` 之一;`status` 仅接受 released|installed|enabled|disabled。 | 否:原样重试仍会失败 |
testmode.no_sellable_package | 422 | 组织尚无已定价套餐,无法创建示例数据。至少需要一个已定价的在售套餐。 | 否:原样重试仍会失败 |
Webhook webhook.*
| code | HTTP | 含义 | 可以重试? |
|---|---|---|---|
webhook.health_check_failed | 422 | Webhook URL 在健康检查中未返回有效响应。 | 否:原样重试仍会失败 |
webhook.url_not_allowed | 422 | 此 Webhook URL 不被允许,必须是公开的 https URL。 | 否:原样重试仍会失败 |
webhook.not_configured | 404 | 此 API 密钥未配置 Webhook。 | 否:原样重试仍会失败 |
webhook.delivery_not_found | 404 | 找不到 Webhook 投递记录。 | 否:原样重试仍会失败 |
webhook.already_configured | 409 | 此 API 密钥已有一个 Webhook,每个密钥仅允许一个。请用 `PUT /v1/webhooks/{id}` 修改,或用 `DELETE /v1/webhooks/{id}` 删除后重建。 | 否:原样重试仍会失败 |
webhook.not_found | 404 | 调用方 API 密钥下不存在此 Webhook 端点。 | 否:原样重试仍会失败 |
组织状态 org.*
| code | HTTP | 含义 | 可以重试? |
|---|---|---|---|
org.invoice_profile_incomplete | 422 | 组织的开票资料不完整(法定名称、税号、地址)。 | 否:原样重试仍会失败 |
org.suspended | 403 | 此组织目前已暂停。 | 否:原样重试仍会失败 |
org.closed | 403 | 此组织已关闭。 | 否:原样重试仍会失败 |
公开嵌入渠道 embed.*
| code | HTTP | 含义 | 可以重试? |
|---|---|---|---|
embed.invalid_token | 401 | 嵌入令牌无效。 | 否:原样重试仍会失败 |
embed.token_revoked | 410 | 此嵌入渠道已停用。 | 否:原样重试仍会失败 |
embed.rate_limited | 429 | 此嵌入渠道调用过快,请稍后重试。 | 是:可原样重试 |
embed.package_not_visible | 404 | 找不到套餐。 | 否:原样重试仍会失败 |
增值税发票 invoice.*
| code | HTTP | 含义 | 可以重试? |
|---|---|---|---|
invoice.order_not_settled | 409 | 订单尚未结算(退款处理中),暂时无法开票。请几分钟后重试。 | 是:可原样重试 |
invoice.profile_incomplete | 422 | 组织的开票资料不完整,请补全后再开票。 | 否:原样重试仍会失败 |
invoice.already_requested | 409 | 此订单已有发票。 | 否:原样重试仍会失败 |
invoice.nothing_to_invoice | 409 | 扣除退款后,此订单已无可开票金额。 | 否:原样重试仍会失败 |
invoice.amount_changed | 409 | 新退款导致订单金额刚刚变化,请核实金额后重新开票。 | 是:可原样重试 |
invoice.buyer_invalid | 422 | 收票单位资料不完整或税号格式错误。 | 否:原样重试仍会失败 |