Skip to main content

Overview

This guide shows you how to add AnySpend x402 payment support to your Express API, enabling you to accept payments in any token while receiving the token of your choice. With just a few lines of code, your API can monetize endpoints and handle automatic payment verification and settlement. Key Benefits:
  • Buyers can pay with any token they hold (ETH, DAI, USDC, etc.)
  • Sellers can receive any token they prefer (USDC, B3, DAI, custom tokens, etc.)
  • AnySpend automatically handles all token conversions
  • No blockchain infrastructure or wallet management required

Prerequisites

Before you begin, make sure you have:
  • Node.js 18+ installed
  • An Express.js API (or willingness to create one)
  • A wallet address to receive USDC payments
  • Basic knowledge of Express middleware

Installation

Install the AnySpend x402 package:
npm install @b3dotfun/anyspend-x402

Quick Start

1. Basic Integration

The simplest way to add payments to your Express API:
import express from 'express';
import { paymentMiddleware, facilitator } from '@b3dotfun/anyspend-x402';

const app = express();

// Define your payment routes and prices
const endpointConfig = {
  '/api/premium-data': {
    amount: '1.00',        // $1.00 in USDC
    asset: 'USD'           // Will be converted to USDC
  },
  '/api/ai-inference': {
    amount: '0.50',        // $0.50 in USDC
    asset: 'USD'
  }
};

// Your USDC receiving address
const recipientAddress = '0xYourWalletAddress...';

// Apply payment middleware
app.use(paymentMiddleware(
  recipientAddress,
  endpointConfig,
  facilitator                    // Pre-configured Anyspend facilitator
));

// Define your protected routes
app.get('/api/premium-data', (req, res) => {
  // This only runs after successful payment
  res.json({
    data: 'Your premium data here',
    message: 'Payment successful!'
  });
});

app.get('/api/ai-inference', (req, res) => {
  res.json({
    result: 'AI inference result',
    model: 'gpt-4'
  });
});

app.listen(3000, () => {
  console.log('API with x402 payments running on port 3000');
});
What this does:
  1. Clients request your API endpoint
  2. Middleware returns 402 Payment Required with payment details
  3. Client pays with their preferred token (ETH, DAI, etc.)
  4. AnySpend facilitator converts to USDC and settles to your wallet
  5. Your route handler executes and returns data

2. Receive Custom Tokens (e.g., B3)

You can configure your API to receive any token, not just USDC:
import express from 'express';
import { paymentMiddleware, facilitator } from '@b3dotfun/anyspend-x402';
import { Address } from 'viem';

const app = express();
const PAYTO_ADDRESS = '0xYourWalletAddress...' as Address;
const NETWORK = 'base' as const;

// Configure to receive B3 tokens
const endpointConfig = {
  'POST /api/premium': {
    price: {
      amount: '100000000000000000000', // 100 B3 tokens (18 decimals)
      asset: {
        address: '0xB3B32F9f8827D4634fE7d973Fa1034Ec9fdDB3B3' as Address,
        decimals: 18,
        eip712: {
          name: 'B3',
          version: '1',
        },
      },
    },
    network: NETWORK,
    config: {
      description: 'Premium data access',
      mimeType: 'application/json',
    },
  }
};

app.use(paymentMiddleware(PAYTO_ADDRESS, endpointConfig, facilitator));

app.post('/api/premium', (req, res) => {
  // Payment verified - you received 100 B3 tokens!
  res.json({
    data: 'Premium ETH price history',
    paymentReceived: '100 B3 tokens'
  });
});

app.listen(3000);
What’s different:
  • Buyers can still pay with any token (ETH, USDC, DAI, etc.)
  • AnySpend converts their payment to B3 tokens
  • You receive exactly 100 B3 tokens in your wallet
  • Complete flexibility for both buyers and sellers!

Configuration

Facilitator URL

The AnySpend facilitator is hosted at:
https://mainnet.anyspend.com/x402
The @b3dotfun/anyspend-x402 package includes a pre-configured facilitator instance:
import { facilitator } from '@b3dotfun/anyspend-x402';

// Pre-configured to use mainnet.anyspend.com/x402
app.use(paymentMiddleware(recipientAddress, endpointConfig, facilitator));

Endpoint Configuration

You have three methods to configure payment requirements: Let AnySpend handle token conversion automatically:
const endpointConfig = {
  '/api/data': {
    amount: '1.00',      // $1.00 worth of any token
    asset: 'USD'
  },
  '/api/compute': {
    amount: '2.50',
    asset: 'USD'
  }
};
Benefits:
  • Clients can pay with any supported token
  • AnySpend converts to USDC automatically
  • You always receive USDC

