跳到主要内容

签名常见错误

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

💡 最快的定位方法

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

错误信息​

响应含义排查
HTTP 403 {"code":"403","msg":"invalid signature"}服务器构造的待签名串与你的不一致下文第 1–11 项
HTTP 403 ... The request time <t> exceeds the specified time <now> limit by <n> secondvalidate-timestamp 超出 recvwindow第 10 项
HTTP 400 appkey <key> not foundAPI key 不存在key 是否复制正确;沙盒与生产的 key 不同
HTTP 400 Absence of [appKey] in client request is prohibited缺少请求头 validate-appkey请求头名称及拼写
HTTP 400 Absence of [validate-timestamp] in client request is prohibited缺少请求头 validate-timestamp请求头名称及拼写
HTTP 400,返回 JSON 解析错误信息(例如 Expected BEGIN_OBJECT but was STRING)发送了请求头 validate-signversion第 1 项
HTTP 400 For input string: "..."validate-timestamp 或 validate-recvwindow 不是数字发送纯整数
HTTP 403 The client source IP address is not configured该 API key 绑定了 IP,而请求来自其他 IPAPI key 的 IP 白名单
HTTP 403 builder code illegal该 API key 配置不正确联系客服
HTTP 200 {"result":{"openapiDocs":...}}请求路径不存在第 9 项
HTTP 403,返回 HTML 页面而不是 JSON请求在到达 API 之前被拦截(CDN / 防火墙)发送 User-Agent 请求头;不要通过代理调用

1. 发送了 validate-signversion 请求头​

不支持 validate-signversion。携带它的请求无论如何签名,都会返回 invalid signature 或 HTTP 400。

❌ validate-signversion: 2
✅ 不发送 validate-signversion 请求头;按实际发送的请求体原样签名(见「签名生成」)

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 排序 → 签名 {"orderSide":..,"symbol":..},发送 {"symbol":..,"orderSide":..}
❌ 请求体构造了两次 → 两次序列化的空格、key 顺序或数字格式可能不同
✅ 只序列化一次 → 用这个字符串签名 → 发送同一个字符串

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

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

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

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

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

5. 表单请求体(application/x-www-form-urlencoded)​

表单请求体按实际发送的原样签名:不排序,也不做 URL 解码。

发送的 body : clientOrderId=a%3Ab&symbol=btc_usdt&orderSide=BUY
❌ 签名 : orderSide=BUY&symbol=btc_usdt&clientOrderId=a:b (排序且解码了)
✅ 签名 : clientOrderId=a%3Ab&symbol=btc_usdt&orderSide=BUY (与请求体完全一致)

6. 空请求体​

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

发送的 body : {}
❌ original : validate-appkey=..&validate-timestamp=..#/az/future/...
✅ original : validate-appkey=..&validate-timestamp=..#/az/future/...#{}

7. 套用了现货的签名规则​

合约没有 method 段,请求头部分也只有两项。

❌ ...#POST#/az/future/trade/v1/order/create#{...}
❌ validate-algorithms=HmacSHA256&validate-appkey=..&validate-recvwindow=5000&validate-timestamp=..#/az/future/...
✅ validate-appkey=..&validate-timestamp=..#/az/future/trade/v1/order/create#{...}

8. query 字符串问题​

query 段由解码后的值构成,按参数名的 ASCII 顺序排序。

请求 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 段,也不要多加 #。

9. 路径问题​

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

❌ https://f-api.azverse.xyz/az/future/trade/v1/order/create (包含了 host)
❌ /future/trade/v1/order/create (缺少 /az 前缀——请求到达不了 API)
❌ 签名 /az/future/.../create 请求 /az/future/.../create/ (末尾斜杠不一致)
✅ /az/future/trade/v1/order/create

10. 时间戳问题​

❌ validate-timestamp: 1791541985 (秒,10 位) → 403 The request time ... exceeds ...
❌ 时间戳生成了两次:请求头里一个值,待签名串里另一个值 → 403 invalid signature
❌ 本地时钟偏差超过 recvwindow
✅ 只用一个 13 位毫秒时间戳,请求头和待签名串都用它;
保持系统时钟同步(NTP);必要时调大 validate-recvwindow(大于 60000 的值按 60000 处理)

11. 密钥与输出格式​

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