Documentation

TypeScript SDK

Add x402 payments to JavaScript/TypeScript applications. Supports Express, Hono, and Next.js.

npm @primersystems/x402

Installation

bash
npm install @primersystems/x402

Quick Start (CLI)

New in v0.5.0 — manage wallets, probe URLs, and make payments from the command line:

bash
# Create a new wallet
npx @primersystems/x402 wallet create

# Check wallet balance
npx @primersystems/x402 wallet balance 0xYourAddress

# Check if a URL requires payment (and get price)
npx @primersystems/x402 probe https://api.example.com/paid

# Preview payment without paying
npx @primersystems/x402 pay https://api.example.com/paid --dry-run

# Make a payment
X402_PRIVATE_KEY=0x... npx @primersystems/x402 pay https://api.example.com/paid --max-amount 0.10

CLI Commands

Command Description
wallet createCreate a new wallet (address, private key, mnemonic)
wallet balance <address>Check USDC balance
wallet from-mnemonicRestore wallet from mnemonic
probe <url>Check if URL requires payment
pay <url>Make a payment (requires X402_PRIVATE_KEY)
pay <url> --dry-runPreview payment without paying
networksList supported networks
openclaw initSet up for OpenClaw agents

Environment Variables

Variable Description
X402_PRIVATE_KEYWallet private key (required for payments)
X402_NETWORKDefault network (default: base)
X402_MAX_AMOUNTDefault max payment amount in USDC
X402_FACILITATORCustom facilitator URL

Wallet Utilities

New in v0.5.0 — programmatic wallet management:

javascript
const { createWallet, walletFromMnemonic, getBalance, x402Probe } = require('@primersystems/x402');

// Create a new wallet
const wallet = createWallet();
console.log(wallet.address);     // 0x...
console.log(wallet.privateKey);  // 0x...
console.log(wallet.mnemonic);    // "word1 word2 ..."

// Restore from mnemonic
const restored = walletFromMnemonic('word1 word2 ...');

// Check balance
const balance = await getBalance('0x...', 'base', 'USDC');
console.log(balance.balance);  // "100.50"

// Probe a URL for x402 support
const probe = await x402Probe('https://api.example.com/paid');
if (probe.supports402) {
  console.log('Payment required:', probe.requirements);
}

Error Handling

New in v0.5.0 — structured errors for programmatic handling:

javascript
const { X402Error, ErrorCodes } = require('@primersystems/x402');

try {
  const response = await x402Fetch(url, signer, { maxAmount: '0.10' });
} catch (error) {
  if (error instanceof X402Error) {
    switch (error.code) {
      case ErrorCodes.INSUFFICIENT_FUNDS:
        console.log('Need more funds:', error.details);
        break;
      case ErrorCodes.AMOUNT_EXCEEDS_MAX:
        console.log('Payment too expensive:', error.details);
        break;
      case ErrorCodes.SETTLEMENT_FAILED:
        console.log('Settlement failed:', error.details);
        break;
    }
  }
}

Error Codes

Code Description
INSUFFICIENT_FUNDSWallet balance too low
AMOUNT_EXCEEDS_MAXPayment exceeds maxAmount
SETTLEMENT_FAILEDFacilitator settlement failed
INVALID_RESPONSEMalformed 402 response
MISSING_PRIVATE_KEYPrivate key not provided
UNSUPPORTED_NETWORKNetwork not supported

Payer (Client)

Wrap fetch or axios to automatically handle 402 responses:

javascript
const { createSigner, x402Fetch } = require('@primersystems/x402');

// Create a signer with CAIP-2 network format
const signer = await createSigner('eip155:8453', process.env.PRIVATE_KEY);

// Wrap fetch to handle 402 automatically
const fetch402 = x402Fetch(fetch, signer, { maxAmount: '1.00' });

// Use like normal fetch — payments happen automatically
const response = await fetch402('https://api.example.com/paid-endpoint');

With Axios

javascript
const { createSigner, x402Axios } = require('@primersystems/x402');

