認証
InfraIO Pay には認証モデルの異なる 2 つの API サーフェス があります。 呼び出し主体に合わせて選択してください:
| サーフェス | パスプレフィックス | 想定対象 | 認証方式 |
|---|---|---|---|
| マーチャント B2B | /b2b/v1/* | あなたのサーバー | HMAC-SHA256 リクエスト署名 |
| ダッシュボード | サービスごと: /auth/*、/payment/*、/merchant/*、/event/*、/user/* など | マーチャントダッシュボードのブラウザセッション | Bearer JWT |
本ページは API キーペアを使ってサーバーから呼び出す B2B サーフェスを 扱います。InfraIO ダッシュボードを組み込んだり社内ツールを構築する 場合は、ダッシュボードサーフェス (別ドキュメント、まだ公開していません) を使ってください。
ゲートウェイは各サーフェスを先頭プレフィックスでルーティングし、
フォワード前にそれを 除去 します:/b2b/v1/checkout-sessions/quick
は payment-service には /v1/checkout-sessions/quick として届き、
ダッシュボードの /payment/v1/orders は /v1/orders として届きます。
したがって、他の場所で素の /v1/* パスを見かけたら、それは公開
プレフィックスが除去された後の バックエンド内部 パスです — あなたの
クライアントは常にプレフィックス付きの形を送信します。(署名における
1 つの帰結:B2B の canonical 文字列は /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 署名鍵。 サーバー限定。データベースパスワードと同じ扱いをしてください。
シークレットキーがブラウザバンドル、Git リポジトリ、ログ行、 共有チャットなどに混入した場合 — 直ちに ダッシュボードから 失効させてください。オーバーラップウィンドウはありません; 失効は即時です。 新しいキーを発行して再デプロイしてください。
リクエストの署名
/b2b/v1/* への各呼び出しは 3 つのヘッダーを伴います:
X-Client-ID: pk_live_70d0becbd7a53047b0dfe88a960d10ad
X-Timestamp: 1715990400
X-Signature: 9a8b7c6d… (hex HMAC-SHA256)署名は canonical 文字列に対して計算されます:
METHOD + "\n" + PATH + "\n" + TIMESTAMP + "\n" + BODYMETHOD— 大文字の HTTP 動詞 (POST、GETなど)。PATH—/b2bプレフィックスを含めた リクエストパス、ホストと クエリ文字列を除く (例:/b2b/v1/checkout-sessions/quick)。 ゲートウェイは/b2bを剥がす 前 の raw な受信パスに対して署名を 検証するため、プレフィックスは必須です。クエリパラメータは 署名対象に 含めません —GET …?cursor=…&limit=20ではパスのみに署名し、?…部分には署名しません。TIMESTAMP— Unix 秒の 10 進文字列 (例:"1715990400")、X-Timestampと完全一致させてください。BODY— 生のリクエストボディバイト。GET/DELETEでは空文字。
シークレット キーで 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 };
}なぜ Bearer ではなく HMAC か
素の Bearer トークン API は、リクエストごとにあなたの唯一のシークレットを ネットワーク越しに送信します。TLS 終端プロキシのログを 1 つでも捕捉 されれば、アカウントの鍵がそのまま渡ります。HMAC 署名なら、シークレット 自体は移動せず、そこから導出された署名 (そのリクエストとその分にだけ バインドされた一回限りの値) だけが流れます。
トレードオフ: 呼び出しごとに署名を計算する必要があります。サーバー SDK ならこれを隠せますが、公開するまでは上記のヘルパーが言語あたり 15 行程度です。
タイムスタンプの許容範囲
バインディング許容範囲は ±5 分 (300 秒) で、merchant-service が
署名を検証する際に強制します。ゲートウェイ自体はディフェンスインデプスとして
やや緩い (310s) ですが、ゲートウェイを通過して内部チェックで失敗した
リクエストは結局 401 invalid_signature で終わります — 契約としては
300s として扱ってください。2 つの含意:
- サーバー時計を NTP で同期 してください。時計がずれた長時間 稼働の cron は断続的に失敗します。
- 署名を事前計算してキューに入れないでください。 リトライキューで
5 分滞留すると、その署名は期限切れになります。
キーのスコープ
シークレットキーは以下のスコープバンドルを 1 つ以上保持します:
| スコープ | 想定用途 |
|---|---|
read | 注文、セッション、返金の一覧 / 読み取り |
write_order | チェックアウトセッション、注文の作成 |
write_refund | 返金の発行、返金リクエストトークンの発行 |
webhook_manage | Webhook エンドポイントの作成 / 更新 / 削除 |
ダッシュボードはデフォルトで「フルアクセス」キー (4 つすべて) を 発行します。Developers → API keys → + Add key から、統合に必要な スコープだけにチェックを入れた制限付きスコープキーを発行できます。
スコープの強制は現状アドバイザリで、ゲートされていません。 スコープは
キーに記録されダッシュボードに表示されますが、ゲートウェイミドルウェアは
まだスコープ外の呼び出しを拒否していません — 有効な sk_… キーは今日
どれもフルアクセスとして振る舞います。エンドポイント単位のスコープ
ゲーティングは次のリリースで対応します。当面はスコープをセキュリティ境界
として頼らず、ラベルとして扱い、アクセス制限はキーのローテーション
/ 失効で行ってください。
署名はどこで検証されるか
HMAC 検証は ゲートウェイで 1 回だけ 行われます。ゲートウェイは:
X-Client-ID、X-Timestamp、X-Signatureを読み取ります。pk_…からマーチャント + シークレットを引き、タイムスタンプ ウィンドウチェックを行い、署名を再計算し、定数時間比較を行います。- 成功時は認証ヘッダーを剥がし、内部ヘッダー (
X-B2B-Auth: 1、X-Merchant-ID、X-Merchant-Domain) を リクエストにスタンプし、 下流サービス (payment-service、merchant-service など) に フォワードします。環境と解決されたスコープは現状注入していません — 環境が必要な下流コードはヘッダーからではなく、リクエストボディ / マーチャント別設定から導出します。 - 失敗時は 401
INVALID_SIGNATUREを返し、バックエンドには 一切触れません。
下流サービスは HMAC を 再実行しません — ゲートウェイが注入した
ヘッダーを信頼し、ゲートウェイが解決したマーチャントに対して動作します。
エンドポイントごとのスコープゲートも 行いません:上述のとおりキーの
スコープは注入されないため、認証済みの sk_… はどれもそのマーチャントの
どのエンドポイントにも到達します(スコープの強制は現状アドバイザリです
— キーのスコープ のコールアウトを参照)。これは
2 点で重要です:
- InfraIO Pay の前に独自のリバースプロキシを運用する場合、
X-B2B-Auth/X-Merchant-IDを剥がさないでください (また、偽造もしないでください — ゲートウェイはこれらを公開エッジで運んでくる受信リクエストを拒否します)。 - 公開ネットワーク経路 (
/b2b/v1/*) は HMAC ステップが走る唯一の サーフェスです。当方サービス間の内部 gRPC は mTLS を使います —X-Client-IDを受け付けない異なる信頼モデルです。
次に
- エラー — 4xx/5xx 時のレスポンス形式。
- セキュリティ → API キー — ローテーション、 失効、シークレットが漏れたときの対処。
- Webhook → 署名検証
— 異なる HMAC スキームを使用 (ヘッダーは
X-Signature: sha256=…、 署名対象はX-Timestamp + "." + raw_body、24 時間のローテーション猶予 ウィンドウ中は任意でX-Signature-Prev)。スキームを混同しないで ください — ハッシュアルゴリズムは共通ですが、署名対象バイトと シークレットファミリ (whsec_…vssk_…) は異なります。