---
title: "Pembayaran x402 2026 v2: Penerapan dan Verifikasi Setelmen"
locale: id
category: tutorial
category_name: "Tutorial"
translation_status: reviewed
license: cc_by
author: "injoys"
source_url: https://injoys.com/en/articles/x402-payments-2026-v2-base-usdc-settlement-guide
published_at: 2026-10-28T19:20:55+09:00
---

# Pembayaran x402 2026 v2: Penerapan dan Verifikasi Setelmen

> 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.

## 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.

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.

Informasi jumlah dan chain mengacu pada dokumentasi resmi x402, Base, dan Circle yang ditautkan sebagai sumber.

## Persiapan praktik dan instalasi

Siapkan 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).

1. Siapkan alamat dompet EVM untuk menerima pembayaran.
2. Siapkan dompet uji terpisah untuk melakukan pembayaran.
3. Dapatkan USDC uji Base Sepolia untuk dompet pembayaran.
4. Buat proyek praktik dengan perintah berikut.
5. Simpan berkas TypeScript selanjutnya dalam folder yang sama.

```bash
mkdir x402-demo
cd x402-demo
npm init -y
npm pkg set type=module
npm 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
npm install --save-dev --save-exact typescript tsx @types/node @types/express
```

Server 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).

Cakupan 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.

## Perbandingan x402 v1 dan v2

Saat 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).

| Aspek | v1 | v2 |
|---|---|---|
| Versi protokol | `x402Version: 1` | `x402Version: 2` |
| Ketentuan pembayaran awal | Isi JSON respons 402 | Header respons `PAYMENT-REQUIRED` |
| Data pembayaran yang ditandatangani | Header permintaan `X-PAYMENT` | Header permintaan `PAYMENT-SIGNATURE` |
| Hasil penyelesaian pembayaran | Header respons `X-PAYMENT-RESPONSE` | Header respons `PAYMENT-RESPONSE` |
| Pengidentifikasi mainnet Base | `base` | `eip155:8453` |
| Pengidentifikasi Base Sepolia | `base-sepolia` | `eip155:84532` |
| Field jumlah dalam ketentuan pembayaran | `maxAmountRequired` | `amount` |
| Deskripsi sumber daya | `resource` dan lainnya di dalam ketentuan pembayaran | Objek `resource` tingkat teratas |
| Ketentuan dalam payload pembayaran | `scheme`, `network`, dan lainnya di tingkat teratas | Objek `accepted` yang memuat ketentuan terpilih |
| Contoh paket TypeScript | `x402-express` | `@x402/express` |

Lokasi 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.

[Spesifikasi HTTP v2](https://github.com/x402-foundation/x402/blob/main/specs/transports-v2/http.md) menjelaskan kode status sebagai berikut.

> Server menunjukkan bahwa pembayaran diperlukan menggunakan kode status HTTP 402 "Payment Required".
>
> Sumber: spesifikasi x402 HTTP v2

Header pembayaran v2 adalah nilai JSON yang dikodekan dengan Base64. Base64 bukan metode enkripsi. Gunakan HTTPS untuk layanan eksternal.

## Urutan permintaan HTTP 402 dan pembayaran

Praktik 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).

1. Klien meminta API berbayar tanpa informasi pembayaran.
2. Server mengembalikan HTTP 402 dan `PAYMENT-REQUIRED`.
3. Klien memilih ketentuan yang diizinkan dari `accepts`.
4. SDK menandatangani otorisasi pembayaran sesuai ketentuan yang dipilih.
5. Klien mengirim ulang permintaan dengan menyertakan `PAYMENT-SIGNATURE`.
6. Server memverifikasi otorisasi melalui `/verify` milik facilitator.
7. Jika verifikasi lolos, server menjalankan pemrosesan sumber daya.
8. Server meminta penyelesaian pembayaran melalui `/settle`.
9. Setelah penyelesaian pembayaran berhasil, server mengirim isi berbayar dan `PAYMENT-RESPONSE`.

Urutan 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.

### Peran resource server dan facilitator

Resource 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).

