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

# 認証

InfraIO Pay には認証モデルの異なる **2 つの API サーフェス** があります。
呼び出し主体に合わせて選択してください:

| サーフェス | パスプレフィックス | 想定対象 | 認証方式 |
| --- | --- | --- | --- |
| **マーチャント B2B** | `/b2b/v1/*` | あなたのサーバー | HMAC-SHA256 リクエスト署名 |
| **ダッシュボード** | マーチャントダッシュボードが使用 | マーチャントダッシュボードのブラウザセッション | Bearer JWT |

本ページは API キーペアを使ってサーバーから呼び出す **B2B** サーフェスを
扱います。ダッシュボードサーフェスは InfraIO Pay マーチャントダッシュボードが
使用するもので、公開された連携用サーフェスではありません。

`/b2b` プレフィックスを含めたフルパスを送信し、同じパスに署名して
ください (下記参照)。

## エンドポイント

| 環境 | ベース URL |
| --- | --- |
| Test | `https://api-dev.infraio.xyz` |
| Live | `https://api.infraio.xyz` |

URL パターンは同じです — 環境は URL ではなく **キーのプレフィックス**
(`pk_test_…` vs `pk_live_…`) で制御します。

## キーペア

マーチャントダッシュボード (**Developers → API keys → + Add key**) から
2 つの値を取得します:

- **Publishable key** (`pk_test_…` または `pk_live_…`) — あなたの
  アカウントを識別します。`X-Client-ID` として送信。ブラウザバンドルに
  埋め込んでも安全です (SDK は既にそうしています)。
- **Secret key** (`sk_test_…` または `sk_live_…`) — HMAC 署名鍵。
  サーバー限定。データベースパスワードと同じ扱いをしてください。

> **Important:**
>
> シークレットキーがブラウザバンドル、Git リポジトリ、ログ行、
> 共有チャットなどに混入した場合 — **直ちに** ダッシュボードから
> 失効させてください。失効は即時で、オーバーラップウィンドウはありません。
> 新しいキーを発行して再デプロイしてください。

## リクエストの署名

`/b2b/v1/*` への各呼び出しは 3 つのヘッダーを伴います:

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

署名は canonical 文字列に対して計算されます:

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

- `METHOD` — 大文字の HTTP 動詞 (`POST`、`GET` など)。
- `PATH` — **`/b2b` プレフィックスを含めた** リクエストパス、ホストと
  **クエリ文字列を除く** (例: `/b2b/v1/checkout-sessions/quick`)。
  プレフィックスは必須です。クエリパラメータは **署名対象に
  含めません** — `GET …?cursor=…&limit=20` ではパスのみに署名し、`?…` 部分には署名しません。
- `TIMESTAMP` — Unix 秒の 10 進文字列 (例: `"1715990400"`)、
  `X-Timestamp` と完全一致させてください。
- `BODY` — 生のリクエストボディバイト。`GET`/`DELETE` では空文字。

**シークレット** キーで 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
```

## なぜ Bearer ではなく HMAC か

素の Bearer トークン API は、リクエストごとにあなたの唯一のシークレットを
ネットワーク越しに送信します。TLS 終端プロキシのログを 1 つでも捕捉
されれば、アカウントの鍵がそのまま渡ります。HMAC 署名なら、シークレット
自体は移動せず、そこから導出された署名 (そのリクエストとその分にだけ
バインドされた一回限りの値) だけが流れます。

トレードオフ: 呼び出しごとに署名を計算する必要があります。サーバー SDK
はまだありませんが、上記のヘルパーは言語あたり 15 行程度です。

## タイムスタンプの許容範囲

許容範囲は **±5 分** (300 秒) です。このウィンドウ外のリクエストは
`401 invalid_signature` で拒否されます。2 つの含意:

1. **サーバー時計を NTP で同期** してください。時計がずれた長時間
   稼働の cron は断続的に失敗します。
2. **署名を事前計算してキューに入れないでください。** リトライキューで
   >5 分滞留すると、その署名は期限切れになります。

## キーのスコープ

シークレットキーは以下のスコープバンドルを 1 つ以上保持します:

| スコープ | 想定用途 |
| --- | --- |
| `read` | 注文、セッション、返金の一覧 / 読み取り |
| `write_order` | チェックアウトセッション、注文の作成 |
| `write_refund` | 返金の発行、返金リクエストトークンの発行 |
| `webhook_manage` | Webhook エンドポイントの作成 / 更新 / 削除 |

ダッシュボードはデフォルトで「フルアクセス」キー (4 つすべて) を
発行します。**Developers → API keys → + Add key** から、統合に必要な
スコープだけにチェックを入れた制限付きスコープキーを発行できます。

> **Warning:**
>
> **スコープはまだ強制されていません。** スコープはキーに記録されダッシュボードに
> 表示されますが、有効な `sk_…` キーであれば、あなたのマーチャントのどの
> `/b2b/v1/*` エンドポイントでも呼び出せます。スコープをセキュリティ境界として
> 頼らないでください。アクセスを制限するにはキーをローテーションまたは失効
> させてください。

## 検証に失敗した場合

署名、`X-Client-ID`、またはタイムスタンプが無効な場合、リクエストは API に
到達する前に **401 `INVALID_SIGNATURE`** で拒否されます。この方式で署名される
のは `/b2b/v1/*` リクエストのみです。Webhook は別のスキームを使用します
(下記参照)。

## 次に

- [エラー](https://docs.infraio.xyz/ja/api-reference/errors) — 4xx/5xx 時のレスポンス形式。
- [セキュリティ → API キー](https://docs.infraio.xyz/ja/security/api-keys) — ローテーション、
  失効、シークレットが漏れたときの対処。
- [Webhook → 署名検証](https://docs.infraio.xyz/ja/webhooks/signature-verification)
  — *異なる* HMAC スキームを使用 (ヘッダーは `X-Signature: sha256=…`、
  署名対象は `X-Timestamp + "." + raw_body`、24 時間のローテーション猶予
  ウィンドウ中は任意で `X-Signature-Prev`)。スキームを混同しないで
  ください — ハッシュアルゴリズムは共通ですが、署名対象バイトと
  シークレットファミリ (`whsec_…` vs `sk_…`) は異なります。
