支払い識別子
冪等性のための支払いID管理とレスポンスキャッシングを提供するエクステンションです。
支払い識別子エクステンションを使用すると、クライアントはリクエストに一意のIDを付与できます。サーバーはこのIDを使ってレスポンスをキャッシュし、同じIDで再試行された場合に重複処理を防ぎます。
主なユースケース
- ネットワーク障害後の安全な再試行
- AIエージェントによる重複支払いの防止
- 決済フローの監査証跡
- べき等なAPI設計の実現
仕組み
- 1クライアントがリクエストの論理的な単位でIDを生成し、
X-Payment-Identifierヘッダーに付与して送信 - 2サーバーが決済完了後にID・レスポンスをキャッシュ
- 3同じIDで再リクエストがきた場合、
onProtectedRequestフックでキャッシュを確認して即時返却 - 4TTLが切れた後は通常通り再処理
冪等性の動作
| シナリオ | 動作 |
|---|---|
| 新しいID | 通常通り処理し、レスポンスをキャッシュ |
| TTL内で同じID | キャッシュされたレスポンスを返す(再処理なし) |
| TTL後に同じID | 通常通り再処理 |
| IDなし | 通常通り処理(キャッシュなし) |
クライアント実装
generatePaymentId() を使ってIDを生成し、支払いフローにフックします:
import { wrapFetchWithPayment, generatePaymentId } from "x402-fetch";
const paymentClient = wrapFetchWithPayment(fetch, signer, {
paymentIdentifier: generatePaymentId(),
});
// 同じ論理リクエストには同じIDを使用
const id = generatePaymentId();
const res1 = await paymentClient("https://api.example.com/data", {
paymentIdentifier: id,
});
// 失敗してもリトライで重複課金されない
const res2 = await paymentClient("https://api.example.com/data", {
paymentIdentifier: id,
});サーバー実装
エクステンションを登録し、ルートで有効化します:
import { paymentIdentifierExtension } from "x402/extensions";
server.registerExtension(paymentIdentifierExtension({
ttl: 3600, // seconds
store: redisStore, // カスタムストア(省略時はメモリ)
}));
const routes = {
"GET /data": {
accepts: [{ scheme: "exact", price: "$0.01", ... }],
extensions: {
paymentIdentifier: { enabled: true },
},
},
};本番環境ではstoreにRedisなどの永続ストアを使用することを推奨します。メモリストアはサーバー再起動でキャッシュが消失します。