Add Payments to Your Express.js API in 5 Minutes
TL;DR: Install @t402/express and @t402/evm, add the middleware to your route, configure your payment address — done. Your API now returns 402 for unauthorized requests and accepts USDT payments via the X-Payment header.
Prerequisites
- Node.js 18+ and npm/pnpm
- An existing Express.js application (or create one fresh)
- A wallet address to receive payments (any EVM address)
Step 1: Install Packages
npm install @t402/express @t402/evm @t402/core
Three packages: @t402/express provides the middleware, @t402/evm handles EVM chain verification, and @t402/core provides shared types.
Step 2: Configure the Middleware
// payment.ts
import { createPaymentMiddleware } from '@t402/express';
import { ExactEvmScheme } from '@t402/evm';
const scheme = ExactEvmScheme.server({
rpcUrl: process.env.RPC_URL || 'https://mainnet.base.org'
});
export const paymentMiddleware = createPaymentMiddleware({
scheme,
facilitatorUrl: 'https://facilitator.t402.io',
defaultRequirements: {
scheme: 'exact',
network: 'eip155:8453',
payTo: process.env.PAY_TO_ADDRESS!,
}
});
Step 3: Protect Your Routes
// app.ts
import express from 'express';
import { paymentMiddleware } from './payment';
const app = express();
// Free endpoint — no payment required
app.get('/health', (req, res) => {
res.json({ status: 'ok' });
});
// Paid endpoint — requires $0.01 USDT
app.get('/api/premium',
paymentMiddleware({ amount: '10000' }), // 0.01 USDT (6 decimals)
(req, res) => {
res.json({ data: 'Premium content here' });
}
);
app.listen(3000);
That's it. When a client hits /api/premium without payment, they get a 402 response with payment requirements. When they include a valid X-Payment header, the middleware verifies the payment and calls your handler.
Step 4: Test It
# Start your server
npm run dev
# Request without payment → 402
curl -s http://localhost:3000/api/premium | jq .
# {
# "error": "Payment required",
# "accepts": [{"scheme":"exact","network":"eip155:8453",...}]
# }
# Pay using the T402 CLI
t402 request http://localhost:3000/api/premium
# {"data":"Premium content here"}
Per-Route Pricing
Different endpoints can have different prices:
// $0.001 per weather query
app.get('/api/weather', paymentMiddleware({ amount: '1000' }), handler);
// $0.05 per AI generation
app.post('/api/generate', paymentMiddleware({ amount: '50000' }), handler);
// $1.00 per premium dataset
app.get('/api/dataset', paymentMiddleware({ amount: '1000000' }), handler);
Accept Multiple Chains
Offer clients a choice of payment networks:
app.get('/api/data',
paymentMiddleware({
amount: '10000',
accepts: [
{ network: 'eip155:8453' }, // Base
{ network: 'eip155:42161' }, // Arbitrum
{ network: 'eip155:10' }, // Optimism
]
});
handler
);
The 402 response will include all accepted networks, and the client picks whichever chain they have funds on.
Environment Variables
# .env
PAY_TO_ADDRESS=0xYourWalletAddress
RPC_URL=https://mainnet.base.org
FACILITATOR_URL=https://facilitator.t402.io
PAY_TO_ADDRESS is where payments are settled. This is your standard EVM wallet address. Funds arrive in USDT on the specified chain.
What Happens Under the Hood
- Client sends GET without payment - middleware returns 402 with payment requirements in X-Payment header and response body
- Client signs an EIP-3009 TransferWithAuthorization (off-chain, no gas)
- Client retries with the signed payload in the X-Payment header
- Middleware sends the payload to the facilitator for verification
- Facilitator validates the signature, amount, recipient, and deadline
- If valid, middleware calls your route handler (the resource is served)
- Facilitator settles the payment on-chain (async, after response)
Next Steps
- Add the Bazaar extension for AI agent discoverability
- Enable gasless payments so users don't need ETH
- Add non-EVM chains (Solana, TON) for broader wallet support
- Deploy your own facilitator for production independence
Full Framework Documentation
T402 also supports Hono, Fastify, Next.js, and raw Fetch/Axios clients.