{"content_id":"gyaevhk2bq","slug":"x402-payments-2026-v2-base-usdc-settlement-guide","locale":"ja","schema_type":"HowTo","category":"tutorial","category_name":"チュートリアル","title":"x402決済 2026 v2の実装と精算検証","summary":"x402 v2でAPIに少額決済を組み込むNode.jsの実習です。Base Sepoliaで決済条件を選択し、署名を再要求して精算を検証し、再送とメインネットへの移行条件を確認してください。","sponsorship_disclosure":null,"affiliate_disclosure":null,"commerce_disclosure":null,"author":{"name":"Injoys 編集部","url":"https://injoys.com/ko/about"},"key_points":["受取先アドレスとテスト決済用ウォレットを用意し、公式のx402 SDKをインストールしてください。","Base Sepolia用の決済ミドルウェアを接続し、決済なしのリクエストに対する402レスポンスを確認してください。","クライアント側で許可したチェーン・トークン・金額・受取人に合う条件だけを選び、署名してください。","精算レスポンスのトランザクションハッシュでUSDC Transferイベントを照会してください。","失敗時のブロックと重複リクエストの復旧をテストした後、メインネットの設定を検討してください。"],"content_markdown":"x402決済は、APIの402レスポンスに決済条件を含めて実装します。Node.jsとテスト用ウォレットを用意してください。まず、Base Sepoliaでv2の署名と精算を検証します。有料レスポンスは、精算が成功した後に返すように設定してください。\n\n金額・チェーン情報は、出典としてリンクしたx402・Base・Circleの公式ドキュメントに基づいています。\n\n## 実習の準備とインストール\n\nAPIを変更できる開発環境とテスト用ウォレットを用意してください。サンプルはNode.js 22とx402 SDK 2.28.0を基準に構成しています。インストール構成は[公式販売者ガイド](https://docs.x402.org/getting-started/quickstart-for-sellers)に従います。\n\n1. 決済を受け取るEVMウォレットのアドレスを用意してください。\n2. 決済用のテストウォレットを別途用意してください。\n3. 決済用ウォレットにBase Sepoliaのテスト用USDCを用意してください。\n4. 以下のコマンドで実習用プロジェクトを作成してください。\n5. 以降のTypeScriptファイルを同じフォルダーに保存してください。\n\n```bash\nmkdir x402-demo\ncd x402-demo\nnpm init -y\nnpm pkg set type=module\nnpm install --save-exact express@5.3.0 viem@2.57.12 @x402/core@2.28.0 @x402/express@2.28.0 @x402/evm@2.28.0 @x402/fetch@2.28.0\nnpm install --save-dev --save-exact typescript tsx @types/node @types/express\n```\n\n受取側のサーバーに、受取ウォレットの秘密鍵は必要ありません。決済用の秘密鍵は、クライアントプロセスでのみ読み込む構成にしてください。ウォレットの署名方式は[公式購入者ガイド](https://docs.x402.org/getting-started/quickstart-for-buyers)に基づいています。\n\nこのサンプルの検証範囲は、型チェックと模擬的な決済フローです。実際にテストネットでの送金まで完了したサンプルではありません。以下の実行手順で、オンチェーンの結果を直接確認してください。\n\n## x402 v1とv2の比較\n\nv2を実装する際は、ヘッダーと本文のフィールドを両方そろえる必要があります。ヘッダー名だけを変更しても、データ構造が一致しません。次の表は[公式移行ガイド](https://docs.x402.org/guides/migration-v1-to-v2)に基づいています。\n\n| 区分 | v1 | v2 |\n|---|---|---|\n| プロトコルバージョン | `x402Version: 1` | `x402Version: 2` |\n| 初回の決済条件 | 402レスポンスのJSON本文 | `PAYMENT-REQUIRED`レスポンスヘッダー |\n| 署名済み決済データ | `X-PAYMENT`リクエストヘッダー | `PAYMENT-SIGNATURE`リクエストヘッダー |\n| 精算結果 | `X-PAYMENT-RESPONSE`レスポンスヘッダー | `PAYMENT-RESPONSE`レスポンスヘッダー |\n| Baseメインネット識別子 | `base` | `eip155:8453` |\n| Base Sepolia識別子 | `base-sepolia` | `eip155:84532` |\n| 決済条件の金額フィールド | `maxAmountRequired` | `amount` |\n| リソースの説明 | 決済条件内の`resource`など | 最上位の`resource`オブジェクト |\n| 決済ペイロードの条件 | 最上位の`scheme`、`network`など | 選択した条件を格納する`accepted`オブジェクト |\n| TypeScriptパッケージの例 | `x402-express` | `@x402/express` |\n\nv1で決済条件を渡す場所は、[HTTP v1仕様](https://github.com/x402-foundation/x402/blob/main/specs/transports-v1/http.md)に明記されています。v2で標準となる受け渡し場所は、`PAYMENT-REQUIRED`ヘッダーです。実装によっては本文も返す場合がありますが、ヘッダーを基準に処理してください。\n\n[HTTP v2仕様](https://github.com/x402-foundation/x402/blob/main/specs/transports-v2/http.md)では、ステータスコードを次のように説明しています。\n\n\u003e サーバーは、HTTP 402「Payment Required」ステータスコードを使って、決済が必要であることを示します。\n\u003e\n\u003e 出典：x402 HTTP v2仕様\n\nv2の決済ヘッダーは、JSONをBase64でエンコードした値です。Base64は暗号化方式ではありません。外部向けサービスではHTTPSを使用してください。\n\n## HTTP 402リクエストと決済の順序\n\nこの実習では、EVMの`exact`方式の基本的な承認フローを使用します。`exact`は、指定された金額を決済する方式です。順序は[x402 v2の基本仕様](https://github.com/x402-foundation/x402/blob/main/specs/x402-specification-v2.md)に従います。\n\n1. クライアントが決済情報なしで有料APIをリクエストします。\n2. サーバーがHTTP 402と`PAYMENT-REQUIRED`を返します。\n3. クライアントが`accepts`から許可された条件を選択します。\n4. SDKが、選択した条件に合う決済承認に署名します。\n5. クライアントが`PAYMENT-SIGNATURE`を付けて再リクエストします。\n6. サーバーがfacilitatorの`/verify`で承認を検証します。\n7. 検証を通過すると、サーバーがリソースの処理を実行します。\n8. サーバーが`/settle`で精算をリクエストします。\n9. 精算が成功した後、有料の本文と`PAYMENT-RESPONSE`を返します。\n\nこの順序は、すべての決済方式に共通する固定の順序ではありません。v2には、精算のタイミングが異なるフローも定義されています。以下のコードでは、基本的な`authorization`フローのみを許可します。\n\n### Resource serverとfacilitatorの役割\n\nResource serverは、価格とコンテンツへのアクセス条件を決定します。Facilitatorは、決済の検証と精算を担当します。役割の区分は[公式facilitatorドキュメント](https://github.com/x402-foundation/x402/blob/main/docs/core-concepts/facilitator.md)に基づいています。\n\n| 構成要素 | 担当する処理 | 成功と見なしてはいけない状態 |\n|---|---|---|\n| クライアント | 条件の選択と決済承認への署名 | 署名の生成だけが完了した状態 |\n| Resource server | 価格の宣言と有料レスポンスの制御 | クライアントが成功したと主張している状態 |\n| Facilitator `/verify` | 署名と決済条件の有効性確認 | `isValid: true`を受け取っただけの状態 |\n| Facilitator `/settle` | この実習ではオンチェーン決済の実行と結果の返却 | 送信したものの結果を確認できていない状態 |\n\n`/verify`は、オンチェーンの状態を変更しない検査です。そのため、検証の成功は入金の完了を意味しません。検証後も、残高や承認状態が変わる可能性があります。\n\n検証や精算に失敗した場合は、有料の本文をブロックしてください。成功を確認できなかったときにブロックする処理を、fail-closedと呼びます。この動作は[公式Express実装](https://github.com/x402-foundation/x402/blob/main/typescript/packages/http/express/src/index.ts)でも確認できます。\n\n## Baseメインネットとテストネットの比較\n\n初回の実行では、Base Sepoliaだけを使用してください。Circleは、テストネットのUSDCに金銭的価値がないと明記しています。アドレスは[CircleのUSDCコントラクト一覧](https://developers.circle.com/stablecoins/usdc-contract-addresses)に基づいています。\n\n| 項目 | Baseメインネット | Base Sepoliaテストネット |\n|---|---|---|\n| 用途 | 実際の資産による決済 | 実装と障害処理のテスト |\n| Chain ID | `8453` | `84532` |\n| x402 v2ネットワーク | `eip155:8453` | `eip155:84532` |\n| USDCコントラクト | `0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913` | `0x036CbD53842c5426634e7929541eC2318f3dCF7e` |\n| 公開RPC | `https://mainnet.base.org` | `https://sepolia.base.org` |\n| トークンの金銭的価値 | 実際に金銭的価値のある資産 | 金銭的価値なし |\n\nChain IDとRPCは、[Baseネットワークドキュメント](https://docs.base.org/get-started/connect-to-base)で確認してください。同じウォレットアドレスでも、チェーンが異なれば残高は別です。テストネットでの成功を、実際の売上として記録してはいけません。\n\nテスト用USDCは、[Circle Testnet Faucet](https://faucet.circle.com/)でリクエストできます。ネットワークにはBase Sepoliaを選択してください。受取アドレスには、決済用テストウォレットのアドレスを入力します。\n\n## Node.jsによる有料APIの実装\n\n有料ルートの前に`paymentMiddleware`を組み込んでください。以下のサンプルは、呼び出し1回あたり**0.001 USDC**を要求します。金額は、公式販売者ガイドの価格例を使用しています。（確認値：呼び出し1回あたり0.001 USDC・出典：docs.x402.org・2026-10-28確認）\n\n### サーバーファイル\n\n次のコードを`server.ts`として保存してください。サーバーは、ローカルインターフェースでのみリクエストを受け付けます。受取アドレスは、環境変数`PAY_TO`から読み込みます。\n\n```typescript\nimport express from \"express\";\nimport { getAddress, zeroAddress } from \"viem\";\nimport { paymentMiddleware } from \"@x402/express\";\nimport { HTTPFacilitatorClient, x402ResourceServer } from \"@x402/core/server\";\nimport { ExactEvmScheme } from \"@x402/evm/exact/server\";\n\nconst payTo = getAddress(process.env.PAY_TO ?? \"\");\nif (payTo === zeroAddress) throw new Error(\"Invalid payTo\");\nconst network = \"eip155:84532\";\nconst resourceServer = new x402ResourceServer(\n  new HTTPFacilitatorClient({ url: \"https://x402.org/facilitator\" }),\n).register(network, new ExactEvmScheme());\n\nconst app = express();\napp.use((_req, res, next) =\u003e {\n  res.setHeader(\"Cache-Control\", \"private, no-store\");\n  next();\n});\napp.use(paymentMiddleware({\n  \"GET /paid-data\": {\n    accepts: [{ scheme: \"exact\", price: \"$0.001\", network, payTo }],\n    description: \"Sample paid JSON\",\n    mimeType: \"application/json\",\n  },\n}, resourceServer));\napp.get(\"/paid-data\", (_req, res) =\u003e {\n  res.json({ content: \"Paid sample content\" });\n});\napp.listen(4021, \"127.0.0.1\");\n```\n\n`https://x402.org/facilitator`は、公式ガイドに掲載されているテスト用のアドレスです。このサンプルでは、Base Sepoliaでの精算に使用します。本番用facilitatorの選択については、[販売者ガイドのメインネット手順](https://docs.x402.org/getting-started/quickstart-for-sellers#running-on-mainnet)を確認してください。\n\n### 価格計算の例\n\nサンプルの価格は、USDCの最小単位で`1000`です。USDCの小数点以下の桁数は6桁です。SDKによる価格変換は、[EVMサーバー実装](https://github.com/x402-foundation/x402/blob/main/typescript/packages/mechanisms/evm/src/exact/server/scheme.ts)に基づいています。\n\n```text\n0.001 USDC × 10^6 = 1000 최소 단위\n```\n\n| 確認する値 | このサンプルでの期待値 |\n|---|---|\n| サーバー設定の`price` | `\"$0.001\"` |\n| 402決済条件の`amount` | `\"1000\"` |\n| 決済条件の`asset` | Base SepoliaのUSDCコントラクト |\n| 決済条件の`payTo` | 設定した受取アドレス |\n| 決済条件の`network` | `eip155:84532` |\n\n`price`はSDKの設定値です。実際のv2決済条件では、`amount`が渡されます。整数の金額は、文字列または`bigint`として比較してください。\n\n### サーバーの起動と402の確認\n\n1つ目のBashターミナルでサーバーを起動してください。入力する値は、秘密鍵ではなく公開の受取アドレスです。実行中は、このターミナルを開いたままにします。\n\n```bash\nread -r -p '수취 지갑 주소: ' PAY_TO\nexport PAY_TO\nnpx tsx server.ts\n```\n\n2つ目のターミナルから、決済なしのリクエストを送信してください。以下のコマンドは、決済に署名しません。HTTPステータスと、デコードした決済条件を出力します。\n\n```bash\nnode --input-type=module \u003c\u003c'JS'\nconst response = await fetch(\"http://127.0.0.1:4021/paid-data\");\nconst header = response.headers.get(\"PAYMENT-REQUIRED\");\nconsole.log(response.status);\nconsole.log(header \u0026\u0026 JSON.parse(\n  Buffer.from(header, \"base64\").toString(\"utf8\")\n));\nJS\n```\n\n正常な決済なしのレスポンスは、HTTP 402です。出力された条件を、上の表と照合してください。有料コンテンツがそのまま返される場合は、ルートの保護設定から修正します。\n\n## 決済条件の選択とクライアントの実装\n\n許可した条件にのみ署名するように、クライアントを制限してください。このサンプルでは、Base SepoliaのUSDCによる指定価格の決済だけを許可します。決済オプションのフィルターには、[公式x402Client実装](https://github.com/x402-foundation/x402/blob/main/typescript/packages/core/src/client/x402Client.ts)の`registerPolicy`を使用します。\n\n1. 以下のコードを`client.ts`として保存してください。\n2. 次の節の`check-transfer.ts`も保存してください。\n3. 2つのファイルを用意してから、実行手順に進んでください。\n\n```typescript\nimport { writeFile } from \"node:fs/promises\";\nimport { getAddress, zeroAddress, type Hex, type Hash } from \"viem\";\nimport { privateKeyToAccount } from \"viem/accounts\";\nimport { x402Client, wrapFetchWithPayment } from \"@x402/fetch\";\nimport { decodePaymentResponseHeader } from \"@x402/core/http\";\nimport { ExactEvmScheme } from \"@x402/evm/exact/client\";\nimport { checkTransfer } from \"./check-transfer.js\";\n\nconst key = process.env.EVM_PRIVATE_KEY ?? \"\";\nif (!/^0x[0-9a-fA-F]{64}$/.test(key)) throw new Error(\"Invalid test key\");\nconst signer = privateKeyToAccount(key as Hex);\nconst payTo = getAddress(process.env.PAY_TO ?? \"\");\nif (payTo === zeroAddress) throw new Error(\"Invalid payTo\");\nconst network = \"eip155:84532\";\nconst token = \"0x036CbD53842c5426634e7929541eC2318f3dCF7e\";\nconst client = new x402Client().register(network, new ExactEvmScheme(signer));\nclient.registerPolicy((version, options) =\u003e version === 2\n  ? options.filter(r =\u003e\n      r.scheme === \"exact\" \u0026\u0026 r.network === network \u0026\u0026\n      r.asset.toLowerCase() === token.toLowerCase() \u0026\u0026\n      r.payTo.toLowerCase() === payTo.toLowerCase() \u0026\u0026 r.amount === \"1000\" \u0026\u0026\n      (!r.extra?.assetTransferMethod || r.extra.assetTransferMethod === \"eip3009\") \u0026\u0026\n      (!r.extra?.paymentFlow || r.extra.paymentFlow === \"authorization\"))\n  : []);\n\nconst paidFetch = wrapFetchWithPayment(fetch, client);\nconst response = await paidFetch(\"http://127.0.0.1:4021/paid-data\", {\n  redirect: \"error\",\n});\nconst raw = response.headers.get(\"PAYMENT-RESPONSE\");\nif (!raw) throw new Error(`No settlement receipt: HTTP ${response.status}`);\nconst settlement = decodePaymentResponseHeader(raw);\nawait writeFile(\"settlement.json\", JSON.stringify(settlement, null, 2));\nif (!response.ok || !settlement.success || settlement.network !== network) {\n  throw new Error(\"Payment unresolved; inspect settlement.json before retrying\");\n}\nconst evidence = await checkTransfer(\n  network, settlement.transaction as Hash, signer.address, payTo, 1000n,\n);\nawait writeFile(\"transfer.json\", JSON.stringify(evidence, null, 2));\nconsole.log(await response.text());\n```\n\n`wrapFetchWithPayment`が、402の処理と署名付きの再リクエストを担当します。使い方は[公式fetchサンプル](https://github.com/x402-foundation/x402/tree/main/examples/typescript/clients/fetch)に従います。許可された決済条件がなければ、決済の生成を中止します。\n\nAIエージェントでも、このクライアントを使用できます。ただし、呼び出し1回あたりの価格制限は、累積支出の制限ではありません。エージェントによる繰り返し呼び出しの予算は、別途管理してください。\n\n## USDC Transferイベントの検証\n\n精算結果のトランザクションハッシュを使って、USDCの送金ログを確認してください。トランザクションの成功状態だけでは、受取人まで確認できません。`Transfer`のフィールドは、[ERC-20標準](https://eips.ethereum.org/EIPS/eip-20)に基づいています。\n\n検証には、次の項目が必要です。\n\n| 検証項目 | 比較対象 |\n|---|---|\n| RPCのChain ID | サーバーが許可したチェーン |\n| トランザクションレシートの`status` | 成功状態 |\n| イベントを発生させたアドレス | そのチェーンの公式USDCコントラクト |\n| `Transfer.from` | 確認済みの支払者 |\n| `Transfer.to` | サーバーが指定した受取アドレス |\n| `Transfer.value` | サーバーが要求した最小単位での金額 |\n\n以下のコードを`check-transfer.ts`として保存してください。照会には、viemの[getTransactionReceipt](https://viem.sh/docs/actions/public/getTransactionReceipt)を使用します。イベントの解釈は、[parseEventLogs](https://viem.sh/docs/contract/parseEventLogs)に基づいています。\n\n```typescript\nimport {\n  createPublicClient, http, getAddress, parseEventLogs,\n  erc20Abi, type Address, type Hash,\n} from \"viem\";\nimport { base, baseSepolia } from \"viem/chains\";\n\nconst configs = {\n  \"eip155:84532\": {\n    chain: baseSepolia,\n    rpc: \"https://sepolia.base.org\",\n    token: \"0x036CbD53842c5426634e7929541eC2318f3dCF7e\",\n  },\n  \"eip155:8453\": {\n    chain: base,\n    rpc: \"https://mainnet.base.org\",\n    token: \"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913\",\n  },\n} as const;\n\nexport async function checkTransfer(\n  network: keyof typeof configs, hash: Hash,\n  payer: Address, payTo: Address, amount: bigint,\n) {\n  const cfg = configs[network];\n  const rpc = createPublicClient({ chain: cfg.chain, transport: http(cfg.rpc) });\n  if (await rpc.getChainId() !== cfg.chain.id) throw new Error(\"Wrong chain\");\n  const receipt = await rpc.getTransactionReceipt({ hash });\n  if (receipt.status !== \"success\") throw new Error(\"Transaction failed\");\n  const logs = parseEventLogs({\n    abi: erc20Abi,\n    eventName: \"Transfer\",\n    logs: receipt.logs.filter(\n      log =\u003e getAddress(log.address) === getAddress(cfg.token),\n    ),\n    strict: true,\n  });\n  const transfer = logs.find(log =\u003e\n    getAddress(log.args.from) === getAddress(payer) \u0026\u0026\n    getAddress(log.args.to) === getAddress(payTo) \u0026\u0026\n    log.args.value === amount,\n  );\n  if (!transfer) throw new Error(\"Matching USDC Transfer not found\");\n  return {\n    network, transaction: hash, logIndex: transfer.logIndex,\n    blockHash: receipt.blockHash, blockNumber: receipt.blockNumber.toString(),\n  };\n}\n```\n\nこの関数は、そのトランザクションでUSDCが送金された事実を確認します。特定のAPI注文に対応するものかどうかは、別途確認する必要があります。本番サーバーでは、保存済みの注文条件を比較値として使用してください。\n\nクライアントがアップロードした`transfer.json`を、アクセス許可の証拠として信用してはいけません。このサンプルのサーバーは、facilitatorの検証・精算結果を使用します。独立した照会をアクセス許可の条件にする場合は、サーバー側で実行する必要があります。\n\nレシートの照会に失敗しても、それだけで決済の失敗を意味するわけではありません。RPCエラーであれば、保存済みのトランザクションハッシュを再照会してください。追加の承認数とチェーン再編成への対応は、運用ポリシーで定めます。\n\n## テスト決済の実行と結果確認\n\nすべてのファイルを保存してから、テスト用ウォレットで実行してください。型チェックでは、署名や送金は実行されません。以下のコマンドでは、先ほどインストールしたTypeScriptツールを使用します。\n\n1. 次のコマンドで型をチェックしてください。\n2. 2つ目のターミナルに、サーバーと同じ受取アドレスを入力してください。\n3. テスト決済用ウォレットの秘密鍵を、非表示で入力してください。\n4. クライアントを1回実行してください。\n5. 保存された精算結果と送金ログを確認してください。\n\n```bash\nnpx tsc --noEmit --strict --skipLibCheck --target ES2022 --module NodeNext --moduleResolution NodeNext server.ts client.ts check-transfer.ts\nread -r -p '같은 수취 지갑 주소: ' PAY_TO\nread -r -s -p '테스트 결제 지갑 개인키: ' EVM_PRIVATE_KEY\nexport PAY_TO EVM_PRIVATE_KEY\nnpx tsx client.ts\nunset EVM_PRIVATE_KEY\n```\n\n| 出力物 | 意味 | 確認する内容 |\n|---|---|---|\n| `settlement.json` | サーバーが返した精算結果 | `success`、`network`、`transaction` |\n| `transfer.json` | RPC照会で見つかった送金記録 | チェーン、トランザクションハッシュ、ログ位置、ブロック情報 |\n| コンソールの有料本文 | このクライアントでの検証後に出力したデータ | リクエストしたコンテンツかどうかの確認 |\n\nクライアントを新たに実行すると、新しい決済が発生する場合があります。この最小限のサンプルには、注文ごとの重複を防ぐストアはありません。再試行機能は、次の節の条件を満たしてから追加してください。\n\n## 検証失敗の条件別の処理\n\n失敗テストでは、有料の本文が露出しないことを確認してください。HTTPステータスだけをチェックすると、ブロック漏れを見逃す可能性があります。基準となる動作は、[公式Expressミドルウェア](https://github.com/x402-foundation/x402/blob/main/typescript/packages/http/express/src/index.ts)を参照します。\n\n| テスト条件 | 期待する処理 | 追加確認 |\n|---|---|---|\n| 決済ヘッダーなし | 402と決済条件を返却 | 有料本文なし |\n| 別のトークン・受取人の提示 | クライアントポリシーで拒否 | 署名を生成しない |\n| 不正な署名 | 検証失敗としてブロック | 有料ハンドラーを実行しない |\n| 残高不足 | 検証または精算の失敗としてブロック | 精算成功として記録しない |\n| 精算結果が失敗 | 有料レスポンスをブロック | 準備済みの本文も送信しない |\n| facilitatorの応答遅延 | 成功未確認の状態で保留 | 新しい承認の自動生成を禁止 |\n| RPCでのレシート照会エラー | トランザクションハッシュを保持して再照会 | 決済失敗と断定しない |\n\n基本的な承認フローでは、精算前にハンドラーが実行されます。精算に失敗しても、外部での処理まで元に戻るわけではありません。このサンプルのように、副作用のない照会から適用してください。\n\nメールの送信や外部での注文作成には、別途設計が必要です。決済と処理の状態を一緒に記録してください。失敗時の再実行範囲を決めることで、処理の重複を減らせます。\n\n## 精算後のレスポンス消失と再送による復旧\n\n決済の完了とコンテンツ配信の完了を、別々の状態として保存してください。決済が完了していても、レスポンスの接続が切れる場合があります。このような状況への対応の根拠となるのは、[Payment-Identifier拡張ドキュメント](https://docs.x402.org/extensions/payment-identifier)です。\n\n同じ処理を再リクエストしても重複処理しない性質を、冪等性と呼びます。`payment-identifier`は、そのための決済識別子を提供します。識別子を追加するだけで、自動的に完成する機能ではありません。\n\n### 重複決済と再送の比較\n\n同じ署名の再利用防止と、注文の重複防止は区別する必要があります。EIP-3009は、使用済みの承認nonceの再利用を防ぎます。この動作は、[ERC-3009仕様](https://eips.ethereum.org/EIPS/eip-3009)に定義されています。\n\n| 状況 | オンチェーン承認の観点 | API運用の観点 |\n|---|---|---|\n| 同じ承認を再び精算 | 使用済みnonceのチェック対象 | 既存結果の照会が必要 |\n| 同じ注文に新しい承認を生成 | 別の承認として処理される可能性 | 重複決済の防止が必要 |\n| 精算成功後にレスポンスが消失 | 資産の移動は完了している可能性 | コンテンツの再配信が必要 |\n| 精算リクエスト後にタイムアウト | 結果がまだ不明確 | 保留状態と精算確認が必要 |\n\n次の手順は、公式拡張を運用用ストアに接続する設計案です。前述の最小限のコードには含まれていません。本番運用に適用する場合は、永続ストアと同時リクエストの制御を追加してください。\n\n1. サーバー側で`payment-identifier`拡張への対応を宣言してください。\n2. クライアント側で論理的なリクエストの識別子を保持してください。\n3. サーバー側で識別子を支払者とリクエスト内容に関連付けてください。\n4. 処理中の状態をアトミックに記録し、同時実行を防いでください。\n5. 精算成功時に、トランザクションの証拠とレスポンス結果を保存してください。\n6. 再リクエストの権限と内容が同じであれば、保存済みの結果を返してください。\n7. 精算結果が不明確な場合は、既存の承認から確認してください。\n\n決済識別子は、パスワードやアクセストークンではありません。識別子を知っているだけのリクエストに、有料キャッシュを返してはいけません。別のユーザーや異なるリクエスト本文に再利用されていないかもチェックしてください。\n\n運用記録には、チェーン・トランザクションハッシュ・ログ位置を一緒に残してください。この記録を注文に関連付けて、別の注文での再利用をブロックします。`Transfer`の一致だけでは、注文との対応関係までは証明できません。\n\n## 決済実装でよくある間違い\n\nバージョンフィールドと成功を示す情報を混同すると、決済の判定が誤ります。以下の表を、デプロイ時のレビューに活用してください。バージョンの違いは、[x402移行ガイド](https://docs.x402.org/guides/migration-v1-to-v2)に基づいています。\n\n| よくある間違い | 修正の基準 |\n|---|---|\n| `extra.version: \"2\"`をx402のバージョンと解釈 | プロトコルバージョンは`x402Version`で確認 |\n| v2で`maxAmountRequired`を使用 | 決済条件の金額には`amount`を使用 |\n| `X-PAYMENT-REQUIRED`ヘッダーを独自に生成 | v1は本文、v2は`PAYMENT-REQUIRED`を使用 |\n| `isValid: true`を入金完了として処理 | `/settle`の結果を別途確認 |\n| トランザクションの最上位の`to`をUSDCの受取人として処理 | USDCの`Transfer.to`を確認 |\n| 署名者とトランザクション送信者が同じと想定 | 承認の支払者と`Transfer.from`を比較 |\n| テストネットでの受取額を売上に合算 | メインネットとテストネットの帳簿を分離 |\n| タイムアウトのたびに新しい決済を生成 | 既存の注文と承認状態から復旧 |\n| SDKのパッケージバージョンとプロトコルバージョンを同一視 | パッケージバージョンと`x402Version`を別々に管理 |\n\nUSDCの署名のドメイン情報にも、バージョン文字列が含まれます。その値は、x402のプロトコルバージョンとは目的が異なります。署名ドメインの構造は、[ERC-3009仕様](https://eips.ethereum.org/EIPS/eip-3009)で確認してください。\n\n## セキュリティ点検と外部サービスの信頼範囲\n\n決済の成功判定は、サーバー側で制御してください。外部facilitatorのレスポンスも、明示的な信頼対象です。自前での運用もできる構成については、[facilitatorドキュメント](https://github.com/x402-foundation/x402/blob/main/docs/core-concepts/facilitator.md)に説明されています。\n\n- クライアントの成功メッセージで、コンテンツへのアクセスを許可しないでください。\n- サーバーが宣言したチェーン・トークン・金額・受取人を比較してください。\n- 秘密鍵やシードフレーズをログに残さないでください。\n- 承認署名の全体も、通常のアクセスログから除外してください。\n- 決済ヘッダーのサイズとリクエストの処理時間を制限してください。\n- 有料レスポンスが共有キャッシュに残らないように点検してください。\n- テスト用キーと本番用キーの保管場所を分けてください。\n- facilitatorの障害時に、決済なしで迂回する経路を作らないでください。\n- 承認の期限切れと重複リクエストを、実際にテストしてください。\n\n実習で固定した依存関係には、セキュリティチェックの警告が残っています。`npm audit`は、zodに関するアドバイザリを報告しました。関連する内容は、[GitHubセキュリティアドバイザリ](https://github.com/advisories/GHSA-mrhx-c7xc-m3w5)で確認してください。\n\n```bash\nnpm audit\n```\n\n本番環境へのデプロイ前に、修正版とのSDKの互換性を確認してください。メジャーバージョンを強制的に置き換えると、動作が変わる可能性があります。この実習を、セキュリティ検証済みのデプロイ構成として使用しないでください。\n\n### 自前のサーバーとCloudflare Gatewayの比較\n\nCloudflare Gatewayを使うと、オリジンサーバーでの検証方法が変わります。2026年9月30日付のドキュメントでは、x402 v2の使用が明記されています。次の区分は、[Cloudflare x402ドキュメント](https://developers.cloudflare.com/monetization-gateway/x402/)に基づいています。\n\n| 区分 | この記事のExpress方式 | Cloudflare Monetization Gateway |\n|---|---|---|\n| クライアントの決済処理 | Expressミドルウェア | Gateway |\n| オリジンサーバーでの検証箇所 | 選択したfacilitatorと連携 | `PAYMENT-CONTEXT` JWTの検証 |\n| 精算結果の受け渡し | ミドルウェアの`PAYMENT-RESPONSE` | Gatewayが精算レシートを生成 |\n| 追加の確認事項 | facilitatorの対応チェーンと認証 | JWTの署名検証とオリジンへのアクセス保護 |\n\n`PAYMENT-CONTEXT`は、Gatewayとオリジンサーバーの間で使うヘッダーです。一般的なx402クライアントの署名ヘッダーではありません。オリジンでの検証は、[Cloudflare Payment validationドキュメント](https://developers.cloudflare.com/monetization-gateway/configuration/payment-validation/)に従ってください。\n\nGatewayを経由しても、JWTを単にデコードするだけではいけません。ドキュメントで指定されたキーを使って、署名を検証してください。ヘッダーがない場合や検証に失敗した場合は、有料コンテンツをブロックします。\n\n## 実際の使用量と精算記録の区別\n\nオンチェーンの送金件数だけでは、コンテンツの消費を証明できません。運用指標は、精算とレスポンスの配信を関連付けて集計してください。これは、決済フローと再送の動作に基づく設計上の提案です。\n\n| 記録 | 答えられる問い | 追加で必要な記録 |\n|---|---|---|\n| 402レスポンス | 決済条件がどれだけリクエストされたか | 実際に決済されたかどうか |\n| 検証成功 | 承認が有効だったか | 精算結果 |\n| USDC送金 | 資産が移動したか | 注文との対応関係 |\n| 注文ごとの精算完了 | 有料リクエストの決済が済んだか | レスポンスの配信状態 |\n| 再配信記録 | 決済済みのリクエストを復旧したか | 新規決済と区別する識別子 |\n\nテストリクエストは、実際の使用量の集計で区別してください。同じ注文の再試行も、別途表示します。取引量そのものを、APIの需要や収益の保証として説明してはいけません。\n\n## Baseメインネットへの移行手順\n\nテストネットで正常時と失敗時の経路を確認してから、メインネットを検討してください。ネットワーク文字列を変更するだけでは、移行は完了しません。基準となる手順は、[公式販売者ガイド](https://docs.x402.org/getting-started/quickstart-for-sellers#running-on-mainnet)に従います。\n\n1. 選択したfacilitatorがBaseメインネットに対応していることを確認してください。\n2. `/supported`で、v2・`exact`・`eip155:8453`への対応を確認してください。\n3. 提供者が要求する認証と利用条件を設定してください。\n4. サーバーとクライアントのネットワークを両方変更してください。\n5. クライアントで許可するUSDCアドレスを、メインネットのアドレスに変更してください。\n6. 402レスポンスの金額と、実際の受取アドレスを再度照合してください。\n7. 重複リクエスト用ストアと障害復旧手順を組み込んでください。\n8. 本番用の依存関係のセキュリティチェックを完了してください。\n9. 設定した少額で、実際の送金とコンテンツの配信を確認してください。\n\nマルチチェーン対応は、すべての組み合わせへの対応を意味しません。SDKとfacilitatorの対応範囲を、両方確認してください。確認の基準は、[ネットワーク・トークン対応ドキュメント](https://docs.x402.org/core-concepts/network-and-token-support)です。","content_html":"\u003cp\u003ex402決済は、APIの402レスポンスに決済条件を含めて実装します。Node.jsとテスト用ウォレットを用意してください。まず、Base Sepoliaでv2の署名と精算を検証します。有料レスポンスは、精算が成功した後に返すように設定してください。\u003c/p\u003e\n\u003cp\u003e金額・チェーン情報は、出典としてリンクしたx402・Base・Circleの公式ドキュメントに基づいています。\u003c/p\u003e\n\u003ch2\u003e\n\u003ca href=\"#%E5%AE%9F%E7%BF%92%E3%81%AE%E6%BA%96%E5%82%99%E3%81%A8%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB\" class=\"anchor\" id=\"実習の準備とインストール\"\u003e\u003c/a\u003e実習の準備とインストール\u003c/h2\u003e\n\u003cp\u003eAPIを変更できる開発環境とテスト用ウォレットを用意してください。サンプルはNode.js 22とx402 SDK 2.28.0を基準に構成しています。インストール構成は\u003ca href=\"https://docs.x402.org/getting-started/quickstart-for-sellers\"\u003e公式販売者ガイド\u003c/a\u003eに従います。\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e決済を受け取るEVMウォレットのアドレスを用意してください。\u003c/li\u003e\n\u003cli\u003e決済用のテストウォレットを別途用意してください。\u003c/li\u003e\n\u003cli\u003e決済用ウォレットにBase Sepoliaのテスト用USDCを用意してください。\u003c/li\u003e\n\u003cli\u003e以下のコマンドで実習用プロジェクトを作成してください。\u003c/li\u003e\n\u003cli\u003e以降のTypeScriptファイルを同じフォルダーに保存してください。\u003c/li\u003e\n\u003c/ol\u003e\n\u003cpre\u003e\u003ccode\u003e\u003cspan\u003emkdir\u003c/span\u003e\u003cspan\u003e x402-demo\n\u003c/span\u003e\u003cspan\u003ecd\u003c/span\u003e\u003cspan\u003e x402-demo\n\u003c/span\u003e\u003cspan\u003enpm\u003c/span\u003e\u003cspan\u003e init\u003c/span\u003e\u003cspan\u003e -y\n\u003c/span\u003e\u003cspan\u003enpm\u003c/span\u003e\u003cspan\u003e pkg set type=module\n\u003c/span\u003e\u003cspan\u003enpm\u003c/span\u003e\u003cspan\u003e install\u003c/span\u003e\u003cspan\u003e --save-exact\u003c/span\u003e\u003cspan\u003e express@5.3.0 viem@2.57.12 @x402/core@2.28.0 @x402/express@2.28.0 @x402/evm@2.28.0 @x402/fetch@2.28.0\n\u003c/span\u003e\u003cspan\u003enpm\u003c/span\u003e\u003cspan\u003e install\u003c/span\u003e\u003cspan\u003e --save-dev --save-exact\u003c/span\u003e\u003cspan\u003e typescript tsx @types/node @types/express\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e受取側のサーバーに、受取ウォレットの秘密鍵は必要ありません。決済用の秘密鍵は、クライアントプロセスでのみ読み込む構成にしてください。ウォレットの署名方式は\u003ca href=\"https://docs.x402.org/getting-started/quickstart-for-buyers\"\u003e公式購入者ガイド\u003c/a\u003eに基づいています。\u003c/p\u003e\n\u003cp\u003eこのサンプルの検証範囲は、型チェックと模擬的な決済フローです。実際にテストネットでの送金まで完了したサンプルではありません。以下の実行手順で、オンチェーンの結果を直接確認してください。\u003c/p\u003e\n\u003ch2\u003e\n\u003ca href=\"#x402-v1%E3%81%A8v2%E3%81%AE%E6%AF%94%E8%BC%83\" class=\"anchor\" id=\"x402-v1とv2の比較\"\u003e\u003c/a\u003ex402 v1とv2の比較\u003c/h2\u003e\n\u003cp\u003ev2を実装する際は、ヘッダーと本文のフィールドを両方そろえる必要があります。ヘッダー名だけを変更しても、データ構造が一致しません。次の表は\u003ca href=\"https://docs.x402.org/guides/migration-v1-to-v2\"\u003e公式移行ガイド\u003c/a\u003eに基づいています。\u003c/p\u003e\n\u003cdiv class=\"overflow-x-auto\"\u003e\u003ctable\u003e\n\u003cthead\u003e\n\u003ctr\u003e\n\u003cth\u003e区分\u003c/th\u003e\n\u003cth\u003ev1\u003c/th\u003e\n\u003cth\u003ev2\u003c/th\u003e\n\u003c/tr\u003e\n\u003c/thead\u003e\n\u003ctbody\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"区分\"\u003eプロトコルバージョン\u003c/td\u003e\n\u003ctd data-label=\"v1\"\u003e\u003ccode\u003ex402Version: 1\u003c/code\u003e\u003c/td\u003e\n\u003ctd data-label=\"v2\"\u003e\u003ccode\u003ex402Version: 2\u003c/code\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"区分\"\u003e初回の決済条件\u003c/td\u003e\n\u003ctd data-label=\"v1\"\u003e402レスポンスのJSON本文\u003c/td\u003e\n\u003ctd data-label=\"v2\"\u003e\n\u003ccode\u003ePAYMENT-REQUIRED\u003c/code\u003eレスポンスヘッダー\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"区分\"\u003e署名済み決済データ\u003c/td\u003e\n\u003ctd data-label=\"v1\"\u003e\n\u003ccode\u003eX-PAYMENT\u003c/code\u003eリクエストヘッダー\u003c/td\u003e\n\u003ctd data-label=\"v2\"\u003e\n\u003ccode\u003ePAYMENT-SIGNATURE\u003c/code\u003eリクエストヘッダー\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"区分\"\u003e精算結果\u003c/td\u003e\n\u003ctd data-label=\"v1\"\u003e\n\u003ccode\u003eX-PAYMENT-RESPONSE\u003c/code\u003eレスポンスヘッダー\u003c/td\u003e\n\u003ctd data-label=\"v2\"\u003e\n\u003ccode\u003ePAYMENT-RESPONSE\u003c/code\u003eレスポンスヘッダー\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"区分\"\u003eBaseメインネット識別子\u003c/td\u003e\n\u003ctd data-label=\"v1\"\u003e\u003ccode\u003ebase\u003c/code\u003e\u003c/td\u003e\n\u003ctd data-label=\"v2\"\u003e\u003ccode\u003eeip155:8453\u003c/code\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"区分\"\u003eBase Sepolia識別子\u003c/td\u003e\n\u003ctd data-label=\"v1\"\u003e\u003ccode\u003ebase-sepolia\u003c/code\u003e\u003c/td\u003e\n\u003ctd data-label=\"v2\"\u003e\u003ccode\u003eeip155:84532\u003c/code\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"区分\"\u003e決済条件の金額フィールド\u003c/td\u003e\n\u003ctd data-label=\"v1\"\u003e\u003ccode\u003emaxAmountRequired\u003c/code\u003e\u003c/td\u003e\n\u003ctd data-label=\"v2\"\u003e\u003ccode\u003eamount\u003c/code\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"区分\"\u003eリソースの説明\u003c/td\u003e\n\u003ctd data-label=\"v1\"\u003e決済条件内の\u003ccode\u003eresource\u003c/code\u003eなど\u003c/td\u003e\n\u003ctd data-label=\"v2\"\u003e最上位の\u003ccode\u003eresource\u003c/code\u003eオブジェクト\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"区分\"\u003e決済ペイロードの条件\u003c/td\u003e\n\u003ctd data-label=\"v1\"\u003e最上位の\u003ccode\u003escheme\u003c/code\u003e、\u003ccode\u003enetwork\u003c/code\u003eなど\u003c/td\u003e\n\u003ctd data-label=\"v2\"\u003e選択した条件を格納する\u003ccode\u003eaccepted\u003c/code\u003eオブジェクト\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"区分\"\u003eTypeScriptパッケージの例\u003c/td\u003e\n\u003ctd data-label=\"v1\"\u003e\u003ccode\u003ex402-express\u003c/code\u003e\u003c/td\u003e\n\u003ctd data-label=\"v2\"\u003e\u003ccode\u003e@x402/express\u003c/code\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/tbody\u003e\n\u003c/table\u003e\u003c/div\u003e\n\u003cp\u003ev1で決済条件を渡す場所は、\u003ca href=\"https://github.com/x402-foundation/x402/blob/main/specs/transports-v1/http.md\"\u003eHTTP v1仕様\u003c/a\u003eに明記されています。v2で標準となる受け渡し場所は、\u003ccode\u003ePAYMENT-REQUIRED\u003c/code\u003eヘッダーです。実装によっては本文も返す場合がありますが、ヘッダーを基準に処理してください。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://github.com/x402-foundation/x402/blob/main/specs/transports-v2/http.md\"\u003eHTTP v2仕様\u003c/a\u003eでは、ステータスコードを次のように説明しています。\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eサーバーは、HTTP 402「Payment Required」ステータスコードを使って、決済が必要であることを示します。\u003c/p\u003e\n\u003cp\u003e出典：x402 HTTP v2仕様\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003ev2の決済ヘッダーは、JSONをBase64でエンコードした値です。Base64は暗号化方式ではありません。外部向けサービスではHTTPSを使用してください。\u003c/p\u003e\n\u003ch2\u003e\n\u003ca href=\"#http-402%E3%83%AA%E3%82%AF%E3%82%A8%E3%82%B9%E3%83%88%E3%81%A8%E6%B1%BA%E6%B8%88%E3%81%AE%E9%A0%86%E5%BA%8F\" class=\"anchor\" id=\"http-402リクエストと決済の順序\"\u003e\u003c/a\u003eHTTP 402リクエストと決済の順序\u003c/h2\u003e\n\u003cp\u003eこの実習では、EVMの\u003ccode\u003eexact\u003c/code\u003e方式の基本的な承認フローを使用します。\u003ccode\u003eexact\u003c/code\u003eは、指定された金額を決済する方式です。順序は\u003ca href=\"https://github.com/x402-foundation/x402/blob/main/specs/x402-specification-v2.md\"\u003ex402 v2の基本仕様\u003c/a\u003eに従います。\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003eクライアントが決済情報なしで有料APIをリクエストします。\u003c/li\u003e\n\u003cli\u003eサーバーがHTTP 402と\u003ccode\u003ePAYMENT-REQUIRED\u003c/code\u003eを返します。\u003c/li\u003e\n\u003cli\u003eクライアントが\u003ccode\u003eaccepts\u003c/code\u003eから許可された条件を選択します。\u003c/li\u003e\n\u003cli\u003eSDKが、選択した条件に合う決済承認に署名します。\u003c/li\u003e\n\u003cli\u003eクライアントが\u003ccode\u003ePAYMENT-SIGNATURE\u003c/code\u003eを付けて再リクエストします。\u003c/li\u003e\n\u003cli\u003eサーバーがfacilitatorの\u003ccode\u003e/verify\u003c/code\u003eで承認を検証します。\u003c/li\u003e\n\u003cli\u003e検証を通過すると、サーバーがリソースの処理を実行します。\u003c/li\u003e\n\u003cli\u003eサーバーが\u003ccode\u003e/settle\u003c/code\u003eで精算をリクエストします。\u003c/li\u003e\n\u003cli\u003e精算が成功した後、有料の本文と\u003ccode\u003ePAYMENT-RESPONSE\u003c/code\u003eを返します。\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eこの順序は、すべての決済方式に共通する固定の順序ではありません。v2には、精算のタイミングが異なるフローも定義されています。以下のコードでは、基本的な\u003ccode\u003eauthorization\u003c/code\u003eフローのみを許可します。\u003c/p\u003e\n\u003ch3\u003e\n\u003ca href=\"#resource-server%E3%81%A8facilitator%E3%81%AE%E5%BD%B9%E5%89%B2\" class=\"anchor\" id=\"resource-serverとfacilitatorの役割\"\u003e\u003c/a\u003eResource serverとfacilitatorの役割\u003c/h3\u003e\n\u003cp\u003eResource serverは、価格とコンテンツへのアクセス条件を決定します。Facilitatorは、決済の検証と精算を担当します。役割の区分は\u003ca href=\"https://github.com/x402-foundation/x402/blob/main/docs/core-concepts/facilitator.md\"\u003e公式facilitatorドキュメント\u003c/a\u003eに基づいています。\u003c/p\u003e\n\u003cdiv class=\"overflow-x-auto\"\u003e\u003ctable\u003e\n\u003cthead\u003e\n\u003ctr\u003e\n\u003cth\u003e構成要素\u003c/th\u003e\n\u003cth\u003e担当する処理\u003c/th\u003e\n\u003cth\u003e成功と見なしてはいけない状態\u003c/th\u003e\n\u003c/tr\u003e\n\u003c/thead\u003e\n\u003ctbody\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"構成要素\"\u003eクライアント\u003c/td\u003e\n\u003ctd data-label=\"担当する処理\"\u003e条件の選択と決済承認への署名\u003c/td\u003e\n\u003ctd data-label=\"成功と見なしてはいけない状態\"\u003e署名の生成だけが完了した状態\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"構成要素\"\u003eResource server\u003c/td\u003e\n\u003ctd data-label=\"担当する処理\"\u003e価格の宣言と有料レスポンスの制御\u003c/td\u003e\n\u003ctd data-label=\"成功と見なしてはいけない状態\"\u003eクライアントが成功したと主張している状態\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"構成要素\"\u003eFacilitator \u003ccode\u003e/verify\u003c/code\u003e\n\u003c/td\u003e\n\u003ctd data-label=\"担当する処理\"\u003e署名と決済条件の有効性確認\u003c/td\u003e\n\u003ctd data-label=\"成功と見なしてはいけない状態\"\u003e\n\u003ccode\u003eisValid: true\u003c/code\u003eを受け取っただけの状態\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"構成要素\"\u003eFacilitator \u003ccode\u003e/settle\u003c/code\u003e\n\u003c/td\u003e\n\u003ctd data-label=\"担当する処理\"\u003eこの実習ではオンチェーン決済の実行と結果の返却\u003c/td\u003e\n\u003ctd data-label=\"成功と見なしてはいけない状態\"\u003e送信したものの結果を確認できていない状態\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/tbody\u003e\n\u003c/table\u003e\u003c/div\u003e\n\u003cp\u003e\u003ccode\u003e/verify\u003c/code\u003eは、オンチェーンの状態を変更しない検査です。そのため、検証の成功は入金の完了を意味しません。検証後も、残高や承認状態が変わる可能性があります。\u003c/p\u003e\n\u003cp\u003e検証や精算に失敗した場合は、有料の本文をブロックしてください。成功を確認できなかったときにブロックする処理を、fail-closedと呼びます。この動作は\u003ca href=\"https://github.com/x402-foundation/x402/blob/main/typescript/packages/http/express/src/index.ts\"\u003e公式Express実装\u003c/a\u003eでも確認できます。\u003c/p\u003e\n\u003ch2\u003e\n\u003ca href=\"#base%E3%83%A1%E3%82%A4%E3%83%B3%E3%83%8D%E3%83%83%E3%83%88%E3%81%A8%E3%83%86%E3%82%B9%E3%83%88%E3%83%8D%E3%83%83%E3%83%88%E3%81%AE%E6%AF%94%E8%BC%83\" class=\"anchor\" id=\"baseメインネットとテストネットの比較\"\u003e\u003c/a\u003eBaseメインネットとテストネットの比較\u003c/h2\u003e\n\u003cp\u003e初回の実行では、Base Sepoliaだけを使用してください。Circleは、テストネットのUSDCに金銭的価値がないと明記しています。アドレスは\u003ca href=\"https://developers.circle.com/stablecoins/usdc-contract-addresses\"\u003eCircleのUSDCコントラクト一覧\u003c/a\u003eに基づいています。\u003c/p\u003e\n\u003cdiv class=\"overflow-x-auto\"\u003e\u003ctable\u003e\n\u003cthead\u003e\n\u003ctr\u003e\n\u003cth\u003e項目\u003c/th\u003e\n\u003cth\u003eBaseメインネット\u003c/th\u003e\n\u003cth\u003eBase Sepoliaテストネット\u003c/th\u003e\n\u003c/tr\u003e\n\u003c/thead\u003e\n\u003ctbody\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"項目\"\u003e用途\u003c/td\u003e\n\u003ctd data-label=\"Baseメインネット\"\u003e実際の資産による決済\u003c/td\u003e\n\u003ctd data-label=\"Base Sepoliaテストネット\"\u003e実装と障害処理のテスト\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"項目\"\u003eChain ID\u003c/td\u003e\n\u003ctd data-label=\"Baseメインネット\"\u003e\u003ccode\u003e8453\u003c/code\u003e\u003c/td\u003e\n\u003ctd data-label=\"Base Sepoliaテストネット\"\u003e\u003ccode\u003e84532\u003c/code\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"項目\"\u003ex402 v2ネットワーク\u003c/td\u003e\n\u003ctd data-label=\"Baseメインネット\"\u003e\u003ccode\u003eeip155:8453\u003c/code\u003e\u003c/td\u003e\n\u003ctd data-label=\"Base Sepoliaテストネット\"\u003e\u003ccode\u003eeip155:84532\u003c/code\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"項目\"\u003eUSDCコントラクト\u003c/td\u003e\n\u003ctd data-label=\"Baseメインネット\"\u003e\u003ccode\u003e0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913\u003c/code\u003e\u003c/td\u003e\n\u003ctd data-label=\"Base Sepoliaテストネット\"\u003e\u003ccode\u003e0x036CbD53842c5426634e7929541eC2318f3dCF7e\u003c/code\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"項目\"\u003e公開RPC\u003c/td\u003e\n\u003ctd data-label=\"Baseメインネット\"\u003e\u003ccode\u003ehttps://mainnet.base.org\u003c/code\u003e\u003c/td\u003e\n\u003ctd data-label=\"Base Sepoliaテストネット\"\u003e\u003ccode\u003ehttps://sepolia.base.org\u003c/code\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"項目\"\u003eトークンの金銭的価値\u003c/td\u003e\n\u003ctd data-label=\"Baseメインネット\"\u003e実際に金銭的価値のある資産\u003c/td\u003e\n\u003ctd data-label=\"Base Sepoliaテストネット\"\u003e金銭的価値なし\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/tbody\u003e\n\u003c/table\u003e\u003c/div\u003e\n\u003cp\u003eChain IDとRPCは、\u003ca href=\"https://docs.base.org/get-started/connect-to-base\"\u003eBaseネットワークドキュメント\u003c/a\u003eで確認してください。同じウォレットアドレスでも、チェーンが異なれば残高は別です。テストネットでの成功を、実際の売上として記録してはいけません。\u003c/p\u003e\n\u003cp\u003eテスト用USDCは、\u003ca href=\"https://faucet.circle.com/\"\u003eCircle Testnet Faucet\u003c/a\u003eでリクエストできます。ネットワークにはBase Sepoliaを選択してください。受取アドレスには、決済用テストウォレットのアドレスを入力します。\u003c/p\u003e\n\u003ch2\u003e\n\u003ca href=\"#nodejs%E3%81%AB%E3%82%88%E3%82%8B%E6%9C%89%E6%96%99api%E3%81%AE%E5%AE%9F%E8%A3%85\" class=\"anchor\" id=\"nodejsによる有料apiの実装\"\u003e\u003c/a\u003eNode.jsによる有料APIの実装\u003c/h2\u003e\n\u003cp\u003e有料ルートの前に\u003ccode\u003epaymentMiddleware\u003c/code\u003eを組み込んでください。以下のサンプルは、呼び出し1回あたり\u003cstrong\u003e0.001 USDC\u003c/strong\u003eを要求します。金額は、公式販売者ガイドの価格例を使用しています。（確認値：呼び出し1回あたり0.001 USDC・出典：docs.x402.org・2026-10-28確認）\u003c/p\u003e\n\u003ch3\u003e\n\u003ca href=\"#%E3%82%B5%E3%83%BC%E3%83%90%E3%83%BC%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB\" class=\"anchor\" id=\"サーバーファイル\"\u003e\u003c/a\u003eサーバーファイル\u003c/h3\u003e\n\u003cp\u003e次のコードを\u003ccode\u003eserver.ts\u003c/code\u003eとして保存してください。サーバーは、ローカルインターフェースでのみリクエストを受け付けます。受取アドレスは、環境変数\u003ccode\u003ePAY_TO\u003c/code\u003eから読み込みます。\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e\u003cspan\u003eimport express from \"express\";\n\u003c/span\u003e\u003cspan\u003eimport { getAddress, zeroAddress } from \"viem\";\n\u003c/span\u003e\u003cspan\u003eimport { paymentMiddleware } from \"@x402/express\";\n\u003c/span\u003e\u003cspan\u003eimport { HTTPFacilitatorClient, x402ResourceServer } from \"@x402/core/server\";\n\u003c/span\u003e\u003cspan\u003eimport { ExactEvmScheme } from \"@x402/evm/exact/server\";\n\u003c/span\u003e\u003cspan\u003e\n\u003c/span\u003e\u003cspan\u003econst payTo = getAddress(process.env.PAY_TO ?? \"\");\n\u003c/span\u003e\u003cspan\u003eif (payTo === zeroAddress) throw new Error(\"Invalid payTo\");\n\u003c/span\u003e\u003cspan\u003econst network = \"eip155:84532\";\n\u003c/span\u003e\u003cspan\u003econst resourceServer = new x402ResourceServer(\n\u003c/span\u003e\u003cspan\u003e  new HTTPFacilitatorClient({ url: \"https://x402.org/facilitator\" }),\n\u003c/span\u003e\u003cspan\u003e).register(network, new ExactEvmScheme());\n\u003c/span\u003e\u003cspan\u003e\n\u003c/span\u003e\u003cspan\u003econst app = express();\n\u003c/span\u003e\u003cspan\u003eapp.use((_req, res, next) =\u0026gt; {\n\u003c/span\u003e\u003cspan\u003e  res.setHeader(\"Cache-Control\", \"private, no-store\");\n\u003c/span\u003e\u003cspan\u003e  next();\n\u003c/span\u003e\u003cspan\u003e});\n\u003c/span\u003e\u003cspan\u003eapp.use(paymentMiddleware({\n\u003c/span\u003e\u003cspan\u003e  \"GET /paid-data\": {\n\u003c/span\u003e\u003cspan\u003e    accepts: [{ scheme: \"exact\", price: \"$0.001\", network, payTo }],\n\u003c/span\u003e\u003cspan\u003e    description: \"Sample paid JSON\",\n\u003c/span\u003e\u003cspan\u003e    mimeType: \"application/json\",\n\u003c/span\u003e\u003cspan\u003e  },\n\u003c/span\u003e\u003cspan\u003e}, resourceServer));\n\u003c/span\u003e\u003cspan\u003eapp.get(\"/paid-data\", (_req, res) =\u0026gt; {\n\u003c/span\u003e\u003cspan\u003e  res.json({ content: \"Paid sample content\" });\n\u003c/span\u003e\u003cspan\u003e});\n\u003c/span\u003e\u003cspan\u003eapp.listen(4021, \"127.0.0.1\");\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e\u003ccode\u003ehttps://x402.org/facilitator\u003c/code\u003eは、公式ガイドに掲載されているテスト用のアドレスです。このサンプルでは、Base Sepoliaでの精算に使用します。本番用facilitatorの選択については、\u003ca href=\"https://docs.x402.org/getting-started/quickstart-for-sellers#running-on-mainnet\"\u003e販売者ガイドのメインネット手順\u003c/a\u003eを確認してください。\u003c/p\u003e\n\u003ch3\u003e\n\u003ca href=\"#%E4%BE%A1%E6%A0%BC%E8%A8%88%E7%AE%97%E3%81%AE%E4%BE%8B\" class=\"anchor\" id=\"価格計算の例\"\u003e\u003c/a\u003e価格計算の例\u003c/h3\u003e\n\u003cp\u003eサンプルの価格は、USDCの最小単位で\u003ccode\u003e1000\u003c/code\u003eです。USDCの小数点以下の桁数は6桁です。SDKによる価格変換は、\u003ca href=\"https://github.com/x402-foundation/x402/blob/main/typescript/packages/mechanisms/evm/src/exact/server/scheme.ts\"\u003eEVMサーバー実装\u003c/a\u003eに基づいています。\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e\u003cspan\u003e0.001 USDC × 10^6 = 1000 최소 단위\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003cdiv class=\"overflow-x-auto\"\u003e\u003ctable\u003e\n\u003cthead\u003e\n\u003ctr\u003e\n\u003cth\u003e確認する値\u003c/th\u003e\n\u003cth\u003eこのサンプルでの期待値\u003c/th\u003e\n\u003c/tr\u003e\n\u003c/thead\u003e\n\u003ctbody\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"確認する値\"\u003eサーバー設定の\u003ccode\u003eprice\u003c/code\u003e\n\u003c/td\u003e\n\u003ctd data-label=\"このサンプルでの期待値\"\u003e\u003ccode\u003e\"$0.001\"\u003c/code\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"確認する値\"\u003e402決済条件の\u003ccode\u003eamount\u003c/code\u003e\n\u003c/td\u003e\n\u003ctd data-label=\"このサンプルでの期待値\"\u003e\u003ccode\u003e\"1000\"\u003c/code\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"確認する値\"\u003e決済条件の\u003ccode\u003easset\u003c/code\u003e\n\u003c/td\u003e\n\u003ctd data-label=\"このサンプルでの期待値\"\u003eBase SepoliaのUSDCコントラクト\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"確認する値\"\u003e決済条件の\u003ccode\u003epayTo\u003c/code\u003e\n\u003c/td\u003e\n\u003ctd data-label=\"このサンプルでの期待値\"\u003e設定した受取アドレス\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"確認する値\"\u003e決済条件の\u003ccode\u003enetwork\u003c/code\u003e\n\u003c/td\u003e\n\u003ctd data-label=\"このサンプルでの期待値\"\u003e\u003ccode\u003eeip155:84532\u003c/code\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/tbody\u003e\n\u003c/table\u003e\u003c/div\u003e\n\u003cp\u003e\u003ccode\u003eprice\u003c/code\u003eはSDKの設定値です。実際のv2決済条件では、\u003ccode\u003eamount\u003c/code\u003eが渡されます。整数の金額は、文字列または\u003ccode\u003ebigint\u003c/code\u003eとして比較してください。\u003c/p\u003e\n\u003ch3\u003e\n\u003ca href=\"#%E3%82%B5%E3%83%BC%E3%83%90%E3%83%BC%E3%81%AE%E8%B5%B7%E5%8B%95%E3%81%A8402%E3%81%AE%E7%A2%BA%E8%AA%8D\" class=\"anchor\" id=\"サーバーの起動と402の確認\"\u003e\u003c/a\u003eサーバーの起動と402の確認\u003c/h3\u003e\n\u003cp\u003e1つ目のBashターミナルでサーバーを起動してください。入力する値は、秘密鍵ではなく公開の受取アドレスです。実行中は、このターミナルを開いたままにします。\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e\u003cspan\u003eread \u003c/span\u003e\u003cspan\u003e-r -p \u003c/span\u003e\u003cspan\u003e'\u003c/span\u003e\u003cspan\u003e수취 지갑 주소: \u003c/span\u003e\u003cspan\u003e' PAY_TO\n\u003c/span\u003e\u003cspan\u003eexport \u003c/span\u003e\u003cspan\u003ePAY_TO\n\u003c/span\u003e\u003cspan\u003enpx\u003c/span\u003e\u003cspan\u003e tsx server.ts\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e2つ目のターミナルから、決済なしのリクエストを送信してください。以下のコマンドは、決済に署名しません。HTTPステータスと、デコードした決済条件を出力します。\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e\u003cspan\u003enode\u003c/span\u003e\u003cspan\u003e --input-type\u003c/span\u003e\u003cspan\u003e=module \u0026lt;\u0026lt;'\u003c/span\u003e\u003cspan\u003eJS\u003c/span\u003e\u003cspan\u003e'\n\u003c/span\u003e\u003cspan\u003econst response = await fetch(\"http://127.0.0.1:4021/paid-data\");\n\u003c/span\u003e\u003cspan\u003econst header = response.headers.get(\"PAYMENT-REQUIRED\");\n\u003c/span\u003e\u003cspan\u003econsole.log(response.status);\n\u003c/span\u003e\u003cspan\u003econsole.log(header \u0026amp;\u0026amp; JSON.parse(\n\u003c/span\u003e\u003cspan\u003e  Buffer.from(header, \"base64\").toString(\"utf8\")\n\u003c/span\u003e\u003cspan\u003e));\n\u003c/span\u003e\u003cspan\u003eJS\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e正常な決済なしのレスポンスは、HTTP 402です。出力された条件を、上の表と照合してください。有料コンテンツがそのまま返される場合は、ルートの保護設定から修正します。\u003c/p\u003e\n\u003ch2\u003e\n\u003ca href=\"#%E6%B1%BA%E6%B8%88%E6%9D%A1%E4%BB%B6%E3%81%AE%E9%81%B8%E6%8A%9E%E3%81%A8%E3%82%AF%E3%83%A9%E3%82%A4%E3%82%A2%E3%83%B3%E3%83%88%E3%81%AE%E5%AE%9F%E8%A3%85\" class=\"anchor\" id=\"決済条件の選択とクライアントの実装\"\u003e\u003c/a\u003e決済条件の選択とクライアントの実装\u003c/h2\u003e\n\u003cp\u003e許可した条件にのみ署名するように、クライアントを制限してください。このサンプルでは、Base SepoliaのUSDCによる指定価格の決済だけを許可します。決済オプションのフィルターには、\u003ca href=\"https://github.com/x402-foundation/x402/blob/main/typescript/packages/core/src/client/x402Client.ts\"\u003e公式x402Client実装\u003c/a\u003eの\u003ccode\u003eregisterPolicy\u003c/code\u003eを使用します。\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e以下のコードを\u003ccode\u003eclient.ts\u003c/code\u003eとして保存してください。\u003c/li\u003e\n\u003cli\u003e次の節の\u003ccode\u003echeck-transfer.ts\u003c/code\u003eも保存してください。\u003c/li\u003e\n\u003cli\u003e2つのファイルを用意してから、実行手順に進んでください。\u003c/li\u003e\n\u003c/ol\u003e\n\u003cpre\u003e\u003ccode\u003e\u003cspan\u003eimport { writeFile } from \"node:fs/promises\";\n\u003c/span\u003e\u003cspan\u003eimport { getAddress, zeroAddress, type Hex, type Hash } from \"viem\";\n\u003c/span\u003e\u003cspan\u003eimport { privateKeyToAccount } from \"viem/accounts\";\n\u003c/span\u003e\u003cspan\u003eimport { x402Client, wrapFetchWithPayment } from \"@x402/fetch\";\n\u003c/span\u003e\u003cspan\u003eimport { decodePaymentResponseHeader } from \"@x402/core/http\";\n\u003c/span\u003e\u003cspan\u003eimport { ExactEvmScheme } from \"@x402/evm/exact/client\";\n\u003c/span\u003e\u003cspan\u003eimport { checkTransfer } from \"./check-transfer.js\";\n\u003c/span\u003e\u003cspan\u003e\n\u003c/span\u003e\u003cspan\u003econst key = process.env.EVM_PRIVATE_KEY ?? \"\";\n\u003c/span\u003e\u003cspan\u003eif (!/^0x[0-9a-fA-F]{64}$/.test(key)) throw new Error(\"Invalid test key\");\n\u003c/span\u003e\u003cspan\u003econst signer = privateKeyToAccount(key as Hex);\n\u003c/span\u003e\u003cspan\u003econst payTo = getAddress(process.env.PAY_TO ?? \"\");\n\u003c/span\u003e\u003cspan\u003eif (payTo === zeroAddress) throw new Error(\"Invalid payTo\");\n\u003c/span\u003e\u003cspan\u003econst network = \"eip155:84532\";\n\u003c/span\u003e\u003cspan\u003econst token = \"0x036CbD53842c5426634e7929541eC2318f3dCF7e\";\n\u003c/span\u003e\u003cspan\u003econst client = new x402Client().register(network, new ExactEvmScheme(signer));\n\u003c/span\u003e\u003cspan\u003eclient.registerPolicy((version, options) =\u0026gt; version === 2\n\u003c/span\u003e\u003cspan\u003e  ? options.filter(r =\u0026gt;\n\u003c/span\u003e\u003cspan\u003e      r.scheme === \"exact\" \u0026amp;\u0026amp; r.network === network \u0026amp;\u0026amp;\n\u003c/span\u003e\u003cspan\u003e      r.asset.toLowerCase() === token.toLowerCase() \u0026amp;\u0026amp;\n\u003c/span\u003e\u003cspan\u003e      r.payTo.toLowerCase() === payTo.toLowerCase() \u0026amp;\u0026amp; r.amount === \"1000\" \u0026amp;\u0026amp;\n\u003c/span\u003e\u003cspan\u003e      (!r.extra?.assetTransferMethod || r.extra.assetTransferMethod === \"eip3009\") \u0026amp;\u0026amp;\n\u003c/span\u003e\u003cspan\u003e      (!r.extra?.paymentFlow || r.extra.paymentFlow === \"authorization\"))\n\u003c/span\u003e\u003cspan\u003e  : []);\n\u003c/span\u003e\u003cspan\u003e\n\u003c/span\u003e\u003cspan\u003econst paidFetch = wrapFetchWithPayment(fetch, client);\n\u003c/span\u003e\u003cspan\u003econst response = await paidFetch(\"http://127.0.0.1:4021/paid-data\", {\n\u003c/span\u003e\u003cspan\u003e  redirect: \"error\",\n\u003c/span\u003e\u003cspan\u003e});\n\u003c/span\u003e\u003cspan\u003econst raw = response.headers.get(\"PAYMENT-RESPONSE\");\n\u003c/span\u003e\u003cspan\u003eif (!raw) throw new Error(`No settlement receipt: HTTP ${response.status}`);\n\u003c/span\u003e\u003cspan\u003econst settlement = decodePaymentResponseHeader(raw);\n\u003c/span\u003e\u003cspan\u003eawait writeFile(\"settlement.json\", JSON.stringify(settlement, null, 2));\n\u003c/span\u003e\u003cspan\u003eif (!response.ok || !settlement.success || settlement.network !== network) {\n\u003c/span\u003e\u003cspan\u003e  throw new Error(\"Payment unresolved; inspect settlement.json before retrying\");\n\u003c/span\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003cspan\u003econst evidence = await checkTransfer(\n\u003c/span\u003e\u003cspan\u003e  network, settlement.transaction as Hash, signer.address, payTo, 1000n,\n\u003c/span\u003e\u003cspan\u003e);\n\u003c/span\u003e\u003cspan\u003eawait writeFile(\"transfer.json\", JSON.stringify(evidence, null, 2));\n\u003c/span\u003e\u003cspan\u003econsole.log(await response.text());\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e\u003ccode\u003ewrapFetchWithPayment\u003c/code\u003eが、402の処理と署名付きの再リクエストを担当します。使い方は\u003ca href=\"https://github.com/x402-foundation/x402/tree/main/examples/typescript/clients/fetch\"\u003e公式fetchサンプル\u003c/a\u003eに従います。許可された決済条件がなければ、決済の生成を中止します。\u003c/p\u003e\n\u003cp\u003eAIエージェントでも、このクライアントを使用できます。ただし、呼び出し1回あたりの価格制限は、累積支出の制限ではありません。エージェントによる繰り返し呼び出しの予算は、別途管理してください。\u003c/p\u003e\n\u003ch2\u003e\n\u003ca href=\"#usdc-transfer%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88%E3%81%AE%E6%A4%9C%E8%A8%BC\" class=\"anchor\" id=\"usdc-transferイベントの検証\"\u003e\u003c/a\u003eUSDC Transferイベントの検証\u003c/h2\u003e\n\u003cp\u003e精算結果のトランザクションハッシュを使って、USDCの送金ログを確認してください。トランザクションの成功状態だけでは、受取人まで確認できません。\u003ccode\u003eTransfer\u003c/code\u003eのフィールドは、\u003ca href=\"https://eips.ethereum.org/EIPS/eip-20\"\u003eERC-20標準\u003c/a\u003eに基づいています。\u003c/p\u003e\n\u003cp\u003e検証には、次の項目が必要です。\u003c/p\u003e\n\u003cdiv class=\"overflow-x-auto\"\u003e\u003ctable\u003e\n\u003cthead\u003e\n\u003ctr\u003e\n\u003cth\u003e検証項目\u003c/th\u003e\n\u003cth\u003e比較対象\u003c/th\u003e\n\u003c/tr\u003e\n\u003c/thead\u003e\n\u003ctbody\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"検証項目\"\u003eRPCのChain ID\u003c/td\u003e\n\u003ctd data-label=\"比較対象\"\u003eサーバーが許可したチェーン\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"検証項目\"\u003eトランザクションレシートの\u003ccode\u003estatus\u003c/code\u003e\n\u003c/td\u003e\n\u003ctd data-label=\"比較対象\"\u003e成功状態\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"検証項目\"\u003eイベントを発生させたアドレス\u003c/td\u003e\n\u003ctd data-label=\"比較対象\"\u003eそのチェーンの公式USDCコントラクト\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"検証項目\"\u003e\u003ccode\u003eTransfer.from\u003c/code\u003e\u003c/td\u003e\n\u003ctd data-label=\"比較対象\"\u003e確認済みの支払者\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"検証項目\"\u003e\u003ccode\u003eTransfer.to\u003c/code\u003e\u003c/td\u003e\n\u003ctd data-label=\"比較対象\"\u003eサーバーが指定した受取アドレス\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"検証項目\"\u003e\u003ccode\u003eTransfer.value\u003c/code\u003e\u003c/td\u003e\n\u003ctd data-label=\"比較対象\"\u003eサーバーが要求した最小単位での金額\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/tbody\u003e\n\u003c/table\u003e\u003c/div\u003e\n\u003cp\u003e以下のコードを\u003ccode\u003echeck-transfer.ts\u003c/code\u003eとして保存してください。照会には、viemの\u003ca href=\"https://viem.sh/docs/actions/public/getTransactionReceipt\"\u003egetTransactionReceipt\u003c/a\u003eを使用します。イベントの解釈は、\u003ca href=\"https://viem.sh/docs/contract/parseEventLogs\"\u003eparseEventLogs\u003c/a\u003eに基づいています。\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e\u003cspan\u003eimport {\n\u003c/span\u003e\u003cspan\u003e  createPublicClient, http, getAddress, parseEventLogs,\n\u003c/span\u003e\u003cspan\u003e  erc20Abi, type Address, type Hash,\n\u003c/span\u003e\u003cspan\u003e} from \"viem\";\n\u003c/span\u003e\u003cspan\u003eimport { base, baseSepolia } from \"viem/chains\";\n\u003c/span\u003e\u003cspan\u003e\n\u003c/span\u003e\u003cspan\u003econst configs = {\n\u003c/span\u003e\u003cspan\u003e  \"eip155:84532\": {\n\u003c/span\u003e\u003cspan\u003e    chain: baseSepolia,\n\u003c/span\u003e\u003cspan\u003e    rpc: \"https://sepolia.base.org\",\n\u003c/span\u003e\u003cspan\u003e    token: \"0x036CbD53842c5426634e7929541eC2318f3dCF7e\",\n\u003c/span\u003e\u003cspan\u003e  },\n\u003c/span\u003e\u003cspan\u003e  \"eip155:8453\": {\n\u003c/span\u003e\u003cspan\u003e    chain: base,\n\u003c/span\u003e\u003cspan\u003e    rpc: \"https://mainnet.base.org\",\n\u003c/span\u003e\u003cspan\u003e    token: \"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913\",\n\u003c/span\u003e\u003cspan\u003e  },\n\u003c/span\u003e\u003cspan\u003e} as const;\n\u003c/span\u003e\u003cspan\u003e\n\u003c/span\u003e\u003cspan\u003eexport async function checkTransfer(\n\u003c/span\u003e\u003cspan\u003e  network: keyof typeof configs, hash: Hash,\n\u003c/span\u003e\u003cspan\u003e  payer: Address, payTo: Address, amount: bigint,\n\u003c/span\u003e\u003cspan\u003e) {\n\u003c/span\u003e\u003cspan\u003e  const cfg = configs[network];\n\u003c/span\u003e\u003cspan\u003e  const rpc = createPublicClient({ chain: cfg.chain, transport: http(cfg.rpc) });\n\u003c/span\u003e\u003cspan\u003e  if (await rpc.getChainId() !== cfg.chain.id) throw new Error(\"Wrong chain\");\n\u003c/span\u003e\u003cspan\u003e  const receipt = await rpc.getTransactionReceipt({ hash });\n\u003c/span\u003e\u003cspan\u003e  if (receipt.status !== \"success\") throw new Error(\"Transaction failed\");\n\u003c/span\u003e\u003cspan\u003e  const logs = parseEventLogs({\n\u003c/span\u003e\u003cspan\u003e    abi: erc20Abi,\n\u003c/span\u003e\u003cspan\u003e    eventName: \"Transfer\",\n\u003c/span\u003e\u003cspan\u003e    logs: receipt.logs.filter(\n\u003c/span\u003e\u003cspan\u003e      log =\u0026gt; getAddress(log.address) === getAddress(cfg.token),\n\u003c/span\u003e\u003cspan\u003e    ),\n\u003c/span\u003e\u003cspan\u003e    strict: true,\n\u003c/span\u003e\u003cspan\u003e  });\n\u003c/span\u003e\u003cspan\u003e  const transfer = logs.find(log =\u0026gt;\n\u003c/span\u003e\u003cspan\u003e    getAddress(log.args.from) === getAddress(payer) \u0026amp;\u0026amp;\n\u003c/span\u003e\u003cspan\u003e    getAddress(log.args.to) === getAddress(payTo) \u0026amp;\u0026amp;\n\u003c/span\u003e\u003cspan\u003e    log.args.value === amount,\n\u003c/span\u003e\u003cspan\u003e  );\n\u003c/span\u003e\u003cspan\u003e  if (!transfer) throw new Error(\"Matching USDC Transfer not found\");\n\u003c/span\u003e\u003cspan\u003e  return {\n\u003c/span\u003e\u003cspan\u003e    network, transaction: hash, logIndex: transfer.logIndex,\n\u003c/span\u003e\u003cspan\u003e    blockHash: receipt.blockHash, blockNumber: receipt.blockNumber.toString(),\n\u003c/span\u003e\u003cspan\u003e  };\n\u003c/span\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eこの関数は、そのトランザクションでUSDCが送金された事実を確認します。特定のAPI注文に対応するものかどうかは、別途確認する必要があります。本番サーバーでは、保存済みの注文条件を比較値として使用してください。\u003c/p\u003e\n\u003cp\u003eクライアントがアップロードした\u003ccode\u003etransfer.json\u003c/code\u003eを、アクセス許可の証拠として信用してはいけません。このサンプルのサーバーは、facilitatorの検証・精算結果を使用します。独立した照会をアクセス許可の条件にする場合は、サーバー側で実行する必要があります。\u003c/p\u003e\n\u003cp\u003eレシートの照会に失敗しても、それだけで決済の失敗を意味するわけではありません。RPCエラーであれば、保存済みのトランザクションハッシュを再照会してください。追加の承認数とチェーン再編成への対応は、運用ポリシーで定めます。\u003c/p\u003e\n\u003ch2\u003e\n\u003ca href=\"#%E3%83%86%E3%82%B9%E3%83%88%E6%B1%BA%E6%B8%88%E3%81%AE%E5%AE%9F%E8%A1%8C%E3%81%A8%E7%B5%90%E6%9E%9C%E7%A2%BA%E8%AA%8D\" class=\"anchor\" id=\"テスト決済の実行と結果確認\"\u003e\u003c/a\u003eテスト決済の実行と結果確認\u003c/h2\u003e\n\u003cp\u003eすべてのファイルを保存してから、テスト用ウォレットで実行してください。型チェックでは、署名や送金は実行されません。以下のコマンドでは、先ほどインストールしたTypeScriptツールを使用します。\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e次のコマンドで型をチェックしてください。\u003c/li\u003e\n\u003cli\u003e2つ目のターミナルに、サーバーと同じ受取アドレスを入力してください。\u003c/li\u003e\n\u003cli\u003eテスト決済用ウォレットの秘密鍵を、非表示で入力してください。\u003c/li\u003e\n\u003cli\u003eクライアントを1回実行してください。\u003c/li\u003e\n\u003cli\u003e保存された精算結果と送金ログを確認してください。\u003c/li\u003e\n\u003c/ol\u003e\n\u003cpre\u003e\u003ccode\u003e\u003cspan\u003enpx\u003c/span\u003e\u003cspan\u003e tsc\u003c/span\u003e\u003cspan\u003e --noEmit --strict --skipLibCheck --target\u003c/span\u003e\u003cspan\u003e ES2022\u003c/span\u003e\u003cspan\u003e --module\u003c/span\u003e\u003cspan\u003e NodeNext\u003c/span\u003e\u003cspan\u003e --moduleResolution\u003c/span\u003e\u003cspan\u003e NodeNext server.ts client.ts check-transfer.ts\n\u003c/span\u003e\u003cspan\u003eread \u003c/span\u003e\u003cspan\u003e-r -p \u003c/span\u003e\u003cspan\u003e'\u003c/span\u003e\u003cspan\u003e같은 수취 지갑 주소: \u003c/span\u003e\u003cspan\u003e' PAY_TO\n\u003c/span\u003e\u003cspan\u003eread \u003c/span\u003e\u003cspan\u003e-r -s -p \u003c/span\u003e\u003cspan\u003e'\u003c/span\u003e\u003cspan\u003e테스트 결제 지갑 개인키: \u003c/span\u003e\u003cspan\u003e' EVM_PRIVATE_KEY\n\u003c/span\u003e\u003cspan\u003eexport \u003c/span\u003e\u003cspan\u003ePAY_TO EVM_PRIVATE_KEY\n\u003c/span\u003e\u003cspan\u003enpx\u003c/span\u003e\u003cspan\u003e tsx client.ts\n\u003c/span\u003e\u003cspan\u003eunset\u003c/span\u003e\u003cspan\u003e EVM_PRIVATE_KEY\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003cdiv class=\"overflow-x-auto\"\u003e\u003ctable\u003e\n\u003cthead\u003e\n\u003ctr\u003e\n\u003cth\u003e出力物\u003c/th\u003e\n\u003cth\u003e意味\u003c/th\u003e\n\u003cth\u003e確認する内容\u003c/th\u003e\n\u003c/tr\u003e\n\u003c/thead\u003e\n\u003ctbody\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"出力物\"\u003e\u003ccode\u003esettlement.json\u003c/code\u003e\u003c/td\u003e\n\u003ctd data-label=\"意味\"\u003eサーバーが返した精算結果\u003c/td\u003e\n\u003ctd data-label=\"確認する内容\"\u003e\n\u003ccode\u003esuccess\u003c/code\u003e、\u003ccode\u003enetwork\u003c/code\u003e、\u003ccode\u003etransaction\u003c/code\u003e\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"出力物\"\u003e\u003ccode\u003etransfer.json\u003c/code\u003e\u003c/td\u003e\n\u003ctd data-label=\"意味\"\u003eRPC照会で見つかった送金記録\u003c/td\u003e\n\u003ctd data-label=\"確認する内容\"\u003eチェーン、トランザクションハッシュ、ログ位置、ブロック情報\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"出力物\"\u003eコンソールの有料本文\u003c/td\u003e\n\u003ctd data-label=\"意味\"\u003eこのクライアントでの検証後に出力したデータ\u003c/td\u003e\n\u003ctd data-label=\"確認する内容\"\u003eリクエストしたコンテンツかどうかの確認\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/tbody\u003e\n\u003c/table\u003e\u003c/div\u003e\n\u003cp\u003eクライアントを新たに実行すると、新しい決済が発生する場合があります。この最小限のサンプルには、注文ごとの重複を防ぐストアはありません。再試行機能は、次の節の条件を満たしてから追加してください。\u003c/p\u003e\n\u003ch2\u003e\n\u003ca href=\"#%E6%A4%9C%E8%A8%BC%E5%A4%B1%E6%95%97%E3%81%AE%E6%9D%A1%E4%BB%B6%E5%88%A5%E3%81%AE%E5%87%A6%E7%90%86\" class=\"anchor\" id=\"検証失敗の条件別の処理\"\u003e\u003c/a\u003e検証失敗の条件別の処理\u003c/h2\u003e\n\u003cp\u003e失敗テストでは、有料の本文が露出しないことを確認してください。HTTPステータスだけをチェックすると、ブロック漏れを見逃す可能性があります。基準となる動作は、\u003ca href=\"https://github.com/x402-foundation/x402/blob/main/typescript/packages/http/express/src/index.ts\"\u003e公式Expressミドルウェア\u003c/a\u003eを参照します。\u003c/p\u003e\n\u003cdiv class=\"overflow-x-auto\"\u003e\u003ctable\u003e\n\u003cthead\u003e\n\u003ctr\u003e\n\u003cth\u003eテスト条件\u003c/th\u003e\n\u003cth\u003e期待する処理\u003c/th\u003e\n\u003cth\u003e追加確認\u003c/th\u003e\n\u003c/tr\u003e\n\u003c/thead\u003e\n\u003ctbody\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"テスト条件\"\u003e決済ヘッダーなし\u003c/td\u003e\n\u003ctd data-label=\"期待する処理\"\u003e402と決済条件を返却\u003c/td\u003e\n\u003ctd data-label=\"追加確認\"\u003e有料本文なし\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"テスト条件\"\u003e別のトークン・受取人の提示\u003c/td\u003e\n\u003ctd data-label=\"期待する処理\"\u003eクライアントポリシーで拒否\u003c/td\u003e\n\u003ctd data-label=\"追加確認\"\u003e署名を生成しない\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"テスト条件\"\u003e不正な署名\u003c/td\u003e\n\u003ctd data-label=\"期待する処理\"\u003e検証失敗としてブロック\u003c/td\u003e\n\u003ctd data-label=\"追加確認\"\u003e有料ハンドラーを実行しない\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"テスト条件\"\u003e残高不足\u003c/td\u003e\n\u003ctd data-label=\"期待する処理\"\u003e検証または精算の失敗としてブロック\u003c/td\u003e\n\u003ctd data-label=\"追加確認\"\u003e精算成功として記録しない\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"テスト条件\"\u003e精算結果が失敗\u003c/td\u003e\n\u003ctd data-label=\"期待する処理\"\u003e有料レスポンスをブロック\u003c/td\u003e\n\u003ctd data-label=\"追加確認\"\u003e準備済みの本文も送信しない\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"テスト条件\"\u003efacilitatorの応答遅延\u003c/td\u003e\n\u003ctd data-label=\"期待する処理\"\u003e成功未確認の状態で保留\u003c/td\u003e\n\u003ctd data-label=\"追加確認\"\u003e新しい承認の自動生成を禁止\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"テスト条件\"\u003eRPCでのレシート照会エラー\u003c/td\u003e\n\u003ctd data-label=\"期待する処理\"\u003eトランザクションハッシュを保持して再照会\u003c/td\u003e\n\u003ctd data-label=\"追加確認\"\u003e決済失敗と断定しない\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/tbody\u003e\n\u003c/table\u003e\u003c/div\u003e\n\u003cp\u003e基本的な承認フローでは、精算前にハンドラーが実行されます。精算に失敗しても、外部での処理まで元に戻るわけではありません。このサンプルのように、副作用のない照会から適用してください。\u003c/p\u003e\n\u003cp\u003eメールの送信や外部での注文作成には、別途設計が必要です。決済と処理の状態を一緒に記録してください。失敗時の再実行範囲を決めることで、処理の重複を減らせます。\u003c/p\u003e\n\u003ch2\u003e\n\u003ca href=\"#%E7%B2%BE%E7%AE%97%E5%BE%8C%E3%81%AE%E3%83%AC%E3%82%B9%E3%83%9D%E3%83%B3%E3%82%B9%E6%B6%88%E5%A4%B1%E3%81%A8%E5%86%8D%E9%80%81%E3%81%AB%E3%82%88%E3%82%8B%E5%BE%A9%E6%97%A7\" class=\"anchor\" id=\"精算後のレスポンス消失と再送による復旧\"\u003e\u003c/a\u003e精算後のレスポンス消失と再送による復旧\u003c/h2\u003e\n\u003cp\u003e決済の完了とコンテンツ配信の完了を、別々の状態として保存してください。決済が完了していても、レスポンスの接続が切れる場合があります。このような状況への対応の根拠となるのは、\u003ca href=\"https://docs.x402.org/extensions/payment-identifier\"\u003ePayment-Identifier拡張ドキュメント\u003c/a\u003eです。\u003c/p\u003e\n\u003cp\u003e同じ処理を再リクエストしても重複処理しない性質を、冪等性と呼びます。\u003ccode\u003epayment-identifier\u003c/code\u003eは、そのための決済識別子を提供します。識別子を追加するだけで、自動的に完成する機能ではありません。\u003c/p\u003e\n\u003ch3\u003e\n\u003ca href=\"#%E9%87%8D%E8%A4%87%E6%B1%BA%E6%B8%88%E3%81%A8%E5%86%8D%E9%80%81%E3%81%AE%E6%AF%94%E8%BC%83\" class=\"anchor\" id=\"重複決済と再送の比較\"\u003e\u003c/a\u003e重複決済と再送の比較\u003c/h3\u003e\n\u003cp\u003e同じ署名の再利用防止と、注文の重複防止は区別する必要があります。EIP-3009は、使用済みの承認nonceの再利用を防ぎます。この動作は、\u003ca href=\"https://eips.ethereum.org/EIPS/eip-3009\"\u003eERC-3009仕様\u003c/a\u003eに定義されています。\u003c/p\u003e\n\u003cdiv class=\"overflow-x-auto\"\u003e\u003ctable\u003e\n\u003cthead\u003e\n\u003ctr\u003e\n\u003cth\u003e状況\u003c/th\u003e\n\u003cth\u003eオンチェーン承認の観点\u003c/th\u003e\n\u003cth\u003eAPI運用の観点\u003c/th\u003e\n\u003c/tr\u003e\n\u003c/thead\u003e\n\u003ctbody\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"状況\"\u003e同じ承認を再び精算\u003c/td\u003e\n\u003ctd data-label=\"オンチェーン承認の観点\"\u003e使用済みnonceのチェック対象\u003c/td\u003e\n\u003ctd data-label=\"API運用の観点\"\u003e既存結果の照会が必要\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"状況\"\u003e同じ注文に新しい承認を生成\u003c/td\u003e\n\u003ctd data-label=\"オンチェーン承認の観点\"\u003e別の承認として処理される可能性\u003c/td\u003e\n\u003ctd data-label=\"API運用の観点\"\u003e重複決済の防止が必要\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"状況\"\u003e精算成功後にレスポンスが消失\u003c/td\u003e\n\u003ctd data-label=\"オンチェーン承認の観点\"\u003e資産の移動は完了している可能性\u003c/td\u003e\n\u003ctd data-label=\"API運用の観点\"\u003eコンテンツの再配信が必要\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"状況\"\u003e精算リクエスト後にタイムアウト\u003c/td\u003e\n\u003ctd data-label=\"オンチェーン承認の観点\"\u003e結果がまだ不明確\u003c/td\u003e\n\u003ctd data-label=\"API運用の観点\"\u003e保留状態と精算確認が必要\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/tbody\u003e\n\u003c/table\u003e\u003c/div\u003e\n\u003cp\u003e次の手順は、公式拡張を運用用ストアに接続する設計案です。前述の最小限のコードには含まれていません。本番運用に適用する場合は、永続ストアと同時リクエストの制御を追加してください。\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003eサーバー側で\u003ccode\u003epayment-identifier\u003c/code\u003e拡張への対応を宣言してください。\u003c/li\u003e\n\u003cli\u003eクライアント側で論理的なリクエストの識別子を保持してください。\u003c/li\u003e\n\u003cli\u003eサーバー側で識別子を支払者とリクエスト内容に関連付けてください。\u003c/li\u003e\n\u003cli\u003e処理中の状態をアトミックに記録し、同時実行を防いでください。\u003c/li\u003e\n\u003cli\u003e精算成功時に、トランザクションの証拠とレスポンス結果を保存してください。\u003c/li\u003e\n\u003cli\u003e再リクエストの権限と内容が同じであれば、保存済みの結果を返してください。\u003c/li\u003e\n\u003cli\u003e精算結果が不明確な場合は、既存の承認から確認してください。\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003e決済識別子は、パスワードやアクセストークンではありません。識別子を知っているだけのリクエストに、有料キャッシュを返してはいけません。別のユーザーや異なるリクエスト本文に再利用されていないかもチェックしてください。\u003c/p\u003e\n\u003cp\u003e運用記録には、チェーン・トランザクションハッシュ・ログ位置を一緒に残してください。この記録を注文に関連付けて、別の注文での再利用をブロックします。\u003ccode\u003eTransfer\u003c/code\u003eの一致だけでは、注文との対応関係までは証明できません。\u003c/p\u003e\n\u003ch2\u003e\n\u003ca href=\"#%E6%B1%BA%E6%B8%88%E5%AE%9F%E8%A3%85%E3%81%A7%E3%82%88%E3%81%8F%E3%81%82%E3%82%8B%E9%96%93%E9%81%95%E3%81%84\" class=\"anchor\" id=\"決済実装でよくある間違い\"\u003e\u003c/a\u003e決済実装でよくある間違い\u003c/h2\u003e\n\u003cp\u003eバージョンフィールドと成功を示す情報を混同すると、決済の判定が誤ります。以下の表を、デプロイ時のレビューに活用してください。バージョンの違いは、\u003ca href=\"https://docs.x402.org/guides/migration-v1-to-v2\"\u003ex402移行ガイド\u003c/a\u003eに基づいています。\u003c/p\u003e\n\u003cdiv class=\"overflow-x-auto\"\u003e\u003ctable\u003e\n\u003cthead\u003e\n\u003ctr\u003e\n\u003cth\u003eよくある間違い\u003c/th\u003e\n\u003cth\u003e修正の基準\u003c/th\u003e\n\u003c/tr\u003e\n\u003c/thead\u003e\n\u003ctbody\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"よくある間違い\"\u003e\n\u003ccode\u003eextra.version: \"2\"\u003c/code\u003eをx402のバージョンと解釈\u003c/td\u003e\n\u003ctd data-label=\"修正の基準\"\u003eプロトコルバージョンは\u003ccode\u003ex402Version\u003c/code\u003eで確認\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"よくある間違い\"\u003ev2で\u003ccode\u003emaxAmountRequired\u003c/code\u003eを使用\u003c/td\u003e\n\u003ctd data-label=\"修正の基準\"\u003e決済条件の金額には\u003ccode\u003eamount\u003c/code\u003eを使用\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"よくある間違い\"\u003e\n\u003ccode\u003eX-PAYMENT-REQUIRED\u003c/code\u003eヘッダーを独自に生成\u003c/td\u003e\n\u003ctd data-label=\"修正の基準\"\u003ev1は本文、v2は\u003ccode\u003ePAYMENT-REQUIRED\u003c/code\u003eを使用\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"よくある間違い\"\u003e\n\u003ccode\u003eisValid: true\u003c/code\u003eを入金完了として処理\u003c/td\u003e\n\u003ctd data-label=\"修正の基準\"\u003e\n\u003ccode\u003e/settle\u003c/code\u003eの結果を別途確認\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"よくある間違い\"\u003eトランザクションの最上位の\u003ccode\u003eto\u003c/code\u003eをUSDCの受取人として処理\u003c/td\u003e\n\u003ctd data-label=\"修正の基準\"\u003eUSDCの\u003ccode\u003eTransfer.to\u003c/code\u003eを確認\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"よくある間違い\"\u003e署名者とトランザクション送信者が同じと想定\u003c/td\u003e\n\u003ctd data-label=\"修正の基準\"\u003e承認の支払者と\u003ccode\u003eTransfer.from\u003c/code\u003eを比較\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"よくある間違い\"\u003eテストネットでの受取額を売上に合算\u003c/td\u003e\n\u003ctd data-label=\"修正の基準\"\u003eメインネットとテストネットの帳簿を分離\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"よくある間違い\"\u003eタイムアウトのたびに新しい決済を生成\u003c/td\u003e\n\u003ctd data-label=\"修正の基準\"\u003e既存の注文と承認状態から復旧\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"よくある間違い\"\u003eSDKのパッケージバージョンとプロトコルバージョンを同一視\u003c/td\u003e\n\u003ctd data-label=\"修正の基準\"\u003eパッケージバージョンと\u003ccode\u003ex402Version\u003c/code\u003eを別々に管理\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/tbody\u003e\n\u003c/table\u003e\u003c/div\u003e\n\u003cp\u003eUSDCの署名のドメイン情報にも、バージョン文字列が含まれます。その値は、x402のプロトコルバージョンとは目的が異なります。署名ドメインの構造は、\u003ca href=\"https://eips.ethereum.org/EIPS/eip-3009\"\u003eERC-3009仕様\u003c/a\u003eで確認してください。\u003c/p\u003e\n\u003ch2\u003e\n\u003ca href=\"#%E3%82%BB%E3%82%AD%E3%83%A5%E3%83%AA%E3%83%86%E3%82%A3%E7%82%B9%E6%A4%9C%E3%81%A8%E5%A4%96%E9%83%A8%E3%82%B5%E3%83%BC%E3%83%93%E3%82%B9%E3%81%AE%E4%BF%A1%E9%A0%BC%E7%AF%84%E5%9B%B2\" class=\"anchor\" id=\"セキュリティ点検と外部サービスの信頼範囲\"\u003e\u003c/a\u003eセキュリティ点検と外部サービスの信頼範囲\u003c/h2\u003e\n\u003cp\u003e決済の成功判定は、サーバー側で制御してください。外部facilitatorのレスポンスも、明示的な信頼対象です。自前での運用もできる構成については、\u003ca href=\"https://github.com/x402-foundation/x402/blob/main/docs/core-concepts/facilitator.md\"\u003efacilitatorドキュメント\u003c/a\u003eに説明されています。\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eクライアントの成功メッセージで、コンテンツへのアクセスを許可しないでください。\u003c/li\u003e\n\u003cli\u003eサーバーが宣言したチェーン・トークン・金額・受取人を比較してください。\u003c/li\u003e\n\u003cli\u003e秘密鍵やシードフレーズをログに残さないでください。\u003c/li\u003e\n\u003cli\u003e承認署名の全体も、通常のアクセスログから除外してください。\u003c/li\u003e\n\u003cli\u003e決済ヘッダーのサイズとリクエストの処理時間を制限してください。\u003c/li\u003e\n\u003cli\u003e有料レスポンスが共有キャッシュに残らないように点検してください。\u003c/li\u003e\n\u003cli\u003eテスト用キーと本番用キーの保管場所を分けてください。\u003c/li\u003e\n\u003cli\u003efacilitatorの障害時に、決済なしで迂回する経路を作らないでください。\u003c/li\u003e\n\u003cli\u003e承認の期限切れと重複リクエストを、実際にテストしてください。\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e実習で固定した依存関係には、セキュリティチェックの警告が残っています。\u003ccode\u003enpm audit\u003c/code\u003eは、zodに関するアドバイザリを報告しました。関連する内容は、\u003ca href=\"https://github.com/advisories/GHSA-mrhx-c7xc-m3w5\"\u003eGitHubセキュリティアドバイザリ\u003c/a\u003eで確認してください。\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e\u003cspan\u003enpm\u003c/span\u003e\u003cspan\u003e audit\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e本番環境へのデプロイ前に、修正版とのSDKの互換性を確認してください。メジャーバージョンを強制的に置き換えると、動作が変わる可能性があります。この実習を、セキュリティ検証済みのデプロイ構成として使用しないでください。\u003c/p\u003e\n\u003ch3\u003e\n\u003ca href=\"#%E8%87%AA%E5%89%8D%E3%81%AE%E3%82%B5%E3%83%BC%E3%83%90%E3%83%BC%E3%81%A8cloudflare-gateway%E3%81%AE%E6%AF%94%E8%BC%83\" class=\"anchor\" id=\"自前のサーバーとcloudflare-gatewayの比較\"\u003e\u003c/a\u003e自前のサーバーとCloudflare Gatewayの比較\u003c/h3\u003e\n\u003cp\u003eCloudflare Gatewayを使うと、オリジンサーバーでの検証方法が変わります。2026年9月30日付のドキュメントでは、x402 v2の使用が明記されています。次の区分は、\u003ca href=\"https://developers.cloudflare.com/monetization-gateway/x402/\"\u003eCloudflare x402ドキュメント\u003c/a\u003eに基づいています。\u003c/p\u003e\n\u003cdiv class=\"overflow-x-auto\"\u003e\u003ctable\u003e\n\u003cthead\u003e\n\u003ctr\u003e\n\u003cth\u003e区分\u003c/th\u003e\n\u003cth\u003eこの記事のExpress方式\u003c/th\u003e\n\u003cth\u003eCloudflare Monetization Gateway\u003c/th\u003e\n\u003c/tr\u003e\n\u003c/thead\u003e\n\u003ctbody\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"区分\"\u003eクライアントの決済処理\u003c/td\u003e\n\u003ctd data-label=\"この記事のExpress方式\"\u003eExpressミドルウェア\u003c/td\u003e\n\u003ctd data-label=\"Cloudflare Monetization Gateway\"\u003eGateway\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"区分\"\u003eオリジンサーバーでの検証箇所\u003c/td\u003e\n\u003ctd data-label=\"この記事のExpress方式\"\u003e選択したfacilitatorと連携\u003c/td\u003e\n\u003ctd data-label=\"Cloudflare Monetization Gateway\"\u003e\n\u003ccode\u003ePAYMENT-CONTEXT\u003c/code\u003e JWTの検証\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"区分\"\u003e精算結果の受け渡し\u003c/td\u003e\n\u003ctd data-label=\"この記事のExpress方式\"\u003eミドルウェアの\u003ccode\u003ePAYMENT-RESPONSE\u003c/code\u003e\n\u003c/td\u003e\n\u003ctd data-label=\"Cloudflare Monetization Gateway\"\u003eGatewayが精算レシートを生成\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"区分\"\u003e追加の確認事項\u003c/td\u003e\n\u003ctd data-label=\"この記事のExpress方式\"\u003efacilitatorの対応チェーンと認証\u003c/td\u003e\n\u003ctd data-label=\"Cloudflare Monetization Gateway\"\u003eJWTの署名検証とオリジンへのアクセス保護\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/tbody\u003e\n\u003c/table\u003e\u003c/div\u003e\n\u003cp\u003e\u003ccode\u003ePAYMENT-CONTEXT\u003c/code\u003eは、Gatewayとオリジンサーバーの間で使うヘッダーです。一般的なx402クライアントの署名ヘッダーではありません。オリジンでの検証は、\u003ca href=\"https://developers.cloudflare.com/monetization-gateway/configuration/payment-validation/\"\u003eCloudflare Payment validationドキュメント\u003c/a\u003eに従ってください。\u003c/p\u003e\n\u003cp\u003eGatewayを経由しても、JWTを単にデコードするだけではいけません。ドキュメントで指定されたキーを使って、署名を検証してください。ヘッダーがない場合や検証に失敗した場合は、有料コンテンツをブロックします。\u003c/p\u003e\n\u003ch2\u003e\n\u003ca href=\"#%E5%AE%9F%E9%9A%9B%E3%81%AE%E4%BD%BF%E7%94%A8%E9%87%8F%E3%81%A8%E7%B2%BE%E7%AE%97%E8%A8%98%E9%8C%B2%E3%81%AE%E5%8C%BA%E5%88%A5\" class=\"anchor\" id=\"実際の使用量と精算記録の区別\"\u003e\u003c/a\u003e実際の使用量と精算記録の区別\u003c/h2\u003e\n\u003cp\u003eオンチェーンの送金件数だけでは、コンテンツの消費を証明できません。運用指標は、精算とレスポンスの配信を関連付けて集計してください。これは、決済フローと再送の動作に基づく設計上の提案です。\u003c/p\u003e\n\u003cdiv class=\"overflow-x-auto\"\u003e\u003ctable\u003e\n\u003cthead\u003e\n\u003ctr\u003e\n\u003cth\u003e記録\u003c/th\u003e\n\u003cth\u003e答えられる問い\u003c/th\u003e\n\u003cth\u003e追加で必要な記録\u003c/th\u003e\n\u003c/tr\u003e\n\u003c/thead\u003e\n\u003ctbody\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"記録\"\u003e402レスポンス\u003c/td\u003e\n\u003ctd data-label=\"答えられる問い\"\u003e決済条件がどれだけリクエストされたか\u003c/td\u003e\n\u003ctd data-label=\"追加で必要な記録\"\u003e実際に決済されたかどうか\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"記録\"\u003e検証成功\u003c/td\u003e\n\u003ctd data-label=\"答えられる問い\"\u003e承認が有効だったか\u003c/td\u003e\n\u003ctd data-label=\"追加で必要な記録\"\u003e精算結果\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"記録\"\u003eUSDC送金\u003c/td\u003e\n\u003ctd data-label=\"答えられる問い\"\u003e資産が移動したか\u003c/td\u003e\n\u003ctd data-label=\"追加で必要な記録\"\u003e注文との対応関係\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"記録\"\u003e注文ごとの精算完了\u003c/td\u003e\n\u003ctd data-label=\"答えられる問い\"\u003e有料リクエストの決済が済んだか\u003c/td\u003e\n\u003ctd data-label=\"追加で必要な記録\"\u003eレスポンスの配信状態\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"記録\"\u003e再配信記録\u003c/td\u003e\n\u003ctd data-label=\"答えられる問い\"\u003e決済済みのリクエストを復旧したか\u003c/td\u003e\n\u003ctd data-label=\"追加で必要な記録\"\u003e新規決済と区別する識別子\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/tbody\u003e\n\u003c/table\u003e\u003c/div\u003e\n\u003cp\u003eテストリクエストは、実際の使用量の集計で区別してください。同じ注文の再試行も、別途表示します。取引量そのものを、APIの需要や収益の保証として説明してはいけません。\u003c/p\u003e\n\u003ch2\u003e\n\u003ca href=\"#base%E3%83%A1%E3%82%A4%E3%83%B3%E3%83%8D%E3%83%83%E3%83%88%E3%81%B8%E3%81%AE%E7%A7%BB%E8%A1%8C%E6%89%8B%E9%A0%86\" class=\"anchor\" id=\"baseメインネットへの移行手順\"\u003e\u003c/a\u003eBaseメインネットへの移行手順\u003c/h2\u003e\n\u003cp\u003eテストネットで正常時と失敗時の経路を確認してから、メインネットを検討してください。ネットワーク文字列を変更するだけでは、移行は完了しません。基準となる手順は、\u003ca href=\"https://docs.x402.org/getting-started/quickstart-for-sellers#running-on-mainnet\"\u003e公式販売者ガイド\u003c/a\u003eに従います。\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e選択したfacilitatorがBaseメインネットに対応していることを確認してください。\u003c/li\u003e\n\u003cli\u003e\n\u003ccode\u003e/supported\u003c/code\u003eで、v2・\u003ccode\u003eexact\u003c/code\u003e・\u003ccode\u003eeip155:8453\u003c/code\u003eへの対応を確認してください。\u003c/li\u003e\n\u003cli\u003e提供者が要求する認証と利用条件を設定してください。\u003c/li\u003e\n\u003cli\u003eサーバーとクライアントのネットワークを両方変更してください。\u003c/li\u003e\n\u003cli\u003eクライアントで許可するUSDCアドレスを、メインネットのアドレスに変更してください。\u003c/li\u003e\n\u003cli\u003e402レスポンスの金額と、実際の受取アドレスを再度照合してください。\u003c/li\u003e\n\u003cli\u003e重複リクエスト用ストアと障害復旧手順を組み込んでください。\u003c/li\u003e\n\u003cli\u003e本番用の依存関係のセキュリティチェックを完了してください。\u003c/li\u003e\n\u003cli\u003e設定した少額で、実際の送金とコンテンツの配信を確認してください。\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eマルチチェーン対応は、すべての組み合わせへの対応を意味しません。SDKとfacilitatorの対応範囲を、両方確認してください。確認の基準は、\u003ca href=\"https://docs.x402.org/core-concepts/network-and-token-support\"\u003eネットワーク・トークン対応ドキュメント\u003c/a\u003eです。\u003c/p\u003e\n","tags":["ステーブルコイン","AIエージェント","AI開発","コンテンツ収益化","開発ツール"],"faqs":[{"question":"x402は通常のUSDC入金確認と何が違うのですか？","answer":"HTTPリクエストの過程で決済条件を伝え、署名済みの決済承認を使って再リクエストします。入金履歴を後から照合する方式とは異なり、有料リソースへのリクエストに決済手続きを結び付けます。"},{"question":"x402 v2の決済ヘッダーは何ですか？","answer":"決済条件はPAYMENT-REQUIRED、決済ペイロードはPAYMENT-SIGNATUREで伝えます。精算結果はPAYMENT-RESPONSEで伝えます。"},{"question":"v1にX-PAYMENT-REQUIREDヘッダーはありますか？","answer":"公式のHTTP v1仕様では、最初の決済条件をJSONレスポンス本文に含めます。決済ペイロードと精算結果には、それぞれX-PAYMENTとX-PAYMENT-RESPONSEを使います。"},{"question":"/verifyが成功すれば決済は完了したのですか？","answer":"/verifyの成功は、決済承認が有効であることを意味します。この実習では、/settleの成功結果を別途確認する必要があります。"},{"question":"Base SepoliaのテストUSDCを実際の収益とみなせますか？","answer":"Circleは、テストネットのUSDCには金銭的価値がないと明記しています。テストネットでの精算は、実際の売上とは分けて記録してください。"},{"question":"受取サーバーに秘密鍵を入れる必要がありますか？","answer":"この例の受取サーバーには、公開の受取アドレスだけを設定します。決済承認に使う秘密鍵は、決済クライアントで読み込みます。"},{"question":"0.001 USDCのamount値はいくつですか？","answer":"USDCの小数点以下6桁を適用すると、最小単位では1000です。v2の決済条件には文字列\"1000\"を使います。"},{"question":"トランザクションレシートが成功を示していれば、USDCの受け取りも確認できたのですか？","answer":"トランザクションの成功ステータスだけでは不十分です。公式USDCコントラクトのTransferイベントで、支払者・受取人・金額を照合してください。"},{"question":"同じ決済リクエストを再送しても安全ですか？","answer":"同一の承認nonceの再利用防止と、注文の重複防止は別です。再試行する前に既存の決済状態を確認し、注文ごとの冪等性処理を実装してください。"},{"question":"PAYMENT-RESPONSEがなければ、もう一度決済すればよいですか？","answer":"レスポンスの消失やタイムアウトの場合、精算結果が不明確な可能性があります。新しい承認を作るのではなく、既存のリクエストの精算状態を確認してください。"},{"question":"x402では特定の会社のfacilitatorだけを使わなければなりませんか？","answer":"公式の構造では、外部facilitatorの利用と自前での運用の両方が認められています。選んだ実装が、必要なプロトコルバージョン・決済方式・チェーンをサポートしているか確認してください。"},{"question":"AIエージェントの支出も自動的に制限されますか？","answer":"この例では、リクエストごとの決済条件だけを制限します。繰り返し呼び出すことによる累積支出は、別途予算ポリシーで管理してください。"},{"question":"Cloudflare PAYMENT-CONTEXTは標準の決済署名ヘッダーですか？","answer":"PAYMENT-CONTEXTは、Gatewayがオリジンサーバーに渡すJWTです。クライアントのPAYMENT-SIGNATUREとは用途が異なります。"},{"question":"サンプルをそのままメインネットにデプロイしてもよいですか？","answer":"このコードはテスト用の最小構成です。本番環境へのデプロイ前に、依存関係のセキュリティ警告、重複リクエストの処理、facilitatorの認証と実際の決済を検証してください。"}],"sources":[{"url":"https://github.com/x402-foundation/x402/blob/main/specs/x402-specification-v2.md","title":"x402 Protocol Specification v2","type":"source"},{"url":"https://github.com/x402-foundation/x402/blob/main/specs/transports-v1/http.md","title":"x402 HTTP Transport v1","type":"source"},{"url":"https://github.com/x402-foundation/x402/blob/main/specs/transports-v2/http.md","title":"x402 HTTP Transport v2","type":"source"},{"url":"https://docs.x402.org/guides/migration-v1-to-v2","title":"x402 Migration Guide: V1 to V2","type":"source"},{"url":"https://github.com/x402-foundation/x402/blob/main/docs/core-concepts/facilitator.md","title":"x402 Facilitator の役割と責任","type":"source"},{"url":"https://docs.x402.org/getting-started/quickstart-for-sellers","title":"x402 Quickstart for Sellers","type":"source"},{"url":"https://docs.x402.org/getting-started/quickstart-for-buyers","title":"x402 Quickstart for Buyers","type":"source"},{"url":"https://github.com/x402-foundation/x402/tree/main/examples/typescript/clients/fetch","title":"x402 公式 TypeScript fetch クライアントの例","type":"source"},{"url":"https://github.com/x402-foundation/x402/blob/main/typescript/packages/http/express/src/index.ts","title":"x402 Express ミドルウェアの実装","type":"source"},{"url":"https://github.com/x402-foundation/x402/blob/main/typescript/packages/core/src/client/x402Client.ts","title":"x402Client の決済条件の選択とポリシーの実装","type":"source"},{"url":"https://github.com/x402-foundation/x402/blob/main/typescript/packages/mechanisms/evm/src/exact/server/scheme.ts","title":"x402 ExactEvmScheme サーバーの価格変換の実装","type":"source"},{"url":"https://developers.circle.com/stablecoins/usdc-contract-addresses","title":"Circle USDC コントラクトアドレス","type":"data_point"},{"url":"https://docs.base.org/get-started/connect-to-base","title":"Base メインネットと Base Sepolia のネットワーク情報","type":"data_point"},{"url":"https://faucet.circle.com/","title":"Circle Testnet Faucet","type":"source"},{"url":"https://viem.sh/docs/actions/public/getTransactionReceipt","title":"viem getTransactionReceipt","type":"source"},{"url":"https://viem.sh/docs/contract/parseEventLogs","title":"viem parseEventLogs","type":"source"},{"url":"https://eips.ethereum.org/EIPS/eip-20","title":"ERC-20 Token Standard","type":"source"},{"url":"https://eips.ethereum.org/EIPS/eip-3009","title":"ERC-3009 Transfer With Authorization","type":"source"},{"url":"https://docs.x402.org/extensions/payment-identifier","title":"x402 Payment-Identifier 冪等性拡張","type":"source"},{"url":"https://developers.cloudflare.com/monetization-gateway/x402/","title":"Cloudflare Monetization Gateway x402 protocol","type":"source"},{"url":"https://developers.cloudflare.com/monetization-gateway/configuration/payment-validation/","title":"Cloudflare Monetization Gateway Payment validation","type":"source"},{"url":"https://docs.x402.org/core-concepts/network-and-token-support","title":"x402 Networks \u0026 Token Support","type":"source"},{"url":"https://github.com/advisories/GHSA-mrhx-c7xc-m3w5","title":"GitHub セキュリティアドバイザリ: Zod の大規模コレクションの検証によるメモリ枯渇","type":"source"}],"images":[{"id":2199,"url":"https://injoys.com/rails/active_storage/blobs/proxy/eyJfcmFpbHMiOnsiZGF0YSI6MzM4MDcsInB1ciI6ImJsb2JfaWQifX0=--49c502fd8da16158a617c3586dfa81ced4bc95df/ai-f0ae2b5c.webp","is_representative":true,"generation_method":"ai_photo","license":"ai_generated","mime_type":"image/webp","width":1536,"height":1024,"translations":{"ko":{"alt":"짙은 회색 스웨터를 입은 남성이 서버 랙 선반에 놓인 태블릿 화면을 살펴보고 있다.","caption":"메인넷으로 전환하기 전에 테스트 환경에서 x402 결제 연동을 검증하세요.","description":null},"en":{"alt":"A man in a dark gray sweater examines a tablet resting on a server rack shelf.","caption":"Validate the x402 payment integration in a test environment before switching to mainnet.","description":null},"ja":{"alt":"濃いグレーのセーターを着た男性が、サーバーラックの棚に置かれたタブレットを確認している。","caption":"メインネットへ移行する前に、テスト環境でx402の決済連携を検証しましょう。","description":null},"es":{"alt":"Un hombre con un suéter gris oscuro revisa una tableta sobre la bandeja de un rack de servidores.","caption":"Valida la integración de pagos x402 en un entorno de prueba antes de pasar a la red principal.","description":null},"id":{"alt":"Seorang pria bersweater abu-abu gelap memeriksa tablet di atas ambalan rak server.","caption":"Validasi integrasi pembayaran x402 di lingkungan uji sebelum beralih ke mainnet.","description":null},"pt":{"alt":"Um homem de suéter cinza-escuro examina um tablet apoiado na prateleira de um rack de servidores.","caption":"Valide a integração de pagamentos x402 em um ambiente de teste antes de migrar para a rede principal.","description":null},"zh-hant":{"alt":"一名穿著深灰色毛衣的男子正在查看伺服器機架層板上的平板電腦。","caption":"切換至主網前，先在測試環境中驗證 x402 支付整合。","description":null},"de":{"alt":"Ein Mann im dunkelgrauen Pullover prüft ein Tablet auf der Ablage eines Serverracks.","caption":"Prüfen Sie die x402-Zahlungsintegration in einer Testumgebung, bevor Sie ins Mainnet wechseln.","description":null}}},{"id":2200,"url":"https://injoys.com/rails/active_storage/blobs/proxy/eyJfcmFpbHMiOnsiZGF0YSI6MzM4MTMsInB1ciI6ImJsb2JfaWQifX0=--4e70727f132519277c68d1c6279d95585a936549/ai-3dbd1c62.webp","is_representative":false,"generation_method":"ai_semi","license":"ai_generated","mime_type":"image/webp","width":1536,"height":1024,"translations":{"ko":{"alt":"창가에서 여성이 평평하게 펼친 노트북으로 입력하고 있으며, 화면에는 목록과 두 경로가 합쳐지는 흐름도가 보인다.","caption":"중복 요청 복구를 시험해 결제 재시도가 이중 정산으로 이어지지 않는지 확인하세요.","description":null},"en":{"alt":"A woman types on a laptop opened flat, with a list and two paths converging on one endpoint on its screen.","caption":"Test duplicate-request recovery to ensure payment retries do not cause a second settlement.","description":null},"ja":{"alt":"窓辺で女性が平らに開いたノートパソコンを操作し、画面には一覧と2本の経路が合流するフロー図が表示されている。","caption":"重複リクエストの復旧をテストし、決済の再試行で二重精算が起きないことを確認しましょう。","description":null},"es":{"alt":"Una mujer teclea en un portátil abierto en plano; la pantalla muestra una lista y dos rutas que convergen.","caption":"Prueba la recuperación de solicitudes duplicadas para evitar una segunda liquidación al reintentar el pago.","description":null},"id":{"alt":"Seorang perempuan mengetik di laptop yang dibuka rata, dengan daftar dan diagram dua jalur yang menyatu di layar.","caption":"Uji pemulihan permintaan duplikat untuk memastikan percobaan ulang pembayaran tidak memicu penyelesaian ganda.","description":null},"pt":{"alt":"Uma mulher digita num notebook aberto na horizontal, com uma lista e duas rotas convergentes na tela.","caption":"Teste a recuperação de solicitações duplicadas para garantir que novas tentativas de pagamento não gerem liquidação dupla.","description":null},"zh-hant":{"alt":"一名女子在窗邊操作完全攤平的筆記型電腦，螢幕顯示清單及兩條路徑匯合的流程圖。","caption":"測試重複請求的復原流程，確認付款重試不會造成重複結算。","description":null},"de":{"alt":"Eine Frau tippt auf einem flach aufgeklappten Laptop; das Display zeigt eine Liste und zwei zusammenlaufende Pfade.","caption":"Testen Sie die Wiederherstellung bei doppelten Anfragen, damit Zahlungswiederholungen keine zweite Abrechnung auslösen.","description":null}}}],"published_at":"2026-10-28T19:20:55+09:00","updated_at":"2026-10-28T19:20:55+09:00","license":"cc_by","translation_status":"reviewed","available_locales":["ko","en","ja","es"],"data_locales":["ko","en","ja","es","id","pt","zh-hant","de"],"url":"https://injoys.com/ja/articles/x402-payments-2026-v2-base-usdc-settlement-guide"}