错误码
本页汇总商户 API 的全部业务错误码。请先理解一条核心约定:
- 成功:
code == 10000,data为加密密文(需验签后解密),message为success。 - 失败:
code == 9999(所有业务错误共用这一个 code),message为机器可读的英文错误 key,data为空——失败响应没有需要解密的内容。
因此你的错误处理逻辑必须按 message 字段的 key 分发,而不是按 code 区分错误类型,也不要按中文文案做字符串匹配。
两类拿不到 JSON 的情况
- 来源 IP 不在白名单:服务端直接返回纯文本
Oops,不是 JSON 信封。如果你解析响应时报 JSON 解析失败且响应体是Oops,请先核对发起请求的服务器出口 IP 是否已报备到白名单。 - 请求体超过平台上限(默认 128KB)会被拒绝;实现上超长 body 也会以
paramError报出。
代码里请对「响应不是合法 JSON」做兜底处理,见本页底部示例。
错误码总表
全部错误 code=9999,按 message key 区分:
| message key | 中文文案 / 含义 | 建议处理动作 |
|---|---|---|
merchantNotExist | 商户不存在 | 核对请求头 ANONY-APP-ID 是否为平台分配的商户号;确认商户号已开通。见 凭据说明 |
authFailed | 身份验证失败 | 仅出现在 getToken:核对 Authorization 头是否为 auth + md5(APP-ID + "&" + secret),注意分隔符是 &、auth 后有空格。见 getToken |
tokenExpired | TOKEN 已过期 | 重新调用 getToken 换取新 token 后重试本次请求。建议在客户端封装「捕获 tokenExpired → 自动刷新 → 重试一次」的逻辑 |
signatureFailed | 签名验证失败 | 一码多因,v2 的时间戳/nonce 问题也报这个 key,见下方专项说明与排查指南 |
publicKeyNotExist | 商户公钥未配置 | 平台侧尚未录入你的商户公钥(public_u)。联系平台完成密钥配置后再联调 |
paramError | 参数错误 | 核对必填字段、字段类型与拼写;金额类字段建议以字符串传递。注意:请求体超长(>128KB)也会报此错 |
invalidCallbackUrl | 回调地址无效 | callbackUrl 必须是格式合法、可公网访问的 URL;不要传内网/环回地址。见 回调机制 |
addressTypeError | 地址类型错误 | addressType 只能使用当前已开放值:trx / solana / eth_bnb / ton;btc 为 Bitcoin 预留值,正式开放前不要使用。见 createAddress |
createAddressFailed | 创建地址失败 | 平台侧临时无法分配地址。稍后用同一个 userOrder 重试(幂等,不会产生重复订单);持续失败请联系平台 |
withdrawAddressError | 提现地址不可用 | 提现目标地址不被接受(例如平台自身的收款地址)。更换为真实的外部收款地址 |
withdrawTypeError | 提现类型错误 | withdrawType 只能是 normal 或 financialConfirmation。见 createWithdrawOrder |
auditorTelegramNotBound | 审核人 Telegram 未绑定 | financialConfirmation 类型提现要求商户先绑定财务审核人的 Telegram;到商户后台完成绑定后重试 |
merchantWithdrawDisabled | 商户提现已关闭 | 商户号的提现功能被关闭。联系平台确认原因并开启 |
onlySupportFinancialConfirmation | 仅支持财务确认提现 | 你的商户号被配置为强制财务确认:把 withdrawType 改为 financialConfirmation 再提交 |
withdrawTypeNotAllowed | 提现类型不允许 | 请求的 withdrawType 与商户号的提现策略不匹配。核对商户配置或联系平台调整 |
currencyNotSupported | 币种不支持 | currency 不在支持的 13 种提现币种枚举内(如 trx.usdt、eth.usdc)。核对拼写与大小写,见 币种枚举 |
currencyNotExist | 币种不存在 | 传入的币种符号平台无法识别。以 getCurrencies 公共接口 返回的 symbol 为准 |
invalidAmount | 金额无效 | amount 必须是合法的正数;建议以字符串传递避免精度问题 |
withdrawMinAmount | 最低提现金额为 :amount :symbol | 文案中的占位符会替换为该币种的实际下限。提交前先用 getCurrencies 查询各币种 withdraw_min 做前置校验 |
invalidAddress | 地址无效 | 提现目标地址未通过合法性校验。提交前可先用公共接口 isAddress 预校验地址与币种是否匹配 |
insufficientBalance | 余额不足 | 余额需覆盖 amount + fee(手续费另计)。先调 merchantInfo 查询余额,并把手续费计入判断 |
withdrawDailyLimitExceeded | 超出日提现限额 | 当日累计提现已达商户日限额。次日再试,或联系平台调整限额;勿对该错误做自动重试 |
limitError | 分页参数超限 | getDeposits 的 limit 必须在 1–100 之间(默认 10)。见 getDeposits |
withdrawOrderNotExist | 提现单不存在 | checkWithdrawOrder 按 userOrder 查无此单。核对订单号是否正确、是否属于当前商户号。见 checkWithdrawOrder |
operationFailed | 操作失败 | 通用兜底错误。稍后重试;持续出现请联系平台,并附上请求时间、接口名与 userOrder |
TRON 链 USDC 充值已关闭
TRON 链 USDC 的充值通道已关闭:向 TRON 地址转入的 USDC 不会入账、不会触发回调,请勿引导用户经 TRON 链充值 USDC。提现币种枚举以上述 13 种为准(不含 trx.usdc)。USDC 请使用 eth.usdc / solana.usdc / bnb.usdc。
signatureFailed:一码多因
signatureFailed 是联调期最常见、也最容易误导排查方向的错误——v2 协议下多种完全不同的失败原因都返回这同一个 key:
- canonical 签名串不一致(换行符不是
\n、三段顺序不是ts, nonce, body); - 签名算法不是 SHA256withRSA,或用错了密钥(上行应使用商户私钥签名);
- 缺少或写错
ANONY-TIMESTAMP/ANONY-NONCE请求头; - 时间戳超出 ±5 分钟时间窗(多为服务器时钟未做 NTP 同步);
- nonce 重复(同一商户号下 nonce 被复用,常见于重试逻辑直接重发原请求)。
所以收到 signatureFailed 时不要只盯着签名代码,按 排查指南 的顺序逐项核对:OAEP 哈希用 SHA-1 → canonical 串逐字节对齐 → 密钥角色正确 → 服务器时间同步 → 重试时重新生成 ts/nonce 并重新签名。协议细节见 加密与签名协议。
重试必须重新签名
任何自动重试都必须重新生成时间戳和 nonce、重新对 canonical 串签名后再发送。原样重发上一次的请求会因 nonce 去重被拒,同样报 signatureFailed。
错误处理示例
以下示例展示推荐的错误分发骨架:先兜底非 JSON 响应(Oops),再按 message key 分发。
php
$res = $client->post('createWithdrawOrder', $params, $token);
if (($res['code'] ?? 0) !== 10000) {
switch ($res['message'] ?? 'operationFailed') {
case 'tokenExpired':
$token = $client->getToken($secret); // 刷新 token 后重试本次请求
break;
case 'signatureFailed':
// 按排查指南逐项核对:canonical 串 / 时间同步 / nonce 是否复用
break;
case 'insufficientBalance':
case 'withdrawDailyLimitExceeded':
// 业务性失败:记录并转人工/延后处理,勿自动重试
break;
default:
// 其余按错误码总表处理
}
}python
r = requests.post(c.base_url + "createWithdrawOrder",
data=req["body"], headers=req["headers"])
try:
body = r.json()
except ValueError:
# 非 JSON 响应:大概率是 IP 白名单不匹配返回的纯文本 "Oops"
raise RuntimeError(f"non-JSON response: {r.text[:50]!r}")
if body["code"] != 10000:
key = body.get("message", "operationFailed")
if key == "tokenExpired":
pass # 重新 getToken 后重试本次请求
elif key == "signatureFailed":
pass # 按排查指南逐项核对,重试前必须重新生成 ts/nonce 并重签
else:
pass # 其余按错误码总表处理js
const r = await fetch(c.baseUrl + 'createWithdrawOrder',
{ method: 'POST', body: req.body, headers: req.headers });
const text = await r.text();
let body;
try {
body = JSON.parse(text);
} catch {
// 非 JSON 响应:大概率是 IP 白名单不匹配返回的纯文本 "Oops"
throw new Error('non-JSON response: ' + text.slice(0, 50));
}
if (body.code !== 10000) {
switch (body.message) {
case 'tokenExpired':
// 重新 getToken 后重试本次请求
break;
case 'signatureFailed':
// 按排查指南逐项核对,重试前必须重新生成 ts/nonce 并重签
break;
default:
// 其余按错误码总表处理
}
}相关页面
- 排查指南:
signatureFailed、解密失败等问题的逐步定位。 - 加密与签名协议:canonical 串、时间窗与 nonce 去重的完整规范。
- 上线自检 selfcheck:正式启用前用真实密钥跑通全链路,提前消灭协议类错误。
- 常见问题