x402 Payment Protocol Paywall UI
npm install @x402/paywallModular paywall UI for the x402 payment protocol with support for EVM and Solana networks.
- Pre-built paywall UI out of the box
- Wallet connection (MetaMask, Coinbase Wallet, Phantom, etc.)
- USDC balance checking
- Multi-network support (EVM + Solana)
- Tree-shakeable - only bundle what you need
- Fully customizable via builder pattern
``bash`
pnpm add @x402/paywall
Choose the import that matches your needs:
| Import | Size | Networks | Use Case |
|--------|------|----------|----------|
| @x402/paywall | 3.5MB | EVM + Solana | Multi-network apps |@x402/paywall/evm
| | 3.4MB | EVM only | Base, Ethereum, Polygon, etc. |@x402/paywall/svm
| | 1.0MB | Solana only | Solana apps |
`typescript
import { createPaywall } from '@x402/paywall';
import { evmPaywall } from '@x402/paywall/evm';
const paywall = createPaywall()
.withNetwork(evmPaywall)
.withConfig({
appName: 'My App',
testnet: true
})
.build();
// Use with Express
app.use(paymentMiddleware(routes, facilitators, schemes, undefined, paywall));
`
`typescript
import { createPaywall } from '@x402/paywall';
import { svmPaywall } from '@x402/paywall/svm';
const paywall = createPaywall()
.withNetwork(svmPaywall)
.withConfig({
appName: 'My Solana App',
testnet: true
})
.build();
`
`typescript
import { createPaywall } from '@x402/paywall';
import { evmPaywall } from '@x402/paywall/evm';
import { svmPaywall } from '@x402/paywall/svm';
const paywall = createPaywall()
.withNetwork(evmPaywall) // First-match priority
.withNetwork(svmPaywall) // Fallback option
.withConfig({
appName: 'Multi-chain App',
testnet: true
})
.build();
`
`typescript`
interface PaywallConfig {
appName?: string; // App name shown in wallet connection
appLogo?: string; // App logo URL
currentUrl?: string; // URL of protected resource
testnet?: boolean; // Use testnet (default: true)
}
When multiple networks are registered, the paywall uses first-match selection:
1. Iterates through paymentRequired.accepts array
2. Finds the first payment requirement that has a registered handler
3. Uses that handler to generate the HTML
Example:
`typescript
// Server returns multiple options
{
"accepts": [
{ "network": "solana:5eykt...", ... }, // First
{ "network": "eip155:8453", ... } // Second
]
}
// If both handlers registered, Solana is selected (it's first in accepts)
const paywall = createPaywall()
.withNetwork(evmPaywall)
.withNetwork(svmPaywall)
.build();
`
EVM Networks (via evmPaywall):eip155:*
- CAIP-2: (e.g., eip155:8453 for Base, eip155:84532 for Base Sepolia)
Solana Networks (via svmPaywall):solana:*
- CAIP-2: (e.g., solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp for mainnet)
`typescript
import express from 'express';
import { paymentMiddleware } from '@x402/express';
import { createPaywall } from '@x402/paywall';
import { evmPaywall } from '@x402/paywall/evm';
const app = express();
const paywall = createPaywall()
.withNetwork(evmPaywall)
.withConfig({ appName: 'My API' })
.build();
app.use(paymentMiddleware(
{ "/api/premium": { price: "$0.10", network: "eip155:84532", payTo: "0x..." } },
facilitators,
schemes,
undefined,
paywall
));
`
If you provide paywallConfig without a custom paywall, @x402/core automatically:@x402/paywall
1. Tries to load if installed
2. Falls back to basic HTML if not installed
`typescript`
// Simple usage - auto-detects @x402/paywall
app.use(paymentMiddleware(routes, facilitators, schemes, {
appName: 'My App',
testnet: true
}));
You can create custom handlers for new networks:
`typescript
import { createPaywall, type PaywallNetworkHandler } from '@x402/paywall';
const suiPaywall: PaywallNetworkHandler = {
supports: (req) => req.network.startsWith('sui:'),
generateHtml: (req, paymentRequired, config) => {
return ...; // Your custom Sui paywall
}
};
const paywall = createPaywall()
.withNetwork(evmPaywall)
.withNetwork(svmPaywall)
.withNetwork(suiPaywall) // Custom handler
.build();
`
`bash`
pnpm build:paywall # Generate HTML templates
pnpm build # Build TypeScript
`bash``
pnpm test # Run unit tests