Skip to main content
This guide shows how to pay for x402-protected resources using a WDK self-custodial wallet on Plasma or Stable. By the end you’ll have a working fetch wrapper that automatically handles 402 Payment Required responses with USD₮.
See a full working demo at github.com/SemanticPay/x402-usdt0-demo

Install

npm install @x402/fetch @x402/evm @tetherto/wdk-wallet-evm

Create a wallet

Create a WalletAccountEvm pointed at the chain you want to pay on. The account derives keys locally from your seed phrase.
import { WalletAccountEvm } from "@tetherto/wdk-wallet-evm";

const account = new WalletAccountEvm(process.env.SEED_PHRASE, {
  provider: "https://rpc.plasma.to", // or "https://rpc.stable.xyz"
});

const address = await account.getAddress();
console.log("Buyer address:", address);

Register with x402

WalletAccountEvm already satisfies the signer interface that x402 expects.
import { x402Client, wrapFetchWithPayment } from "@x402/fetch";
import { registerExactEvmScheme } from "@x402/evm/exact/client";

const client = new x402Client();
registerExactEvmScheme(client, { signer: account });

const fetchWithPayment = wrapFetchWithPayment(fetch, client);
That’s it. fetchWithPayment now intercepts any 402 Payment Required response, signs an EIP-3009 transferWithAuthorization using your WDK wallet, and retries the request with the payment header attached.

Make a paid request

const response = await fetchWithPayment("https://api.example.com/weather", {
  method: "GET",
});

const data = await response.json();
console.log("Response:", data);
If the endpoint requires payment, the x402 client handles the full flow automatically:
  1. Initial request returns 402 with a PAYMENT-REQUIRED header
  2. Client parses the payment requirements (amount, token, network, recipient)
  3. Client signs an EIP-3009 authorization with your WDK wallet
  4. Client retries the request with the PAYMENT-SIGNATURE header
  5. The facilitator settles the payment on-chain and the server returns the resource

Full example

import { WalletAccountEvm } from "@tetherto/wdk-wallet-evm";
import { x402Client, wrapFetchWithPayment, x402HTTPClient } from "@x402/fetch";
import { registerExactEvmScheme } from "@x402/evm/exact/client";

// --- Config ---
const SEED_PHRASE = process.env.SEED_PHRASE;
const RPC = process.env.RPC_URL || "https://rpc.plasma.to";
const ENDPOINT = process.env.ENDPOINT || "https://api.example.com/weather";

// --- Wallet ---
const account = new WalletAccountEvm(SEED_PHRASE, {
  provider: RPC,
});

console.log("Address:", await account.getAddress());

// --- x402 client ---
const client = new x402Client();
registerExactEvmScheme(client, { signer: account });
const fetchWithPayment = wrapFetchWithPayment(fetch, client);

// --- Request ---
const response = await fetchWithPayment(ENDPOINT, { method: "GET" });
const body = await response.json();
console.log("Response:", body);

// --- Receipt ---
if (response.ok) {
  const httpClient = new x402HTTPClient(client);
  const receipt = httpClient.getPaymentSettleResponse(
    (name) => response.headers.get(name),
  );
  console.log("Payment receipt:", JSON.stringify(receipt, null, 2));
}

Environment variables

# .env
SEED_PHRASE="your twelve word seed phrase here"
RPC_URL="https://rpc.plasma.to"         # Plasma mainnet
# RPC_URL="https://rpc.stable.xyz"      # Stable mainnet
ENDPOINT="https://api.example.com/weather"
Your seed phrase controls your funds. Never commit it to version control. Use environment variables or a secrets manager.

Checking your balance

Before making paid requests, verify your wallet has USDT0 on the target chain:
const USDT0_PLASMA = "0xB8CE59FC3717ada4C02eaDF9682A9e934F625ebb";
const USDT0_STABLE = "0x779Ded0c9e1022225f8E0630b35a9b54bE713736";

const balance = await account.getTokenBalance(USDT0_PLASMA);
console.log("USDT0 balance:", Number(balance) / 1e6, "USDâ‚®");
USDT0 uses 6 decimals. A balance of 1000000 equals 1.00 USDâ‚®.

Using Axios instead of fetch

import { x402Client, wrapAxiosWithPayment } from "@x402/axios";
import { registerExactEvmScheme } from "@x402/evm/exact/client";
import axios from "axios";

const client = new x402Client();
registerExactEvmScheme(client, { signer: account });

const api = wrapAxiosWithPayment(
  axios.create({ baseURL: "https://api.example.com" }),
  client,
);

const response = await api.get("/weather");
console.log("Response:", response.data);

What happens under the hood

x402 uses EIP-3009 (transferWithAuthorization) for payment settlement. When your WDK wallet signs a payment, it creates an off-chain authorization that allows the facilitator to transfer a specific amount of USDT0 from your address to the seller’s address. The facilitator then submits this authorization on-chain in a single transaction. Because Plasma and Stable both support EIP-3009 natively on their USDT0 contracts, the facilitator can settle payments without any gas cost to the buyer. The buyer only pays the exact amount specified in the payment requirements.
Last modified on February 17, 2026