{"content_id":"gyaevhk2bq","slug":"x402-payments-2026-v2-base-usdc-settlement-guide","locale":"id","schema_type":"HowTo","category":"tutorial","category_name":"Tutorial","title":"Pembayaran x402 2026 v2: Penerapan dan Verifikasi Setelmen","summary":"Praktik Node.js untuk menambahkan pembayaran mikro pada API dengan x402 v2. Pilih ketentuan pembayaran, minta ulang tanda tangan, dan verifikasi setelmen di Base Sepolia, serta periksa pengiriman ulang dan syarat peralihan ke mainnet.","sponsorship_disclosure":null,"affiliate_disclosure":null,"commerce_disclosure":null,"author":{"name":"injoys","url":"https://injoys.com/ko/about"},"key_points":["Siapkan alamat penerima dan dompet untuk pembayaran uji, lalu instal SDK resmi x402.","Hubungkan middleware pembayaran untuk Base Sepolia dan periksa respons 402 untuk permintaan tanpa pembayaran.","Pilih dan tandatangani hanya ketentuan yang sesuai dengan chain, token, jumlah, dan penerima yang diizinkan klien.","Cari event USDC Transfer menggunakan hash transaksi dari respons setelmen.","Uji pemblokiran saat gagal dan pemulihan permintaan duplikat, lalu tinjau pengaturan mainnet."],"content_markdown":"Pembayaran x402 diimplementasikan dengan menyertakan ketentuan pembayaran dalam respons 402 API. Siapkan Node.js dan dompet uji. Verifikasi terlebih dahulu tanda tangan dan penyelesaian pembayaran v2 di Base Sepolia. Atur agar respons berbayar hanya dikirim setelah penyelesaian pembayaran berhasil.\n\nInformasi jumlah dan chain mengacu pada dokumentasi resmi x402, Base, dan Circle yang ditautkan sebagai sumber.\n\n## Persiapan praktik dan instalasi\n\nSiapkan lingkungan pengembangan yang memungkinkan Anda mengubah API serta dompet uji. Contoh ini disusun berdasarkan Node.js 22 dan x402 SDK 2.28.0. Struktur instalasinya mengikuti [panduan resmi penjual](https://docs.x402.org/getting-started/quickstart-for-sellers).\n\n1. Siapkan alamat dompet EVM untuk menerima pembayaran.\n2. Siapkan dompet uji terpisah untuk melakukan pembayaran.\n3. Dapatkan USDC uji Base Sepolia untuk dompet pembayaran.\n4. Buat proyek praktik dengan perintah berikut.\n5. Simpan berkas TypeScript selanjutnya dalam folder yang sama.\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\nServer penerima tidak memerlukan kunci privat dompet penerima. Atur agar kunci privat pembayaran hanya dibaca oleh proses klien. Metode penandatanganan dompet mengacu pada [panduan resmi pembeli](https://docs.x402.org/getting-started/quickstart-for-buyers).\n\nCakupan verifikasi contoh ini adalah pemeriksaan tipe dan simulasi alur pembayaran. Contoh ini belum menuntaskan transfer nyata di testnet. Periksa sendiri hasil on-chain melalui langkah eksekusi di bawah.\n\n## Perbandingan x402 v1 dan v2\n\nSaat mengimplementasikan v2, sesuaikan header dan field isi sekaligus. Mengganti nama header saja akan membuat struktur data tidak cocok. Tabel berikut mengacu pada [panduan migrasi resmi](https://docs.x402.org/guides/migration-v1-to-v2).\n\n| Aspek | v1 | v2 |\n|---|---|---|\n| Versi protokol | `x402Version: 1` | `x402Version: 2` |\n| Ketentuan pembayaran awal | Isi JSON respons 402 | Header respons `PAYMENT-REQUIRED` |\n| Data pembayaran yang ditandatangani | Header permintaan `X-PAYMENT` | Header permintaan `PAYMENT-SIGNATURE` |\n| Hasil penyelesaian pembayaran | Header respons `X-PAYMENT-RESPONSE` | Header respons `PAYMENT-RESPONSE` |\n| Pengidentifikasi mainnet Base | `base` | `eip155:8453` |\n| Pengidentifikasi Base Sepolia | `base-sepolia` | `eip155:84532` |\n| Field jumlah dalam ketentuan pembayaran | `maxAmountRequired` | `amount` |\n| Deskripsi sumber daya | `resource` dan lainnya di dalam ketentuan pembayaran | Objek `resource` tingkat teratas |\n| Ketentuan dalam payload pembayaran | `scheme`, `network`, dan lainnya di tingkat teratas | Objek `accepted` yang memuat ketentuan terpilih |\n| Contoh paket TypeScript | `x402-express` | `@x402/express` |\n\nLokasi penyampaian ketentuan pembayaran v1 ditetapkan dalam [spesifikasi HTTP v1](https://github.com/x402-foundation/x402/blob/main/specs/transports-v1/http.md). Lokasi penyampaian standar v2 adalah header `PAYMENT-REQUIRED`. Implementasi juga dapat mengembalikan isi respons, tetapi gunakan header sebagai acuan pemrosesan.\n\n[Spesifikasi HTTP v2](https://github.com/x402-foundation/x402/blob/main/specs/transports-v2/http.md) menjelaskan kode status sebagai berikut.\n\n\u003e Server menunjukkan bahwa pembayaran diperlukan menggunakan kode status HTTP 402 \"Payment Required\".\n\u003e\n\u003e Sumber: spesifikasi x402 HTTP v2\n\nHeader pembayaran v2 adalah nilai JSON yang dikodekan dengan Base64. Base64 bukan metode enkripsi. Gunakan HTTPS untuk layanan eksternal.\n\n## Urutan permintaan HTTP 402 dan pembayaran\n\nPraktik ini menggunakan alur otorisasi dasar metode EVM `exact`. `exact` adalah metode pembayaran dengan jumlah yang telah ditentukan. Urutannya mengikuti [spesifikasi inti x402 v2](https://github.com/x402-foundation/x402/blob/main/specs/x402-specification-v2.md).\n\n1. Klien meminta API berbayar tanpa informasi pembayaran.\n2. Server mengembalikan HTTP 402 dan `PAYMENT-REQUIRED`.\n3. Klien memilih ketentuan yang diizinkan dari `accepts`.\n4. SDK menandatangani otorisasi pembayaran sesuai ketentuan yang dipilih.\n5. Klien mengirim ulang permintaan dengan menyertakan `PAYMENT-SIGNATURE`.\n6. Server memverifikasi otorisasi melalui `/verify` milik facilitator.\n7. Jika verifikasi lolos, server menjalankan pemrosesan sumber daya.\n8. Server meminta penyelesaian pembayaran melalui `/settle`.\n9. Setelah penyelesaian pembayaran berhasil, server mengirim isi berbayar dan `PAYMENT-RESPONSE`.\n\nUrutan ini bukan urutan tetap yang berlaku untuk semua metode pembayaran. v2 juga mendefinisikan alur dengan waktu penyelesaian pembayaran yang berbeda. Kode di bawah hanya mengizinkan alur `authorization` dasar.\n\n### Peran resource server dan facilitator\n\nResource server menentukan harga dan ketentuan akses konten. Facilitator menangani verifikasi dan penyelesaian pembayaran. Pembagian peran mengacu pada [dokumentasi resmi facilitator](https://github.com/x402-foundation/x402/blob/main/docs/core-concepts/facilitator.md).\n\n| Komponen | Tugas | Kondisi yang tidak boleh dianggap berhasil |\n|---|---|---|\n| Klien | Memilih ketentuan dan menandatangani otorisasi pembayaran | Baru selesai membuat tanda tangan |\n| Resource server | Menetapkan harga dan mengendalikan respons berbayar | Klien mengklaim pembayaran berhasil |\n| Facilitator `/verify` | Memastikan keabsahan tanda tangan dan ketentuan pembayaran | Hanya menerima `isValid: true` |\n| Facilitator `/settle` | Dalam praktik ini, menjalankan pembayaran on-chain dan mengembalikan hasilnya | Sudah dikirim, tetapi hasilnya belum dikonfirmasi |\n\n`/verify` adalah pemeriksaan yang tidak mengubah status on-chain. Karena itu, keberhasilan verifikasi tidak berarti dana telah diterima. Saldo atau status otorisasi masih dapat berubah setelah verifikasi.\n\nBlokir isi berbayar jika verifikasi atau penyelesaian pembayaran gagal. Pemblokiran saat keberhasilan belum dapat dipastikan disebut fail-closed. Perilaku ini juga dapat dilihat dalam [implementasi resmi Express](https://github.com/x402-foundation/x402/blob/main/typescript/packages/http/express/src/index.ts).\n\n## Perbandingan mainnet dan testnet Base\n\nGunakan hanya Base Sepolia untuk eksekusi pertama. Circle menyatakan bahwa USDC testnet tidak memiliki nilai finansial. Alamat mengacu pada [daftar kontrak USDC Circle](https://developers.circle.com/stablecoins/usdc-contract-addresses).\n\n| Aspek | Mainnet Base | Testnet Base Sepolia |\n|---|---|---|\n| Kegunaan | Pembayaran dengan aset nyata | Menguji implementasi dan penanganan kegagalan |\n| Chain ID | `8453` | `84532` |\n| Jaringan x402 v2 | `eip155:8453` | `eip155:84532` |\n| Kontrak USDC | `0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913` | `0x036CbD53842c5426634e7929541eC2318f3dCF7e` |\n| RPC publik | `https://mainnet.base.org` | `https://sepolia.base.org` |\n| Nilai finansial token | Aset dengan nilai finansial nyata | Tidak memiliki nilai finansial |\n\nPeriksa Chain ID dan RPC dalam [dokumentasi jaringan Base](https://docs.base.org/get-started/connect-to-base). Meski alamat dompet sama, saldo pada chain yang berbeda tetap terpisah. Jangan mencatat keberhasilan di testnet sebagai pendapatan nyata.\n\nUSDC uji dapat diminta melalui [Circle Testnet Faucet](https://faucet.circle.com/). Pilih Base Sepolia sebagai jaringan. Masukkan alamat dompet uji pembayaran sebagai alamat penerima.\n\n## Implementasi API berbayar Node.js\n\nPasang `paymentMiddleware` sebelum rute berbayar. Contoh di bawah mensyaratkan **0.001 USDC** per panggilan. Jumlah tersebut menggunakan contoh harga dalam panduan resmi penjual. (Nilai terverifikasi 0.001 USDC per panggilan · sumber docs.x402.org · diperiksa pada 2026-10-28)\n\n### Berkas server\n\nSimpan kode berikut sebagai `server.ts`. Server hanya menerima permintaan melalui antarmuka lokal. Alamat penerima dibaca dari variabel lingkungan `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` adalah alamat pengujian dalam panduan resmi. Dalam contoh ini, alamat tersebut digunakan untuk penyelesaian pembayaran Base Sepolia. Untuk memilih facilitator produksi, lihat [prosedur mainnet dalam panduan penjual](https://docs.x402.org/getting-started/quickstart-for-sellers#running-on-mainnet).\n\n### Contoh perhitungan harga\n\nHarga contoh ini adalah `1000` dalam satuan terkecil USDC. USDC memiliki 6 angka desimal. Konversi harga oleh SDK mengacu pada [implementasi server 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| Nilai yang diperiksa | Nilai yang diharapkan dalam contoh ini |\n|---|---|\n| `price` dalam konfigurasi server | `\"$0.001\"` |\n| `amount` dalam ketentuan pembayaran 402 | `\"1000\"` |\n| `asset` dalam ketentuan pembayaran | Kontrak USDC Base Sepolia |\n| `payTo` dalam ketentuan pembayaran | Alamat penerima yang dikonfigurasi |\n| `network` dalam ketentuan pembayaran | `eip155:84532` |\n\n`price` adalah nilai konfigurasi SDK. Ketentuan pembayaran v2 yang sebenarnya menyampaikan `amount`. Bandingkan jumlah bilangan bulat sebagai string atau `bigint`.\n\n### Menjalankan server dan memeriksa 402\n\nJalankan server di terminal Bash pertama. Nilai yang dimasukkan adalah alamat publik penerima, bukan kunci privat. Biarkan terminal ini tetap terbuka selama server berjalan.\n\n```bash\nread -r -p '수취 지갑 주소: ' PAY_TO\nexport PAY_TO\nnpx tsx server.ts\n```\n\nKirim permintaan tanpa pembayaran dari terminal kedua. Perintah berikut tidak menandatangani pembayaran. Perintah ini menampilkan status HTTP dan ketentuan pembayaran yang telah didekodekan.\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\nRespons normal tanpa pembayaran adalah HTTP 402. Bandingkan ketentuan yang ditampilkan dengan tabel di atas. Jika konten berbayar langsung dikembalikan, perbaiki konfigurasi perlindungan rute terlebih dahulu.\n\n## Pemilihan ketentuan pembayaran dan implementasi klien\n\nBatasi klien agar hanya menandatangani ketentuan yang diizinkan. Contoh ini hanya mengizinkan harga tetap dalam USDC Base Sepolia. Filter opsi pembayaran menggunakan `registerPolicy` dari [implementasi resmi x402Client](https://github.com/x402-foundation/x402/blob/main/typescript/packages/core/src/client/x402Client.ts).\n\n1. Simpan kode berikut sebagai `client.ts`.\n2. Simpan juga `check-transfer.ts` dari bagian berikutnya.\n3. Setelah kedua berkas siap, lanjutkan ke langkah eksekusi.\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` menangani 402 dan pengiriman ulang permintaan bertanda tangan. Penggunaannya mengikuti [contoh fetch resmi](https://github.com/x402-foundation/x402/tree/main/examples/typescript/clients/fetch). Jika tidak ada ketentuan pembayaran yang diizinkan, pembuatan pembayaran dihentikan.\n\nKlien ini juga dapat digunakan oleh agen AI. Namun, batas harga per panggilan bukan batas pengeluaran kumulatif. Kelola anggaran panggilan berulang agen secara terpisah.\n\n## Verifikasi event Transfer USDC\n\nPeriksa log transfer USDC menggunakan hash transaksi dari hasil penyelesaian pembayaran. Status transaksi berhasil saja tidak memastikan siapa penerimanya. Field `Transfer` mengacu pada [standar ERC-20](https://eips.ethereum.org/EIPS/eip-20).\n\nVerifikasi memerlukan hal-hal berikut.\n\n| Aspek verifikasi | Pembanding |\n|---|---|\n| Chain ID dari RPC | Chain yang diizinkan server |\n| `status` pada tanda terima transaksi | Status berhasil |\n| Alamat yang memancarkan event | Kontrak USDC resmi pada chain tersebut |\n| `Transfer.from` | Pembayar yang telah dipastikan |\n| `Transfer.to` | Alamat penerima yang ditetapkan server |\n| `Transfer.value` | Jumlah dalam satuan terkecil yang diminta server |\n\nSimpan kode berikut sebagai `check-transfer.ts`. Kueri menggunakan [getTransactionReceipt](https://viem.sh/docs/actions/public/getTransactionReceipt) dari viem. Penguraian event mengacu pada [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\nFungsi ini memastikan bahwa transfer USDC terjadi dalam transaksi tersebut. Keterkaitannya dengan pesanan API tertentu harus diperiksa secara terpisah. Pada server produksi, gunakan ketentuan pesanan yang tersimpan sebagai nilai pembanding.\n\nJangan memercayai `transfer.json` yang diunggah klien sebagai bukti untuk memberikan akses. Server dalam contoh ini menggunakan hasil verifikasi dan penyelesaian pembayaran dari facilitator. Jika kueri independen dijadikan syarat pemberian akses, jalankan kueri tersebut di server.\n\nKegagalan mengambil tanda terima tidak serta-merta berarti pembayaran gagal. Jika terjadi galat RPC, kueri ulang hash transaksi yang tersimpan. Jumlah konfirmasi tambahan dan penanganan reorganisasi chain ditetapkan dalam kebijakan operasional.\n\n## Menjalankan pembayaran uji dan memeriksa hasil\n\nSetelah semua berkas disimpan, jalankan dengan dompet uji. Pemeriksaan tipe tidak melakukan penandatanganan atau transfer. Perintah berikut menggunakan alat TypeScript yang telah dipasang sebelumnya.\n\n1. Periksa tipe dengan perintah berikut.\n2. Masukkan alamat penerima yang sama dengan server di terminal kedua.\n3. Masukkan kunci privat dompet pembayaran uji dengan input tersembunyi.\n4. Jalankan klien satu kali.\n5. Periksa hasil penyelesaian pembayaran dan log transfer yang tersimpan.\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| Keluaran | Arti | Hal yang diperiksa |\n|---|---|---|\n| `settlement.json` | Hasil penyelesaian pembayaran yang dikembalikan server | `success`, `network`, `transaction` |\n| `transfer.json` | Catatan transfer yang ditemukan melalui kueri RPC | Chain, hash transaksi, posisi log, informasi blok |\n| Isi berbayar di konsol | Data yang ditampilkan setelah verifikasi oleh klien ini | Pastikan konten sesuai permintaan |\n\nMenjalankan ulang klien dapat menimbulkan pembayaran baru. Contoh minimal ini tidak memiliki penyimpanan pencegah duplikasi per pesanan. Tambahkan fitur percobaan ulang setelah memenuhi ketentuan pada bagian berikutnya.\n\n## Penanganan berdasarkan kondisi kegagalan verifikasi\n\nDalam pengujian kegagalan, pastikan isi berbayar tidak terungkap. Memeriksa status HTTP saja dapat membuat kegagalan pemblokiran terlewat. Lihat [middleware resmi Express](https://github.com/x402-foundation/x402/blob/main/typescript/packages/http/express/src/index.ts) sebagai acuan perilaku.\n\n| Kondisi pengujian | Penanganan yang diharapkan | Pemeriksaan tambahan |\n|---|---|---|\n| Tidak ada header pembayaran | Mengembalikan 402 dan ketentuan pembayaran | Tidak ada isi berbayar |\n| Token atau penerima berbeda diajukan | Ditolak oleh kebijakan klien | Tanda tangan tidak dibuat |\n| Tanda tangan tidak valid | Diblokir karena verifikasi gagal | Handler berbayar tidak dijalankan |\n| Saldo tidak cukup | Diblokir karena verifikasi atau penyelesaian pembayaran gagal | Tidak dicatat sebagai penyelesaian pembayaran berhasil |\n| Hasil penyelesaian pembayaran gagal | Respons berbayar diblokir | Isi yang sudah disiapkan pun tidak dikirim |\n| Respons facilitator terlambat | Ditangguhkan karena keberhasilan belum dipastikan | Jangan membuat otorisasi baru secara otomatis |\n| Galat kueri tanda terima RPC | Simpan hash transaksi dan lakukan kueri ulang | Jangan langsung menyimpulkan pembayaran gagal |\n\nDalam alur otorisasi dasar, handler dijalankan sebelum penyelesaian pembayaran. Kegagalan penyelesaian pembayaran tidak membatalkan tindakan eksternal. Mulailah dari kueri tanpa efek samping seperti dalam contoh ini.\n\nPengiriman email atau pembuatan pesanan eksternal memerlukan rancangan tersendiri. Catat status pembayaran dan pekerjaan secara bersamaan. Tentukan cakupan eksekusi ulang saat terjadi kegagalan untuk mengurangi pekerjaan ganda.\n\n## Pemulihan respons yang hilang setelah penyelesaian pembayaran dan pengiriman ulang\n\nSimpan penyelesaian pembayaran dan penyelesaian pengiriman konten sebagai status terpisah. Pembayaran mungkin sudah selesai, tetapi koneksi respons terputus. Acuan penanganan situasi ini adalah [dokumentasi ekstensi Payment-Identifier](https://docs.x402.org/extensions/payment-identifier).\n\nSifat tidak memproses pekerjaan yang sama dua kali meskipun diminta ulang disebut idempotensi. `payment-identifier` menyediakan pengidentifikasi pembayaran untuk tujuan ini. Fitur ini tidak otomatis lengkap hanya dengan menambahkan pengidentifikasi.\n\n### Perbandingan pembayaran ganda dan pengiriman ulang\n\nPencegahan penggunaan ulang tanda tangan yang sama harus dibedakan dari pencegahan duplikasi pesanan. EIP-3009 mencegah penggunaan ulang nonce otorisasi yang telah digunakan. Perilaku ini didefinisikan dalam [spesifikasi ERC-3009](https://eips.ethereum.org/EIPS/eip-3009).\n\n| Situasi | Dari sisi otorisasi on-chain | Dari sisi operasional API |\n|---|---|---|\n| Menyelesaikan pembayaran ulang dengan otorisasi yang sama | Diperiksa sebagai nonce yang sudah digunakan | Perlu mengambil hasil sebelumnya |\n| Membuat otorisasi baru untuk pesanan yang sama | Dapat diperlakukan sebagai otorisasi terpisah | Perlu mencegah pembayaran ganda |\n| Respons hilang setelah penyelesaian pembayaran berhasil | Perpindahan aset mungkin sudah selesai | Perlu mengirim ulang konten |\n| Batas waktu terlampaui setelah permintaan penyelesaian pembayaran | Hasil masih belum jelas | Perlu status tertunda dan pemeriksaan penyelesaian pembayaran |\n\nProsedur berikut adalah rancangan untuk menghubungkan ekstensi resmi dengan penyimpanan produksi. Prosedur ini tidak termasuk dalam kode minimal sebelumnya. Untuk menerapkannya di produksi, tambahkan penyimpanan persisten dan pengendalian permintaan serentak.\n\n1. Deklarasikan dukungan ekstensi `payment-identifier` di server.\n2. Simpan pengidentifikasi permintaan logis di klien.\n3. Hubungkan pengidentifikasi dengan pembayar dan isi permintaan di server.\n4. Catat status sedang diproses secara atomik untuk mencegah eksekusi serentak.\n5. Saat penyelesaian pembayaran berhasil, simpan bukti transaksi dan hasil respons.\n6. Jika hak akses dan isi permintaan ulang sama, kembalikan hasil yang tersimpan.\n7. Jika hasil penyelesaian pembayaran belum jelas, periksa otorisasi sebelumnya terlebih dahulu.\n\nPengidentifikasi pembayaran bukan kata sandi atau token akses. Jangan mengembalikan cache berbayar kepada permintaan yang hanya mengetahui pengidentifikasi. Periksa juga apakah pengidentifikasi digunakan ulang oleh pengguna lain atau untuk isi permintaan yang berbeda.\n\nSimpan chain, hash transaksi, dan posisi log bersama-sama dalam catatan operasional. Hubungkan catatan ini dengan pesanan untuk memblokir penggunaan ulang pada pesanan lain. Kecocokan `Transfer` saja tidak membuktikan keterkaitannya dengan pesanan.\n\n## Kesalahan umum dalam implementasi pembayaran\n\nMencampuradukkan field versi dan indikator keberhasilan dapat menyebabkan kesalahan penentuan pembayaran. Gunakan tabel berikut saat meninjau deployment. Perbedaan versi mengacu pada [panduan migrasi x402](https://docs.x402.org/guides/migration-v1-to-v2).\n\n| Kesalahan umum | Acuan perbaikan |\n|---|---|\n| Menafsirkan `extra.version: \"2\"` sebagai versi x402 | Periksa versi protokol melalui `x402Version` |\n| Menggunakan `maxAmountRequired` pada v2 | Gunakan `amount` untuk jumlah dalam ketentuan pembayaran |\n| Membuat header `X-PAYMENT-REQUIRED` sendiri | v1 menggunakan isi respons, v2 menggunakan `PAYMENT-REQUIRED` |\n| Memperlakukan `isValid: true` sebagai dana telah diterima | Periksa hasil `/settle` secara terpisah |\n| Memperlakukan `to` tingkat teratas transaksi sebagai penerima USDC | Periksa `Transfer.to` USDC |\n| Menganggap penanda tangan sama dengan pengirim transaksi | Bandingkan pembayar dalam otorisasi dengan `Transfer.from` |\n| Menjumlahkan penerimaan testnet sebagai pendapatan | Pisahkan pembukuan mainnet dan testnet |\n| Membuat pembayaran baru setiap kali batas waktu terlampaui | Pulihkan pesanan dan status otorisasi sebelumnya terlebih dahulu |\n| Menyamakan versi paket SDK dengan versi protokol | Kelola versi paket dan `x402Version` secara terpisah |\n\nInformasi domain tanda tangan USDC juga memuat string versi. Nilai tersebut memiliki tujuan berbeda dari versi protokol x402. Lihat [spesifikasi ERC-3009](https://eips.ethereum.org/EIPS/eip-3009) untuk struktur domain tanda tangan.\n\n## Pemeriksaan keamanan dan batas kepercayaan layanan eksternal\n\nKendalikan penentuan keberhasilan pembayaran di server. Respons facilitator eksternal juga merupakan sumber yang secara eksplisit dipercaya. Arsitektur yang memungkinkan pengoperasian mandiri dijelaskan dalam [dokumentasi facilitator](https://github.com/x402-foundation/x402/blob/main/docs/core-concepts/facilitator.md).\n\n- Jangan membuka akses konten berdasarkan pesan keberhasilan dari klien.\n- Bandingkan chain, token, jumlah, dan penerima yang ditetapkan server.\n- Jangan mencatat kunci privat dan frasa pemulihan dalam log.\n- Kecualikan juga tanda tangan otorisasi lengkap dari log akses umum.\n- Batasi ukuran header pembayaran dan waktu pemrosesan permintaan.\n- Pastikan respons berbayar tidak tersimpan dalam cache bersama.\n- Pisahkan lokasi penyimpanan kunci uji dan kunci produksi.\n- Jangan membuat jalur pintas tanpa pembayaran saat facilitator mengalami gangguan.\n- Uji secara langsung kedaluwarsa otorisasi dan permintaan duplikat.\n\nDependensi yang dipatok dalam praktik ini masih memiliki peringatan pemeriksaan keamanan. `npm audit` melaporkan advisori terkait zod. Lihat rinciannya dalam [advisori keamanan GitHub](https://github.com/advisories/GHSA-mrhx-c7xc-m3w5).\n\n```bash\nnpm audit\n```\n\nPeriksa kompatibilitas SDK versi perbaikan sebelum deployment produksi. Mengganti versi mayor secara paksa dapat mengubah perilaku. Jangan gunakan praktik ini sebagai konfigurasi deployment yang telah lolos verifikasi keamanan.\n\n### Perbandingan server sendiri dan Cloudflare Gateway\n\nPenggunaan Cloudflare Gateway mengubah metode verifikasi di server asal. Dokumentasi bertanggal 30 September 2026 menyatakan penggunaan x402 v2. Perbedaan berikut mengacu pada [dokumentasi Cloudflare x402](https://developers.cloudflare.com/monetization-gateway/x402/).\n\n| Aspek | Metode Express dalam artikel ini | Cloudflare Monetization Gateway |\n|---|---|---|\n| Pemrosesan pembayaran klien | Middleware Express | Gateway |\n| Titik verifikasi server asal | Integrasi dengan facilitator yang dipilih | Verifikasi JWT `PAYMENT-CONTEXT` |\n| Penyampaian hasil penyelesaian pembayaran | `PAYMENT-RESPONSE` dari middleware | Gateway membuat tanda terima penyelesaian pembayaran |\n| Hal tambahan yang diperiksa | Chain yang didukung facilitator dan autentikasi | Verifikasi tanda tangan JWT dan perlindungan akses ke server asal |\n\n`PAYMENT-CONTEXT` adalah header antara Gateway dan server asal. Header ini bukan header tanda tangan klien x402 biasa. Ikuti [dokumentasi Cloudflare Payment validation](https://developers.cloudflare.com/monetization-gateway/configuration/payment-validation/) untuk verifikasi di server asal.\n\nMeski melewati Gateway, jangan sekadar mendekodekan JWT. Verifikasi tanda tangannya dengan kunci yang ditentukan dokumentasi. Blokir konten berbayar jika header tidak ada atau verifikasi gagal.\n\n## Membedakan penggunaan nyata dan catatan penyelesaian pembayaran\n\nJumlah transfer on-chain saja tidak dapat membuktikan konsumsi konten. Hitung metrik operasional dengan menghubungkan penyelesaian pembayaran dan pengiriman respons. Ini merupakan usulan rancangan berdasarkan alur pembayaran dan perilaku pengiriman ulang.\n\n| Catatan | Pertanyaan yang dapat dijawab | Catatan tambahan yang diperlukan |\n|---|---|---|\n| Respons 402 | Berapa kali ketentuan pembayaran diminta | Apakah pembayaran benar-benar dilakukan |\n| Verifikasi berhasil | Apakah otorisasi valid | Hasil penyelesaian pembayaran |\n| Transfer USDC | Apakah aset berpindah | Keterkaitan dengan pesanan |\n| Penyelesaian pembayaran per pesanan | Apakah permintaan berbayar telah dibayar | Status pengiriman respons |\n| Catatan pengiriman ulang | Apakah permintaan yang sudah dibayar telah dipulihkan | Pengidentifikasi yang membedakannya dari pembayaran baru |\n\nPisahkan permintaan uji dalam penghitungan penggunaan nyata. Tandai juga percobaan ulang pesanan yang sama secara terpisah. Jangan menggambarkan volume transaksi itu sendiri sebagai permintaan API atau jaminan pendapatan.\n\n## Prosedur peralihan ke mainnet Base\n\nPertimbangkan mainnet setelah memeriksa jalur normal dan kegagalan di testnet. Peralihan tidak selesai hanya dengan mengganti string jaringan. Prosedur acuan mengikuti [panduan resmi penjual](https://docs.x402.org/getting-started/quickstart-for-sellers#running-on-mainnet).\n\n1. Pastikan facilitator yang dipilih mendukung mainnet Base.\n2. Periksa dukungan v2, `exact`, dan `eip155:8453` melalui `/supported`.\n3. Konfigurasikan autentikasi dan ketentuan penggunaan yang diwajibkan penyedia.\n4. Ubah jaringan server dan klien secara bersamaan.\n5. Ubah alamat USDC yang diizinkan klien menjadi alamat mainnet.\n6. Bandingkan kembali jumlah dalam respons 402 dan alamat penerima sebenarnya.\n7. Integrasikan penyimpanan permintaan duplikat dan prosedur pemulihan gangguan.\n8. Selesaikan pemeriksaan keamanan dependensi produksi.\n9. Verifikasi transfer nyata dan pengiriman konten dengan jumlah kecil yang telah dikonfigurasi.\n\nDukungan multichain tidak berarti semua kombinasi didukung. Periksa cakupan dukungan SDK dan facilitator secara bersamaan. Gunakan [dokumentasi dukungan jaringan dan token](https://docs.x402.org/core-concepts/network-and-token-support) sebagai acuan pemeriksaan.","content_html":"\u003cp\u003ePembayaran x402 diimplementasikan dengan menyertakan ketentuan pembayaran dalam respons 402 API. Siapkan Node.js dan dompet uji. Verifikasi terlebih dahulu tanda tangan dan penyelesaian pembayaran v2 di Base Sepolia. Atur agar respons berbayar hanya dikirim setelah penyelesaian pembayaran berhasil.\u003c/p\u003e\n\u003cp\u003eInformasi jumlah dan chain mengacu pada dokumentasi resmi x402, Base, dan Circle yang ditautkan sebagai sumber.\u003c/p\u003e\n\u003ch2\u003e\n\u003ca href=\"#persiapan-praktik-dan-instalasi\" class=\"anchor\" id=\"persiapan-praktik-dan-instalasi\"\u003e\u003c/a\u003ePersiapan praktik dan instalasi\u003c/h2\u003e\n\u003cp\u003eSiapkan lingkungan pengembangan yang memungkinkan Anda mengubah API serta dompet uji. Contoh ini disusun berdasarkan Node.js 22 dan x402 SDK 2.28.0. Struktur instalasinya mengikuti \u003ca href=\"https://docs.x402.org/getting-started/quickstart-for-sellers\"\u003epanduan resmi penjual\u003c/a\u003e.\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003eSiapkan alamat dompet EVM untuk menerima pembayaran.\u003c/li\u003e\n\u003cli\u003eSiapkan dompet uji terpisah untuk melakukan pembayaran.\u003c/li\u003e\n\u003cli\u003eDapatkan USDC uji Base Sepolia untuk dompet pembayaran.\u003c/li\u003e\n\u003cli\u003eBuat proyek praktik dengan perintah berikut.\u003c/li\u003e\n\u003cli\u003eSimpan berkas TypeScript selanjutnya dalam folder yang sama.\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\u003eServer penerima tidak memerlukan kunci privat dompet penerima. Atur agar kunci privat pembayaran hanya dibaca oleh proses klien. Metode penandatanganan dompet mengacu pada \u003ca href=\"https://docs.x402.org/getting-started/quickstart-for-buyers\"\u003epanduan resmi pembeli\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eCakupan verifikasi contoh ini adalah pemeriksaan tipe dan simulasi alur pembayaran. Contoh ini belum menuntaskan transfer nyata di testnet. Periksa sendiri hasil on-chain melalui langkah eksekusi di bawah.\u003c/p\u003e\n\u003ch2\u003e\n\u003ca href=\"#perbandingan-x402-v1-dan-v2\" class=\"anchor\" id=\"perbandingan-x402-v1-dan-v2\"\u003e\u003c/a\u003ePerbandingan x402 v1 dan v2\u003c/h2\u003e\n\u003cp\u003eSaat mengimplementasikan v2, sesuaikan header dan field isi sekaligus. Mengganti nama header saja akan membuat struktur data tidak cocok. Tabel berikut mengacu pada \u003ca href=\"https://docs.x402.org/guides/migration-v1-to-v2\"\u003epanduan migrasi resmi\u003c/a\u003e.\u003c/p\u003e\n\u003cdiv class=\"overflow-x-auto\"\u003e\u003ctable\u003e\n\u003cthead\u003e\n\u003ctr\u003e\n\u003cth\u003eAspek\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=\"Aspek\"\u003eVersi protokol\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=\"Aspek\"\u003eKetentuan pembayaran awal\u003c/td\u003e\n\u003ctd data-label=\"v1\"\u003eIsi JSON respons 402\u003c/td\u003e\n\u003ctd data-label=\"v2\"\u003eHeader respons \u003ccode\u003ePAYMENT-REQUIRED\u003c/code\u003e\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"Aspek\"\u003eData pembayaran yang ditandatangani\u003c/td\u003e\n\u003ctd data-label=\"v1\"\u003eHeader permintaan \u003ccode\u003eX-PAYMENT\u003c/code\u003e\n\u003c/td\u003e\n\u003ctd data-label=\"v2\"\u003eHeader permintaan \u003ccode\u003ePAYMENT-SIGNATURE\u003c/code\u003e\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"Aspek\"\u003eHasil penyelesaian pembayaran\u003c/td\u003e\n\u003ctd data-label=\"v1\"\u003eHeader respons \u003ccode\u003eX-PAYMENT-RESPONSE\u003c/code\u003e\n\u003c/td\u003e\n\u003ctd data-label=\"v2\"\u003eHeader respons \u003ccode\u003ePAYMENT-RESPONSE\u003c/code\u003e\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"Aspek\"\u003ePengidentifikasi mainnet Base\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=\"Aspek\"\u003ePengidentifikasi Base 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=\"Aspek\"\u003eField jumlah dalam ketentuan pembayaran\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=\"Aspek\"\u003eDeskripsi sumber daya\u003c/td\u003e\n\u003ctd data-label=\"v1\"\u003e\n\u003ccode\u003eresource\u003c/code\u003e dan lainnya di dalam ketentuan pembayaran\u003c/td\u003e\n\u003ctd data-label=\"v2\"\u003eObjek \u003ccode\u003eresource\u003c/code\u003e tingkat teratas\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"Aspek\"\u003eKetentuan dalam payload pembayaran\u003c/td\u003e\n\u003ctd data-label=\"v1\"\u003e\n\u003ccode\u003escheme\u003c/code\u003e, \u003ccode\u003enetwork\u003c/code\u003e, dan lainnya di tingkat teratas\u003c/td\u003e\n\u003ctd data-label=\"v2\"\u003eObjek \u003ccode\u003eaccepted\u003c/code\u003e yang memuat ketentuan terpilih\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"Aspek\"\u003eContoh paket TypeScript\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\u003eLokasi penyampaian ketentuan pembayaran v1 ditetapkan dalam \u003ca href=\"https://github.com/x402-foundation/x402/blob/main/specs/transports-v1/http.md\"\u003espesifikasi HTTP v1\u003c/a\u003e. Lokasi penyampaian standar v2 adalah header \u003ccode\u003ePAYMENT-REQUIRED\u003c/code\u003e. Implementasi juga dapat mengembalikan isi respons, tetapi gunakan header sebagai acuan pemrosesan.\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://github.com/x402-foundation/x402/blob/main/specs/transports-v2/http.md\"\u003eSpesifikasi HTTP v2\u003c/a\u003e menjelaskan kode status sebagai berikut.\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eServer menunjukkan bahwa pembayaran diperlukan menggunakan kode status HTTP 402 \"Payment Required\".\u003c/p\u003e\n\u003cp\u003eSumber: spesifikasi x402 HTTP v2\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003eHeader pembayaran v2 adalah nilai JSON yang dikodekan dengan Base64. Base64 bukan metode enkripsi. Gunakan HTTPS untuk layanan eksternal.\u003c/p\u003e\n\u003ch2\u003e\n\u003ca href=\"#urutan-permintaan-http-402-dan-pembayaran\" class=\"anchor\" id=\"urutan-permintaan-http-402-dan-pembayaran\"\u003e\u003c/a\u003eUrutan permintaan HTTP 402 dan pembayaran\u003c/h2\u003e\n\u003cp\u003ePraktik ini menggunakan alur otorisasi dasar metode EVM \u003ccode\u003eexact\u003c/code\u003e. \u003ccode\u003eexact\u003c/code\u003e adalah metode pembayaran dengan jumlah yang telah ditentukan. Urutannya mengikuti \u003ca href=\"https://github.com/x402-foundation/x402/blob/main/specs/x402-specification-v2.md\"\u003espesifikasi inti x402 v2\u003c/a\u003e.\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003eKlien meminta API berbayar tanpa informasi pembayaran.\u003c/li\u003e\n\u003cli\u003eServer mengembalikan HTTP 402 dan \u003ccode\u003ePAYMENT-REQUIRED\u003c/code\u003e.\u003c/li\u003e\n\u003cli\u003eKlien memilih ketentuan yang diizinkan dari \u003ccode\u003eaccepts\u003c/code\u003e.\u003c/li\u003e\n\u003cli\u003eSDK menandatangani otorisasi pembayaran sesuai ketentuan yang dipilih.\u003c/li\u003e\n\u003cli\u003eKlien mengirim ulang permintaan dengan menyertakan \u003ccode\u003ePAYMENT-SIGNATURE\u003c/code\u003e.\u003c/li\u003e\n\u003cli\u003eServer memverifikasi otorisasi melalui \u003ccode\u003e/verify\u003c/code\u003e milik facilitator.\u003c/li\u003e\n\u003cli\u003eJika verifikasi lolos, server menjalankan pemrosesan sumber daya.\u003c/li\u003e\n\u003cli\u003eServer meminta penyelesaian pembayaran melalui \u003ccode\u003e/settle\u003c/code\u003e.\u003c/li\u003e\n\u003cli\u003eSetelah penyelesaian pembayaran berhasil, server mengirim isi berbayar dan \u003ccode\u003ePAYMENT-RESPONSE\u003c/code\u003e.\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eUrutan ini bukan urutan tetap yang berlaku untuk semua metode pembayaran. v2 juga mendefinisikan alur dengan waktu penyelesaian pembayaran yang berbeda. Kode di bawah hanya mengizinkan alur \u003ccode\u003eauthorization\u003c/code\u003e dasar.\u003c/p\u003e\n\u003ch3\u003e\n\u003ca href=\"#peran-resource-server-dan-facilitator\" class=\"anchor\" id=\"peran-resource-server-dan-facilitator\"\u003e\u003c/a\u003ePeran resource server dan facilitator\u003c/h3\u003e\n\u003cp\u003eResource server menentukan harga dan ketentuan akses konten. Facilitator menangani verifikasi dan penyelesaian pembayaran. Pembagian peran mengacu pada \u003ca href=\"https://github.com/x402-foundation/x402/blob/main/docs/core-concepts/facilitator.md\"\u003edokumentasi resmi facilitator\u003c/a\u003e.\u003c/p\u003e\n\u003cdiv class=\"overflow-x-auto\"\u003e\u003ctable\u003e\n\u003cthead\u003e\n\u003ctr\u003e\n\u003cth\u003eKomponen\u003c/th\u003e\n\u003cth\u003eTugas\u003c/th\u003e\n\u003cth\u003eKondisi yang tidak boleh dianggap berhasil\u003c/th\u003e\n\u003c/tr\u003e\n\u003c/thead\u003e\n\u003ctbody\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"Komponen\"\u003eKlien\u003c/td\u003e\n\u003ctd data-label=\"Tugas\"\u003eMemilih ketentuan dan menandatangani otorisasi pembayaran\u003c/td\u003e\n\u003ctd data-label=\"Kondisi yang tidak boleh dianggap berhasil\"\u003eBaru selesai membuat tanda tangan\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"Komponen\"\u003eResource server\u003c/td\u003e\n\u003ctd data-label=\"Tugas\"\u003eMenetapkan harga dan mengendalikan respons berbayar\u003c/td\u003e\n\u003ctd data-label=\"Kondisi yang tidak boleh dianggap berhasil\"\u003eKlien mengklaim pembayaran berhasil\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"Komponen\"\u003eFacilitator \u003ccode\u003e/verify\u003c/code\u003e\n\u003c/td\u003e\n\u003ctd data-label=\"Tugas\"\u003eMemastikan keabsahan tanda tangan dan ketentuan pembayaran\u003c/td\u003e\n\u003ctd data-label=\"Kondisi yang tidak boleh dianggap berhasil\"\u003eHanya menerima \u003ccode\u003eisValid: true\u003c/code\u003e\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"Komponen\"\u003eFacilitator \u003ccode\u003e/settle\u003c/code\u003e\n\u003c/td\u003e\n\u003ctd data-label=\"Tugas\"\u003eDalam praktik ini, menjalankan pembayaran on-chain dan mengembalikan hasilnya\u003c/td\u003e\n\u003ctd data-label=\"Kondisi yang tidak boleh dianggap berhasil\"\u003eSudah dikirim, tetapi hasilnya belum dikonfirmasi\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 adalah pemeriksaan yang tidak mengubah status on-chain. Karena itu, keberhasilan verifikasi tidak berarti dana telah diterima. Saldo atau status otorisasi masih dapat berubah setelah verifikasi.\u003c/p\u003e\n\u003cp\u003eBlokir isi berbayar jika verifikasi atau penyelesaian pembayaran gagal. Pemblokiran saat keberhasilan belum dapat dipastikan disebut fail-closed. Perilaku ini juga dapat dilihat dalam \u003ca href=\"https://github.com/x402-foundation/x402/blob/main/typescript/packages/http/express/src/index.ts\"\u003eimplementasi resmi Express\u003c/a\u003e.\u003c/p\u003e\n\u003ch2\u003e\n\u003ca href=\"#perbandingan-mainnet-dan-testnet-base\" class=\"anchor\" id=\"perbandingan-mainnet-dan-testnet-base\"\u003e\u003c/a\u003ePerbandingan mainnet dan testnet Base\u003c/h2\u003e\n\u003cp\u003eGunakan hanya Base Sepolia untuk eksekusi pertama. Circle menyatakan bahwa USDC testnet tidak memiliki nilai finansial. Alamat mengacu pada \u003ca href=\"https://developers.circle.com/stablecoins/usdc-contract-addresses\"\u003edaftar kontrak USDC Circle\u003c/a\u003e.\u003c/p\u003e\n\u003cdiv class=\"overflow-x-auto\"\u003e\u003ctable\u003e\n\u003cthead\u003e\n\u003ctr\u003e\n\u003cth\u003eAspek\u003c/th\u003e\n\u003cth\u003eMainnet Base\u003c/th\u003e\n\u003cth\u003eTestnet Base Sepolia\u003c/th\u003e\n\u003c/tr\u003e\n\u003c/thead\u003e\n\u003ctbody\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"Aspek\"\u003eKegunaan\u003c/td\u003e\n\u003ctd data-label=\"Mainnet Base\"\u003ePembayaran dengan aset nyata\u003c/td\u003e\n\u003ctd data-label=\"Testnet Base Sepolia\"\u003eMenguji implementasi dan penanganan kegagalan\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"Aspek\"\u003eChain ID\u003c/td\u003e\n\u003ctd data-label=\"Mainnet Base\"\u003e\u003ccode\u003e8453\u003c/code\u003e\u003c/td\u003e\n\u003ctd data-label=\"Testnet Base Sepolia\"\u003e\u003ccode\u003e84532\u003c/code\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"Aspek\"\u003eJaringan x402 v2\u003c/td\u003e\n\u003ctd data-label=\"Mainnet Base\"\u003e\u003ccode\u003eeip155:8453\u003c/code\u003e\u003c/td\u003e\n\u003ctd data-label=\"Testnet Base Sepolia\"\u003e\u003ccode\u003eeip155:84532\u003c/code\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"Aspek\"\u003eKontrak USDC\u003c/td\u003e\n\u003ctd data-label=\"Mainnet Base\"\u003e\u003ccode\u003e0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913\u003c/code\u003e\u003c/td\u003e\n\u003ctd data-label=\"Testnet Base Sepolia\"\u003e\u003ccode\u003e0x036CbD53842c5426634e7929541eC2318f3dCF7e\u003c/code\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"Aspek\"\u003eRPC publik\u003c/td\u003e\n\u003ctd data-label=\"Mainnet Base\"\u003e\u003ccode\u003ehttps://mainnet.base.org\u003c/code\u003e\u003c/td\u003e\n\u003ctd data-label=\"Testnet 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=\"Aspek\"\u003eNilai finansial token\u003c/td\u003e\n\u003ctd data-label=\"Mainnet Base\"\u003eAset dengan nilai finansial nyata\u003c/td\u003e\n\u003ctd data-label=\"Testnet Base Sepolia\"\u003eTidak memiliki nilai finansial\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/tbody\u003e\n\u003c/table\u003e\u003c/div\u003e\n\u003cp\u003ePeriksa Chain ID dan RPC dalam \u003ca href=\"https://docs.base.org/get-started/connect-to-base\"\u003edokumentasi jaringan Base\u003c/a\u003e. Meski alamat dompet sama, saldo pada chain yang berbeda tetap terpisah. Jangan mencatat keberhasilan di testnet sebagai pendapatan nyata.\u003c/p\u003e\n\u003cp\u003eUSDC uji dapat diminta melalui \u003ca href=\"https://faucet.circle.com/\"\u003eCircle Testnet Faucet\u003c/a\u003e. Pilih Base Sepolia sebagai jaringan. Masukkan alamat dompet uji pembayaran sebagai alamat penerima.\u003c/p\u003e\n\u003ch2\u003e\n\u003ca href=\"#implementasi-api-berbayar-nodejs\" class=\"anchor\" id=\"implementasi-api-berbayar-nodejs\"\u003e\u003c/a\u003eImplementasi API berbayar Node.js\u003c/h2\u003e\n\u003cp\u003ePasang \u003ccode\u003epaymentMiddleware\u003c/code\u003e sebelum rute berbayar. Contoh di bawah mensyaratkan \u003cstrong\u003e0.001 USDC\u003c/strong\u003e per panggilan. Jumlah tersebut menggunakan contoh harga dalam panduan resmi penjual. (Nilai terverifikasi 0.001 USDC per panggilan · sumber docs.x402.org · diperiksa pada 2026-10-28)\u003c/p\u003e\n\u003ch3\u003e\n\u003ca href=\"#berkas-server\" class=\"anchor\" id=\"berkas-server\"\u003e\u003c/a\u003eBerkas server\u003c/h3\u003e\n\u003cp\u003eSimpan kode berikut sebagai \u003ccode\u003eserver.ts\u003c/code\u003e. Server hanya menerima permintaan melalui antarmuka lokal. Alamat penerima dibaca dari variabel lingkungan \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 adalah alamat pengujian dalam panduan resmi. Dalam contoh ini, alamat tersebut digunakan untuk penyelesaian pembayaran Base Sepolia. Untuk memilih facilitator produksi, lihat \u003ca href=\"https://docs.x402.org/getting-started/quickstart-for-sellers#running-on-mainnet\"\u003eprosedur mainnet dalam panduan penjual\u003c/a\u003e.\u003c/p\u003e\n\u003ch3\u003e\n\u003ca href=\"#contoh-perhitungan-harga\" class=\"anchor\" id=\"contoh-perhitungan-harga\"\u003e\u003c/a\u003eContoh perhitungan harga\u003c/h3\u003e\n\u003cp\u003eHarga contoh ini adalah \u003ccode\u003e1000\u003c/code\u003e dalam satuan terkecil USDC. USDC memiliki 6 angka desimal. Konversi harga oleh SDK mengacu pada \u003ca href=\"https://github.com/x402-foundation/x402/blob/main/typescript/packages/mechanisms/evm/src/exact/server/scheme.ts\"\u003eimplementasi server EVM\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\u003eNilai yang diperiksa\u003c/th\u003e\n\u003cth\u003eNilai yang diharapkan dalam contoh ini\u003c/th\u003e\n\u003c/tr\u003e\n\u003c/thead\u003e\n\u003ctbody\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"Nilai yang diperiksa\"\u003e\n\u003ccode\u003eprice\u003c/code\u003e dalam konfigurasi server\u003c/td\u003e\n\u003ctd data-label=\"Nilai yang diharapkan dalam contoh ini\"\u003e\u003ccode\u003e\"$0.001\"\u003c/code\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"Nilai yang diperiksa\"\u003e\n\u003ccode\u003eamount\u003c/code\u003e dalam ketentuan pembayaran 402\u003c/td\u003e\n\u003ctd data-label=\"Nilai yang diharapkan dalam contoh ini\"\u003e\u003ccode\u003e\"1000\"\u003c/code\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"Nilai yang diperiksa\"\u003e\n\u003ccode\u003easset\u003c/code\u003e dalam ketentuan pembayaran\u003c/td\u003e\n\u003ctd data-label=\"Nilai yang diharapkan dalam contoh ini\"\u003eKontrak USDC Base Sepolia\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"Nilai yang diperiksa\"\u003e\n\u003ccode\u003epayTo\u003c/code\u003e dalam ketentuan pembayaran\u003c/td\u003e\n\u003ctd data-label=\"Nilai yang diharapkan dalam contoh ini\"\u003eAlamat penerima yang dikonfigurasi\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"Nilai yang diperiksa\"\u003e\n\u003ccode\u003enetwork\u003c/code\u003e dalam ketentuan pembayaran\u003c/td\u003e\n\u003ctd data-label=\"Nilai yang diharapkan dalam contoh ini\"\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 adalah nilai konfigurasi SDK. Ketentuan pembayaran v2 yang sebenarnya menyampaikan \u003ccode\u003eamount\u003c/code\u003e. Bandingkan jumlah bilangan bulat sebagai string atau \u003ccode\u003ebigint\u003c/code\u003e.\u003c/p\u003e\n\u003ch3\u003e\n\u003ca href=\"#menjalankan-server-dan-memeriksa-402\" class=\"anchor\" id=\"menjalankan-server-dan-memeriksa-402\"\u003e\u003c/a\u003eMenjalankan server dan memeriksa 402\u003c/h3\u003e\n\u003cp\u003eJalankan server di terminal Bash pertama. Nilai yang dimasukkan adalah alamat publik penerima, bukan kunci privat. Biarkan terminal ini tetap terbuka selama server berjalan.\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\u003eKirim permintaan tanpa pembayaran dari terminal kedua. Perintah berikut tidak menandatangani pembayaran. Perintah ini menampilkan status HTTP dan ketentuan pembayaran yang telah didekodekan.\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\u003eRespons normal tanpa pembayaran adalah HTTP 402. Bandingkan ketentuan yang ditampilkan dengan tabel di atas. Jika konten berbayar langsung dikembalikan, perbaiki konfigurasi perlindungan rute terlebih dahulu.\u003c/p\u003e\n\u003ch2\u003e\n\u003ca href=\"#pemilihan-ketentuan-pembayaran-dan-implementasi-klien\" class=\"anchor\" id=\"pemilihan-ketentuan-pembayaran-dan-implementasi-klien\"\u003e\u003c/a\u003ePemilihan ketentuan pembayaran dan implementasi klien\u003c/h2\u003e\n\u003cp\u003eBatasi klien agar hanya menandatangani ketentuan yang diizinkan. Contoh ini hanya mengizinkan harga tetap dalam USDC Base Sepolia. Filter opsi pembayaran menggunakan \u003ccode\u003eregisterPolicy\u003c/code\u003e dari \u003ca href=\"https://github.com/x402-foundation/x402/blob/main/typescript/packages/core/src/client/x402Client.ts\"\u003eimplementasi resmi x402Client\u003c/a\u003e.\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003eSimpan kode berikut sebagai \u003ccode\u003eclient.ts\u003c/code\u003e.\u003c/li\u003e\n\u003cli\u003eSimpan juga \u003ccode\u003echeck-transfer.ts\u003c/code\u003e dari bagian berikutnya.\u003c/li\u003e\n\u003cli\u003eSetelah kedua berkas siap, lanjutkan ke langkah eksekusi.\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 menangani 402 dan pengiriman ulang permintaan bertanda tangan. Penggunaannya mengikuti \u003ca href=\"https://github.com/x402-foundation/x402/tree/main/examples/typescript/clients/fetch\"\u003econtoh fetch resmi\u003c/a\u003e. Jika tidak ada ketentuan pembayaran yang diizinkan, pembuatan pembayaran dihentikan.\u003c/p\u003e\n\u003cp\u003eKlien ini juga dapat digunakan oleh agen AI. Namun, batas harga per panggilan bukan batas pengeluaran kumulatif. Kelola anggaran panggilan berulang agen secara terpisah.\u003c/p\u003e\n\u003ch2\u003e\n\u003ca href=\"#verifikasi-event-transfer-usdc\" class=\"anchor\" id=\"verifikasi-event-transfer-usdc\"\u003e\u003c/a\u003eVerifikasi event Transfer USDC\u003c/h2\u003e\n\u003cp\u003ePeriksa log transfer USDC menggunakan hash transaksi dari hasil penyelesaian pembayaran. Status transaksi berhasil saja tidak memastikan siapa penerimanya. Field \u003ccode\u003eTransfer\u003c/code\u003e mengacu pada \u003ca href=\"https://eips.ethereum.org/EIPS/eip-20\"\u003estandar ERC-20\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eVerifikasi memerlukan hal-hal berikut.\u003c/p\u003e\n\u003cdiv class=\"overflow-x-auto\"\u003e\u003ctable\u003e\n\u003cthead\u003e\n\u003ctr\u003e\n\u003cth\u003eAspek verifikasi\u003c/th\u003e\n\u003cth\u003ePembanding\u003c/th\u003e\n\u003c/tr\u003e\n\u003c/thead\u003e\n\u003ctbody\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"Aspek verifikasi\"\u003eChain ID dari RPC\u003c/td\u003e\n\u003ctd data-label=\"Pembanding\"\u003eChain yang diizinkan server\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"Aspek verifikasi\"\u003e\n\u003ccode\u003estatus\u003c/code\u003e pada tanda terima transaksi\u003c/td\u003e\n\u003ctd data-label=\"Pembanding\"\u003eStatus berhasil\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"Aspek verifikasi\"\u003eAlamat yang memancarkan event\u003c/td\u003e\n\u003ctd data-label=\"Pembanding\"\u003eKontrak USDC resmi pada chain tersebut\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"Aspek verifikasi\"\u003e\u003ccode\u003eTransfer.from\u003c/code\u003e\u003c/td\u003e\n\u003ctd data-label=\"Pembanding\"\u003ePembayar yang telah dipastikan\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"Aspek verifikasi\"\u003e\u003ccode\u003eTransfer.to\u003c/code\u003e\u003c/td\u003e\n\u003ctd data-label=\"Pembanding\"\u003eAlamat penerima yang ditetapkan server\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"Aspek verifikasi\"\u003e\u003ccode\u003eTransfer.value\u003c/code\u003e\u003c/td\u003e\n\u003ctd data-label=\"Pembanding\"\u003eJumlah dalam satuan terkecil yang diminta server\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/tbody\u003e\n\u003c/table\u003e\u003c/div\u003e\n\u003cp\u003eSimpan kode berikut sebagai \u003ccode\u003echeck-transfer.ts\u003c/code\u003e. Kueri menggunakan \u003ca href=\"https://viem.sh/docs/actions/public/getTransactionReceipt\"\u003egetTransactionReceipt\u003c/a\u003e dari viem. Penguraian event mengacu pada \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\u003eFungsi ini memastikan bahwa transfer USDC terjadi dalam transaksi tersebut. Keterkaitannya dengan pesanan API tertentu harus diperiksa secara terpisah. Pada server produksi, gunakan ketentuan pesanan yang tersimpan sebagai nilai pembanding.\u003c/p\u003e\n\u003cp\u003eJangan memercayai \u003ccode\u003etransfer.json\u003c/code\u003e yang diunggah klien sebagai bukti untuk memberikan akses. Server dalam contoh ini menggunakan hasil verifikasi dan penyelesaian pembayaran dari facilitator. Jika kueri independen dijadikan syarat pemberian akses, jalankan kueri tersebut di server.\u003c/p\u003e\n\u003cp\u003eKegagalan mengambil tanda terima tidak serta-merta berarti pembayaran gagal. Jika terjadi galat RPC, kueri ulang hash transaksi yang tersimpan. Jumlah konfirmasi tambahan dan penanganan reorganisasi chain ditetapkan dalam kebijakan operasional.\u003c/p\u003e\n\u003ch2\u003e\n\u003ca href=\"#menjalankan-pembayaran-uji-dan-memeriksa-hasil\" class=\"anchor\" id=\"menjalankan-pembayaran-uji-dan-memeriksa-hasil\"\u003e\u003c/a\u003eMenjalankan pembayaran uji dan memeriksa hasil\u003c/h2\u003e\n\u003cp\u003eSetelah semua berkas disimpan, jalankan dengan dompet uji. Pemeriksaan tipe tidak melakukan penandatanganan atau transfer. Perintah berikut menggunakan alat TypeScript yang telah dipasang sebelumnya.\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003ePeriksa tipe dengan perintah berikut.\u003c/li\u003e\n\u003cli\u003eMasukkan alamat penerima yang sama dengan server di terminal kedua.\u003c/li\u003e\n\u003cli\u003eMasukkan kunci privat dompet pembayaran uji dengan input tersembunyi.\u003c/li\u003e\n\u003cli\u003eJalankan klien satu kali.\u003c/li\u003e\n\u003cli\u003ePeriksa hasil penyelesaian pembayaran dan log transfer yang tersimpan.\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\u003eKeluaran\u003c/th\u003e\n\u003cth\u003eArti\u003c/th\u003e\n\u003cth\u003eHal yang diperiksa\u003c/th\u003e\n\u003c/tr\u003e\n\u003c/thead\u003e\n\u003ctbody\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"Keluaran\"\u003e\u003ccode\u003esettlement.json\u003c/code\u003e\u003c/td\u003e\n\u003ctd data-label=\"Arti\"\u003eHasil penyelesaian pembayaran yang dikembalikan server\u003c/td\u003e\n\u003ctd data-label=\"Hal yang diperiksa\"\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=\"Keluaran\"\u003e\u003ccode\u003etransfer.json\u003c/code\u003e\u003c/td\u003e\n\u003ctd data-label=\"Arti\"\u003eCatatan transfer yang ditemukan melalui kueri RPC\u003c/td\u003e\n\u003ctd data-label=\"Hal yang diperiksa\"\u003eChain, hash transaksi, posisi log, informasi blok\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"Keluaran\"\u003eIsi berbayar di konsol\u003c/td\u003e\n\u003ctd data-label=\"Arti\"\u003eData yang ditampilkan setelah verifikasi oleh klien ini\u003c/td\u003e\n\u003ctd data-label=\"Hal yang diperiksa\"\u003ePastikan konten sesuai permintaan\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/tbody\u003e\n\u003c/table\u003e\u003c/div\u003e\n\u003cp\u003eMenjalankan ulang klien dapat menimbulkan pembayaran baru. Contoh minimal ini tidak memiliki penyimpanan pencegah duplikasi per pesanan. Tambahkan fitur percobaan ulang setelah memenuhi ketentuan pada bagian berikutnya.\u003c/p\u003e\n\u003ch2\u003e\n\u003ca href=\"#penanganan-berdasarkan-kondisi-kegagalan-verifikasi\" class=\"anchor\" id=\"penanganan-berdasarkan-kondisi-kegagalan-verifikasi\"\u003e\u003c/a\u003ePenanganan berdasarkan kondisi kegagalan verifikasi\u003c/h2\u003e\n\u003cp\u003eDalam pengujian kegagalan, pastikan isi berbayar tidak terungkap. Memeriksa status HTTP saja dapat membuat kegagalan pemblokiran terlewat. Lihat \u003ca href=\"https://github.com/x402-foundation/x402/blob/main/typescript/packages/http/express/src/index.ts\"\u003emiddleware resmi Express\u003c/a\u003e sebagai acuan perilaku.\u003c/p\u003e\n\u003cdiv class=\"overflow-x-auto\"\u003e\u003ctable\u003e\n\u003cthead\u003e\n\u003ctr\u003e\n\u003cth\u003eKondisi pengujian\u003c/th\u003e\n\u003cth\u003ePenanganan yang diharapkan\u003c/th\u003e\n\u003cth\u003ePemeriksaan tambahan\u003c/th\u003e\n\u003c/tr\u003e\n\u003c/thead\u003e\n\u003ctbody\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"Kondisi pengujian\"\u003eTidak ada header pembayaran\u003c/td\u003e\n\u003ctd data-label=\"Penanganan yang diharapkan\"\u003eMengembalikan 402 dan ketentuan pembayaran\u003c/td\u003e\n\u003ctd data-label=\"Pemeriksaan tambahan\"\u003eTidak ada isi berbayar\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"Kondisi pengujian\"\u003eToken atau penerima berbeda diajukan\u003c/td\u003e\n\u003ctd data-label=\"Penanganan yang diharapkan\"\u003eDitolak oleh kebijakan klien\u003c/td\u003e\n\u003ctd data-label=\"Pemeriksaan tambahan\"\u003eTanda tangan tidak dibuat\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"Kondisi pengujian\"\u003eTanda tangan tidak valid\u003c/td\u003e\n\u003ctd data-label=\"Penanganan yang diharapkan\"\u003eDiblokir karena verifikasi gagal\u003c/td\u003e\n\u003ctd data-label=\"Pemeriksaan tambahan\"\u003eHandler berbayar tidak dijalankan\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"Kondisi pengujian\"\u003eSaldo tidak cukup\u003c/td\u003e\n\u003ctd data-label=\"Penanganan yang diharapkan\"\u003eDiblokir karena verifikasi atau penyelesaian pembayaran gagal\u003c/td\u003e\n\u003ctd data-label=\"Pemeriksaan tambahan\"\u003eTidak dicatat sebagai penyelesaian pembayaran berhasil\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"Kondisi pengujian\"\u003eHasil penyelesaian pembayaran gagal\u003c/td\u003e\n\u003ctd data-label=\"Penanganan yang diharapkan\"\u003eRespons berbayar diblokir\u003c/td\u003e\n\u003ctd data-label=\"Pemeriksaan tambahan\"\u003eIsi yang sudah disiapkan pun tidak dikirim\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"Kondisi pengujian\"\u003eRespons facilitator terlambat\u003c/td\u003e\n\u003ctd data-label=\"Penanganan yang diharapkan\"\u003eDitangguhkan karena keberhasilan belum dipastikan\u003c/td\u003e\n\u003ctd data-label=\"Pemeriksaan tambahan\"\u003eJangan membuat otorisasi baru secara otomatis\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"Kondisi pengujian\"\u003eGalat kueri tanda terima RPC\u003c/td\u003e\n\u003ctd data-label=\"Penanganan yang diharapkan\"\u003eSimpan hash transaksi dan lakukan kueri ulang\u003c/td\u003e\n\u003ctd data-label=\"Pemeriksaan tambahan\"\u003eJangan langsung menyimpulkan pembayaran gagal\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/tbody\u003e\n\u003c/table\u003e\u003c/div\u003e\n\u003cp\u003eDalam alur otorisasi dasar, handler dijalankan sebelum penyelesaian pembayaran. Kegagalan penyelesaian pembayaran tidak membatalkan tindakan eksternal. Mulailah dari kueri tanpa efek samping seperti dalam contoh ini.\u003c/p\u003e\n\u003cp\u003ePengiriman email atau pembuatan pesanan eksternal memerlukan rancangan tersendiri. Catat status pembayaran dan pekerjaan secara bersamaan. Tentukan cakupan eksekusi ulang saat terjadi kegagalan untuk mengurangi pekerjaan ganda.\u003c/p\u003e\n\u003ch2\u003e\n\u003ca href=\"#pemulihan-respons-yang-hilang-setelah-penyelesaian-pembayaran-dan-pengiriman-ulang\" class=\"anchor\" id=\"pemulihan-respons-yang-hilang-setelah-penyelesaian-pembayaran-dan-pengiriman-ulang\"\u003e\u003c/a\u003ePemulihan respons yang hilang setelah penyelesaian pembayaran dan pengiriman ulang\u003c/h2\u003e\n\u003cp\u003eSimpan penyelesaian pembayaran dan penyelesaian pengiriman konten sebagai status terpisah. Pembayaran mungkin sudah selesai, tetapi koneksi respons terputus. Acuan penanganan situasi ini adalah \u003ca href=\"https://docs.x402.org/extensions/payment-identifier\"\u003edokumentasi ekstensi Payment-Identifier\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eSifat tidak memproses pekerjaan yang sama dua kali meskipun diminta ulang disebut idempotensi. \u003ccode\u003epayment-identifier\u003c/code\u003e menyediakan pengidentifikasi pembayaran untuk tujuan ini. Fitur ini tidak otomatis lengkap hanya dengan menambahkan pengidentifikasi.\u003c/p\u003e\n\u003ch3\u003e\n\u003ca href=\"#perbandingan-pembayaran-ganda-dan-pengiriman-ulang\" class=\"anchor\" id=\"perbandingan-pembayaran-ganda-dan-pengiriman-ulang\"\u003e\u003c/a\u003ePerbandingan pembayaran ganda dan pengiriman ulang\u003c/h3\u003e\n\u003cp\u003ePencegahan penggunaan ulang tanda tangan yang sama harus dibedakan dari pencegahan duplikasi pesanan. EIP-3009 mencegah penggunaan ulang nonce otorisasi yang telah digunakan. Perilaku ini didefinisikan dalam \u003ca href=\"https://eips.ethereum.org/EIPS/eip-3009\"\u003espesifikasi ERC-3009\u003c/a\u003e.\u003c/p\u003e\n\u003cdiv class=\"overflow-x-auto\"\u003e\u003ctable\u003e\n\u003cthead\u003e\n\u003ctr\u003e\n\u003cth\u003eSituasi\u003c/th\u003e\n\u003cth\u003eDari sisi otorisasi on-chain\u003c/th\u003e\n\u003cth\u003eDari sisi operasional API\u003c/th\u003e\n\u003c/tr\u003e\n\u003c/thead\u003e\n\u003ctbody\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"Situasi\"\u003eMenyelesaikan pembayaran ulang dengan otorisasi yang sama\u003c/td\u003e\n\u003ctd data-label=\"Dari sisi otorisasi on-chain\"\u003eDiperiksa sebagai nonce yang sudah digunakan\u003c/td\u003e\n\u003ctd data-label=\"Dari sisi operasional API\"\u003ePerlu mengambil hasil sebelumnya\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"Situasi\"\u003eMembuat otorisasi baru untuk pesanan yang sama\u003c/td\u003e\n\u003ctd data-label=\"Dari sisi otorisasi on-chain\"\u003eDapat diperlakukan sebagai otorisasi terpisah\u003c/td\u003e\n\u003ctd data-label=\"Dari sisi operasional API\"\u003ePerlu mencegah pembayaran ganda\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"Situasi\"\u003eRespons hilang setelah penyelesaian pembayaran berhasil\u003c/td\u003e\n\u003ctd data-label=\"Dari sisi otorisasi on-chain\"\u003ePerpindahan aset mungkin sudah selesai\u003c/td\u003e\n\u003ctd data-label=\"Dari sisi operasional API\"\u003ePerlu mengirim ulang konten\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"Situasi\"\u003eBatas waktu terlampaui setelah permintaan penyelesaian pembayaran\u003c/td\u003e\n\u003ctd data-label=\"Dari sisi otorisasi on-chain\"\u003eHasil masih belum jelas\u003c/td\u003e\n\u003ctd data-label=\"Dari sisi operasional API\"\u003ePerlu status tertunda dan pemeriksaan penyelesaian pembayaran\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/tbody\u003e\n\u003c/table\u003e\u003c/div\u003e\n\u003cp\u003eProsedur berikut adalah rancangan untuk menghubungkan ekstensi resmi dengan penyimpanan produksi. Prosedur ini tidak termasuk dalam kode minimal sebelumnya. Untuk menerapkannya di produksi, tambahkan penyimpanan persisten dan pengendalian permintaan serentak.\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003eDeklarasikan dukungan ekstensi \u003ccode\u003epayment-identifier\u003c/code\u003e di server.\u003c/li\u003e\n\u003cli\u003eSimpan pengidentifikasi permintaan logis di klien.\u003c/li\u003e\n\u003cli\u003eHubungkan pengidentifikasi dengan pembayar dan isi permintaan di server.\u003c/li\u003e\n\u003cli\u003eCatat status sedang diproses secara atomik untuk mencegah eksekusi serentak.\u003c/li\u003e\n\u003cli\u003eSaat penyelesaian pembayaran berhasil, simpan bukti transaksi dan hasil respons.\u003c/li\u003e\n\u003cli\u003eJika hak akses dan isi permintaan ulang sama, kembalikan hasil yang tersimpan.\u003c/li\u003e\n\u003cli\u003eJika hasil penyelesaian pembayaran belum jelas, periksa otorisasi sebelumnya terlebih dahulu.\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003ePengidentifikasi pembayaran bukan kata sandi atau token akses. Jangan mengembalikan cache berbayar kepada permintaan yang hanya mengetahui pengidentifikasi. Periksa juga apakah pengidentifikasi digunakan ulang oleh pengguna lain atau untuk isi permintaan yang berbeda.\u003c/p\u003e\n\u003cp\u003eSimpan chain, hash transaksi, dan posisi log bersama-sama dalam catatan operasional. Hubungkan catatan ini dengan pesanan untuk memblokir penggunaan ulang pada pesanan lain. Kecocokan \u003ccode\u003eTransfer\u003c/code\u003e saja tidak membuktikan keterkaitannya dengan pesanan.\u003c/p\u003e\n\u003ch2\u003e\n\u003ca href=\"#kesalahan-umum-dalam-implementasi-pembayaran\" class=\"anchor\" id=\"kesalahan-umum-dalam-implementasi-pembayaran\"\u003e\u003c/a\u003eKesalahan umum dalam implementasi pembayaran\u003c/h2\u003e\n\u003cp\u003eMencampuradukkan field versi dan indikator keberhasilan dapat menyebabkan kesalahan penentuan pembayaran. Gunakan tabel berikut saat meninjau deployment. Perbedaan versi mengacu pada \u003ca href=\"https://docs.x402.org/guides/migration-v1-to-v2\"\u003epanduan migrasi x402\u003c/a\u003e.\u003c/p\u003e\n\u003cdiv class=\"overflow-x-auto\"\u003e\u003ctable\u003e\n\u003cthead\u003e\n\u003ctr\u003e\n\u003cth\u003eKesalahan umum\u003c/th\u003e\n\u003cth\u003eAcuan perbaikan\u003c/th\u003e\n\u003c/tr\u003e\n\u003c/thead\u003e\n\u003ctbody\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"Kesalahan umum\"\u003eMenafsirkan \u003ccode\u003eextra.version: \"2\"\u003c/code\u003e sebagai versi x402\u003c/td\u003e\n\u003ctd data-label=\"Acuan perbaikan\"\u003ePeriksa versi protokol melalui \u003ccode\u003ex402Version\u003c/code\u003e\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"Kesalahan umum\"\u003eMenggunakan \u003ccode\u003emaxAmountRequired\u003c/code\u003e pada v2\u003c/td\u003e\n\u003ctd data-label=\"Acuan perbaikan\"\u003eGunakan \u003ccode\u003eamount\u003c/code\u003e untuk jumlah dalam ketentuan pembayaran\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"Kesalahan umum\"\u003eMembuat header \u003ccode\u003eX-PAYMENT-REQUIRED\u003c/code\u003e sendiri\u003c/td\u003e\n\u003ctd data-label=\"Acuan perbaikan\"\u003ev1 menggunakan isi respons, v2 menggunakan \u003ccode\u003ePAYMENT-REQUIRED\u003c/code\u003e\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"Kesalahan umum\"\u003eMemperlakukan \u003ccode\u003eisValid: true\u003c/code\u003e sebagai dana telah diterima\u003c/td\u003e\n\u003ctd data-label=\"Acuan perbaikan\"\u003ePeriksa hasil \u003ccode\u003e/settle\u003c/code\u003e secara terpisah\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"Kesalahan umum\"\u003eMemperlakukan \u003ccode\u003eto\u003c/code\u003e tingkat teratas transaksi sebagai penerima USDC\u003c/td\u003e\n\u003ctd data-label=\"Acuan perbaikan\"\u003ePeriksa \u003ccode\u003eTransfer.to\u003c/code\u003e USDC\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"Kesalahan umum\"\u003eMenganggap penanda tangan sama dengan pengirim transaksi\u003c/td\u003e\n\u003ctd data-label=\"Acuan perbaikan\"\u003eBandingkan pembayar dalam otorisasi dengan \u003ccode\u003eTransfer.from\u003c/code\u003e\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"Kesalahan umum\"\u003eMenjumlahkan penerimaan testnet sebagai pendapatan\u003c/td\u003e\n\u003ctd data-label=\"Acuan perbaikan\"\u003ePisahkan pembukuan mainnet dan testnet\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"Kesalahan umum\"\u003eMembuat pembayaran baru setiap kali batas waktu terlampaui\u003c/td\u003e\n\u003ctd data-label=\"Acuan perbaikan\"\u003ePulihkan pesanan dan status otorisasi sebelumnya terlebih dahulu\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"Kesalahan umum\"\u003eMenyamakan versi paket SDK dengan versi protokol\u003c/td\u003e\n\u003ctd data-label=\"Acuan perbaikan\"\u003eKelola versi paket dan \u003ccode\u003ex402Version\u003c/code\u003e secara terpisah\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/tbody\u003e\n\u003c/table\u003e\u003c/div\u003e\n\u003cp\u003eInformasi domain tanda tangan USDC juga memuat string versi. Nilai tersebut memiliki tujuan berbeda dari versi protokol x402. Lihat \u003ca href=\"https://eips.ethereum.org/EIPS/eip-3009\"\u003espesifikasi ERC-3009\u003c/a\u003e untuk struktur domain tanda tangan.\u003c/p\u003e\n\u003ch2\u003e\n\u003ca href=\"#pemeriksaan-keamanan-dan-batas-kepercayaan-layanan-eksternal\" class=\"anchor\" id=\"pemeriksaan-keamanan-dan-batas-kepercayaan-layanan-eksternal\"\u003e\u003c/a\u003ePemeriksaan keamanan dan batas kepercayaan layanan eksternal\u003c/h2\u003e\n\u003cp\u003eKendalikan penentuan keberhasilan pembayaran di server. Respons facilitator eksternal juga merupakan sumber yang secara eksplisit dipercaya. Arsitektur yang memungkinkan pengoperasian mandiri dijelaskan dalam \u003ca href=\"https://github.com/x402-foundation/x402/blob/main/docs/core-concepts/facilitator.md\"\u003edokumentasi facilitator\u003c/a\u003e.\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eJangan membuka akses konten berdasarkan pesan keberhasilan dari klien.\u003c/li\u003e\n\u003cli\u003eBandingkan chain, token, jumlah, dan penerima yang ditetapkan server.\u003c/li\u003e\n\u003cli\u003eJangan mencatat kunci privat dan frasa pemulihan dalam log.\u003c/li\u003e\n\u003cli\u003eKecualikan juga tanda tangan otorisasi lengkap dari log akses umum.\u003c/li\u003e\n\u003cli\u003eBatasi ukuran header pembayaran dan waktu pemrosesan permintaan.\u003c/li\u003e\n\u003cli\u003ePastikan respons berbayar tidak tersimpan dalam cache bersama.\u003c/li\u003e\n\u003cli\u003ePisahkan lokasi penyimpanan kunci uji dan kunci produksi.\u003c/li\u003e\n\u003cli\u003eJangan membuat jalur pintas tanpa pembayaran saat facilitator mengalami gangguan.\u003c/li\u003e\n\u003cli\u003eUji secara langsung kedaluwarsa otorisasi dan permintaan duplikat.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eDependensi yang dipatok dalam praktik ini masih memiliki peringatan pemeriksaan keamanan. \u003ccode\u003enpm audit\u003c/code\u003e melaporkan advisori terkait zod. Lihat rinciannya dalam \u003ca href=\"https://github.com/advisories/GHSA-mrhx-c7xc-m3w5\"\u003eadvisori keamanan GitHub\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\u003ePeriksa kompatibilitas SDK versi perbaikan sebelum deployment produksi. Mengganti versi mayor secara paksa dapat mengubah perilaku. Jangan gunakan praktik ini sebagai konfigurasi deployment yang telah lolos verifikasi keamanan.\u003c/p\u003e\n\u003ch3\u003e\n\u003ca href=\"#perbandingan-server-sendiri-dan-cloudflare-gateway\" class=\"anchor\" id=\"perbandingan-server-sendiri-dan-cloudflare-gateway\"\u003e\u003c/a\u003ePerbandingan server sendiri dan Cloudflare Gateway\u003c/h3\u003e\n\u003cp\u003ePenggunaan Cloudflare Gateway mengubah metode verifikasi di server asal. Dokumentasi bertanggal 30 September 2026 menyatakan penggunaan x402 v2. Perbedaan berikut mengacu pada \u003ca href=\"https://developers.cloudflare.com/monetization-gateway/x402/\"\u003edokumentasi Cloudflare x402\u003c/a\u003e.\u003c/p\u003e\n\u003cdiv class=\"overflow-x-auto\"\u003e\u003ctable\u003e\n\u003cthead\u003e\n\u003ctr\u003e\n\u003cth\u003eAspek\u003c/th\u003e\n\u003cth\u003eMetode Express dalam artikel ini\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=\"Aspek\"\u003ePemrosesan pembayaran klien\u003c/td\u003e\n\u003ctd data-label=\"Metode Express dalam artikel ini\"\u003eMiddleware Express\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=\"Aspek\"\u003eTitik verifikasi server asal\u003c/td\u003e\n\u003ctd data-label=\"Metode Express dalam artikel ini\"\u003eIntegrasi dengan facilitator yang dipilih\u003c/td\u003e\n\u003ctd data-label=\"Cloudflare Monetization Gateway\"\u003eVerifikasi JWT \u003ccode\u003ePAYMENT-CONTEXT\u003c/code\u003e\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"Aspek\"\u003ePenyampaian hasil penyelesaian pembayaran\u003c/td\u003e\n\u003ctd data-label=\"Metode Express dalam artikel ini\"\u003e\n\u003ccode\u003ePAYMENT-RESPONSE\u003c/code\u003e dari middleware\u003c/td\u003e\n\u003ctd data-label=\"Cloudflare Monetization Gateway\"\u003eGateway membuat tanda terima penyelesaian pembayaran\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"Aspek\"\u003eHal tambahan yang diperiksa\u003c/td\u003e\n\u003ctd data-label=\"Metode Express dalam artikel ini\"\u003eChain yang didukung facilitator dan autentikasi\u003c/td\u003e\n\u003ctd data-label=\"Cloudflare Monetization Gateway\"\u003eVerifikasi tanda tangan JWT dan perlindungan akses ke server asal\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 adalah header antara Gateway dan server asal. Header ini bukan header tanda tangan klien x402 biasa. Ikuti \u003ca href=\"https://developers.cloudflare.com/monetization-gateway/configuration/payment-validation/\"\u003edokumentasi Cloudflare Payment validation\u003c/a\u003e untuk verifikasi di server asal.\u003c/p\u003e\n\u003cp\u003eMeski melewati Gateway, jangan sekadar mendekodekan JWT. Verifikasi tanda tangannya dengan kunci yang ditentukan dokumentasi. Blokir konten berbayar jika header tidak ada atau verifikasi gagal.\u003c/p\u003e\n\u003ch2\u003e\n\u003ca href=\"#membedakan-penggunaan-nyata-dan-catatan-penyelesaian-pembayaran\" class=\"anchor\" id=\"membedakan-penggunaan-nyata-dan-catatan-penyelesaian-pembayaran\"\u003e\u003c/a\u003eMembedakan penggunaan nyata dan catatan penyelesaian pembayaran\u003c/h2\u003e\n\u003cp\u003eJumlah transfer on-chain saja tidak dapat membuktikan konsumsi konten. Hitung metrik operasional dengan menghubungkan penyelesaian pembayaran dan pengiriman respons. Ini merupakan usulan rancangan berdasarkan alur pembayaran dan perilaku pengiriman ulang.\u003c/p\u003e\n\u003cdiv class=\"overflow-x-auto\"\u003e\u003ctable\u003e\n\u003cthead\u003e\n\u003ctr\u003e\n\u003cth\u003eCatatan\u003c/th\u003e\n\u003cth\u003ePertanyaan yang dapat dijawab\u003c/th\u003e\n\u003cth\u003eCatatan tambahan yang diperlukan\u003c/th\u003e\n\u003c/tr\u003e\n\u003c/thead\u003e\n\u003ctbody\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"Catatan\"\u003eRespons 402\u003c/td\u003e\n\u003ctd data-label=\"Pertanyaan yang dapat dijawab\"\u003eBerapa kali ketentuan pembayaran diminta\u003c/td\u003e\n\u003ctd data-label=\"Catatan tambahan yang diperlukan\"\u003eApakah pembayaran benar-benar dilakukan\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"Catatan\"\u003eVerifikasi berhasil\u003c/td\u003e\n\u003ctd data-label=\"Pertanyaan yang dapat dijawab\"\u003eApakah otorisasi valid\u003c/td\u003e\n\u003ctd data-label=\"Catatan tambahan yang diperlukan\"\u003eHasil penyelesaian pembayaran\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"Catatan\"\u003eTransfer USDC\u003c/td\u003e\n\u003ctd data-label=\"Pertanyaan yang dapat dijawab\"\u003eApakah aset berpindah\u003c/td\u003e\n\u003ctd data-label=\"Catatan tambahan yang diperlukan\"\u003eKeterkaitan dengan pesanan\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"Catatan\"\u003ePenyelesaian pembayaran per pesanan\u003c/td\u003e\n\u003ctd data-label=\"Pertanyaan yang dapat dijawab\"\u003eApakah permintaan berbayar telah dibayar\u003c/td\u003e\n\u003ctd data-label=\"Catatan tambahan yang diperlukan\"\u003eStatus pengiriman respons\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd data-label=\"Catatan\"\u003eCatatan pengiriman ulang\u003c/td\u003e\n\u003ctd data-label=\"Pertanyaan yang dapat dijawab\"\u003eApakah permintaan yang sudah dibayar telah dipulihkan\u003c/td\u003e\n\u003ctd data-label=\"Catatan tambahan yang diperlukan\"\u003ePengidentifikasi yang membedakannya dari pembayaran baru\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/tbody\u003e\n\u003c/table\u003e\u003c/div\u003e\n\u003cp\u003ePisahkan permintaan uji dalam penghitungan penggunaan nyata. Tandai juga percobaan ulang pesanan yang sama secara terpisah. Jangan menggambarkan volume transaksi itu sendiri sebagai permintaan API atau jaminan pendapatan.\u003c/p\u003e\n\u003ch2\u003e\n\u003ca href=\"#prosedur-peralihan-ke-mainnet-base\" class=\"anchor\" id=\"prosedur-peralihan-ke-mainnet-base\"\u003e\u003c/a\u003eProsedur peralihan ke mainnet Base\u003c/h2\u003e\n\u003cp\u003ePertimbangkan mainnet setelah memeriksa jalur normal dan kegagalan di testnet. Peralihan tidak selesai hanya dengan mengganti string jaringan. Prosedur acuan mengikuti \u003ca href=\"https://docs.x402.org/getting-started/quickstart-for-sellers#running-on-mainnet\"\u003epanduan resmi penjual\u003c/a\u003e.\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003ePastikan facilitator yang dipilih mendukung mainnet Base.\u003c/li\u003e\n\u003cli\u003ePeriksa dukungan v2, \u003ccode\u003eexact\u003c/code\u003e, dan \u003ccode\u003eeip155:8453\u003c/code\u003e melalui \u003ccode\u003e/supported\u003c/code\u003e.\u003c/li\u003e\n\u003cli\u003eKonfigurasikan autentikasi dan ketentuan penggunaan yang diwajibkan penyedia.\u003c/li\u003e\n\u003cli\u003eUbah jaringan server dan klien secara bersamaan.\u003c/li\u003e\n\u003cli\u003eUbah alamat USDC yang diizinkan klien menjadi alamat mainnet.\u003c/li\u003e\n\u003cli\u003eBandingkan kembali jumlah dalam respons 402 dan alamat penerima sebenarnya.\u003c/li\u003e\n\u003cli\u003eIntegrasikan penyimpanan permintaan duplikat dan prosedur pemulihan gangguan.\u003c/li\u003e\n\u003cli\u003eSelesaikan pemeriksaan keamanan dependensi produksi.\u003c/li\u003e\n\u003cli\u003eVerifikasi transfer nyata dan pengiriman konten dengan jumlah kecil yang telah dikonfigurasi.\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eDukungan multichain tidak berarti semua kombinasi didukung. Periksa cakupan dukungan SDK dan facilitator secara bersamaan. Gunakan \u003ca href=\"https://docs.x402.org/core-concepts/network-and-token-support\"\u003edokumentasi dukungan jaringan dan token\u003c/a\u003e sebagai acuan pemeriksaan.\u003c/p\u003e\n","tags":["Stablecoin","Agen AI","Pengembangan AI","Monetisasi konten","Alat pengembangan"],"faqs":[{"question":"Apa perbedaan x402 dengan konfirmasi setoran USDC biasa?","answer":"Syarat pembayaran disampaikan selama proses permintaan HTTP, lalu permintaan dikirim ulang dengan otorisasi pembayaran yang telah ditandatangani. Berbeda dengan cara mencocokkan riwayat setoran belakangan, mekanisme ini menghubungkan proses pembayaran dengan permintaan sumber daya berbayar."},{"question":"Apa saja header pembayaran pada x402 v2?","answer":"Syarat pembayaran disampaikan melalui PAYMENT-REQUIRED, sedangkan payload pembayaran melalui PAYMENT-SIGNATURE. Hasil penyelesaian pembayaran disampaikan melalui PAYMENT-RESPONSE."},{"question":"Apakah ada header X-PAYMENT-REQUIRED pada v1?","answer":"Spesifikasi resmi HTTP v1 menempatkan syarat pembayaran awal dalam badan respons JSON. Payload pembayaran dan hasil penyelesaian pembayaran masing-masing menggunakan X-PAYMENT dan X-PAYMENT-RESPONSE."},{"question":"Jika /verify berhasil, apakah pembayaran sudah selesai?","answer":"Keberhasilan /verify berarti otorisasi pembayaran valid. Dalam praktik ini, hasil keberhasilan /settle harus diperiksa secara terpisah."},{"question":"Apakah USDC uji di Base Sepolia dapat dianggap sebagai pendapatan nyata?","answer":"Circle menyatakan bahwa USDC di testnet tidak memiliki nilai finansial. Catat penyelesaian pembayaran di testnet secara terpisah dari penjualan nyata."},{"question":"Apakah kunci privat harus dimasukkan ke server penerima?","answer":"Pada contoh ini, hanya alamat publik penerima yang dikonfigurasi di server penerima. Kunci privat yang digunakan untuk otorisasi pembayaran dibaca di klien pembayaran."},{"question":"Berapa nilai amount untuk 0.001 USDC?","answer":"Dengan menerapkan 6 digit desimal USDC, nilainya dalam satuan terkecil adalah 1000. Gunakan string \"1000\" dalam syarat pembayaran v2."},{"question":"Jika tanda terima transaksi menunjukkan keberhasilan, apakah penerimaan USDC juga sudah terkonfirmasi?","answer":"Status transaksi berhasil saja tidak cukup. Cocokkan pembayar, penerima, dan jumlah pada peristiwa Transfer dari kontrak USDC resmi."},{"question":"Apakah aman mengirim ulang permintaan pembayaran yang sama?","answer":"Pencegahan penggunaan ulang nonce otorisasi yang sama dan pencegahan duplikasi pesanan adalah dua hal yang berbeda. Sebelum mencoba lagi, periksa status pembayaran yang sudah ada dan terapkan penanganan idempotensi untuk setiap pesanan."},{"question":"Jika tidak ada PAYMENT-RESPONSE, apakah cukup melakukan pembayaran lagi?","answer":"Jika respons hilang atau waktu tunggu habis, hasil penyelesaian pembayaran mungkin tidak jelas. Jangan langsung membuat otorisasi baru; periksa status penyelesaian pembayaran dari permintaan yang sudah ada."},{"question":"Apakah x402 harus menggunakan facilitator dari perusahaan tertentu saja?","answer":"Struktur resminya mengizinkan penggunaan facilitator eksternal maupun pengoperasian sendiri. Pastikan implementasi yang dipilih mendukung versi protokol, metode pembayaran, dan chain yang diperlukan."},{"question":"Apakah pengeluaran agen AI juga dibatasi secara otomatis?","answer":"Contoh ini hanya membatasi syarat pembayaran per permintaan. Kelola pengeluaran kumulatif akibat pemanggilan berulang dengan kebijakan anggaran terpisah."},{"question":"Apakah Cloudflare PAYMENT-CONTEXT merupakan header tanda tangan pembayaran standar?","answer":"PAYMENT-CONTEXT adalah JWT yang diteruskan Gateway ke server asal. Fungsinya berbeda dari PAYMENT-SIGNATURE milik klien."},{"question":"Bolehkah contoh ini langsung di-deploy ke mainnet?","answer":"Kode ini adalah konfigurasi minimal untuk pengujian. Sebelum melakukan deployment ke lingkungan produksi, verifikasi peringatan keamanan dependensi, penanganan permintaan duplikat, autentikasi fasilitator, dan penyelesaian pembayaran yang sebenarnya."}],"sources":[{"url":"https://github.com/x402-foundation/x402/blob/main/specs/x402-specification-v2.md","title":"Spesifikasi protokol x402 v2","type":"source"},{"url":"https://github.com/x402-foundation/x402/blob/main/specs/transports-v1/http.md","title":"Transport HTTP x402 v1","type":"source"},{"url":"https://github.com/x402-foundation/x402/blob/main/specs/transports-v2/http.md","title":"Transport HTTP x402 v2","type":"source"},{"url":"https://docs.x402.org/guides/migration-v1-to-v2","title":"Panduan migrasi x402: V1 ke V2","type":"source"},{"url":"https://github.com/x402-foundation/x402/blob/main/docs/core-concepts/facilitator.md","title":"Peran dan tanggung jawab Facilitator x402","type":"source"},{"url":"https://docs.x402.org/getting-started/quickstart-for-sellers","title":"Panduan mulai cepat x402 untuk penjual","type":"source"},{"url":"https://docs.x402.org/getting-started/quickstart-for-buyers","title":"Panduan mulai cepat x402 untuk pembeli","type":"source"},{"url":"https://github.com/x402-foundation/x402/tree/main/examples/typescript/clients/fetch","title":"Contoh klien fetch TypeScript resmi x402","type":"source"},{"url":"https://github.com/x402-foundation/x402/blob/main/typescript/packages/http/express/src/index.ts","title":"Implementasi middleware Express x402","type":"source"},{"url":"https://github.com/x402-foundation/x402/blob/main/typescript/packages/core/src/client/x402Client.ts","title":"Pemilihan ketentuan pembayaran dan implementasi kebijakan x402Client","type":"source"},{"url":"https://github.com/x402-foundation/x402/blob/main/typescript/packages/mechanisms/evm/src/exact/server/scheme.ts","title":"Implementasi konversi harga pada server x402 ExactEvmScheme","type":"source"},{"url":"https://developers.circle.com/stablecoins/usdc-contract-addresses","title":"Alamat kontrak USDC Circle","type":"data_point"},{"url":"https://docs.base.org/get-started/connect-to-base","title":"Informasi jaringan mainnet Base dan 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":"Standar token ERC-20","type":"source"},{"url":"https://eips.ethereum.org/EIPS/eip-3009","title":"Transfer dengan otorisasi ERC-3009","type":"source"},{"url":"https://docs.x402.org/extensions/payment-identifier","title":"Ekstensi idempotensi Payment-Identifier x402","type":"source"},{"url":"https://developers.cloudflare.com/monetization-gateway/x402/","title":"Protokol x402 Cloudflare Monetization Gateway","type":"source"},{"url":"https://developers.cloudflare.com/monetization-gateway/configuration/payment-validation/","title":"Validasi pembayaran Cloudflare Monetization Gateway","type":"source"},{"url":"https://docs.x402.org/core-concepts/network-and-token-support","title":"Dukungan jaringan \u0026 token x402","type":"source"},{"url":"https://github.com/advisories/GHSA-mrhx-c7xc-m3w5","title":"Peringatan keamanan GitHub: kehabisan memori akibat validasi koleksi berukuran besar oleh 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/en/articles/x402-payments-2026-v2-base-usdc-settlement-guide"}