| Komponen | Tugas | Kondisi yang tidak boleh dianggap berhasil |
|---|---|---|
| Klien | Memilih ketentuan dan menandatangani otorisasi pembayaran | Baru selesai membuat tanda tangan |
| Resource server | Menetapkan harga dan mengendalikan respons berbayar | Klien mengklaim pembayaran berhasil |
| Facilitator `/verify` | Memastikan keabsahan tanda tangan dan ketentuan pembayaran | Hanya menerima `isValid: true` |
| Facilitator `/settle` | Dalam praktik ini, menjalankan pembayaran on-chain dan mengembalikan hasilnya | Sudah dikirim, tetapi hasilnya belum dikonfirmasi |

`/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.

Blokir 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).

## Perbandingan mainnet dan testnet Base

Gunakan 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).

| Aspek | Mainnet Base | Testnet Base Sepolia |
|---|---|---|
| Kegunaan | Pembayaran dengan aset nyata | Menguji implementasi dan penanganan kegagalan |
| Chain ID | `8453` | `84532` |
| Jaringan x402 v2 | `eip155:8453` | `eip155:84532` |
| Kontrak USDC | `0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913` | `0x036CbD53842c5426634e7929541eC2318f3dCF7e` |
| RPC publik | `https://mainnet.base.org` | `https://sepolia.base.org` |
| Nilai finansial token | Aset dengan nilai finansial nyata | Tidak memiliki nilai finansial |

Periksa 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.

USDC uji dapat diminta melalui [Circle Testnet Faucet](https://faucet.circle.com/). Pilih Base Sepolia sebagai jaringan. Masukkan alamat dompet uji pembayaran sebagai alamat penerima.

## Implementasi API berbayar Node.js

Pasang `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)

### Berkas server

Simpan kode berikut sebagai `server.ts`. Server hanya menerima permintaan melalui antarmuka lokal. Alamat penerima dibaca dari variabel lingkungan `PAY_TO`.

```typescript
import express from "express";
import { getAddress, zeroAddress } from "viem";
import { paymentMiddleware } from "@x402/express";
import { HTTPFacilitatorClient, x402ResourceServer } from "@x402/core/server";
import { ExactEvmScheme } from "@x402/evm/exact/server";

const payTo = getAddress(process.env.PAY_TO ?? "");
if (payTo === zeroAddress) throw new Error("Invalid payTo");
const network = "eip155:84532";
const resourceServer = new x402ResourceServer(
  new HTTPFacilitatorClient({ url: "https://x402.org/facilitator" }),
).register(network, new ExactEvmScheme());

const app = express();
app.use((_req, res, next) => {
  res.setHeader("Cache-Control", "private, no-store");
  next();
});
app.use(paymentMiddleware({
  "GET /paid-data": {
    accepts: [{ scheme: "exact", price: "$0.001", network, payTo }],
    description: "Sample paid JSON",
    mimeType: "application/json",
  },
}, resourceServer));
app.get("/paid-data", (_req, res) => {
  res.json({ content: "Paid sample content" });
});
app.listen(4021, "127.0.0.1");
```

`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).

### Contoh perhitungan harga

Harga 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).

```text
0.001 USDC × 10^6 = 1000 최소 단위
```

| Nilai yang diperiksa | Nilai yang diharapkan dalam contoh ini |
|---|---|
| `price` dalam konfigurasi server | `"$0.001"` |
| `amount` dalam ketentuan pembayaran 402 | `"1000"` |
| `asset` dalam ketentuan pembayaran | Kontrak USDC Base Sepolia |
| `payTo` dalam ketentuan pembayaran | Alamat penerima yang dikonfigurasi |
| `network` dalam ketentuan pembayaran | `eip155:84532` |

`price` adalah nilai konfigurasi SDK. Ketentuan pembayaran v2 yang sebenarnya menyampaikan `amount`. Bandingkan jumlah bilangan bulat sebagai string atau `bigint`.

### Menjalankan server dan memeriksa 402

Jalankan server di terminal Bash pertama. Nilai yang dimasukkan adalah alamat publik penerima, bukan kunci privat. Biarkan terminal ini tetap terbuka selama server berjalan.

```bash
read -r -p '수취 지갑 주소: ' PAY_TO
export PAY_TO
npx tsx server.ts
```

Kirim permintaan tanpa pembayaran dari terminal kedua. Perintah berikut tidak menandatangani pembayaran. Perintah ini menampilkan status HTTP dan ketentuan pembayaran yang telah didekodekan.

