跳到主要内容

签名常见错误

本页列出现货 API 中最常导致签名失败的错误,并给出每种错误的错误写法与正确写法。签名规则本身见签名生成。

💡 最快的定位方法

  1. 用测试向量运行你的签名代码,确认得到完全相同的 original 和 signature。
  2. 打印失败请求的 original 字符串,与实际发出的请求对照:method、path、query、body 以及所有 validate-* 请求头的值必须逐字符一致。

错误码​

鉴权错误以 HTTP 200 和 "rc":1 返回,原因在 mc 中,例如 {"rc":1,"mc":"AUTH_103","ma":[],"result":null}。

mc含义排查
AUTH_001缺少请求头 validate-appkey请求头名称及拼写
AUTH_002缺少请求头 validate-timestamp请求头名称及拼写
AUTH_003缺少请求头 validate-recvwindow第 9 项
AUTH_004validate-recvwindow 不在 5000 ~ 60000 之间第 9 项
AUTH_005缺少请求头 validate-algorithms第 9 项
AUTH_006validate-algorithms 不是受支持的算法名称第 9 项
AUTH_007缺少请求头 validate-signature请求头名称及拼写
AUTH_101API key 不存在key 是否复制正确;沙盒与生产的 key 不同
AUTH_102API key 未激活API key 状态
AUTH_103签名错误——服务器构造的待签名串与你的不一致下文第 1–10 项
AUTH_104请求来自未绑定到该 API key 的 IPAPI key 的 IP 白名单
AUTH_105validate-timestamp 超出 recvwindow第 8 项
AUTH_106该 API key 没有此接口的权限API key 权限(例如 TRADE、TRANSFER)
For input string: "..."validate-timestamp 或 validate-recvwindow 不是数字发送纯整数

如果返回 HTTP 403 且内容是 HTML 页面而不是 JSON,说明请求在到达 API 之前就被拦截了(CDN / 防火墙):请发送 User-Agent 请求头,并且不要通过代理调用。

1. 套用了合约的签名规则​

现货需要 method 段,请求头部分有四项。

❌ validate-appkey=..&validate-timestamp=..#POST#/az/spot/order#{...} (合约式的请求头部分)
❌ validate-algorithms=..&validate-appkey=..&validate-recvwindow=..&validate-timestamp=..#/az/spot/order#{...} (缺少 method)
❌ ...#post#/az/spot/order#{...} (method 为小写)
✅ validate-algorithms=HmacSHA256&validate-appkey=..&validate-recvwindow=5000&validate-timestamp=..#POST#/az/spot/order#{...}

2. HTTP 库重新序列化了请求体​

你签名的是一个 JSON 字符串,但 HTTP 库发送的却是另一个。最常见的是 Python requests 使用 json= 参数:它会重新序列化,并在 , 和 : 后面加上空格。

# ❌ signed '{"symbol":"btc_usdt","price":"70000"}' but sent '{"symbol": "btc_usdt", "price": "70000"}'
body = json.dumps(payload, separators=(",", ":"))
sign(... + "#" + body)
requests.post(url, json=payload, headers=headers)

# ✅ send the exact string that was signed
requests.post(url, data=body.encode("utf-8"), headers=headers)

任何接收对象并自行序列化的客户端都存在同样问题(例如传入 POJO 或 Map 的 Java RestTemplate、WebClient):请只构造一次 JSON 字符串,并把这个字符串作为请求体发送。

3. 签名的内容不是实际发送的请求体​

❌ 对 str(payload) 签名 → {'symbol': 'btc_usdt', 'price': None} (Python repr,不是 JSON)
❌ 只在签名时对 key 排序 → 签名 {"side":..,"symbol":..},发送 {"symbol":..,"side":..}
❌ 请求体构造了两次 → 两次序列化的空格、key 顺序或数字格式可能不同
✅ 只序列化一次 → 用这个字符串签名 → 发送同一个字符串

服务器不会对请求体排序。key 的顺序任意,只要签名的字符串与发送的字符串完全一致即可。

4. 数字、精度与小数位​

