<!-- Source: https://docs.infraio.xyz/zh-TW/api-reference/authentication -->
<!-- Last updated: 2026-10-04 -->

# 身分驗證

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 簽章金鑰。
  僅限伺服端使用。視同資料庫密碼般保護。

> **Important:**
>
> 若 secret key 不慎流入瀏覽器 bundle、git repo、log 或聊天室 —
> **立刻從儀表板撤銷它**。撤銷會立即生效，沒有重疊視窗。請發行新金鑰
> 並重新部署。

## 簽章請求

每個對 `/b2b/v1/*` 的呼叫都會帶三個標頭:

```http
X-Client-ID:  pk_live_70d0becbd7a53047b0dfe88a960d10ad
X-Timestamp:  1715990400
X-Signature:  9a8b7c6d…             (hex HMAC-SHA256)
```

簽章是針對 canonical string 計算的:

```
METHOD + "\n" + PATH + "\n" + TIMESTAMP + "\n" + BODY
```

- `METHOD` — 大寫的 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**

```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 };
}
```

**Go**

```go
package infraio

import (
    "crypto/hmac"
    "crypto/sha256"
    "encoding/hex"
    "strconv"
    "time"
)

func Sign(method, path, body, secret string) (timestamp, signature string) {
    timestamp = strconv.FormatInt(time.Now().Unix(), 10)
    input := method + "\n" + path + "\n" + timestamp + "\n" + body
    mac := hmac.New(sha256.New, []byte(secret))
    mac.Write([]byte(input))
    return timestamp, hex.EncodeToString(mac.Sum(nil))
}
```

**Python**

```python
import hmac, hashlib, time

def sign(method: str, path: str, body: str, secret: str):
    timestamp = str(int(time.time()))
    input_ = f"{method}\n{path}\n{timestamp}\n{body}"
    signature = hmac.new(
        secret.encode(), input_.encode(), hashlib.sha256
    ).hexdigest()
    return timestamp, signature
```

## 為什麼是 HMAC,而非 Bearer?

純 Bearer token 的 API 會在每次請求都把你唯一的 secret 上線傳輸。
任何拿到 TLS 終止點代理 log 的人都能取得你帳號的金鑰。HMAC 簽章
代表 secret 永遠不會旅行 — 只傳遞它派生出的簽章，而該簽章是一次性
的(綁定該確切的請求 + 該確切的分鐘)。

代價是:每次呼叫都要計算簽章。目前尚無伺服器端 SDK，
但上面的輔助程式碼每種語言只需約 15 行。

## Timestamp 容差

容差是 **±5 分鐘**(300 秒)。超出此視窗的請求會被拒絕，
並回傳 `401 invalid_signature`。兩項影響:

1. **同步你的伺服器時鐘**(使用 NTP)。長時間執行的 cron,若時鐘
   漂移，會間歇性失敗。
2. **不要預先計算並排隊簽章。** 若一個請求在 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。

> **Warning:**
>
> **Scope 目前尚未強制執行。** Scope 會記錄於金鑰上並顯示在儀表板，
> 但任何有效的 `sk_…` 金鑰都能呼叫你商家的任意 `/b2b/v1/*` endpoint。
> 請不要把 scope 當成安全邊界，並透過輪替或撤銷金鑰來限制存取。

## 驗證失敗

若簽章、`X-Client-ID` 或 timestamp 無效，請求會在到達 API 之前被拒絕，
並回傳 **401 `INVALID_SIGNATURE`**。只有 `/b2b/v1/*` 請求採用這種簽章方式。
Webhook 使用另一套機制(見下文)。

## 下一步

- [錯誤](https://docs.infraio.xyz/zh-TW/api-reference/errors) — 4xx/5xx 的回應格式。
- [安全 → API 金鑰](https://docs.infraio.xyz/zh-TW/security/api-keys) — 輪替、撤銷，以及
  secret 外洩時該怎麼做。
- [Webhooks → 簽章驗證](https://docs.infraio.xyz/zh-TW/webhooks/signature-verification)
  — 使用*不同*的 HMAC 機制(標頭 `X-Signature: sha256=…`,簽
  `X-Timestamp + "." + raw_body`,加上 24 小時輪替寬限視窗內的選用
  `X-Signature-Prev`)。請別把兩套機制混用 — 雖然 hash 演算法相同，
  但所簽位元組與 secret 系列(`whsec_…` vs `sk_…`)都不一樣。