const signer = await createSigner('eip155:8453', process.env.PRIVATE_KEY);
const axios402 = x402Axios(axios.create(), signer, { maxAmount: '1.00' });

const response = await axios402.get('https://api.example.com/paid-endpoint');

With viem

javascript
import { createWalletClient, http } from 'viem';
import { base } from 'viem/chains';
import { privateKeyToAccount } from 'viem/accounts';
import { createSigner, x402Fetch } from '@primersystems/x402';

// Create a viem wallet client
const walletClient = createWalletClient({
  account: privateKeyToAccount(process.env.PRIVATE_KEY),
  chain: base,
  transport: http()
});

// Pass the wallet client directly to createSigner
const signer = await createSigner(walletClient);
const fetch402 = x402Fetch(fetch, signer, { maxAmount: '1.00' });

Payer Options

Option Required Description
maxAmount Yes Maximum payment per request (e.g., '1.00')
facilitator No Custom facilitator URL
verify No Verify payment before sending (default: true)

Payee (Server)

Add payment requirements to your API routes using middleware.

Express

javascript
const { x402Express } = require('@primersystems/x402');

app.use(x402Express('0xYourWalletAddress', {
  '/api/premium': {
    amount: '0.01',                                     // $0.01 USDC
    asset: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',  // USDC on Base
    network: 'eip155:8453'                              // Base mainnet
  },
  '/api/data/*': {
    amount: '0.001',
    asset: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
    network: 'eip155:8453'
  }
}));

Hono

javascript
const { x402Hono } = require('@primersystems/x402');

app.use('*', x402Hono('0xYourWalletAddress', {
  '/api/premium': {
    amount: '0.01',
    asset: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
    network: 'eip155:8453'
  }
}));

Next.js (App Router)

javascript
import { x402Next } from '@primersystems/x402';

async function handler(req) {
  return Response.json({ data: 'premium content' });
}

export const GET = x402Next(handler, {
  payTo: '0xYourWalletAddress',
  amount: '0.01',
  asset: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
  network: 'eip155:8453'
});

Single Route Protection

For protecting individual routes without full middleware:

javascript
const { x402Protect } = require('@primersystems/x402');

app.get('/api/premium',
  x402Protect('0xYourWallet', '0.01', '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', 'eip155:8453'),
  (req, res) => {
    res.json({ data: 'premium content' });
  }
);

Token Approval (ERC-20)

For non-EIP-3009 tokens, approve the Prism contract once:

javascript
const { createSigner, approveToken } = require('@primersystems/x402');

const signer = await createSigner('eip155:8453', process.env.PRIVATE_KEY);

// One-time approval (requires gas)
await approveToken(signer, '0xTokenAddress');

// Now payments with this token are gasless

Custom Facilitator

For non-Base networks, specify your own facilitator:

javascript
// Payee
app.use(x402Express('0xAddress', routes, {
  facilitator: 'https://your-facilitator.com'
}));

// Payer
const fetch402 = x402Fetch(fetch, signer, {
  maxAmount: '1.00',
  facilitator: 'https://your-facilitator.com'
});

Testing

The SDK includes utilities for testing without real payments:

javascript
const { createMockFacilitator, createTestPayment } = require('@primersystems/x402/testing');

describe('paid endpoint', () => {
  let mock;

  beforeAll(async () => {
    mock = await createMockFacilitator({ mode: 'approve' });
  });

  afterAll(() => mock.close());

  test('accepts payment', async () => {
    const payment = createTestPayment({ amount: '10000' });

    const res = await fetch('/api/premium', {
      headers: { 'PAYMENT-SIGNATURE': payment }
    });

    expect(res.status).toBe(200);
  });
});

Debug Logging

bash
DEBUG=x402:* node app.js
DEBUG=x402:payer,x402:payee node app.js

OpenClaw Integration

This SDK is compatible with OpenClaw AI agents. Set up your agent with one command:

bash
npx @primersystems/x402 openclaw init

This creates a wallet, configures the network, and installs the x402 skill. Check status with:

bash
npx @primersystems/x402 openclaw status