```bash
node --input-type=module <<'JS'
const response = await fetch("http://127.0.0.1:4021/paid-data");
const header = response.headers.get("PAYMENT-REQUIRED");
console.log(response.status);
console.log(header && JSON.parse(
  Buffer.from(header, "base64").toString("utf8")
));
JS
```

Respons 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.

## Pemilihan ketentuan pembayaran dan implementasi klien

Batasi 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).

1. Simpan kode berikut sebagai `client.ts`.
2. Simpan juga `check-transfer.ts` dari bagian berikutnya.
3. Setelah kedua berkas siap, lanjutkan ke langkah eksekusi.

```typescript
import { writeFile } from "node:fs/promises";
import { getAddress, zeroAddress, type Hex, type Hash } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { x402Client, wrapFetchWithPayment } from "@x402/fetch";
import { decodePaymentResponseHeader } from "@x402/core/http";
import { ExactEvmScheme } from "@x402/evm/exact/client";
import { checkTransfer } from "./check-transfer.js";

const key = process.env.EVM_PRIVATE_KEY ?? "";
if (!/^0x[0-9a-fA-F]{64}$/.test(key)) throw new Error("Invalid test key");
const signer = privateKeyToAccount(key as Hex);
const payTo = getAddress(process.env.PAY_TO ?? "");
if (payTo === zeroAddress) throw new Error("Invalid payTo");
const network = "eip155:84532";
const token = "0x036CbD53842c5426634e7929541eC2318f3dCF7e";
const client = new x402Client().register(network, new ExactEvmScheme(signer));
client.registerPolicy((version, options) => version === 2
  ? options.filter(r =>
      r.scheme === "exact" && r.network === network &&
      r.asset.toLowerCase() === token.toLowerCase() &&
      r.payTo.toLowerCase() === payTo.toLowerCase() && r.amount === "1000" &&
      (!r.extra?.assetTransferMethod || r.extra.assetTransferMethod === "eip3009") &&
      (!r.extra?.paymentFlow || r.extra.paymentFlow === "authorization"))
  : []);

const paidFetch = wrapFetchWithPayment(fetch, client);
const response = await paidFetch("http://127.0.0.1:4021/paid-data", {
  redirect: "error",
});
const raw = response.headers.get("PAYMENT-RESPONSE");
if (!raw) throw new Error(`No settlement receipt: HTTP ${response.status}`);
const settlement = decodePaymentResponseHeader(raw);
await writeFile("settlement.json", JSON.stringify(settlement, null, 2));
if (!response.ok || !settlement.success || settlement.network !== network) {
  throw new Error("Payment unresolved; inspect settlement.json before retrying");
}
const evidence = await checkTransfer(
  network, settlement.transaction as Hash, signer.address, payTo, 1000n,
);
await writeFile("transfer.json", JSON.stringify(evidence, null, 2));
console.log(await response.text());
```

`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.

Klien ini juga dapat digunakan oleh agen AI. Namun, batas harga per panggilan bukan batas pengeluaran kumulatif. Kelola anggaran panggilan berulang agen secara terpisah.

## Verifikasi event Transfer USDC

Periksa 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).

Verifikasi memerlukan hal-hal berikut.

| Aspek verifikasi | Pembanding |
|---|---|
| Chain ID dari RPC | Chain yang diizinkan server |
| `status` pada tanda terima transaksi | Status berhasil |
| Alamat yang memancarkan event | Kontrak USDC resmi pada chain tersebut |
| `Transfer.from` | Pembayar yang telah dipastikan |
| `Transfer.to` | Alamat penerima yang ditetapkan server |
| `Transfer.value` | Jumlah dalam satuan terkecil yang diminta server |

Simpan 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).

```typescript
import {
  createPublicClient, http, getAddress, parseEventLogs,
  erc20Abi, type Address, type Hash,
} from "viem";
import { base, baseSepolia } from "viem/chains";

const configs = {
  "eip155:84532": {
    chain: baseSepolia,
    rpc: "https://sepolia.base.org",
    token: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
  },
  "eip155:8453": {
    chain: base,
    rpc: "https://mainnet.base.org",
    token: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
  },
} as const;

