TypeScript SDK

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

npm @primersystems/x402

Installation

bash
npm install @primersystems/x402

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