Method 2: Specific Token Requirements

Require specific tokens on specific networks. You can receive any token, not just USDC! Example 1: Receive USDC (Stablecoin)
const endpointConfig = {
  '/api/data': {
    amount: '1000000',   // 1 USDC (6 decimals)
    asset: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', // USDC on Base
    network: 'base-mainnet'
  }
};
Example 2: Receive B3 Tokens (Custom Token)
import { Address } from 'viem';

const PAYTO_ADDRESS = '0xYourWalletAddress...' as Address;
const NETWORK = 'base' as const;

const endpointConfig = {
  'POST /api/premium': {
    price: {
      amount: '100000000000000000000', // 100 B3 tokens (100 * 10^18)
      asset: {
        address: '0xB3B32F9f8827D4634fE7d973Fa1034Ec9fdDB3B3' as Address,
        decimals: 18,
        eip712: {
          name: 'B3',
          version: '1',
        },
      },
    },
    network: NETWORK,
    config: {
      description: 'Access to premium ETH price history data from CoinGecko',
      mimeType: 'application/json',
    },
  }
};

app.use(paymentMiddleware(PAYTO_ADDRESS, endpointConfig, facilitator));
Example 3: Receive DAI (Stablecoin)
const endpointConfig = {
  '/api/ai-inference': {
    amount: '1000000000000000000', // 1 DAI (18 decimals)
    asset: '0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb', // DAI on Base
    network: 'base-mainnet'
  }
};
Example 4: Receive Native ETH
const endpointConfig = {
  '/api/compute': {
    amount: '500000000000000', // 0.0005 ETH (18 decimals)
    asset: '0x0000000000000000000000000000000000000000', // Native ETH
    network: 'base-mainnet'
  }
};
Example 5: Receive Custom ERC-20 Token For any ERC-20 token with permit support:
const endpointConfig = {
  '/api/data': {
    price: {
      amount: '1000000000000000000', // 1 token (adjust decimals)
      asset: {
        address: '0xYourTokenAddress...' as Address,
        decimals: 18, // Your token's decimals
        eip712: {
          name: 'YourTokenName', // From token contract
          version: '1',          // EIP-712 version
        },
      },
    },
    network: 'base-mainnet',
    config: {
      description: 'Your API endpoint description',
      mimeType: 'application/json',
    },
  }
};
Key Points:
  • Buyers can still pay with any token they hold
  • AnySpend automatically converts their token to the one you want to receive
  • You receive exactly the token you specified in your configuration
  • Both buyer and seller flexibility are maintained

Finding Token Details

To configure a custom token, you need to know its EIP-712 parameters. Here’s how to find them: Method 1: Check Token Contract Use a blockchain explorer like Basescan:
// Read from the token contract
const name = await token.read.name();      // e.g., "B3"
const version = await token.read.version(); // e.g., "1"
const decimals = await token.read.decimals(); // e.g., 18
Method 2: Common Tokens
TokenNameVersionDecimalsBase Address
USDCUSD Coin260x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913
DAIDai Stablecoin1180x50c5725949A6F0c72E6C4a641F24049A917DB0Cb
B3B31180xB3B32F9f8827D4634fE7d973Fa1034Ec9fdDB3B3
Method 3: Use Viem
import { createPublicClient, http } from 'viem';
import { base } from 'viem/chains';

const client = createPublicClient({
  chain: base,
  transport: http()
});

const [name, version, decimals] = await Promise.all([
  client.readContract({
    address: tokenAddress,
    abi: [{
      name: 'name',
      type: 'function',
      stateMutability: 'view',
      inputs: [],
      outputs: [{ type: 'string' }]
    }],
    functionName: 'name'
  }),
  client.readContract({
    address: tokenAddress,
    abi: [{
      name: 'version',
      type: 'function',
      stateMutability: 'view',
      inputs: [],
      outputs: [{ type: 'string' }]
    }],
    functionName: 'version'
  }),
  client.readContract({
    address: tokenAddress,
    abi: [{
      name: 'decimals',
      type: 'function',
      stateMutability: 'view',
      inputs: [],
      outputs: [{ type: 'uint8' }]
    }],
    functionName: 'decimals'
  })
]);

console.log({ name, version, decimals });

Method 3: Flexible (Let Client Choose)