export async function checkTransfer(
  network: keyof typeof configs, hash: Hash,
  payer: Address, payTo: Address, amount: bigint,
) {
  const cfg = configs[network];
  const rpc = createPublicClient({ chain: cfg.chain, transport: http(cfg.rpc) });
  if (await rpc.getChainId() !== cfg.chain.id) throw new Error("Wrong chain");
  const receipt = await rpc.getTransactionReceipt({ hash });
  if (receipt.status !== "success") throw new Error("Transaction failed");
  const logs = parseEventLogs({
    abi: erc20Abi,
    eventName: "Transfer",
    logs: receipt.logs.filter(
      log => getAddress(log.address) === getAddress(cfg.token),
    ),
    strict: true,
  });
  const transfer = logs.find(log =>
    getAddress(log.args.from) === getAddress(payer) &&
    getAddress(log.args.to) === getAddress(payTo) &&
    log.args.value === amount,
  );
  if (!transfer) throw new Error("Matching USDC Transfer not found");
  return {
    network, transaction: hash, logIndex: transfer.logIndex,
    blockHash: receipt.blockHash, blockNumber: receipt.blockNumber.toString(),
  };
}
```

Fungsi 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.

Jangan 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.

Kegagalan 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.

## Menjalankan pembayaran uji dan memeriksa hasil

Setelah semua berkas disimpan, jalankan dengan dompet uji. Pemeriksaan tipe tidak melakukan penandatanganan atau transfer. Perintah berikut menggunakan alat TypeScript yang telah dipasang sebelumnya.

1. Periksa tipe dengan perintah berikut.
2. Masukkan alamat penerima yang sama dengan server di terminal kedua.
3. Masukkan kunci privat dompet pembayaran uji dengan input tersembunyi.
4. Jalankan klien satu kali.
5. Periksa hasil penyelesaian pembayaran dan log transfer yang tersimpan.

```bash
npx tsc --noEmit --strict --skipLibCheck --target ES2022 --module NodeNext --moduleResolution NodeNext server.ts client.ts check-transfer.ts
read -r -p '같은 수취 지갑 주소: ' PAY_TO
read -r -s -p '테스트 결제 지갑 개인키: ' EVM_PRIVATE_KEY
export PAY_TO EVM_PRIVATE_KEY
npx tsx client.ts
unset EVM_PRIVATE_KEY
```

| Keluaran | Arti | Hal yang diperiksa |
|---|---|---|
| `settlement.json` | Hasil penyelesaian pembayaran yang dikembalikan server | `success`, `network`, `transaction` |
| `transfer.json` | Catatan transfer yang ditemukan melalui kueri RPC | Chain, hash transaksi, posisi log, informasi blok |
| Isi berbayar di konsol | Data yang ditampilkan setelah verifikasi oleh klien ini | Pastikan konten sesuai permintaan |

Menjalankan 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.

## Penanganan berdasarkan kondisi kegagalan verifikasi

Dalam 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.

| Kondisi pengujian | Penanganan yang diharapkan | Pemeriksaan tambahan |
|---|---|---|
| Tidak ada header pembayaran | Mengembalikan 402 dan ketentuan pembayaran | Tidak ada isi berbayar |
| Token atau penerima berbeda diajukan | Ditolak oleh kebijakan klien | Tanda tangan tidak dibuat |
| Tanda tangan tidak valid | Diblokir karena verifikasi gagal | Handler berbayar tidak dijalankan |
| Saldo tidak cukup | Diblokir karena verifikasi atau penyelesaian pembayaran gagal | Tidak dicatat sebagai penyelesaian pembayaran berhasil |
| Hasil penyelesaian pembayaran gagal | Respons berbayar diblokir | Isi yang sudah disiapkan pun tidak dikirim |
| Respons facilitator terlambat | Ditangguhkan karena keberhasilan belum dipastikan | Jangan membuat otorisasi baru secara otomatis |
| Galat kueri tanda terima RPC | Simpan hash transaksi dan lakukan kueri ulang | Jangan langsung menyimpulkan pembayaran gagal |

Dalam 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.

Pengiriman 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.

## Pemulihan respons yang hilang setelah penyelesaian pembayaran dan pengiriman ulang

Simpan 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).

Sifat 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.

### Perbandingan pembayaran ganda dan pengiriman ulang

Pencegahan 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).

| Situasi | Dari sisi otorisasi on-chain | Dari sisi operasional API |
|---|---|---|
| Menyelesaikan pembayaran ulang dengan otorisasi yang sama | Diperiksa sebagai nonce yang sudah digunakan | Perlu mengambil hasil sebelumnya |
| Membuat otorisasi baru untuk pesanan yang sama | Dapat diperlakukan sebagai otorisasi terpisah | Perlu mencegah pembayaran ganda |
| Respons hilang setelah penyelesaian pembayaran berhasil | Perpindahan aset mungkin sudah selesai | Perlu mengirim ulang konten |
| Batas waktu terlampaui setelah permintaan penyelesaian pembayaran | Hasil masih belum jelas | Perlu status tertunda dan pemeriksaan penyelesaian pembayaran |

Prosedur 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.

1. Deklarasikan dukungan ekstensi `payment-identifier` di server.
2. Simpan pengidentifikasi permintaan logis di klien.
3. Hubungkan pengidentifikasi dengan pembayar dan isi permintaan di server.
4. Catat status sedang diproses secara atomik untuk mencegah eksekusi serentak.
5. Saat penyelesaian pembayaran berhasil, simpan bukti transaksi dan hasil respons.
6. Jika hak akses dan isi permintaan ulang sama, kembalikan hasil yang tersimpan.
7. Jika hasil penyelesaian pembayaran belum jelas, periksa otorisasi sebelumnya terlebih dahulu.

Pengidentifikasi 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.

Simpan 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.

## Kesalahan umum dalam implementasi pembayaran

Mencampuradukkan 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).

| Kesalahan umum | Acuan perbaikan |
|---|---|
| Menafsirkan `extra.version: "2"` sebagai versi x402 | Periksa versi protokol melalui `x402Version` |
| Menggunakan `maxAmountRequired` pada v2 | Gunakan `amount` untuk jumlah dalam ketentuan pembayaran |
| Membuat header `X-PAYMENT-REQUIRED` sendiri | v1 menggunakan isi respons, v2 menggunakan `PAYMENT-REQUIRED` |
| Memperlakukan `isValid: true` sebagai dana telah diterima | Periksa hasil `/settle` secara terpisah |
| Memperlakukan `to` tingkat teratas transaksi sebagai penerima USDC | Periksa `Transfer.to` USDC |
| Menganggap penanda tangan sama dengan pengirim transaksi | Bandingkan pembayar dalam otorisasi dengan `Transfer.from` |
| Menjumlahkan penerimaan testnet sebagai pendapatan | Pisahkan pembukuan mainnet dan testnet |
| Membuat pembayaran baru setiap kali batas waktu terlampaui | Pulihkan pesanan dan status otorisasi sebelumnya terlebih dahulu |
| Menyamakan versi paket SDK dengan versi protokol | Kelola versi paket dan `x402Version` secara terpisah |

Informasi 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.

## Pemeriksaan keamanan dan batas kepercayaan layanan eksternal

Kendalikan 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).

- Jangan membuka akses konten berdasarkan pesan keberhasilan dari klien.
- Bandingkan chain, token, jumlah, dan penerima yang ditetapkan server.
- Jangan mencatat kunci privat dan frasa pemulihan dalam log.
- Kecualikan juga tanda tangan otorisasi lengkap dari log akses umum.
- Batasi ukuran header pembayaran dan waktu pemrosesan permintaan.
- Pastikan respons berbayar tidak tersimpan dalam cache bersama.
- Pisahkan lokasi penyimpanan kunci uji dan kunci produksi.
- Jangan membuat jalur pintas tanpa pembayaran saat facilitator mengalami gangguan.
- Uji secara langsung kedaluwarsa otorisasi dan permintaan duplikat.

Dependensi 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).

```bash
npm audit
```

Periksa 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.

### Perbandingan server sendiri dan Cloudflare Gateway

Penggunaan 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/).

| Aspek | Metode Express dalam artikel ini | Cloudflare Monetization Gateway |
|---|---|---|
| Pemrosesan pembayaran klien | Middleware Express | Gateway |
| Titik verifikasi server asal | Integrasi dengan facilitator yang dipilih | Verifikasi JWT `PAYMENT-CONTEXT` |
| Penyampaian hasil penyelesaian pembayaran | `PAYMENT-RESPONSE` dari middleware | Gateway membuat tanda terima penyelesaian pembayaran |
| Hal tambahan yang diperiksa | Chain yang didukung facilitator dan autentikasi | Verifikasi tanda tangan JWT dan perlindungan akses ke server asal |

`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.

