diff --git a/README.md b/README.md index 086478f..77241e9 100644 --- a/README.md +++ b/README.md @@ -74,5 +74,5 @@ Ready to build? Start here: * [Quickstart for Sellers](getting-started/quickstart-for-sellers.md) * [Quickstart for Buyers](getting-started/quickstart-for-buyers.md) -* [Explore Core Concepts](broken-reference) +* [Explore Core Concepts](core-concepts/) * [Join our community on Discord](https://discord.gg/invite/cdp) diff --git a/SUMMARY.md b/SUMMARY.md index e2b6ea3..a97b2a4 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -23,3 +23,6 @@ ## Guides * [MCP Server with x402](guides/mcp-server-with-x402.md) +* [Implementation Patterns](guides/implementation-patterns.md) +* [Security Best Practices](guides/security-best-practices.md) +* [Troubleshooting](guides/troubleshooting.md) diff --git a/core-concepts/wallet.md b/core-concepts/wallet.md index 90995bd..ed28893 100644 --- a/core-concepts/wallet.md +++ b/core-concepts/wallet.md @@ -1,35 +1,250 @@ # Wallet -This page explains the role of the **wallet** in the x402 protocol. +This page explains the role of the **wallet** in the x402 protocol and provides practical examples for integration. In x402, a wallet is both a payment mechanism and a form of unique identity for buyers and sellers. Wallet addresses are used to send, receive, and verify payments, while also serving as identifiers within the protocol. -### Role of the Wallet +## Role of the Wallet -#### For Buyers +### For Buyers Buyers use wallets to: -* Store USDC/crypto -* Sign payment payloads +* Store USDC/crypto for payments +* Sign payment payloads cryptographically * Authorize onchain payments programmatically +* Maintain payment history and transaction records Wallets enable buyers, including AI agents, to transact without account creation or credential management. -#### For Sellers +### For Sellers Sellers use wallets to: -* Receive USDC/crypto payments +* Receive USDC/crypto payments from buyers * Define their payment destination within server configurations +* Track incoming payments and revenue +* Manage multi-signature setups for enhanced security A seller's wallet address is included in the payment requirements provided to buyers. -[CDP's Wallet API ](https://docs.cdp.coinbase.com/wallet-api-v2/docs/welcome)is our recommended option for programmatic payments and secure key management. +## Wallet Integration Examples -### Summary +### Setting Up a Wallet for Buyers -* Wallets enable programmatic, permissionless payments in x402. -* Buyers use wallets to pay for services. -* Sellers use wallets to receive payments. -* Wallet addresses also act as unique identifiers within the protocol. +```javascript +import { Wallet } from 'ethers'; +import { createX402Client } from '@coinbase/x402'; + +// Create or import a wallet +const wallet = new Wallet(process.env.PRIVATE_KEY, provider); + +// Initialize x402 client with wallet +const client = createX402Client({ + wallet: wallet, + facilitator: "https://x402.org/facilitator" +}); + +// Check wallet balance before making payments +async function checkBalance() { + const balance = await wallet.getBalance(); + const usdcBalance = await usdcContract.balanceOf(wallet.address); + + console.log(`ETH Balance: ${ethers.utils.formatEther(balance)}`); + console.log(`USDC Balance: ${ethers.utils.formatUnits(usdcBalance, 6)}`); +} +``` + +### Configuring a Wallet for Sellers + +```javascript +// Server configuration +const RECIPIENT_WALLET = process.env.RECIPIENT_ADDRESS; + +app.use(paymentMiddleware( + RECIPIENT_WALLET, // Your receiving wallet address + { + "GET /premium-content": { + price: "$0.05", + network: "base-sepolia", + } + } +)); + +// Monitor incoming payments +async function monitorPayments() { + const filter = usdcContract.filters.Transfer(null, RECIPIENT_WALLET); + + usdcContract.on(filter, (from, to, amount, event) => { + console.log(`Payment received: ${ethers.utils.formatUnits(amount, 6)} USDC from ${from}`); + // Update your records, trigger fulfillment, etc. + }); +} +``` + +### Wallet Security Best Practices + +#### For Production Use + +```javascript +// Use environment variables for private keys (development only) +const wallet = new Wallet(process.env.PRIVATE_KEY, provider); + +// Better: Use hardware wallets or key management services +import { LedgerSigner } from '@ethersproject/hardware-wallets'; + +const ledger = new LedgerSigner(provider, "hid", "m/44'/60'/0'/0/0"); + +// Or use cloud key management +import { KmsSigner } from '@aws-sdk/kms-signer'; + +const kmsSigner = new KmsSigner({ + keyId: process.env.KMS_KEY_ID, + region: 'us-east-1' +}); +``` + +#### Multi-signature Wallets + +```javascript +// For high-value seller wallets, consider multi-sig +const MULTISIG_WALLET = "0x742d35Cc6554C6FaA94f0678DD7C5A4B8A6E3A1"; + +// Configure multiple signers +const signers = [ + new Wallet(process.env.SIGNER_1_KEY, provider), + new Wallet(process.env.SIGNER_2_KEY, provider), + new Wallet(process.env.SIGNER_3_KEY, provider) +]; + +// Require 2 of 3 signatures for withdrawals +const multiSigContract = new ethers.Contract( + MULTISIG_WALLET, + multiSigABI, + provider +); +``` + +## Wallet Types and Recommendations + +### Development and Testing + +**MetaMask/Browser Wallets** +- Good for: Development and testing +- Pros: Easy setup, familiar interface +- Cons: Manual transaction approval required + +**Programmatic Wallets** +- Good for: Automated testing, CI/CD +- Pros: Fully automated, scriptable +- Cons: Private key management required + +### Production Environments + +**Hardware Wallets (Ledger/Trezor)** +- Good for: High-security seller wallets +- Pros: Private keys never leave device +- Cons: Requires physical device access + +**Cloud Key Management (AWS KMS, Azure Key Vault)** +- Good for: Scalable production deployments +- Pros: Enterprise security, compliance +- Cons: Cloud dependency, complexity + +**Multi-signature Wallets** +- Good for: High-value treasury management +- Pros: Distributed security, governance +- Cons: Multiple signatures required for transactions + +## Wallet Management Utilities + +### Balance Monitoring + +```javascript +class WalletMonitor { + constructor(wallet, tokens = []) { + this.wallet = wallet; + this.tokens = tokens; + } + + async getBalances() { + const balances = { + eth: await this.wallet.getBalance(), + tokens: {} + }; + + for (const token of this.tokens) { + const contract = new ethers.Contract(token.address, ERC20_ABI, this.wallet); + balances.tokens[token.symbol] = await contract.balanceOf(this.wallet.address); + } + + return balances; + } + + async monitorLowBalance(threshold = ethers.utils.parseEther("0.01")) { + const balance = await this.wallet.getBalance(); + + if (balance.lt(threshold)) { + console.warn(`Low balance warning: ${ethers.utils.formatEther(balance)} ETH`); + // Send alert, auto-refill, etc. + } + } +} +``` + +### Transaction History + +```javascript +async function getPaymentHistory(walletAddress, startBlock = 0) { + const filter = { + address: USDC_CONTRACT_ADDRESS, + topics: [ + ethers.utils.id("Transfer(address,address,uint256)"), + null, // from (any address) + ethers.utils.hexZeroPad(walletAddress, 32) // to our wallet + ], + fromBlock: startBlock + }; + + const logs = await provider.getLogs(filter); + + return logs.map(log => { + const decoded = usdcContract.interface.parseLog(log); + return { + from: decoded.args.from, + amount: ethers.utils.formatUnits(decoded.args.value, 6), + txHash: log.transactionHash, + blockNumber: log.blockNumber + }; + }); +} +``` + +## Recommended Wallet Solutions + +### For Developers + +**[CDP Wallet API](https://docs.cdp.coinbase.com/wallet-api-v2/docs/welcome)** - Our recommended option for programmatic payments and secure key management. + +**[Ethers.js Wallet](https://docs.ethers.io/v5/api/signer/#Wallet)** - Simple, reliable wallet implementation for Node.js applications. + +**[Web3.js](https://web3js.readthedocs.io/)** - Alternative JavaScript wallet library with broad ecosystem support. + +### For Production + +**[Fireblocks](https://www.fireblocks.com/)** - Enterprise-grade wallet infrastructure with institutional security. + +**[AWS KMS](https://aws.amazon.com/kms/)** - Cloud-native key management for scalable applications. + +**[Gnosis Safe](https://safe.global/)** - Multi-signature wallet for shared custody and governance. + +## Summary + +* Wallets enable programmatic, permissionless payments in x402 +* Buyers use wallets to authorize and send payments automatically +* Sellers use wallets to receive payments and manage treasury +* Wallet addresses serve as unique identifiers within the protocol +* Security considerations vary significantly between development and production use +* Choose wallet solutions based on your security requirements and operational needs + +The wallet is the foundation of trust and identity in the x402 ecosystem, making secure wallet management critical for successful implementations. diff --git a/faq.md b/faq.md index e320ec5..e49a513 100644 --- a/faq.md +++ b/faq.md @@ -137,7 +137,197 @@ We acknowledge that the repo is primarily under Coinbase ownership today. This i * Confirm your wallet has _mainnet_ USDC. * Gas fees are higher on mainnet; fund the wallet with a small amount of ETH for gas. +### Development and Integration + +#### What programming languages can I use with x402? + +The x402 protocol itself is language-agnostic since it uses standard HTTP. Official SDKs are available for: +- **TypeScript/JavaScript**: Express, Next.js, Hono middleware +- **Python**: FastAPI and Flask middleware + +Community implementations exist for other languages, and you can implement the protocol directly using any HTTP client library. + +#### How do I handle failed payments or network issues? + +Implement retry logic with exponential backoff: +```javascript +async function retryPayment(paymentFn, maxRetries = 3) { + for (let attempt = 1; attempt <= maxRetries; attempt++) { + try { + return await paymentFn(); + } catch (error) { + if (attempt === maxRetries) throw error; + await new Promise(resolve => setTimeout(resolve, 1000 * Math.pow(2, attempt))); + } + } +} +``` + +Also implement circuit breakers for facilitator connectivity issues and proper error handling for different failure modes. + +#### Can I use x402 with existing authentication systems? + +Yes! x402 payments can work alongside traditional auth: +```javascript +app.use('/api/premium', authenticateUser, paymentMiddleware, handler); +``` + +This allows you to have both authenticated users and anonymous payments, or require both authentication and payment for premium features. + +#### How do I test x402 integration locally? + +Use test networks like Base Sepolia with the test facilitator: +```javascript +const testConfig = { + network: "base-sepolia", + facilitator: "https://x402.org/facilitator", + recipient: "0x742d35Cc6554C6FaA94f0678DD7C5A4B8A6E3A1" +}; +``` + +Create test wallets with testnet tokens from faucets, and use them to simulate real payment flows. + +#### What happens if a user pays but my server is down? + +Payment verification happens on-chain, so payments are preserved even if your server is down. When your server comes back online: +1. The user can retry the request with the same payment proof +2. Your server validates the payment against the blockchain +3. The service is provided as normal + +Implement idempotency to handle duplicate requests safely. + +### Business and Pricing + +#### How should I price my API endpoints? + +Consider these factors: +- **Computational cost**: More expensive operations should cost more +- **Value provided**: Charge based on the value users receive +- **Market rates**: Research what similar services charge +- **User behavior**: High-volume users may need different pricing + +Start with simple per-request pricing and iterate based on usage patterns. + +#### Can I implement subscription-like models with x402? + +Yes, several approaches work: +- **Time-based**: Users pay for access periods (hour/day/month) +- **Credit-based**: Users pre-purchase credits to spend over time +- **Usage-based**: Combine payments with rate limiting for fair usage + +See the [Implementation Patterns](guides/implementation-patterns.md) guide for detailed examples. + +#### How do I handle refunds or disputes? + +x402 payments are on-chain transactions that cannot be automatically reversed. For refunds: +1. Implement a separate refund system in your application +2. Send refunds to the original payment address +3. Keep detailed transaction logs for dispute resolution +4. Consider escrow systems for high-value services + +#### Can I do partial payments or installments? + +Yes, you can implement various payment structures: +- **Staged payments**: Charge different amounts for different service phases +- **Micro-subscriptions**: Very small recurring payments +- **Usage tracking**: Accumulate usage and charge periodically + +The key is designing your application logic to handle these patterns. + +### Technical Operations + +#### How do I monitor payment success rates? + +Implement comprehensive logging and metrics: +```javascript +// Track payment metrics +const paymentMetrics = { + attempts: 0, + successes: 0, + failures: 0, + averageTime: 0 +}; + +function logPaymentAttempt(success, duration) { + paymentMetrics.attempts++; + if (success) paymentMetrics.successes++; + else paymentMetrics.failures++; + + // Update rolling average + paymentMetrics.averageTime = + (paymentMetrics.averageTime + duration) / 2; +} +``` + +Monitor facilitator response times, payment verification success rates, and user completion funnels. + +#### What's the best way to handle high-volume payments? + +For high traffic: +1. **Load balancing**: Distribute payment processing across multiple servers +2. **Caching**: Cache payment verifications temporarily to reduce facilitator calls +3. **Async processing**: Queue payment verifications for non-critical paths +4. **Rate limiting**: Implement smart rate limiting to prevent abuse +5. **Database optimization**: Index payment-related data properly + +#### How do I handle different blockchain networks? + +Configure network-specific settings: +```javascript +const networkConfigs = { + 'ethereum': { chainId: 1, facilitator: 'https://mainnet-facilitator.x402.org' }, + 'base': { chainId: 8453, facilitator: 'https://base-facilitator.x402.org' }, + 'arbitrum': { chainId: 42161, facilitator: 'https://arbitrum-facilitator.x402.org' } +}; + +function getNetworkConfig(network) { + return networkConfigs[network] || networkConfigs['base']; +} +``` + +Consider gas costs, confirmation times, and user preferences when choosing networks. + +#### How secure is x402? + +x402 inherits the security properties of the underlying blockchain: +- **Payment proofs** are cryptographically signed and verifiable +- **Facilitators** cannot steal funds or forge payments +- **Double-spending** is prevented by blockchain consensus +- **Privacy** depends on the chosen blockchain's privacy features + +Follow security best practices in the [Security Guide](guides/security-best-practices.md) for additional protection. + +### Troubleshooting Common Issues + +#### "Payment verification failed" - what does this mean? + +Common causes and solutions: +1. **Network mismatch**: Ensure client and server use the same network +2. **Expired payment**: Check if payment was submitted within the timeout window +3. **Invalid signature**: Verify the wallet is signing transactions correctly +4. **Facilitator issues**: Check facilitator status and connectivity + +See the [Troubleshooting Guide](guides/troubleshooting.md) for detailed debugging steps. + +#### My middleware isn't triggering payments + +Check these common issues: +1. **Middleware order**: Payment middleware must be registered before route handlers +2. **Route matching**: Ensure your route patterns match exactly +3. **HTTP methods**: Verify GET/POST methods are configured correctly +4. **Network configuration**: Confirm you're using the right network and facilitator URL + +#### Payments are slow - how can I optimize? + +Performance optimization strategies: +1. **Choose faster networks**: Base and Arbitrum have lower latency than Ethereum +2. **Optimize gas prices**: Use dynamic gas pricing for faster confirmations +3. **Cache verifications**: Temporarily cache payment proofs to avoid re-verification +4. **Async processing**: Don't block user experience waiting for blockchain confirmation + ### Still have questions? • Reach out in the [Discord channel](https://discord.gg/invite/cdp)\ -• Open a GitHub Discussion or Issue in the [x402 repo](https://github.com/coinbase/x402) +• Open a GitHub Discussion or Issue in the [x402 repo](https://github.com/coinbase/x402)\ +• Check the [Troubleshooting Guide](guides/troubleshooting.md) for technical issues\ +• Review [Implementation Patterns](guides/implementation-patterns.md) for design guidance diff --git a/guides/implementation-patterns.md b/guides/implementation-patterns.md new file mode 100644 index 0000000..52d1784 --- /dev/null +++ b/guides/implementation-patterns.md @@ -0,0 +1,503 @@ +# Implementation Patterns + +This guide showcases common implementation patterns for x402 services, from simple pay-per-request APIs to complex subscription models. + +## Pattern 1: Simple Pay-Per-Request API + +The most straightforward x402 implementation - charge users for individual API calls. + +### Use Cases +- Data lookup services +- Image/text processing APIs +- One-off computational tasks + +### Implementation + +```javascript +import express from 'express'; +import { paymentMiddleware } from 'x402-express'; + +const app = express(); + +app.use(paymentMiddleware( + process.env.RECIPIENT_ADDRESS, + { + "GET /weather/:city": { + price: "$0.01", + network: "base-sepolia", + config: { + description: "Weather data for any city", + maxTimeoutSeconds: 60 + } + } + } +)); + +app.get('/weather/:city', async (req, res) => { + const { city } = req.params; + + // Fetch weather data + const weatherData = await fetchWeatherData(city); + + res.json({ + city, + ...weatherData, + timestamp: new Date().toISOString() + }); +}); +``` + +### Benefits +- Simple to implement and understand +- Predictable costs for users +- No state management required + +### Considerations +- Can be expensive for high-volume users +- No bulk discounts or loyalty incentives + +## Pattern 2: Tiered Pricing by Complexity + +Different endpoints with different pricing based on computational complexity or value provided. + +### Use Cases +- AI/ML services with varying model sizes +- Image processing with different quality levels +- Data analysis with different detail levels + +### Implementation + +```javascript +const pricingTiers = { + "POST /ai/analyze/basic": { + price: "$0.02", + description: "Basic sentiment analysis" + }, + "POST /ai/analyze/advanced": { + price: "$0.10", + description: "Advanced NLP with entity extraction" + }, + "POST /ai/analyze/premium": { + price: "$0.50", + description: "Full analysis with custom model training" + } +}; + +app.use(paymentMiddleware( + process.env.RECIPIENT_ADDRESS, + pricingTiers +)); + +app.post('/ai/analyze/:tier', async (req, res) => { + const { tier } = req.params; + const { text } = req.body; + + let result; + switch (tier) { + case 'basic': + result = await basicAnalysis(text); + break; + case 'advanced': + result = await advancedAnalysis(text); + break; + case 'premium': + result = await premiumAnalysis(text); + break; + default: + return res.status(400).json({ error: 'Invalid tier' }); + } + + res.json({ + tier, + analysis: result, + processingTime: result.processingTime + }); +}); +``` + +## Pattern 3: Credit-Based System + +Users pre-purchase credits and consume them over multiple API calls. + +### Use Cases +- High-volume API usage +- Variable pricing per operation +- Subscription-like experience without time limits + +### Implementation + +```javascript +import Redis from 'ioredis'; + +const redis = new Redis(process.env.REDIS_URL); + +// Credit purchase endpoints +const creditPackages = { + "POST /credits/small": { credits: 100, price: "$5.00" }, + "POST /credits/medium": { credits: 500, price: "$20.00" }, + "POST /credits/large": { credits: 2000, price: "$60.00" } +}; + +app.use(paymentMiddleware( + process.env.RECIPIENT_ADDRESS, + creditPackages +)); + +// Credit purchase handlers +Object.entries(creditPackages).forEach(([endpoint, config]) => { + const route = endpoint.split(' ')[1]; + app.post(route, async (req, res) => { + const { walletAddress } = req.body; + + await redis.incrby(`credits:${walletAddress}`, config.credits); + const newBalance = await redis.get(`credits:${walletAddress}`); + + res.json({ + creditsAdded: config.credits, + newBalance: parseInt(newBalance), + cost: config.price + }); + }); +}); + +// Credit-consuming middleware +async function consumeCredits(cost) { + return async (req, res, next) => { + const walletAddress = req.headers['x-wallet-address']; + + if (!walletAddress) { + return res.status(400).json({ error: 'Wallet address required' }); + } + + const credits = await redis.get(`credits:${walletAddress}`); + const balance = parseInt(credits) || 0; + + if (balance < cost) { + return res.status(402).json({ + error: 'Insufficient credits', + required: cost, + balance, + purchaseOptions: creditPackages + }); + } + + // Consume credits + await redis.decrby(`credits:${walletAddress}`, cost); + req.creditsUsed = cost; + next(); + }; +} + +// API endpoints that consume credits +app.post('/api/process', consumeCredits(1), async (req, res) => { + // Process the request + const result = await processData(req.body); + + const remainingCredits = await redis.get(`credits:${req.headers['x-wallet-address']}`); + + res.json({ + result, + creditsUsed: req.creditsUsed, + creditsRemaining: parseInt(remainingCredits) + }); +}); +``` + +## Pattern 4: Time-Based Subscriptions + +Users purchase access for a specific time period. + +### Use Cases +- Real-time data feeds +- Streaming services +- Premium feature access + +### Implementation + +```javascript +const subscriptions = new Map(); // In production, use a database + +app.use(paymentMiddleware( + process.env.RECIPIENT_ADDRESS, + { + "POST /subscribe/hour": { + price: "$1.00", + description: "1-hour premium access" + }, + "POST /subscribe/day": { + price: "$5.00", + description: "24-hour premium access" + }, + "POST /subscribe/month": { + price: "$20.00", + description: "30-day premium access" + } + } +)); + +app.post('/subscribe/:duration', (req, res) => { + const { duration } = req.params; + const { walletAddress } = req.body; + + const durations = { + hour: 60 * 60 * 1000, + day: 24 * 60 * 60 * 1000, + month: 30 * 24 * 60 * 60 * 1000 + }; + + const expiresAt = Date.now() + durations[duration]; + subscriptions.set(walletAddress, { expiresAt, plan: duration }); + + res.json({ + subscribed: true, + plan: duration, + expiresAt: new Date(expiresAt).toISOString() + }); +}); + +// Subscription validation middleware +function requireSubscription(req, res, next) { + const walletAddress = req.headers['x-wallet-address']; + const subscription = subscriptions.get(walletAddress); + + if (!subscription || subscription.expiresAt < Date.now()) { + return res.status(402).json({ + error: 'Active subscription required', + subscriptionOptions: ['/subscribe/hour', '/subscribe/day', '/subscribe/month'] + }); + } + + req.subscription = subscription; + next(); +} + +// Protected endpoints +app.get('/premium/data', requireSubscription, (req, res) => { + res.json({ + data: getPremiumData(), + subscription: req.subscription + }); +}); +``` + +## Pattern 5: Usage-Based Billing with Rate Limits + +Combine payments with rate limiting to create fair usage policies. + +### Implementation + +```javascript +import rateLimit from 'express-rate-limit'; + +// Different rate limits for different payment tiers +const createRateLimiter = (windowMs, max, price) => { + return rateLimit({ + windowMs, + max, + keyGenerator: (req) => req.headers['x-wallet-address'] || req.ip, + handler: (req, res) => { + res.status(402).json({ + error: 'Rate limit exceeded', + resetTime: new Date(Date.now() + windowMs), + upgradeOption: { + endpoint: '/upgrade', + price: price, + description: 'Pay to increase rate limit' + } + }); + } + }); +}; + +// Free tier: 10 requests per hour +app.use('/api/free', createRateLimiter(60 * 60 * 1000, 10, '$1.00')); + +// Paid tier upgrade +app.use(paymentMiddleware( + process.env.RECIPIENT_ADDRESS, + { + "POST /upgrade": { + price: "$1.00", + description: "Upgrade to 100 requests per hour" + } + } +)); + +const upgradeTracker = new Map(); + +app.post('/upgrade', (req, res) => { + const { walletAddress } = req.body; + const expiresAt = Date.now() + (60 * 60 * 1000); // 1 hour + + upgradeTracker.set(walletAddress, expiresAt); + + res.json({ + upgraded: true, + expiresAt: new Date(expiresAt).toISOString(), + newLimit: 100 + }); +}); + +// Premium tier: 100 requests per hour for paying users +app.use('/api/premium', (req, res, next) => { + const walletAddress = req.headers['x-wallet-address']; + const upgrade = upgradeTracker.get(walletAddress); + + if (upgrade && upgrade > Date.now()) { + // Use higher rate limit + return createRateLimiter(60 * 60 * 1000, 100, '$5.00')(req, res, next); + } else { + // Use standard rate limit + return createRateLimiter(60 * 60 * 1000, 10, '$1.00')(req, res, next); + } +}); +``` + +## Pattern 6: Dynamic Pricing + +Adjust prices based on demand, computational cost, or other factors. + +### Implementation + +```javascript +class DynamicPricer { + constructor() { + this.basePrice = 0.01; // $0.01 + this.demandMultiplier = 1.0; + this.requestCount = 0; + this.resetInterval = 60000; // 1 minute + + // Reset demand multiplier periodically + setInterval(() => { + this.updateDemandMultiplier(); + }, this.resetInterval); + } + + updateDemandMultiplier() { + // Increase price during high demand + if (this.requestCount > 100) { + this.demandMultiplier = Math.min(3.0, this.demandMultiplier * 1.1); + } else if (this.requestCount < 10) { + this.demandMultiplier = Math.max(0.5, this.demandMultiplier * 0.9); + } + + this.requestCount = 0; + } + + getCurrentPrice(complexity = 1) { + this.requestCount++; + return (this.basePrice * complexity * this.demandMultiplier).toFixed(3); + } +} + +const pricer = new DynamicPricer(); + +// Dynamic pricing middleware +app.use('/api/dynamic', (req, res, next) => { + const complexity = req.headers['x-complexity'] || 1; + const currentPrice = pricer.getCurrentPrice(complexity); + + // Check if user has made payment for current price + const paymentHeader = req.headers['x-payment']; + if (!paymentHeader) { + return res.status(402).json({ + error: 'Payment required', + currentPrice: `$${currentPrice}`, + demandLevel: pricer.demandMultiplier.toFixed(2), + paymentRequired: { + amount: currentPrice, + currency: 'USD' + } + }); + } + + // Verify payment amount matches current price + // (simplified - in practice, verify with facilitator) + next(); +}); +``` + +## Pattern 7: Bulk Operations with Discount + +Encourage larger purchases with volume discounts. + +### Implementation + +```javascript +function calculateBulkPrice(quantity) { + const basePrice = 0.10; + let discount = 0; + + if (quantity >= 100) discount = 0.20; // 20% off for 100+ + else if (quantity >= 50) discount = 0.15; // 15% off for 50+ + else if (quantity >= 10) discount = 0.10; // 10% off for 10+ + + const unitPrice = basePrice * (1 - discount); + return { + unitPrice: unitPrice.toFixed(3), + totalPrice: (unitPrice * quantity).toFixed(2), + discount: (discount * 100).toFixed(0) + '%', + savings: ((basePrice - unitPrice) * quantity).toFixed(2) + }; +} + +app.post('/api/bulk-quote', (req, res) => { + const { quantity } = req.body; + const pricing = calculateBulkPrice(quantity); + + res.json({ + quantity, + ...pricing, + validFor: '10 minutes' + }); +}); + +app.use(paymentMiddleware( + process.env.RECIPIENT_ADDRESS, + { + "POST /api/bulk-process": { + // Price calculated dynamically based on quantity + price: (req) => { + const quantity = req.body.quantity || 1; + return `$${calculateBulkPrice(quantity).totalPrice}`; + }, + config: { + description: "Bulk processing with volume discounts" + } + } + } +)); +``` + +## Choosing the Right Pattern + +### Consider These Factors: + +**User Behavior** +- High-volume users → Credit system or subscriptions +- Sporadic usage → Pay-per-request +- Predictable usage → Time-based subscriptions + +**Service Characteristics** +- Variable computational cost → Tiered pricing +- Real-time data → Subscriptions +- Batch processing → Bulk operations + +**Business Goals** +- Revenue predictability → Subscriptions +- Usage growth → Credit systems +- Market penetration → Dynamic pricing with discounts + +**Technical Complexity** +- Simple APIs → Pay-per-request +- Complex services → Multiple patterns combined + +### Implementation Tips + +1. **Start Simple**: Begin with pay-per-request, add complexity as needed +2. **Monitor Usage**: Track metrics to optimize pricing and patterns +3. **User Experience**: Provide clear pricing information and options +4. **Flexibility**: Allow users to switch between different payment models +5. **Testing**: A/B test different pricing strategies and patterns + +These patterns can be combined and customized based on your specific use case and user needs. The key is to align your payment model with how users want to consume your service. \ No newline at end of file diff --git a/guides/security-best-practices.md b/guides/security-best-practices.md new file mode 100644 index 0000000..a8c2801 --- /dev/null +++ b/guides/security-best-practices.md @@ -0,0 +1,217 @@ +# Security Best Practices + +When implementing x402 payment systems, security should be your top priority. This guide covers essential security considerations for both sellers and buyers. + +## For Sellers + +### Wallet Security + +**Private Key Management** +- Never store private keys in your application code or environment variables in production +- Use hardware security modules (HSMs) or secure key management services +- Implement key rotation policies +- Consider using multi-signature wallets for high-value transactions + +**Receiving Address Validation** +- Verify that payment destinations match your expected receiving addresses +- Implement address whitelisting for automated systems +- Monitor all incoming transactions for anomalies + +### Payment Verification + +**Always Verify Before Serving** +- Never trust client-provided payment proofs without verification +- Use the facilitator's `/verify` endpoint for all payment validations +- Implement proper error handling for failed verifications +- Cache verification results appropriately to avoid replay attacks + +**Rate Limiting** +```javascript +// Example: Implement rate limiting per wallet address +const rateLimit = new Map(); + +function checkRateLimit(walletAddress) { + const now = Date.now(); + const requests = rateLimit.get(walletAddress) || []; + + // Remove old requests (older than 1 minute) + const recentRequests = requests.filter(time => now - time < 60000); + + if (recentRequests.length >= 10) { + throw new Error('Rate limit exceeded'); + } + + recentRequests.push(now); + rateLimit.set(walletAddress, recentRequests); +} +``` + +### Network Security + +**HTTPS Everywhere** +- Always use HTTPS for all x402 endpoints +- Implement proper TLS certificate validation +- Use HTTP Strict Transport Security (HSTS) headers + +**Input Validation** +- Validate all incoming payment payloads +- Sanitize user inputs before processing +- Implement proper schema validation for payment requests + +### Monitoring and Logging + +**Transaction Monitoring** +- Log all payment attempts (successful and failed) +- Monitor for unusual payment patterns +- Set up alerts for failed verification attempts +- Track payment amounts and frequencies + +**Security Logging** +```javascript +// Example: Security-focused logging +function logPaymentAttempt(request, result) { + console.log({ + timestamp: new Date().toISOString(), + clientIP: request.ip, + userAgent: request.headers['user-agent'], + paymentAmount: result.amount, + walletAddress: result.fromAddress, + verified: result.success, + facilitator: result.facilitatorUsed + }); +} +``` + +## For Buyers + +### Wallet Protection + +**Secure Key Storage** +- Use encrypted storage for private keys +- Implement proper key derivation functions +- Consider using hardware wallets for high-value operations +- Never share or expose private keys + +**Transaction Signing** +- Verify transaction details before signing +- Implement transaction preview mechanisms +- Use deterministic signing to prevent replay attacks +- Validate recipient addresses + +### Payment Verification + +**Facilitator Trust** +- Only use trusted facilitators +- Verify facilitator certificates and reputation +- Implement failover mechanisms for facilitator unavailability +- Monitor facilitator behavior for anomalies + +**Amount Validation** +```javascript +// Example: Verify payment amounts match expectations +function validatePaymentAmount(requested, actual, tolerance = 0.001) { + const difference = Math.abs(requested - actual); + if (difference > tolerance) { + throw new Error(`Payment amount mismatch: expected ${requested}, got ${actual}`); + } +} +``` + +## Network-Level Security + +### Smart Contract Interactions + +**Contract Verification** +- Always verify smart contract addresses +- Check contract source code when possible +- Monitor for contract upgrades or changes +- Implement multi-signature requirements for critical operations + +**Gas Management** +- Set appropriate gas limits and prices +- Monitor gas usage patterns +- Implement gas price oracles for dynamic pricing +- Consider Layer 2 solutions for cost optimization + +### Privacy Considerations + +**Transaction Privacy** +- Be aware of on-chain transaction visibility +- Consider using privacy-preserving payment methods when needed +- Implement proper data handling for user information +- Follow applicable privacy regulations (GDPR, CCPA, etc.) + +## Incident Response + +### Security Incident Handling + +**Preparation** +- Establish incident response procedures +- Identify key personnel and contact information +- Prepare communication templates +- Set up monitoring and alerting systems + +**Response Steps** +1. **Immediate Actions** + - Isolate affected systems + - Stop processing payments if necessary + - Preserve evidence and logs + - Notify relevant stakeholders + +2. **Investigation** + - Analyze transaction logs + - Identify the scope of the incident + - Determine root cause + - Document findings + +3. **Recovery** + - Implement fixes and patches + - Resume operations safely + - Monitor for recurring issues + - Update security measures + +## Compliance and Legal + +### Regulatory Considerations + +**Know Your Customer (KYC)** +- Understand applicable KYC requirements +- Implement appropriate identity verification +- Maintain proper record-keeping +- Consider jurisdiction-specific regulations + +**Anti-Money Laundering (AML)** +- Implement transaction monitoring +- Report suspicious activities as required +- Maintain transaction records +- Follow sanctions and prohibited persons lists + +### Audit and Compliance + +**Regular Security Audits** +- Conduct periodic security assessments +- Test incident response procedures +- Review access controls and permissions +- Update security policies and procedures + +**Documentation** +- Maintain security policies and procedures +- Document system architecture and data flows +- Keep audit trails for all transactions +- Prepare for regulatory examinations + +## Tools and Resources + +### Security Tools +- [Slither](https://github.com/crytic/slither) - Smart contract static analyzer +- [MythX](https://mythx.io/) - Smart contract security analysis +- [OpenZeppelin](https://openzeppelin.com/) - Secure smart contract libraries +- [Consensys Diligence](https://consensys.net/diligence/) - Security auditing services + +### Monitoring Solutions +- [Forta](https://forta.org/) - Real-time threat detection +- [OpenZeppelin Defender](https://defender.openzeppelin.com/) - Security operations +- [Chainlink VRF](https://chain.link/vrf) - Verifiable randomness +- [Tenderly](https://tenderly.co/) - Smart contract monitoring + +Remember: Security is an ongoing process, not a one-time implementation. Stay updated with the latest security practices and regularly review your implementation as the x402 ecosystem evolves. \ No newline at end of file diff --git a/guides/troubleshooting.md b/guides/troubleshooting.md new file mode 100644 index 0000000..2edae62 --- /dev/null +++ b/guides/troubleshooting.md @@ -0,0 +1,393 @@ +# Troubleshooting Guide + +This guide helps you diagnose and resolve common issues when implementing or using x402 payment systems. + +## Payment Verification Issues + +### Problem: "Payment verification failed" + +**Symptoms:** +- 402 response received but payment doesn't verify +- Facilitator returns verification errors +- Transactions appear on-chain but server rejects them + +**Common Causes & Solutions:** + +1. **Incorrect Payment Payload Format** + ```javascript + // ❌ Incorrect - missing required fields + const payload = { + amount: "1000000", // Missing token, recipient, etc. + }; + + // ✅ Correct - complete payload structure + const payload = { + amount: "1000000", + token: "0xA0b86a33E6427B0655DEAD94DD2584768DED86b", + recipient: "0x742d35Cc6554C6FaA94f0678DD7C5A4B8A6E3A1", + nonce: "12345", + signature: "0x...", + // ... other required fields + }; + ``` + +2. **Network Mismatch** + ```javascript + // Ensure client and server use same network + const config = { + network: "base-sepolia", // Must match on both sides + facilitator: "https://x402.org/facilitator" + }; + ``` + +3. **Expired Payment** + - Check timestamp in payment payload + - Verify payment was submitted within timeout window + - Adjust `maxTimeoutSeconds` if needed + +### Problem: "Insufficient funds" during payment + +**Diagnosis:** +```javascript +// Check wallet balance +const balance = await wallet.getBalance(tokenAddress); +console.log(`Wallet balance: ${balance}`); +console.log(`Required amount: ${paymentAmount}`); +console.log(`Gas estimate: ${gasEstimate}`); +``` + +**Solutions:** +- Fund wallet with required tokens +- Account for gas fees in balance calculations +- Use Layer 2 networks for lower costs + +## Network and Connection Issues + +### Problem: Facilitator connection timeouts + +**Symptoms:** +- Network timeout errors +- HTTP 5xx responses from facilitator +- Slow payment processing + +**Troubleshooting Steps:** + +1. **Check Facilitator Status** + ```bash + curl -I https://x402.org/facilitator/health + ``` + +2. **Test Network Connectivity** + ```javascript + // Add timeout and retry logic + const fetchWithTimeout = async (url, options, timeout = 5000) => { + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), timeout); + + try { + const response = await fetch(url, { + ...options, + signal: controller.signal + }); + return response; + } finally { + clearTimeout(timeoutId); + } + }; + ``` + +3. **Implement Circuit Breaker** + ```javascript + class CircuitBreaker { + constructor(failureThreshold = 5, resetTimeout = 60000) { + this.failureThreshold = failureThreshold; + this.resetTimeout = resetTimeout; + this.failures = 0; + this.state = 'CLOSED'; // CLOSED, OPEN, HALF_OPEN + this.nextAttempt = 0; + } + + async call(fn) { + if (this.state === 'OPEN') { + if (Date.now() < this.nextAttempt) { + throw new Error('Circuit breaker is OPEN'); + } + this.state = 'HALF_OPEN'; + } + + try { + const result = await fn(); + this.onSuccess(); + return result; + } catch (error) { + this.onFailure(); + throw error; + } + } + + onSuccess() { + this.failures = 0; + this.state = 'CLOSED'; + } + + onFailure() { + this.failures++; + if (this.failures >= this.failureThreshold) { + this.state = 'OPEN'; + this.nextAttempt = Date.now() + this.resetTimeout; + } + } + } + ``` + +## Smart Contract Issues + +### Problem: Transaction reverts or fails + +**Common Revert Reasons:** + +1. **ERC20 Approval Issues** + ```javascript + // Check and set approval before payment + const allowance = await token.allowance(wallet.address, spenderAddress); + if (allowance < amount) { + await token.approve(spenderAddress, amount); + } + ``` + +2. **Gas Estimation Errors** + ```javascript + // Add buffer to gas estimates + const gasEstimate = await contract.estimateGas.method(...args); + const gasLimit = gasEstimate.mul(120).div(100); // Add 20% buffer + ``` + +3. **Slippage Issues** + ```javascript + // Account for price movements + const slippageTolerance = 0.005; // 0.5% + const minAmountOut = expectedAmount * (1 - slippageTolerance); + ``` + +### Problem: "Nonce too low" or "Nonce too high" errors + +**Solutions:** +```javascript +// Get current nonce and queue transactions properly +const nonce = await wallet.getTransactionCount('pending'); + +// For multiple transactions, increment nonce manually +const tx1 = await wallet.sendTransaction({ ...txParams, nonce }); +const tx2 = await wallet.sendTransaction({ ...txParams, nonce: nonce + 1 }); +``` + +## Integration Issues + +### Problem: Middleware not triggering + +**Express.js Debugging:** +```javascript +// Add debugging middleware +app.use((req, res, next) => { + console.log(`${req.method} ${req.path}`); + console.log('Headers:', req.headers); + next(); +}); + +// Ensure middleware order is correct +app.use(paymentMiddleware); // Must be before route handlers +app.get('/api/endpoint', handler); +``` + +**Next.js Debugging:** +```javascript +// Check middleware.ts configuration +export const config = { + matcher: [ + '/api/protected/:path*', + '/((?!api|_next/static|_next/image|favicon.ico).*)', + ] +}; + +// Verify middleware runs +export function middleware(request) { + console.log('Middleware triggered for:', request.nextUrl.pathname); + return paymentMiddleware(request); +} +``` + +### Problem: CORS errors in browser + +**Server Configuration:** +```javascript +// Add CORS headers for x402 endpoints +app.use(cors({ + origin: ['http://localhost:3000', 'https://yourdomain.com'], + methods: ['GET', 'POST', 'OPTIONS'], + allowedHeaders: ['Content-Type', 'Authorization', 'X-PAYMENT'], + credentials: true +})); +``` + +## Development Environment Issues + +### Problem: "Module not found" errors + +**Common Solutions:** + +1. **TypeScript Configuration** + ```json + // tsconfig.json + { + "compilerOptions": { + "moduleResolution": "node", + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "skipLibCheck": true + } + } + ``` + +2. **Package Installation** + ```bash + # Install all required dependencies + npm install x402-express @coinbase/x402 + # or + npm install x402-next @coinbase/x402 + ``` + +3. **Environment Variables** + ```bash + # .env.local + NEXT_PUBLIC_RPC_URL=https://base-sepolia.g.alchemy.com/v2/your-key + WALLET_PRIVATE_KEY=your-private-key-for-testing + FACILITATOR_URL=https://x402.org/facilitator + ``` + +### Problem: Rate limiting during development + +**Solutions:** +```javascript +// Implement development-friendly rate limiting +const rateLimiter = rateLimit({ + windowMs: 15 * 60 * 1000, // 15 minutes + max: process.env.NODE_ENV === 'development' ? 1000 : 100, + message: 'Too many requests from this IP', + standardHeaders: true, + legacyHeaders: false, +}); +``` + +## Production Issues + +### Problem: High gas costs + +**Optimization Strategies:** + +1. **Batch Transactions** + ```javascript + // Bundle multiple payments + const batchPayment = await contract.batchTransfer( + recipients, + amounts, + { gasLimit: estimatedGas } + ); + ``` + +2. **Use Layer 2 Networks** + ```javascript + // Switch to more cost-effective networks + const networks = { + 'ethereum': { chainId: 1, gasMultiplier: 1.0 }, + 'base': { chainId: 8453, gasMultiplier: 0.1 }, + 'arbitrum': { chainId: 42161, gasMultiplier: 0.1 } + }; + ``` + +3. **Implement Gas Price Oracle** + ```javascript + async function getOptimalGasPrice() { + try { + const gasPrice = await provider.getGasPrice(); + const fastGasPrice = gasPrice.mul(110).div(100); // 10% higher for faster confirmation + return fastGasPrice; + } catch (error) { + return ethers.utils.parseUnits('20', 'gwei'); // Fallback + } + } + ``` + +## Monitoring and Logging + +### Setting Up Comprehensive Logging + +```javascript +const winston = require('winston'); + +const logger = winston.createLogger({ + level: 'info', + format: winston.format.combine( + winston.format.timestamp(), + winston.format.errors({ stack: true }), + winston.format.json() + ), + defaultMeta: { service: 'x402-api' }, + transports: [ + new winston.transports.File({ filename: 'error.log', level: 'error' }), + new winston.transports.File({ filename: 'combined.log' }) + ] +}); + +// Log payment events +function logPaymentEvent(event, data) { + logger.info('Payment event', { + event, + timestamp: Date.now(), + ...data + }); +} +``` + +### Health Check Endpoint + +```javascript +app.get('/health', async (req, res) => { + try { + // Check database connection + await db.ping(); + + // Check facilitator connectivity + const facilitatorStatus = await fetch(`${FACILITATOR_URL}/health`); + + // Check blockchain connectivity + const blockNumber = await provider.getBlockNumber(); + + res.json({ + status: 'healthy', + timestamp: Date.now(), + services: { + database: 'connected', + facilitator: facilitatorStatus.ok ? 'connected' : 'disconnected', + blockchain: blockNumber > 0 ? 'connected' : 'disconnected' + } + }); + } catch (error) { + res.status(503).json({ + status: 'unhealthy', + error: error.message + }); + } +}); +``` + +## Getting Help + +If you're still experiencing issues after trying these solutions: + +1. **Check the logs** - Enable verbose logging to identify the exact error +2. **Test in isolation** - Create minimal reproduction cases +3. **Review documentation** - Ensure you're following the latest implementation patterns +4. **Community support** - Join the [Discord community](https://discord.gg/invite/cdp) for help +5. **GitHub issues** - Report bugs or request features on the [x402 repository](https://github.com/coinbase/x402) + +Remember to include relevant error messages, code snippets, and environment details when seeking help. \ No newline at end of file