Allow clients to specify their preferred payment token:
const endpointConfig = {
  '/api/data': {
    amount: '1.00',
    asset: 'USD',
    flexible: true       // Accept X-PREFERRED-TOKEN header
  }
};
Clients can then specify their token preference:
curl https://api.example.com/data \
  -H "X-PREFERRED-TOKEN: 0x4200000000000000000000000000000000000006" \
  -H "X-PREFERRED-NETWORK: base-mainnet"

Advanced Configuration

Dynamic Pricing

Calculate prices based on request parameters:
import { paymentMiddleware, facilitator } from '@b3dotfun/anyspend-x402';

app.use('/api/compute', (req, res, next) => {
  // Calculate price based on compute units requested
  const units = parseInt(req.query.units as string) || 1;
  const pricePerUnit = 0.10; // $0.10 per unit
  const totalPrice = (units * pricePerUnit).toFixed(2);

  // Attach dynamic config to request
  req.x402Config = {
    amount: totalPrice,
    asset: 'USD'
  };

  next();
}, paymentMiddleware(recipientAddress, {
  '/api/compute': (req) => req.x402Config // Dynamic config
}, facilitator));

app.get('/api/compute', (req, res) => {
  const units = parseInt(req.query.units as string) || 1;
  res.json({
    result: `Computed ${units} units`,
    cost: req.x402Config.amount
  });
});

Custom Facilitator Configuration

If you need custom facilitator settings:
import { Facilitator } from '@b3dotfun/anyspend-x402';

const customFacilitator = new Facilitator({
  baseUrl: 'https://mainnet.anyspend.com/x402',
  apiKey: process.env.CDP_API_KEY_ID,        // Optional: Coinbase CDP
  apiSecret: process.env.CDP_API_KEY_SECRET,
  timeout: 30000,                             // 30s timeout
  retries: 3                                  // Retry failed requests
});

app.use(paymentMiddleware(
  recipientAddress,
  endpointConfig,
  customFacilitator
));

Multiple Recipients

Route payments to different wallets based on endpoint:
const endpointConfig = {
  '/api/service-a': {
    amount: '1.00',
    asset: 'USD',
    recipient: '0xWalletA...'
  },
  '/api/service-b': {
    amount: '2.00',
    asset: 'USD',
    recipient: '0xWalletB...'
  }
};

// Pass null as recipient, use per-endpoint recipients
app.use(paymentMiddleware(null, endpointConfig, facilitator));

Protected Routes

Protect Specific Routes

Only protect endpoints that require payment:
// Public route - no payment required
app.get('/api/public', (req, res) => {
  res.json({ message: 'This is free!' });
});

// Protected route - payment required
app.use('/api/premium', paymentMiddleware(
  recipientAddress,
  {
    '/api/premium': { amount: '1.00', asset: 'USD' }
  },
  facilitator
));

app.get('/api/premium', (req, res) => {
  res.json({ message: 'This cost money!' });
});

Route-Specific Middleware

Apply different payment configs to different route groups:
// Basic tier - $0.50
app.use('/api/basic', paymentMiddleware(
  recipientAddress,
  {
    '/api/basic/*': { amount: '0.50', asset: 'USD' }
  },
  facilitator
));

// Premium tier - $2.00
app.use('/api/premium', paymentMiddleware(
  recipientAddress,
  {
    '/api/premium/*': { amount: '2.00', asset: 'USD' }
  },
  facilitator
));

Testing

Local Development

Test your API locally with testnet USDC:
import { Facilitator } from '@b3dotfun/anyspend-x402';

const testFacilitator = new Facilitator({
  baseUrl: 'https://testnet.anyspend.com/x402', // Testnet facilitator
});

const endpointConfig = {
  '/api/test': {
    amount: '0.01',
    asset: 'USD',
    network: 'base-sepolia' // Use testnet
  }
};

app.use(paymentMiddleware(
  '0xYourTestWallet...',
  endpointConfig,
  testFacilitator
));
Get testnet USDC from the Coinbase Faucet.

Test Client Request

Test with curl:
# 1. Get payment requirements
curl https://your-api.com/api/premium-data

# Response: 402 Payment Required
# {
#   "x402": "0.2",
#   "requirements": [{
#     "scheme": "exact",
#     "network": "base-mainnet",
#     "amount": "1000000",
#     "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
#     "recipient": "0xYourWallet..."
#   }]
# }

# 2. Make payment with X-PAYMENT header (requires signed payload)
curl https://your-api.com/api/premium-data \
  -H "X-PAYMENT: eyJ4NDAyIjoi..." \
  -H "Content-Type: application/json"
Or use the x402 client SDK (see Quickstart for Buyers).

Monitoring Payments

Access Payment Metadata