Meski melewati Gateway, jangan sekadar mendekodekan JWT. Verifikasi tanda tangannya dengan kunci yang ditentukan dokumentasi. Blokir konten berbayar jika header tidak ada atau verifikasi gagal.

## Membedakan penggunaan nyata dan catatan penyelesaian pembayaran

Jumlah 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.

| Catatan | Pertanyaan yang dapat dijawab | Catatan tambahan yang diperlukan |
|---|---|---|
| Respons 402 | Berapa kali ketentuan pembayaran diminta | Apakah pembayaran benar-benar dilakukan |
| Verifikasi berhasil | Apakah otorisasi valid | Hasil penyelesaian pembayaran |
| Transfer USDC | Apakah aset berpindah | Keterkaitan dengan pesanan |
| Penyelesaian pembayaran per pesanan | Apakah permintaan berbayar telah dibayar | Status pengiriman respons |
| Catatan pengiriman ulang | Apakah permintaan yang sudah dibayar telah dipulihkan | Pengidentifikasi yang membedakannya dari pembayaran baru |

Pisahkan 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.

## Prosedur peralihan ke mainnet Base

Pertimbangkan 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).

1. Pastikan facilitator yang dipilih mendukung mainnet Base.
2. Periksa dukungan v2, `exact`, dan `eip155:8453` melalui `/supported`.
3. Konfigurasikan autentikasi dan ketentuan penggunaan yang diwajibkan penyedia.
4. Ubah jaringan server dan klien secara bersamaan.
5. Ubah alamat USDC yang diizinkan klien menjadi alamat mainnet.
6. Bandingkan kembali jumlah dalam respons 402 dan alamat penerima sebenarnya.
7. Integrasikan penyimpanan permintaan duplikat dan prosedur pemulihan gangguan.
8. Selesaikan pemeriksaan keamanan dependensi produksi.
9. Verifikasi transfer nyata dan pengiriman konten dengan jumlah kecil yang telah dikonfigurasi.