请求体和 query 都按文本参与签名,因此数字在待签名串和请求中的写法必须完全一致。

❌ 签名 "price":82000 发送 "price":82000.0 (整数与浮点格式不同)
❌ 签名 "quantity":0.3 发送 "quantity":0.30000000000000004 (浮点运算 0.1 + 0.2)
❌ 签名 "price":"82000.00" 发送 "price":"82000" (只对一侧做了舍入/格式化)
❌ query:签名 price=82000 发送 ?price=82000.0
✅ 价格和数量以字符串传递,例如 "price":"82000.00"、"quantity":"0.001",
并在所有地方使用同一个字符串

使用字符串还能精确保留你想要的小数位,避免订单到达交易所之前发生浮点舍入。

5. 空请求体​

只要发送了请求体,它就是待签名串的一部分,即使是 {}。

发送的 body : {}
❌ original : ...#POST#/az/spot/...
✅ original : ...#POST#/az/spot/...#{}

6. query 字符串问题​

query 段由解码后的值构成,按参数名的 ASCII 顺序排序,并单独作为一个 # 段。

请求 URL : /az/spot/balance?currency=usdt
❌ ...#GET#/az/spot/balance?currency=usdt (query 直接拼在 path 后面)
✅ ...#GET#/az/spot/balance#currency=usdt

请求 URL : ...?symbol=btc_usdt&Limit=10&endTime=1700000000000
❌ symbol=btc_usdt&Limit=10&endTime=1700000000000 (未排序)
❌ endTime=1700000000000&Limit=10&symbol=btc_usdt (忽略大小写排序)
✅ Limit=10&endTime=1700000000000&symbol=btc_usdt (ASCII:大写字母排在小写字母之前)

请求 URL : ...?symbols=btc_usdt%2Ceth_usdt
❌ symbols=btc_usdt%2Ceth_usdt (编码后的值)
✅ symbols=btc_usdt,eth_usdt (解码后的值)
  • URL 中的字面量 + 会被解码为空格。请在 URL 中把 + 编码为 %2B,签名时使用 +。
  • 同一个参数名出现多次时,只使用第一个值——请避免重复的参数名。
  • 参数一律以 key=value 形式发送;不带 = 的参数名(?flag)无法可靠签名。
  • 没有查询参数 → 没有 query 段,也不要多加 #。

7. 路径问题​

待签名串中的 path 必须是实际请求的路径。

❌ https://s-api.azverse.xyz/az/spot/order (包含了 host)
❌ 签名 /az/spot/order 请求 /az/spot/order/ (末尾斜杠不一致)
✅ /az/spot/order

8. 时间戳问题​

❌ validate-timestamp: 1791541985 (秒,10 位) → AUTH_105
❌ 时间戳生成了两次:请求头里一个值,待签名串里另一个值 → AUTH_103
❌ 本地时钟偏差超过 recvwindow → AUTH_105
✅ 只用一个 13 位毫秒时间戳,请求头和待签名串都用它;
保持系统时钟同步(NTP);必要时调大 validate-recvwindow(最大 60000)

9. 必填请求头及其取值​

❌ 缺少 validate-recvwindow → AUTH_003
❌ validate-recvwindow: 3000 或 70000 → AUTH_004(必须在 5000 – 60000 之间)
❌ 缺少 validate-algorithms → AUTH_005
❌ validate-algorithms: SHA256 → AUTH_006(应使用 HmacSHA256)
❌ 请求头写的是 HmacSHA512,代码按 HMAC-SHA256 计算 → AUTH_103
✅ 五个请求头全部发送,并把完全相同的值放入待签名串

10. 密钥与输出格式​

❌ 输出为 Base64 → 必须输出 hex(HmacSHA256 为 64 个字符)
❌ secretKey 末尾带空格或换行(从文件或控制台复制时带入)
❌ 把 secretKey 按 hex 解码成字节 → secretKey 按原字符串(UTF-8)直接使用
❌ 在生产环境使用沙盒 key,或使用了另一个 API key 的 secretKey
✅ hex( HMAC-SHA256( secretKey, original ) )