簽名常見錯誤
本頁列出現貨 API 最常導致簽名失敗的錯誤,並逐一給出錯誤與正確的做法。簽名規則本身見簽名生成。
💡 最快定位問題的方法
- 用測試向量執行你的簽名程式碼,確認得到完全相同的
original和signature。- 輸出失敗請求的
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_004 | validate-recvwindow 不在 5000 至 60000 之間 | 第 9 項 |
| AUTH_005 | 缺少請求頭 validate-algorithms | 第 9 項 |
| AUTH_006 | validate-algorithms 不是支援的演算法名稱 | 第 9 項 |
| AUTH_007 | 缺少請求頭 validate-signature | 請求頭名稱及拼寫 |
| AUTH_101 | API key 不存在 | key 是否複製正確;沙盒與生產環境的 key 不同 |
| AUTH_102 | API key 未啟用 | API key 狀態 |
| AUTH_103 | 簽名錯誤——伺服器構建的待簽名串與你的不同 | 下文第 1 – 10 項 |
| AUTH_104 | 請求來自未與 API key 綁定的 IP | API key 的 IP 白名單 |
| AUTH_105 | validate-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. 數字精度與小數位
請求體和查詢參數都是以文字參與簽名,因此數字在待簽名串和請求中的寫法必須完全相同。
❌ 簽名 "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 段由解碼後的值構建,按 key 的 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 出現多次時只取第一個值——請避免重複的 key。
- 一律發送
key=value;不帶=的 key(?flag)無法可靠簽名。 - 沒有查詢參數 → 不加 query 段,也不加多餘的
#。
7. 路徑問題
待簽名串中的 path 必須是實際請求的路徑。
❌ https://s-api.azverse.xyz/az/spot/order (包含了域名)
❌ 簽名 /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 的 secret
✅ hex( HMAC-SHA256( secretKey, original ) )