Dukungan 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.

## FAQ

### Apa perbedaan x402 dengan konfirmasi setoran USDC biasa?
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.

### Apa saja header pembayaran pada x402 v2?
Syarat pembayaran disampaikan melalui PAYMENT-REQUIRED, sedangkan payload pembayaran melalui PAYMENT-SIGNATURE. Hasil penyelesaian pembayaran disampaikan melalui PAYMENT-RESPONSE.

### Apakah ada header X-PAYMENT-REQUIRED pada v1?
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.

### Jika /verify berhasil, apakah pembayaran sudah selesai?
Keberhasilan /verify berarti otorisasi pembayaran valid. Dalam praktik ini, hasil keberhasilan /settle harus diperiksa secara terpisah.

### Apakah USDC uji di Base Sepolia dapat dianggap sebagai pendapatan nyata?
Circle menyatakan bahwa USDC di testnet tidak memiliki nilai finansial. Catat penyelesaian pembayaran di testnet secara terpisah dari penjualan nyata.

### Apakah kunci privat harus dimasukkan ke server penerima?
Pada contoh ini, hanya alamat publik penerima yang dikonfigurasi di server penerima. Kunci privat yang digunakan untuk otorisasi pembayaran dibaca di klien pembayaran.

### Berapa nilai amount untuk 0.001 USDC?
Dengan menerapkan 6 digit desimal USDC, nilainya dalam satuan terkecil adalah 1000. Gunakan string "1000" dalam syarat pembayaran v2.

### Jika tanda terima transaksi menunjukkan keberhasilan, apakah penerimaan USDC juga sudah terkonfirmasi?
Status transaksi berhasil saja tidak cukup. Cocokkan pembayar, penerima, dan jumlah pada peristiwa Transfer dari kontrak USDC resmi.

### Apakah aman mengirim ulang permintaan pembayaran yang sama?
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.

### Jika tidak ada PAYMENT-RESPONSE, apakah cukup melakukan pembayaran lagi?
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.

### Apakah x402 harus menggunakan facilitator dari perusahaan tertentu saja?
Struktur resminya mengizinkan penggunaan facilitator eksternal maupun pengoperasian sendiri. Pastikan implementasi yang dipilih mendukung versi protokol, metode pembayaran, dan chain yang diperlukan.