The middleware attaches payment information to the request:
app.get('/api/premium', (req, res) => {
  // Access payment details
  const payment = req.x402Payment;

  console.log('Payment received:', {
    txHash: payment.txHash,
    network: payment.network,
    amount: payment.amount,
    token: payment.token,
    from: payment.from
  });

  res.json({
    data: 'Your premium data',
    paymentTx: payment.txHash
  });
});

Webhook Notifications

Get notified when payments are settled:
import { paymentMiddleware, facilitator } from '@b3dotfun/anyspend-x402';

app.use(paymentMiddleware(
  recipientAddress,
  endpointConfig,
  facilitator,
  {
    onPaymentSettled: async (payment) => {
      console.log('Payment settled:', payment);

      // Log to database
      await db.payments.create({
        txHash: payment.txHash,
        amount: payment.amount,
        from: payment.from,
        timestamp: new Date()
      });

      // Send notification
      await sendEmail({
        to: 'admin@example.com',
        subject: 'Payment Received',
        body: `Received ${payment.amount} USDC from ${payment.from}`
      });
    }
  }
));

Examples

AI Agent API

import express from 'express';
import { paymentMiddleware, facilitator } from '@b3dotfun/anyspend-x402';

const app = express();
app.use(express.json());

const endpointConfig = {
  '/api/chat': {
    amount: '0.10',     // $0.10 per request
    asset: 'USD'
  },
  '/api/image': {
    amount: '0.50',     // $0.50 per image
    asset: 'USD'
  }
};

app.use(paymentMiddleware(
  process.env.USDC_WALLET_ADDRESS,
  endpointConfig,
  facilitator
));

app.post('/api/chat', async (req, res) => {
  const { message } = req.body;

  // Call your AI service
  const response = await openai.chat.completions.create({
    model: 'gpt-4',
    messages: [{ role: 'user', content: message }]
  });

  res.json({
    response: response.choices[0].message.content,
    payment: req.x402Payment.txHash
  });
});

app.post('/api/image', async (req, res) => {
  const { prompt } = req.body;

  const image = await openai.images.generate({
    model: 'dall-e-3',
    prompt
  });

  res.json({
    imageUrl: image.data[0].url,
    payment: req.x402Payment.txHash
  });
});

app.listen(3000);

Data API with Rate Limiting

const endpointConfig = {
  '/api/data/basic': {
    amount: '0.01',      // $0.01 per request
    asset: 'USD'
  },
  '/api/data/premium': {
    amount: '0.10',      // $0.10 per request
    asset: 'USD'
  }
};

// Track usage per payer
const usage = new Map();

app.use(paymentMiddleware(
  recipientAddress,
  endpointConfig,
  facilitator,
  {
    onPaymentSettled: (payment) => {
      const count = usage.get(payment.from) || 0;
      usage.set(payment.from, count + 1);
    }
  }
));

app.get('/api/data/premium', (req, res) => {
  const payer = req.x402Payment.from;
  const requests = usage.get(payer) || 0;

  res.json({
    data: getPremiumData(),
    requestCount: requests,
    discount: requests > 100 ? '10%' : 'none'
  });
});

Best Practices

Store sensitive data in environment variables:
const recipientAddress = process.env.USDC_WALLET_ADDRESS;
const apiKey = process.env.CDP_API_KEY_ID;
Log all payment events for debugging and analytics:
{
  onPaymentSettled: (payment) => {
    logger.info('Payment received', {
      txHash: payment.txHash,
      amount: payment.amount,
      from: payment.from,
      endpoint: payment.endpoint
    });
  }
}
Provide clear error messages when payments fail:
app.use((err, req, res, next) => {
  if (err.code === 'X402_PAYMENT_FAILED') {
    res.status(402).json({
      error: 'Payment verification failed',
      message: 'Please try again with a valid payment'
    });
  }
  next(err);
});
Always test with testnet before deploying to production:
const facilitatorUrl = process.env.NODE_ENV === 'production'
  ? 'https://mainnet.anyspend.com/x402'
  : 'https://testnet.anyspend.com/x402';

What’s Next?

Troubleshooting

Check that:
  • Your facilitator URL is correct: https://mainnet.anyspend.com/x402
  • Your recipient address is valid
  • The network configuration matches the client’s network
Make sure your endpoint config allows flexible payments:
{
  '/api/endpoint': {
    amount: '1.00',
    asset: 'USD',
    flexible: true  // Allow any token
  }
}
Verify:
  • Your recipient address is correct
  • You’re checking the correct network
  • Payment was settled (check req.x402Payment.txHash)

Getting Help