Skip to content

错误码

本页汇总商户 API 的全部业务错误码。请先理解一条核心约定:

  • 成功code == 10000data 为加密密文(需验签后解密),messagesuccess
  • 失败code == 9999所有业务错误共用这一个 code),message 为机器可读的英文错误 key,data 为空——失败响应没有需要解密的内容。

因此你的错误处理逻辑必须message 字段的 key 分发,而不是按 code 区分错误类型,也不要按中文文案做字符串匹配。

两类拿不到 JSON 的情况

  1. 来源 IP 不在白名单:服务端直接返回纯文本 Oops,不是 JSON 信封。如果你解析响应时报 JSON 解析失败且响应体是 Oops,请先核对发起请求的服务器出口 IP 是否已报备到白名单。
  2. 请求体超过平台上限(默认 128KB)会被拒绝;实现上超长 body 也会以 paramError 报出。

代码里请对「响应不是合法 JSON」做兜底处理,见本页底部示例。

错误码总表

全部错误 code=9999,按 message key 区分:

message key中文文案 / 含义建议处理动作
merchantNotExist商户不存在核对请求头 ANONY-APP-ID 是否为平台分配的商户号;确认商户号已开通。见 凭据说明
authFailed身份验证失败仅出现在 getToken:核对 Authorization 头是否为 auth + md5(APP-ID + "&" + secret),注意分隔符是 &auth 后有空格。见 getToken
tokenExpiredTOKEN 已过期重新调用 getToken 换取新 token 后重试本次请求。建议在客户端封装「捕获 tokenExpired → 自动刷新 → 重试一次」的逻辑
signatureFailed签名验证失败一码多因,v2 的时间戳/nonce 问题也报这个 key,见下方专项说明排查指南
publicKeyNotExist商户公钥未配置平台侧尚未录入你的商户公钥(public_u)。联系平台完成密钥配置后再联调
paramError参数错误核对必填字段、字段类型与拼写;金额类字段建议以字符串传递。注意:请求体超长(>128KB)也会报此错
invalidCallbackUrl回调地址无效callbackUrl 必须是格式合法、可公网访问的 URL;不要传内网/环回地址。见 回调机制
addressTypeError地址类型错误addressType 只能使用当前已开放值:trx / solana / eth_bnb / tonbtc 为 Bitcoin 预留值,正式开放前不要使用。见 createAddress
createAddressFailed创建地址失败平台侧临时无法分配地址。稍后用同一个 userOrder 重试(幂等,不会产生重复订单);持续失败请联系平台
withdrawAddressError提现地址不可用提现目标地址不被接受(例如平台自身的收款地址)。更换为真实的外部收款地址
withdrawTypeError提现类型错误withdrawType 只能是 normalfinancialConfirmation。见 createWithdrawOrder
auditorTelegramNotBound审核人 Telegram 未绑定financialConfirmation 类型提现要求商户先绑定财务审核人的 Telegram;到商户后台完成绑定后重试
merchantWithdrawDisabled商户提现已关闭商户号的提现功能被关闭。联系平台确认原因并开启
onlySupportFinancialConfirmation仅支持财务确认提现你的商户号被配置为强制财务确认:把 withdrawType 改为 financialConfirmation 再提交
withdrawTypeNotAllowed提现类型不允许请求的 withdrawType 与商户号的提现策略不匹配。核对商户配置或联系平台调整
currencyNotSupported币种不支持currency 不在支持的 13 种提现币种枚举内(如 trx.usdteth.usdc)。核对拼写与大小写,见 币种枚举
currencyNotExist币种不存在传入的币种符号平台无法识别。以 getCurrencies 公共接口 返回的 symbol 为准
invalidAmount金额无效amount 必须是合法的正数;建议以字符串传递避免精度问题
withdrawMinAmount最低提现金额为 :amount :symbol文案中的占位符会替换为该币种的实际下限。提交前先用 getCurrencies 查询各币种 withdraw_min 做前置校验
invalidAddress地址无效提现目标地址未通过合法性校验。提交前可先用公共接口 isAddress 预校验地址与币种是否匹配
insufficientBalance余额不足余额需覆盖 amount + fee(手续费另计)。先调 merchantInfo 查询余额,并把手续费计入判断
withdrawDailyLimitExceeded超出日提现限额当日累计提现已达商户日限额。次日再试,或联系平台调整限额;勿对该错误做自动重试
limitError分页参数超限getDepositslimit 必须在 1–100 之间(默认 10)。见 getDeposits
withdrawOrderNotExist提现单不存在checkWithdrawOrderuserOrder 查无此单。核对订单号是否正确、是否属于当前商户号。见 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:
      // 其余按错误码总表处理
  }
}

相关页面