### Apakah pengeluaran agen AI juga dibatasi secara otomatis?
Contoh ini hanya membatasi syarat pembayaran per permintaan. Kelola pengeluaran kumulatif akibat pemanggilan berulang dengan kebijakan anggaran terpisah.

### Apakah Cloudflare PAYMENT-CONTEXT merupakan header tanda tangan pembayaran standar?
PAYMENT-CONTEXT adalah JWT yang diteruskan Gateway ke server asal. Fungsinya berbeda dari PAYMENT-SIGNATURE milik klien.

### Bolehkah contoh ini langsung di-deploy ke mainnet?
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

- [Spesifikasi protokol x402 v2](https://github.com/x402-foundation/x402/blob/main/specs/x402-specification-v2.md)
- [Transport HTTP x402 v1](https://github.com/x402-foundation/x402/blob/main/specs/transports-v1/http.md)
- [Transport HTTP x402 v2](https://github.com/x402-foundation/x402/blob/main/specs/transports-v2/http.md)
- [Panduan migrasi x402: V1 ke V2](https://docs.x402.org/guides/migration-v1-to-v2)
- [Peran dan tanggung jawab Facilitator x402](https://github.com/x402-foundation/x402/blob/main/docs/core-concepts/facilitator.md)
- [Panduan mulai cepat x402 untuk penjual](https://docs.x402.org/getting-started/quickstart-for-sellers)
- [Panduan mulai cepat x402 untuk pembeli](https://docs.x402.org/getting-started/quickstart-for-buyers)
- [Contoh klien fetch TypeScript resmi x402](https://github.com/x402-foundation/x402/tree/main/examples/typescript/clients/fetch)
- [Implementasi middleware Express x402](https://github.com/x402-foundation/x402/blob/main/typescript/packages/http/express/src/index.ts)
- [Pemilihan ketentuan pembayaran dan implementasi kebijakan x402Client](https://github.com/x402-foundation/x402/blob/main/typescript/packages/core/src/client/x402Client.ts)
- [Implementasi konversi harga pada server x402 ExactEvmScheme](https://github.com/x402-foundation/x402/blob/main/typescript/packages/mechanisms/evm/src/exact/server/scheme.ts)
- [Alamat kontrak USDC Circle](https://developers.circle.com/stablecoins/usdc-contract-addresses)
- [Informasi jaringan mainnet Base dan Base Sepolia](https://docs.base.org/get-started/connect-to-base)
- [Circle Testnet Faucet](https://faucet.circle.com/)
- [viem getTransactionReceipt](https://viem.sh/docs/actions/public/getTransactionReceipt)
- [viem parseEventLogs](https://viem.sh/docs/contract/parseEventLogs)
- [Standar token ERC-20](https://eips.ethereum.org/EIPS/eip-20)
- [Transfer dengan otorisasi ERC-3009](https://eips.ethereum.org/EIPS/eip-3009)
- [Ekstensi idempotensi Payment-Identifier x402](https://docs.x402.org/extensions/payment-identifier)
- [Protokol x402 Cloudflare Monetization Gateway](https://developers.cloudflare.com/monetization-gateway/x402/)
- [Validasi pembayaran Cloudflare Monetization Gateway](https://developers.cloudflare.com/monetization-gateway/configuration/payment-validation/)
- [Dukungan jaringan & token x402](https://docs.x402.org/core-concepts/network-and-token-support)
- [Peringatan keamanan GitHub: kehabisan memori akibat validasi koleksi berukuran besar oleh Zod](https://github.com/advisories/GHSA-mrhx-c7xc-m3w5)

## Images

![Seorang pria bersweater abu-abu gelap memeriksa tablet di atas ambalan rak server.](https://injoys.com/rails/active_storage/blobs/proxy/eyJfcmFpbHMiOnsiZGF0YSI6MzM4MDcsInB1ciI6ImJsb2JfaWQifX0=--49c502fd8da16158a617c3586dfa81ced4bc95df/ai-f0ae2b5c.webp)
![Seorang perempuan mengetik di laptop yang dibuka rata, dengan daftar dan diagram dua jalur yang menyatu di layar.](https://injoys.com/rails/active_storage/blobs/proxy/eyJfcmFpbHMiOnsiZGF0YSI6MzM4MTMsInB1ciI6ImJsb2JfaWQifX0=--4e70727f132519277c68d1c6279d95585a936549/ai-3dbd1c62.webp)