跳至主要内容

簽名常見錯誤

本頁列出合約 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 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 appkey <key> not foundAPI key 不存在key 是否複製正確;沙盒與生產環境的 key 不同
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 configuredAPI key 已綁定 IP,而請求來自其他 IPAPI key 的 IP 白名單
HTTP 200 {"result":{"openapiDocs":...}}路徑不存在第 9 項
HTTP 403 builder code illegalAPI key 配置不正確聯絡客服
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. 數字精度與小數位​

請求體和查詢參數都是以文字參與簽名,因此數字在待簽名串和請求中的寫法必須完全相同。

❌ 簽名 "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 段由解碼後的值構建,並按 key 的 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 出現多次時只取第一個值——請避免重複的 key。
  • 一律發送 key=value;不帶 = 的 key(?flag)無法可靠簽名。
  • 沒有查詢參數 → 不加 query 段,也不加多餘的 #。

9. 路徑問題​

待簽名串中的 path 必須是實際請求的路徑。

❌ https://f-api.azverse.xyz/az/future/trade/v1/order/create (包含了域名)
❌ /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 的 secret
❌ 發送 validate-algorithms: HmacSHA512,卻以 HMAC-SHA256 計算 → 請求頭與演算法必須一致
✅ hex( HMAC-SHA256( secretKey, original ) )