Complete setup instructions for the x402 Solana protocol implementation.
- Node.js 18+ (for native fetch support)
- npm or yarn
- Solana CLI (optional, for devnet testing)
cd x402_ts
npm installThe facilitator needs a Solana keypair to sign and pay for transactions.
Option A: Using Solana CLI
solana-keygen new --outfile facilitator-keypair.json
solana-keygen pubkey facilitator-keypair.jsonOption B: Using Node.js
import { generateKeyPairSigner } from '@solana/kit';
import bs58 from 'bs58';
const signer = await generateKeyPairSigner();
console.log('Public Key:', signer.address);
// Export private key bytes if neededCreate .env file from template:
cp env.example .envEdit .env with your configuration:
# Facilitator Configuration
FACILITATOR_PORT=3001
FACILITATOR_PRIVATE_KEY=<your_facilitator_private_key_base58>
FACILITATOR_PUBLIC_KEY=<your_facilitator_public_key>
# Solana Configuration
SOLANA_RPC_URL=https://api.devnet.solana.com
SOLANA_WS_URL=wss://api.devnet.solana.com
SOLANA_NETWORK=devnet
# Server Configuration
SERVER_PORT=3000
FACILITATOR_URL=http://localhost:3001
MERCHANT_SOLANA_ADDRESS=<merchant_wallet_address>
# Payment Configuration
MAX_PAYMENT_AMOUNT=1000000000
SIMULATE_TRANSACTIONS=false
# Database
DATABASE_PATH=./src/facilitator/nonce.db
NONCE_EXPIRY_HOURS=24The facilitator needs SOL to pay gas fees:
solana airdrop 2 <FACILITATOR_PUBLIC_KEY> --url devnetVerify balance:
solana balance <FACILITATOR_PUBLIC_KEY> --url devnetnpm run buildThis compiles TypeScript files from src/ to dist/.
Using PM2 (recommended):
npm startThis starts both facilitator and server as background processes.
View logs:
npm run logsStop applications:
npm stopRestart applications:
npm restartUsing npm scripts directly (development):
# Terminal 1: Start facilitator
npm run dev:facilitator
# Terminal 2: Start server
npm run dev:servernpm run generate:clientThis creates test-client-keypair.json with a new Solana wallet.
solana airdrop 1 <CLIENT_PUBLIC_KEY> --url devnetGet the public key from test-client-keypair.json or from the script output.
Main test (TRUE x402 instant finality):
npm testThis test:
- Creates a payment request with signed transaction
- Sends to server's protected endpoint
- Facilitator verifies and settles payment
- Client's SOL moves to merchant on-chain
- Returns premium content
Test HTTP 402 response (no payment):
npm run test:402This test:
- Requests protected resource without payment
- Expects HTTP 402 Payment Required response
- Verifies payment requirements in response
Test replay attack prevention:
npm run test:replayThis test:
- Sends payment request twice with same nonce
- First attempt succeeds
- Second attempt is rejected (replay attack blocked)
Facilitator health:
curl http://localhost:3001/health | jq .Expected response:
{
"success": true,
"data": {
"status": "healthy",
"timestamp": "2025-10-08T...",
"facilitator": "38dZtt5G8rRT..."
}
}Server health:
curl http://localhost:3000/health | jq .View statistics:
curl http://localhost:3001/stats | jq .Expected response:
{
"success": true,
"data": {
"totalNonces": 25,
"usedNonces": 25,
"activeNonces": 0,
"expiredNonces": 0
}
}curl http://localhost:3000/public | jq .This should return successfully without requiring payment.
curl http://localhost:3000/api/premium-dataThis should return HTTP 402 with payment requirements:
{
"error": "Payment Required",
"accepts": [
{
"maxAmountRequired": "10000000",
"asset": "SOL",
"payTo": "merchant_address",
"network": "devnet",
"resource": "/api/premium-data"
}
]
}npx pm2 listnpm run pm2:monit# Facilitator logs
npx pm2 logs x402-facilitator
# Server logs
npx pm2 logs x402-servernpm run pm2:deleteEdit files in src/ directory.
npm run buildnpm restartOr use watch mode for automatic restart:
npm run dev:facilitator # Terminal 1
npm run dev:server # Terminal 2npm test# Lint TypeScript
npm run lint
# Format code
npm run fmt
# Check formatting
npm run fmt:checkSolution:
- Ensure
.envfile exists in project root - Verify
FACILITATOR_PRIVATE_KEYis set and on a single line - Restart applications after updating
.env
Solution:
# Stop PM2 processes
npm stop
# Or kill process manually
lsof -ti:3001 | xargs kill -9Solution:
- Fund test client wallet:
solana airdrop 1 <address> --url devnet - Check client balance:
solana balance <address> --url devnet - Ensure
SIMULATE_TRANSACTIONS=falsein.env
Solution:
- Check all required environment variables in
.env - Ensure URLs are valid (start with http:// or https://)
- Verify port numbers are integers
- Check private key is valid base58 string
Solution:
- Ensure facilitator has SOL for gas:
solana balance <facilitator> --url devnet - Check Solana devnet status: https://status.solana.com/
- Verify RPC endpoint is accessible:
curl https://api.devnet.solana.com - Check transaction on explorer: https://explorer.solana.com/?cluster=devnet
Solution:
# Stop applications
npm stop
# Remove database
rm src/facilitator/nonce.db
# Restart applications (database will be recreated)
npm startSolution:
- PM2 loads
.envvianode_args: '-r dotenv/config'inecosystem.config.cjs - Verify
.envfile path is correct - Restart PM2:
npm run pm2:delete && npm start
- Use HTTPS for all endpoints
- Store private keys securely (not in code or version control)
- Use environment variables or secret management system
- Enable rate limiting on facilitator endpoints
- Set up monitoring and alerting
- Configure firewall rules (only expose necessary ports)
- Use mainnet RPC endpoints (not devnet)
- Implement request signing/authentication
- Set reasonable
MAX_PAYMENT_AMOUNT - Configure log rotation
- Back up nonce database regularly
# Production settings
SOLANA_NETWORK=mainnet-beta
SOLANA_RPC_URL=https://api.mainnet-beta.solana.com
SIMULATE_TRANSACTIONS=false
MAX_PAYMENT_AMOUNT=1000000000
NONCE_EXPIRY_HOURS=24Use PM2 in production:
# Start with PM2
npm start
# Save PM2 configuration
npx pm2 save
# Setup PM2 startup script
npx pm2 startup# Real-time monitoring
npm run pm2:monit
# View logs
npm run logs
# Check application health
curl http://localhost:3001/health
curl http://localhost:3000/healthFor issues or questions:
- Check this guide and README.md
- Review logs:
npm run logs - Check PM2 status:
npx pm2 list - Verify configuration: review
.envfile - Test connectivity:
curlhealth endpoints