身分驗證
InfraIO Pay 有兩個 API 介面,各自採用不同的驗證模型。請依照 呼叫者身份選擇:
| 介面 | 路徑前綴 | 對象 | 驗證 |
|---|---|---|---|
| 商家 B2B | /b2b/v1/* | 你的伺服器 | HMAC-SHA256 請求簽章 |
| 儀表板 | 供商家儀表板使用 | 商家儀表板的瀏覽器 session | Bearer JWT |
本頁介紹 B2B 介面,也就是你從伺服器以 API 金鑰組呼叫的介面。 儀表板介面由 InfraIO Pay 商家儀表板使用,並非公開的整合介面。
請一律傳送含 /b2b 前綴的完整路徑,並對同一路徑簽章(見下文)。
Endpoints
| 環境 | Base URL |
|---|---|
| Test | https://api-dev.infraio.xyz |
| Live | https://api.infraio.xyz |
URL 模式相同 — 環境是由金鑰前綴(pk_test_… vs pk_live_…)
控制,而非 URL。
金鑰組
你會從商家儀表板取得兩個值(Developers → API keys → + Add key):
- Publishable key(
pk_test_…或pk_live_…)— 識別你的帳號。 以X-Client-ID傳遞。可以安全地嵌入瀏覽器 bundle(SDK 已經這樣做)。 - Secret key(
sk_test_…或sk_live_…)— HMAC 簽章金鑰。 僅限伺服端使用。視同資料庫密碼般保護。
若 secret key 不慎流入瀏覽器 bundle、git repo、log 或聊天室 — 立刻從儀表板撤銷它。撤銷會立即生效,沒有重疊視窗。請發行新金鑰 並重新部署。
簽章請求
每個對 /b2b/v1/* 的呼叫都會帶三個標頭:
X-Client-ID: pk_live_70d0becbd7a53047b0dfe88a960d10ad
X-Timestamp: 1715990400
X-Signature: 9a8b7c6d… (hex HMAC-SHA256)簽章是針對 canonical string 計算的:
METHOD + "\n" + PATH + "\n" + TIMESTAMP + "\n" + BODYMETHOD— 大寫的 HTTP verb(POST、GET等)。PATH— 請求路徑含/b2b前綴,不含 host 且不含 query string (例如/b2b/v1/checkout-sessions/quick)。前綴必須存在。Query 參數 不參與簽章 — 對於GET …?cursor=…&limit=20,只簽路徑,不要 簽?…部分。TIMESTAMP— Unix 秒,以十進位字串表達(例如"1715990400"), 須與X-Timestamp完全一致。BODY— 原始請求 body 位元組。GET/DELETE為空字串。
以 secret key 做 HMAC-SHA256,輸出 hex:
Node / TS
import { createHmac } from "node:crypto";
function sign({ method, path, body, secret }: {
method: string; path: string; body: string; secret: string;
}) {
const timestamp = Math.floor(Date.now() / 1000).toString();
const input = [method.toUpperCase(), path, timestamp, body].join("\n");
const signature = createHmac("sha256", secret).update(input).digest("hex");
return { timestamp, signature };
}為什麼是 HMAC,而非 Bearer?
純 Bearer token 的 API 會在每次請求都把你唯一的 secret 上線傳輸。 任何拿到 TLS 終止點代理 log 的人都能取得你帳號的金鑰。HMAC 簽章 代表 secret 永遠不會旅行 — 只傳遞它派生出的簽章,而該簽章是一次性 的(綁定該確切的請求 + 該確切的分鐘)。
代價是:每次呼叫都要計算簽章。目前尚無伺服器端 SDK, 但上面的輔助程式碼每種語言只需約 15 行。
Timestamp 容差
容差是 ±5 分鐘(300 秒)。超出此視窗的請求會被拒絕,
並回傳 401 invalid_signature。兩項影響:
- 同步你的伺服器時鐘(使用 NTP)。長時間執行的 cron,若時鐘 漂移,會間歇性失敗。
- 不要預先計算並排隊簽章。 若一個請求在 retry queue 中停留 >5 分鐘,它的簽章會過期。
Key scopes
Secret key 帶有一或多個下列 scope 組合:
| Scope | 預期用途 |
|---|---|
read | 列出 / 讀取訂單、session、退款 |
write_order | 建立結帳 session、訂單 |
write_refund | 發起退款、鑄造退款申請 token |
webhook_manage | 建立 / 更新 / 刪除 webhook endpoint |
儀表板預設發行「full access」金鑰(全部四個 scope)。你可以從 Developers → API keys → + Add key 鑄造受限 scope 金鑰,並只 勾選該整合所需的 scope。
Scope 目前尚未強制執行。 Scope 會記錄於金鑰上並顯示在儀表板,
但任何有效的 sk_… 金鑰都能呼叫你商家的任意 /b2b/v1/* endpoint。
請不要把 scope 當成安全邊界,並透過輪替或撤銷金鑰來限制存取。
驗證失敗
若簽章、X-Client-ID 或 timestamp 無效,請求會在到達 API 之前被拒絕,
並回傳 401 INVALID_SIGNATURE。只有 /b2b/v1/* 請求採用這種簽章方式。
Webhook 使用另一套機制(見下文)。
下一步
- 錯誤 — 4xx/5xx 的回應格式。
- 安全 → API 金鑰 — 輪替、撤銷,以及 secret 外洩時該怎麼做。
- Webhooks → 簽章驗證
— 使用不同的 HMAC 機制(標頭
X-Signature: sha256=…,簽X-Timestamp + "." + raw_body,加上 24 小時輪替寬限視窗內的選用X-Signature-Prev)。請別把兩套機制混用 — 雖然 hash 演算法相同, 但所簽位元組與 secret 系列(whsec_…vssk_…)都不一樣。