X402 Protocol + Payment Channels
Complete Integration Guide: Transform HTTP 402 from theory to reality with instant, cost-effective micropayments on Solana
Overview
A complete payment system that combines:
- x402 Protocol: HTTP 402 Payment Required standard for micropayments
- Payment Channels: Off-chain scaling solution for instant, gasless payments
- Solana: High-performance blockchain for settlement
Result: APIs that accept payments as naturally as they handle authentication, with <10ms latency and $0 fees per payment.
The Problem This Solves
Traditional On-Chain Payments (scheme: 'exact')
Every API call → Blockchain transaction → 400-800ms latency → $0.0005 fee
❌ Too slow for real-time APIs
❌ Too expensive for sub-cent payments
❌ Can't scale to 100+ requests/minutePayment Channels Solution (scheme: 'channel')
Setup: 1 on-chain transaction (open channel)
Usage: Unlimited off-chain payments → <10ms latency → $0 fees
Settle: 1 on-chain transaction (claim batch)
✅ 91% faster than on-chain
✅ 94% cheaper overall
✅ Scales to thousands of req/minQuick Start: Add Payment Channels to Your x402 API
Step 1: Install the Package
npm install @x402-solana/core@latestVersion requirement: >= 0.3.0 (payment channels support)
Step 2: Update Your Server Code
Before (On-Chain Only):
import { TransactionVerifier } from '@x402-solana/core';
const verifier = new TransactionVerifier({
rpcUrl: 'https://api.devnet.solana.com',
commitment: 'confirmed',
});
// Only supports scheme: 'exact' (on-chain)After (Hybrid: On-Chain + Channels):
import {
TransactionVerifier,
ChannelPaymentVerifier, // ✨ NEW
parseX402Payment, // ✨ NEW
} from '@x402-solana/core';
// For on-chain payments (scheme: 'exact')
const onChainVerifier = new TransactionVerifier({
rpcUrl: 'https://api.devnet.solana.com',
commitment: 'confirmed',
});
// For payment channel payments (scheme: 'channel') ✨
const channelVerifier = new ChannelPaymentVerifier({
connection: new Connection('https://api.devnet.solana.com'),
programId: 'H8SsYx7Z8qp12AvaX8oEWDCHWo8JYmEK21zWLWcfW4Zc',
});
// Now your API supports BOTH payment methods!Step 3: Handle Both Payment Types
app.get('/api/premium-data', async (req, res) => {
const paymentHeader = req.headers['x-payment'];
if (!paymentHeader) {
// Return 402 with BOTH payment options
return res.status(402).json({
x402Version: 1,
accepts: [
{
// Option 1: On-chain (slower, per-payment fees)
scheme: 'exact',
network: 'solana-devnet',
payTo: 'YourUSDCTokenAccount...',
maxAmountRequired: '100000', // $0.10
asset: 'USDC',
},
{
// Option 2: Payment channel (instant, zero fees) ✨
scheme: 'channel',
network: 'solana-devnet',
payTo: 'YourServerWallet...',
maxAmountRequired: '100000',
asset: 'USDC',
programId: 'H8SsYx7Z8qp12AvaX8oEWDCHWo8JYmEK21zWLWcfW4Zc',
},
],
});
}
// Parse the payment header
const parsed = parseX402Payment(paymentHeader);
if (!parsed.success || !parsed.payment) {
return res.status(400).json({ error: 'Invalid payment header' });
}
// Route to appropriate verifier based on scheme
let verificationResult;
if (parsed.payment.scheme === 'exact') {
// On-chain verification (400-800ms)
const payload = parsed.payment.payload as any;
verificationResult = await onChainVerifier.verifyPayment({
signature: payload.transactionSignature,
expectedRecipient: 'YourUSDCTokenAccount...',
expectedAmountUSD: 0.10,
});
}
else if (parsed.payment.scheme === 'channel') {
// Channel verification (<10ms) ✨
const payload = parsed.payment.payload as any;
verificationResult = await channelVerifier.verifyChannelPayment(
{
channelId: payload.channelId,
amount: payload.amount,
nonce: payload.nonce,
signature: payload.channelSignature,
expiry: payload.expiry,
},
'YourServerWallet...',
{ minClaimIncrement: 1000n }
);
}
if (!verificationResult.valid) {
return res.status(402).json({ error: verificationResult.error });
}
// Payment verified! Return the premium data
res.json({ data: 'Your premium API response' });
});That's it! Your API now supports both payment methods, and clients can choose based on their usage pattern.
Package Ecosystem
Our payment channel solution consists of 4 packages that work together:
┌─────────────────────────────────────────────────────────────┐
│ Package Ecosystem │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. @x402-solana/core (npm) ← Foundation │
│ └─ HTTP 402 protocol + on-chain payment verification │
│ │
│ 2. @solana-payment-channel/core (local) ← Core Logic │
│ └─ Payment channel management + off-chain signatures │
│ │
│ 3. @solana-payment-channel/client (local) ← Browser SDK │
│ └─ Automatic payment client + wallet integration │
│ │
│ 4. @solana-payment-channel/server (local) ← Server SDK │
│ └─ Express/Fastify/NestJS middleware │
└─────────────────────────────────────────────────────────────┘@x402-solana/core (Foundation)
Basic HTTP 402 protocol with on-chain USDC verification.
npm install @x402-solana/coreWhen to use: Simple APIs with low request frequency (<10 req/min), or as fallback.
@solana-payment-channel/core (Core Logic)
Payment channel management, off-chain signatures, state management.
npm install @solana-payment-channel/core @solana/web3.js @coral-xyz/anchorWhen to use: Building custom integrations, need full control over channel lifecycle.
@solana-payment-channel/client (Browser SDK)
Automatic payment client for browsers with wallet integration.
npm install @solana-payment-channel/client @solana/wallet-adapter-reactWhen to use: Building frontend apps, need automatic payment handling.
@solana-payment-channel/server (Server SDK)
Express/Fastify/NestJS middleware for automatic payment verification.
npm install @solana-payment-channel/serverWhen to use: Building Node.js servers, need plug-and-play payment protection.
Package Comparison
| Package | Use Case | Complexity | Setup Time |
|---|---|---|---|
| @x402-solana/core | Basic on-chain payments | Low | 5 min |
| @solana-payment-channel/core | Custom channel logic | Medium | 30 min |
| @solana-payment-channel/client | Browser auto-pay | Low | 10 min |
| @solana-payment-channel/server | Server middleware | Very Low | 3 min |
Real-World Performance Comparison
Based on test suite: 50 API Payments × $0.10 = $5.00 Total
| Metric | On-Chain (exact) | Channel (channel) | Improvement |
|---|---|---|---|
| Total Cost | $0.0250 | $0.0015 | 94% cheaper |
| Cost/Payment | $0.0005 | $0.0000 | 100% savings |
| Avg Latency | 2,019ms | 175ms | 91% faster |
| RPC Calls | 150 | 6 | 96% fewer |
| On-Chain Txs | 50 | 3 | 94% reduction |
Payment Channel Program Features
Program ID: H8SsYx7Z8qp12AvaX8oEWDCHWo8JYmEK21zWLWcfW4Zc
1. open_channel - Lock funds, establish trust
pub fn open_channel(
channel_id: [u8; 32],
initial_deposit: u64, // Micro-USDC (e.g., 5_000_000 = $5)
expiry: i64, // Unix timestamp
credit_limit: u64, // Optional overdraft (0-1000 USDC)
)- • Minimum deposit: 1 USDC (prevents spam)
- • Maximum credit limit: 1000 USDC
- • USDC locked in program-controlled PDA
- • Emits ChannelOpened event
2. claim_payment - Server claims accumulated payments
pub fn claim_payment(
amount: u64, // Total claimed so far (cumulative)
nonce: u64, // Must increase monotonically
client_signature: [u8; 64], // Ed25519 signature
)- • Ed25519 signature verification using domain separator
- • Nonce protection: prevents replay (max increment: 10,000)
- • Overdraft support: can claim up to deposit + credit_limit
- • Batch claiming: 100s of payments → 1 transaction
3. add_funds - Top up without closing
pub fn add_funds(
amount: u64,
)- • Auto debt settlement: if client has overdraft debt, payment goes to server first
- • Remaining funds added to channel balance
- • No need to close/reopen channel
4. close_channel - Reclaim remaining funds
pub fn close_channel()- • Client can close anytime (gets refund of unused funds)
- • Anyone can close after expiry
- • Cannot close with outstanding debt (must settle first)
- • Returns remaining USDC to client
Complete Client Example
Client opens a channel, makes 50 payments, server claims once
import { ChannelManager, createPaymentAuthorizationV2 } from '@solana-payment-channel/core';
import { Connection, Keypair, PublicKey } from '@solana/web3.js';
const connection = new Connection('https://api.devnet.solana.com');
const clientWallet = Keypair.fromSecretKey(/* your secret */);
const serverPubkey = new PublicKey('ServerWallet...');
// Step 1: Open channel (1 on-chain tx, ~$0.0005)
const manager = new ChannelManager({
rpcUrl: 'https://api.devnet.solana.com',
programId: new PublicKey('H8SsYx7Z8qp12AvaX8oEWDCHWo8JYmEK21zWLWcfW4Zc'),
usdcMint: new PublicKey('Gh9ZwEmdLJ8DscKNTkTqPbNwLNNBjuSzaG9Vp2KGtKJr'),
network: 'devnet',
}, clientWallet);
const channelId = await manager.openChannel({
serverPubkey,
initialDeposit: BigInt(5_000_000), // $5.00
// Optional: creditLimit: BigInt(1_000_000) // $1 overdraft
});
console.log('Channel opened:', channelId);
// Step 2: Make 50 API calls (all off-chain, $0 fees, <10ms each)
for (let i = 1; i <= 50; i++) {
const cumulativeAmount = BigInt(i * 100_000); // $0.10 per call
const nonce = BigInt(i);
// Create signed authorization (off-chain, <1ms)
const authorization = await createPaymentAuthorizationV2(
new PublicKey(channelId),
serverPubkey,
cumulativeAmount,
nonce,
BigInt(Math.floor(Date.now() / 1000) + 3600), // 1 hour expiry
clientWallet
);
// Make API call with X-PAYMENT header
const response = await fetch('https://your-api.com/premium-data', {
headers: {
'X-Payment': createChannelPaymentHeader(
channelId,
cumulativeAmount.toString(),
nonce.toString(),
authorization.signature.toString('base64'),
'solana-devnet',
),
},
});
const data = await response.json();
console.log(`Call ${i}/50: ${response.status}`, data);
}
// Step 3: Close channel when done (1 on-chain tx, ~$0.0005)
await manager.closeChannel(channelId);
console.log('Channel closed, unused funds returned');Step-by-Step Integration Tutorials
Tutorial 1: Add Channels to Existing x402 API
Current state: You have an API using @x402-solana/core for on-chain payments
Goal: Support payment channels without breaking existing clients
// 1. Install latest version
npm install @x402-solana/core@latest
// 2. Import channel verifier
import { ChannelPaymentVerifier, parseX402Payment } from '@x402-solana/core';
// 3. Initialize verifier
const channelVerifier = new ChannelPaymentVerifier({
connection: new Connection(process.env.SOLANA_RPC_URL),
programId: process.env.CHANNEL_PROGRAM_ID,
});
// 4. Update payment endpoint
app.post('/api/verify-payment', async (req, res) => {
const paymentHeader = req.headers['x-payment'];
const parsed = parseX402Payment(paymentHeader);
// Route based on scheme
if (parsed.payment.scheme === 'channel') {
// Use channel verifier (<10ms)
const result = await channelVerifier.verifyChannelPayment(/*...*/);
} else {
// Use existing on-chain verifier (400-800ms)
const result = await onChainVerifier.verifyPayment(/*...*/);
}
});Tutorial 2: Deploy Your Own Payment Channel Program
# 1. Clone the repo
git clone https://github.com/yourusername/solana-payment-channels
cd solana-payment-channels
# 2. Build the Anchor program
anchor build
# 3. Deploy to devnet
anchor deploy --provider.cluster devnet
# 4. Copy the program ID
# Output: Program Id: H8SsYx7Z8qp12AvaX8oEWDCHWo8JYmEK21zWLWcfW4Zc
# 5. Update your .env
echo "CHANNEL_PROGRAM_ID=H8SsYx7Z..." >> .env
# 6. Test it
npm testMonitoring & Observability
Track Channel Performance
import { ChannelPaymentVerifier } from '@x402-solana/core';
const verifier = new ChannelPaymentVerifier(/*...*/);
// Add metrics tracking
app.use((req, res, next) => {
const start = Date.now();
res.on('finish', () => {
const duration = Date.now() - start;
const scheme = req.headers['x-payment'] ?
parseX402Payment(req.headers['x-payment']).payment.scheme :
'none';
// Log to your metrics service
metrics.histogram('api.payment.latency', duration, { scheme });
metrics.increment('api.payment.count', { scheme });
});
next();
});Solana Explorer Links
// Monitor channel state on-chain
const channelPDA = PublicKey.findProgramAddressSync(
[Buffer.from('channel'), Buffer.from(channelId, 'hex')],
new PublicKey(programId)
);
console.log(`View channel: https://explorer.solana.com/address/${channelPDA}?cluster=devnet`);
// Monitor claim transactions
console.log(`View claim: https://explorer.solana.com/tx/${claimSignature}?cluster=devnet`);Common Issues & Solutions
Issue: "Channel not found on-chain"
Cause: Channel not opened yet or wrong program ID
Solution: Verify program ID matches deployment, ensure open_channel succeeded
Issue: "Invalid signature"
Cause: Message format mismatch between client signing and server verification
Solution: Both must use same message structure (109 bytes with domain separator)
Issue: "Nonce increment too large"
Cause: Nonce jumped >10,000 (anti-griefing protection)
Solution: Use sequential nonces (1, 2, 3...), don't skip
Issue: "Cannot close with debt"
Cause: Client has overdraft debt
Solution: Call add_funds to settle debt before close_channel
Resources
Program on Solana Explorer
View the deployed payment channel program:
H8SsYx7Z8qp12AvaX8oEWDCHWo8JYmEK21zWLWcfW4Zc