本页内容
错误码
用稳定的机器可读错误码处理 Merchant API 失败,含字段级校验明细和文档化的恢复路径。
错误码目录
在 code 中(以及逐字段在 errors[].code 中)返回的稳定机器可读标识符。这是当前在线集合——以下每个错误码都至少由运行中的 API 的一个处理器、校验器或中间件产生,不是理论上的超集。已有错误码的语义永不改变,新错误码随新场景加入。改名只在极少数情况下发生,每次都会在更新日志中公布。
程序化判断用 code。detail 仅英文,供日志/兜底 UI 使用;客户端按 code 做本地化。
字段级错误码
由请求校验发出。以下每个错误码都会把 field 设为出错字段的 snake_case 名称。当多个字段同时出错时,顶层 code 为 validation_failed,每个字段的失败明细进入 errors[]。
| 错误码 | 何时出现 | 处理方式 |
|---|---|---|
validation_failed |
多个字段同时出错时的顶层 code。每个字段的失败在 errors[] 中携带自己的 code 和 field。 |
检查 errors[],逐一修复列出的 field。 |
field_required |
必填字段为空、为 null 或只有空白字符。 | 为 field 提供值。 |
field_too_long |
字段超过允许的最大长度。 | 截断到该端点文档说明的上限。 |
field_out_of_range |
数值超出允许范围。 | 将数值收敛到文档说明的范围。 |
field_invalid_enum |
值不在接受的枚举成员中(如未知的 currency / network,或项目未启用的代币)。 |
查阅枚举参考;对于代币,确认项目已启用该代币+网络组合。 |
field_invalid_format |
值不符合字段期望的形态——无法解析的 id(如缺少 inv_… 前缀的 invoice_id),或不是 "10.00" 这类普通十进制字符串的 amount。 |
按文档格式发送字段;原样使用 API 返回的 id。 |
entity_id_empty |
提供的 id 是全零/空 GUID(00000000-0000-…)。field 指出错的 id——如 invoice_id 或 withdrawal_id。 |
原样发送 API 返回的 id;绝不要发送全零或空 id。 |
field_must_be_positive |
十进制/整数必须大于 0。 | 发送正值。 |
field_percentage_out_of_range |
百分比必须在 0 到 100(含)之间。 | 将值收敛到 [0, 100]。 |
query_parameter_unknown |
列表请求中包含该端点不支持的查询参数。 | 移除该参数,或改用文档说明的 snake_case 名称。 |
pagination_cursor_invalid |
游标格式错误、已过期、被篡改、属于其他资源,或与不同的过滤条件组合使用。 | 不带 cursor 重新开始分页,然后跟随 next_cursor 时保持相同的过滤条件。 |
amount_too_many_decimals |
对于法币流程的账单,amount 的小数位数超过该币种的最小单位(如 USD 的 23.345、JPY 的 23.01)。 |
按币种最小单位取整。 |
amount_too_large |
创建账单或转出时的 amount 超出支持范围——高于 API 存储的 20 位整数上限。 |
发送支持范围内的金额。 |
通用 HTTP 状态兜底码
当失败没有附带具体业务错误码时返回。每一项都是某个 HTTP 状态的兜底:请求失败了,但没有更细粒度的错误码描述原因。
| 错误码 | HTTP | 何时出现 | 处理方式 |
|---|---|---|---|
forbidden |
403 | 已认证,但该凭证无权执行此操作——其作用域(能力)或环境不允许,例如用 Sandbox 密钥调用仅正式环境可用的端点。 | 改用能力与环境都匹配该操作的凭证。归属问题永远不会是 403:属于其他商户的资源返回 not_found(404),而不是 forbidden。 |
payment_key_required |
403 | 端点需要 Payment(pk_)密钥,但请求使用的是 Payout(rk_)密钥——例如列出账单。 |
改用 Payment 密钥调用。 |
payout_key_required |
403 | 端点需要 Payout(rk_)密钥,但请求使用的是 Payment(pk_)密钥——例如查询余额。 |
改用 Payout 密钥调用。注意 Payout 密钥还需先配置 IP 白名单才能使用。 |
internal_error |
500 | 我们这边的意外错误或 bug。 | 重试一次。若持续失败,联系支持并附上响应发生的时间。 |
validation_failed |
400 | 请求未通过校验,但没有单一字段级错误码适用。 | 读取 detail 字段——里面说明了被拒绝的内容。 |
认证
仅由 HMAC 认证处理器在 401 Unauthorized 响应中返回。每个错误码定位一个具体的失败点,让客户端能给出可操作的提示,而不是笼统的「认证失败」。
| 错误码 | HTTP | 何时出现 | 处理方式 |
|---|---|---|---|
unauthorized |
401 | 通用兜底:以下具体错误码均不适用时——通常是向需要认证的端点发请求但没有 Authorization 请求头(或认证方案无法识别)。 |
发送合法的 HMAC Authorization 请求头;端到端复查签名流程。 |
authorization_missing |
401 | Authorization 请求头的值为空(发送了但为空白)。 |
发送完整的 Authorization: HMAC-SHA256 {apiKeyId}:{base64signature} 请求头。 |
authorization_malformed |
401 | 请求头存在但格式错误:scheme 能解析,但凭证部分缺少冒号、API key ID 无法解析,或签名为空。 | 按身份认证重新构造请求头;最常见的原因是 API key ID 中有拼写错误。 |
timestamp_missing |
401 | 缺少 X-Request-Timestamp 请求头,或其值无法解析为正整数。 |
将该请求头设为当前 Unix 时间戳(秒)。 |
timestamp_expired |
401 | 时间戳超出允许的时钟偏差窗口(默认 5 分钟)。 | 用 NTP 校准时钟;用新的时间戳重新签名。 |
invalid_credentials |
401 | 凭证不可用:key ID 未知、密钥已吊销、环境/类型前缀与存储的密钥不匹配,或签名校验失败。四种情况刻意共用一个错误码——如果给出可区分的回答,等于向不持有 secret 的调用方确认哪些 key ID 存在。 | 检查 API key ID 是否正确且处于活跃状态,签名是否用匹配的 secret 计算。签名是最常见的原因:核对你的 string-to-sign 拼装({ts}\n{METHOD}\n{path}\n{query}\n{bodyHash}),并确认空请求体时 bodyHash 为空(不要对空字符串做哈希)。 |
ip_not_allowed |
401 | Payout 密钥配置了 IP 白名单,而你的调用方 IP 不在其中。只有 Payout 密钥有 IP 检查——Payment 密钥可从任意 IP 调用。该检查在签名通过之后才执行,因此来自错误网络的合法签名同样会失败。 | 在控制台中将调用方 IP(或其 CIDR 段)加入该 Payout 密钥的白名单。 |
payout_whitelist_required |
401 | 新生成的 Payout 密钥尚未配置 IP 白名单。Payout 密钥在商户添加至少一个允许的 IP 之前保持不可用——与 ip_not_allowed 区分开,便于客户端分辨「控制台未配置」和「来源 IP 不对」。 |
打开控制台的 API 密钥页面,在该 Payout 密钥的 IP 白名单中配置至少一个 IP 或 CIDR。 |
限流
当请求超过商户限流时,以 429 Too Many Requests 返回。计数以商户为单位,按一秒窗口进行,密钥不单独计算:你的所有 API 密钥合用一份额度。在 API 之前,还有一层按 IP 地址计数的限流;本页说明的是商户这一层。
| 错误码 | HTTP | 何时出现 | 处理方式 |
|---|---|---|---|
rate_limited |
429 | 超过按商户计算的限流。有两条限制:所有端点的全局限额(默认 30 次/秒),以及账单创建 POST /v1/invoices 的更严格限额(默认 5 次/秒)。响应会说明触发了哪条以及限额是多少。 |
读取 Retry-After 请求头(退避秒数),到时后重试;大多数带重试中间件的 HTTP 客户端会自动遵守。 |
传输/框架
当请求根本没有到达常规处理器时,由框架错误处理器返回。
| 错误码 | HTTP | 何时出现 | 处理方式 |
|---|---|---|---|
malformed_request |
400 | 请求体无法解析——JSON 格式错误。 | 确认请求体是合法的 JSON。 |
payload_too_large |
413 | 请求体超过 1 MiB 限制。 | 发送更小的载荷;把大批量任务拆成多个请求。 |
unsupported_media_type |
415 | Content-Type 不是 application/json。 |
设置 Content-Type: application/json。 |
method_not_allowed |
405 | 路径存在,但不支持此 HTTP 方法。 | 使用端点文档说明的方法(如用 POST 创建)。 |
route_not_found |
404 | 没有端点匹配该请求路径。 | 对照 API 参考重新核对 URL。 |
账单
| 错误码 | HTTP | 何时出现 | 处理方式 |
|---|---|---|---|
invoice_not_found |
404 | 账单 id 解析不到任何记录,或解析到属于其他商户的账单。 | 原样使用 POST /v1/invoices 返回的 id。 |
project_not_found |
404 | 创建账单请求中的 project_id 解析不到调用方可见的任何项目。 |
使用控制台中的项目 id(必须为活跃状态,且与 API 密钥属于同一商户)。 |
invoice_terminal |
410 | 对已处于 expired 或 cancelled 的账单调用了 confirm-payment。(paid 账单返回的是 invoice_not_awaiting_client,409——不是本错误码。) |
刷新账单状态;无需再做其他操作。 |
invoice_deadline_passed |
410 | 在账单截止时间之后调用了 confirm-payment。 | 创建新账单。 |
requested_amount_invalid |
400 | 账单的 amount 缺失、格式错误、超出支持的十进制范围,或不为正数。 |
以正的十进制字符串发送 amount,并携带所选的 currency 和可选的 network。 |
invoice_amount_below_minimum |
400 | 账单折算后的 USD 价值低于目录中该代币/网络的最低限额。 | 将金额提高到所选代币展示的最低限额以上。 |
deposit_amount_exceeds_limit |
400 | 预期付款折算后的 USD 价值超过商户每笔充值的 MaxDepositAmount 策略上限。约束的是确认时的报价金额,而非实际链上到账。 |
降低账单金额,或联系支持提高限额。 |
currency_not_enabled_for_project |
400 | 请求的加密货币不在项目的已启用代币列表中(任意网络)。 | 在项目上启用该币种,或从项目的 enabled_tokens 中选择。 |
tokens_required |
409 | 在一个完全没有启用代币的项目上创建账单或确认付款。 | 在控制台中为该项目启用至少一个代币。 |
acceptance_disabled |
503 | 所请求的代币目前不接受收款。 | 改用其他代币,或稍后重试。 |
exchange_rate_unavailable |
503 | 汇率提供方没有该代币/法币对的新鲜报价。 | 重试;汇率以短间隔刷新。 |
network_unavailable |
503 | 自动确认无法使用所选网络。 | 放弃自动确认;让客户通过托管页面选择代币。 |
payment_method_unavailable |
503 | 所选代币或网络当前无法接收付款。自 2026-08-23 起取代 no_available_address、address_lease_failed 和 bridge_unavailable。 |
重试,或让客户改选其他代币或网络。 |
client_confirmation_failed |
409 | 客户端确认(代币选择、自动确认)被拒绝。detail 字段包含具体原因。 |
检查 detail;常见情况:账单已过 awaiting_client 状态、代币不被允许。 |
invoice_not_awaiting_client |
409 | 对不再处于 awaiting_client 的账单调用了 confirm-payment。 |
刷新账单状态;客户已完成选择。 |
invoice_cannot_be_cancelled |
409 | 账单不再处于 awaiting_client——客户已选择代币,或账单已支付或已过期。对已经取消的账单再次取消返回 200,而不是本错误。 |
检查当前状态;只有 awaiting_client 状态下才能取消。 |
simulate_payment_failed |
409 | 仅 sandbox 可用的 simulate_payment 被拒绝。 |
检查 detail;通常是状态不对或金额不匹配。 |
not_sandbox |
403 | 对正式账单调用了 simulate-payment(sandbox 模拟器)。 |
只有 sandbox 账单可以模拟——改用 sandbox 账单,或用真实的链上付款驱动正式账单。 |
Widget 账单
由 POST /public/v1/sdk(Low-Code SDK 账单创建)返回。
| 错误码 | HTTP | 何时出现 | 处理方式 |
|---|---|---|---|
widget_key_missing |
401 | 在需要 widget 认证的端点上缺少 X-Sdk-Key 请求头或其值为空。 |
在 X-Sdk-Key 中发送公开 SDK 密钥。 |
widget_key_invalid |
401 | X-Sdk-Key 存在,但无法解析为 Paymos API key ID。 |
从控制台复制密钥;检查是否有多余空白、错误前缀或意外的 URL 编码。 |
widget_key_not_found |
401 | 密钥能解析,但解析不到活跃凭证——已吊销、已删除,或不是 Payment 类型的密钥(Payout 密钥无法驱动 widget)。 | 使用控制台中处于已启用状态的 Payment 密钥。 |
widget_inactive |
403 | 目标项目的 widget 已关闭,或该项目的 widget 从未初始化。 | 在项目设置中启用 widget。 |
terminal_not_enabled |
403 | 请求携带 source=terminal,但项目的集成方式不是「收银终端」。 |
新建收银终端类型的项目,或从 Low-Code 项目发送 source=embed。 |
embed_not_enabled |
403 | 请求携带 source=embed,但项目的集成方式不是「Low-Code」。 |
使用项目自身的集成方式,或新建 Low-Code 项目。 |
origin_not_allowed |
401 | 浏览器 Origin 不匹配允许来源策略。该检查执行两次——认证层把 origin 与该密钥下所有启用 widget 的项目取并集匹配,处理器再与精确的目标项目匹配——两次都回同一个 401、同一个错误码、同一段 detail。这是刻意的:第二次检查若换个状态码,就等于告诉持有那把公开可抓取的 pk_ 密钥的调用方,这个 origin 注册在同一商户的另一个项目下,测试域名和未发布的品牌可以被一次一次探出来。 |
把域名加入 widget 的允许来源。同源请求——例如从同一主机提供服务的 POS 终端——跳过该检查。 |
收款通道
| 错误码 | HTTP | 何时出现 | 处理方式 |
|---|---|---|---|
payment_channels_disabled |
503 | 本环境下你的账户未开通收款通道。所有通道和充值接口都返回它,沙箱模拟器也不例外。 | 联系支持开通。重试不会改变结果。 |
payment_channel_not_found |
404 | pc_ id 解析不到该凭证可以看到的任何东西——不存在、属于其他商户、在另一个环境,或位于 key 作用域之外的项目。四种情况刻意无法区分。 |
原样使用 POST /v1/payment-channels 返回的 id,并用同环境同项目的 key 签名。 |
payment_channel_deposit_not_found |
404 | pcd_ id 解析不到该凭证可以看到的任何东西。与上面同样的四种原因。 |
原样使用 webhook 载荷或轮询流中的 id。 |
payment_channel_external_id_invalid |
400 | external_id 为空、全是空白,或超过 128 字符——创建时和作为列表筛选条件时都算。 |
发送非空白、最长 128 字符的标识。 |
payment_channel_project_has_no_supported_tokens |
409 | 项目没有启用任何收款通道可收的代币,通道将完全没有路线。 | 先在项目上启用至少一个受支持的代币,再创建通道。 |
payment_channel_simulation_sandbox_only |
403 | 对正式通道调用了 simulate-deposit。 |
模拟只对沙箱通道存在。正式通道请用真实转账驱动。 |
payment_key_required |
403 | 请求用 Payout(rk_)key 签名。通道访问走 Payment key,正如转出走 Payout key。 |
用同环境的 pk_ key 签名。 |
pagination_cursor_invalid |
400 | 列表或数据流游标格式错误、超过 24 小时,或绑定了不同的筛选条件——对数据流来说,还包括不同的商户、环境或项目作用域。 | 从第一页重新开始。按 pcd_ 去重让整轮重新同步无害。 |
currency_not_enabled_for_project |
400 | 传给 simulate-deposit 的代币不在项目的已启用代币列表中。 |
在项目上启用它,或改用通道 networks[].tokens 里已经返回的代币。 |
acceptance_disabled |
503 | 所请求代币的收款目前在平台级被停用。 | 改用其他代币,或等待收款重新启用。 |
amount_too_many_decimals |
400 | 模拟金额的小数位超过该代币支持的位数。商户的钱绝不会被悄悄舍入成另一个数。 | 发送前把金额舍入到该代币的精度。 |
转出
| 错误码 | HTTP | 何时出现 | 处理方式 |
|---|---|---|---|
withdrawal_not_found |
404 | 转出 id 解析不到任何记录,或解析到属于其他商户的转出。 | 原样使用 POST /v1/withdrawals 返回的 id。 |
insufficient_balance |
409 | 商户可用余额低于 amount + fee。 |
充值,或降低转出金额。 |
whitelist_required |
403 | 目标地址未列入所选网络的白名单。(该网络支持白名单;这个具体地址尚未添加。) | 把地址加入白名单后重试。 |
destination_address_invalid |
400 | 地址未通过该网络的格式/校验和验证。 | 对照网络规范重新核对地址。 |
withdrawal_amount_below_minimum |
400 | 金额低于所选代币的策略最低限额。 | 将金额提高到最低限额以上(在 detail 中返回)。 |
withdrawal_quota_exceeded |
409 | 进行中的转出数量达到商户策略上限。 | 等待在途转出结算完成,或联系支持提高限额。 |
withdrawal_amount_exceeds_limit |
400 | 转出本金的 USD 等值超过商户每笔交易的 MaxWithdrawalAmount 策略上限。 |
降低金额,或联系支持提高限额。 |
exchange_rate_unavailable |
503 | 商户设置了 MaxWithdrawalAmount 上限,但没有新鲜的代币/USD 报价可用于核对本金——限额无法验证,因此请求按失败关闭处理。 |
重试;汇率以短间隔刷新。 |
withdrawal_not_enabled |
400 | Paymos 不支持你请求的代币/网络组合的转出。detail 会指明该组合——如 Withdrawals are not available for USDT on TRC20. 哪些组合可转出由我们的配置决定,不由商户决定。 |
改用 Paymos 支持转出的网络,或联系支持询问你需要的组合。 |
withdrawal_cannot_be_cancelled |
409 | 转出已越过可取消的状态。 | 检查当前状态。执行一开始,窗口就关闭——那时仍在 created,还没有任何签名。 |
withdrawal_simulation_failed |
409 | 仅 sandbox 可用的 simulate_completion 被拒绝。 |
检查 detail。 |
merchant_suspended |
403 | 商户账户的转出流程已被暂停,无法创建转出。 | 联系支持解除暂停。 |
outbound_frozen |
503 | 转出被冻结——可能是平台级,也可能只针对你这笔转出所用的特定网络和代币。 | 等待冻结解除,换用其他网络/代币,或联系支持。 |
withdrawal_network_unavailable |
503 | 该代币在此网络上的转出目前无法完成。 | 换用其他目标网络,或稍后重试。 |
项目状态
当项目在当前状态下无法开具账单时,由账单创建和付款确认返回。
| 错误码 | HTTP | 何时出现 | 处理方式 |
|---|---|---|---|
project_state_invalid |
409 | 项目不是 Active——处于 Suspended 或 Archived。响应不会说明是哪一种。 |
在控制台查看项目状态:已归档则恢复,已暂停则激活。或者把请求发给一个已处于活跃状态的项目。 |