Troubleshooting Guide
Common issues and solutions for 402x integrations.
Payment Issues
Payment Not Confirming
Symptoms:
- Payment stuck in "pending" state
- Transaction not appearing on blockchain
- User charged but content not unlocked
Possible Causes & Solutions:
1. Insufficient Funds
javascript
// Check wallet balance
const balance = await provider.getBalance(userAddress);
console.log('Balance:', ethers.utils.formatEther(balance));
// Solution: Ask user to add funds
if (balance < paymentAmount) {
throw new Error('Insufficient funds. Please add funds to your wallet.');
}2. Network Congestion
javascript
// Check current gas prices
const gasPrice = await provider.getGasPrice();
console.log('Gas Price:', ethers.utils.formatUnits(gasPrice, 'gwei'), 'gwei');
// Solution: Retry with higher gas price or wait
if (gasPrice > MAX_GAS_PRICE) {
await retryLater();
}3. Wrong Network
javascript
// Verify correct network (Base mainnet = 8453)
const network = await provider.getNetwork();
console.log('Network:', network.chainId);
if (network.chainId !== 8453) {
throw new Error('Please switch to Base network');
}4. Expired Invoice
javascript
// Check invoice expiration
const invoice = await x402.invoices.get(invoiceId);
const isExpired = Date.now() > invoice.expiresAt;
if (isExpired) {
// Create new invoice
const newInvoice = await x402.invoices.create({
amount: invoice.amount,
description: invoice.description
});
}Prevention:
javascript
// Set reasonable expiration times
const invoice = await x402.invoices.create({
amount: 1.00,
expiresAt: Date.now() + (15 * 60 * 1000) // 15 minutes
});
// Monitor payment status
const payment = await x402.payments.monitor(invoiceId, {
timeout: 60000, // 1 minute
onPending: () => console.log('Payment pending...'),
onFailed: (error) => console.error('Payment failed:', error)
});Payment Fails Silently
Symptoms:
- No error messages
- Payment appears successful but nothing happens
- Webhook not triggered
Debug Steps:
javascript
// Enable detailed logging
const x402 = new X402({
apiKey: process.env.X402_API_KEY,
debug: true, // Verbose logging
logLevel: 'trace'
});
// Check all stages
try {
console.log('1. Creating payment...');
const payment = await x402.payments.create({...});
console.log('2. Payment created:', payment.id);
console.log('3. Waiting for confirmation...');
await payment.waitForConfirmation({ timeout: 30000 });
console.log('4. Payment confirmed!');
} catch (error) {
console.error('Failed at stage:', error);
}Common Issues:
- Missing Webhook Handler
javascript
// ❌ Wrong: No webhook endpoint
app.post('/api/payment', async (req, res) => {
const payment = await x402.payments.create({...});
// Missing: Webhook handler to unlock content
});
// ✅ Correct: Webhook handler
app.post('/webhooks/402x', async (req, res) => {
const event = req.body;
if (event.type === 'payment.confirmed') {
await unlockContent(event.data.invoiceId);
}
res.sendStatus(200);
});- Incorrect Error Handling
javascript
// ❌ Wrong: Swallowing errors
try {
await processPayment();
} catch (error) {
// Silent failure
}
// ✅ Correct: Proper error handling
try {
await processPayment();
} catch (error) {
logger.error('Payment failed:', error);
await notifyUser(error.message);
throw error; // Re-throw for monitoring
}Webhook Issues
Webhooks Not Received
Checklist:
- [ ] Webhook URL is publicly accessible
- [ ] Using HTTPS (not HTTP)
- [ ] Firewall allows incoming connections
- [ ] Server responds with 200 status
- [ ] Response time <5 seconds
- [ ] Correct webhook URL in dashboard
Test Webhook Endpoint:
bash
# Test if endpoint is reachable
curl -X POST https://your-domain.com/webhooks/402x \
-H "Content-Type: application/json" \
-H "X-402-Signature: test" \
-d '{"type": "test", "data": {}}'
# Should return: 200 OKCheck Webhook Logs:
javascript
// In dashboard, check webhook delivery logs
// Or via API:
const deliveries = await x402.webhooks.deliveries.list({
webhookId: 'whk_abc123',
limit: 10
});
deliveries.forEach(delivery => {
console.log('Status:', delivery.statusCode);
console.log('Response:', delivery.response);
console.log('Error:', delivery.error);
});Common Issues:
- Localhost URLs (Development)
javascript
// ❌ Wrong: Localhost not accessible
webhookUrl: 'http://localhost:3000/webhooks'
// ✅ Correct: Use ngrok or similar
webhookUrl: 'https://abc123.ngrok.io/webhooks'
// Or use webhook.site for testing
webhookUrl: 'https://webhook.site/unique-id'- Slow Endpoint Response
javascript
// ❌ Wrong: Slow processing blocks response
app.post('/webhooks/402x', async (req, res) => {
await processPayment(req.body); // Takes 10 seconds
res.sendStatus(200); // Timeout!
});
// ✅ Correct: Acknowledge immediately, process async
app.post('/webhooks/402x', async (req, res) => {
res.sendStatus(200); // Respond immediately
// Process asynchronously
processPayment(req.body).catch(console.error);
});- Invalid Signature Verification
javascript
// Verify webhook signature
import crypto from 'crypto';
function verifyWebhook(req) {
const signature = req.headers['x-402-signature'];
const payload = JSON.stringify(req.body);
const expectedSignature = crypto
.createHmac('sha256', webhookSecret)
.update(payload)
.digest('hex');
if (signature !== expectedSignature) {
throw new Error('Invalid signature');
}
}API Issues
401 Unauthorized
Symptoms:
- API returns 401 status
- "Invalid API key" error
- Authentication failures
Solutions:
javascript
// 1. Check API key format
const apiKey = process.env.X402_API_KEY;
console.log('Key prefix:', apiKey?.substring(0, 8));
// Production: sk_live_...
// Sandbox: sk_test_...
// 2. Verify environment match
if (apiKey.startsWith('sk_test_') && environment === 'production') {
throw new Error('Using test key in production!');
}
// 3. Check Authorization header
const headers = {
'Authorization': `Bearer ${apiKey}`, // Must include "Bearer "
'Content-Type': 'application/json'
};
// 4. Regenerate key if compromised
// Go to dashboard.402x.io/api-keys429 Too Many Requests
Symptoms:
- API returns 429 status
- "Rate limit exceeded" error
Solutions:
javascript
// 1. Implement exponential backoff
async function fetchWithRetry(url, options, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
const response = await fetch(url, options);
if (response.status === 429) {
const retryAfter = response.headers.get('Retry-After') || 1;
await sleep(retryAfter * 1000 * Math.pow(2, i));
continue;
}
return response;
} catch (error) {
if (i === maxRetries - 1) throw error;
}
}
}
// 2. Batch requests
// ❌ Wrong: Multiple individual requests
for (const item of items) {
await x402.payments.create(item); // Rate limited!
}
// ✅ Correct: Batch API
await x402.payments.createBatch(items);
// 3. Cache responses
const cache = new Map();
async function getCachedPayment(id) {
if (cache.has(id)) {
return cache.get(id);
}
const payment = await x402.payments.get(id);
cache.set(id, payment);
return payment;
}
// 4. Request higher limits (enterprise)
// Contact: [email protected]500 Internal Server Error
Symptoms:
- API returns 500 status
- Unpredictable failures
Debug:
javascript
// 1. Check status page
// Visit: status.402x.io
// 2. Enable request IDs
const response = await fetch('https://api.402x.io/payments', {
headers: {
'Authorization': `Bearer ${apiKey}`,
'X-Request-ID': crypto.randomUUID() // Track request
}
});
const requestId = response.headers.get('X-Request-ID');
console.log('Request ID:', requestId); // Share with support
// 3. Implement retry logic
async function apiCallWithRetry(fn, retries = 3) {
for (let i = 0; i < retries; i++) {
try {
return await fn();
} catch (error) {
if (error.status === 500 && i < retries - 1) {
await sleep(1000 * (i + 1));
continue;
}
throw error;
}
}
}
// 4. Report to support
// If persistent, email: [email protected] with Request IDIntegration Issues
Widget Not Displaying
Symptoms:
- Payment button doesn't appear
- Paywall content shows but no unlock option
Debug:
javascript
// 1. Check console for errors
// Open browser DevTools → Console
// 2. Verify script loaded
console.log('402x loaded:', typeof window.X402 !== 'undefined');
// 3. Check script src
// ✅ Correct
<script src="https://402x.io/widget.js"></script>
// ❌ Wrong
<script src="http://402x.io/widget.js"></script> // HTTP not HTTPS
<script src="https://402x.io/widgets.js"></script> // Wrong path
// 4. Verify data attributes
<script
src="https://402x.io/widget.js"
data-type="button" // ✅ lowercase
data-price="1.00" // ✅ number
data-label="Buy Now" // ✅ string
></script>
// 5. Check Content Security Policy
// Add to HTML <head>:
<meta http-equiv="Content-Security-Policy"
content="script-src 'self' https://402x.io;">Content Not Unlocking
Symptoms:
- Payment confirmed but content still locked
- User reports payment but no access
Debug Flow:
javascript
// 1. Verify payment status
const payment = await x402.payments.get(paymentId);
console.log('Payment status:', payment.status);
console.log('Amount paid:', payment.amount);
console.log('Expected amount:', expectedAmount);
// 2. Check webhook received
const webhookLogs = await x402.webhooks.deliveries.list({
paymentId: paymentId
});
if (webhookLogs.length === 0) {
console.error('No webhook received!');
}
// 3. Verify unlock logic
app.post('/webhooks/402x', async (req, res) => {
const event = req.body;
console.log('Webhook received:', event.type);
if (event.type === 'payment.confirmed') {
console.log('Payment ID:', event.data.id);
console.log('Invoice ID:', event.data.invoiceId);
// Check unlock function
const unlocked = await unlockContent(event.data.invoiceId);
console.log('Content unlocked:', unlocked);
}
res.sendStatus(200);
});
// 4. Check session/cookie issues
// Make sure payment and content delivery use same sessionPerformance Issues
Slow API Response
Symptoms:
- API calls taking >1 second
- Timeouts
- Poor user experience
Optimization:
javascript
// 1. Use pagination
// ❌ Wrong: Fetch all payments
const payments = await x402.payments.list(); // Slow!
// ✅ Correct: Paginate
const payments = await x402.payments.list({
limit: 20,
startingAfter: lastPaymentId
});
// 2. Select specific fields
const payments = await x402.payments.list({
fields: ['id', 'amount', 'status'] // Only needed fields
});
// 3. Cache aggressively
import Redis from 'ioredis';
const redis = new Redis();
async function getCachedData(key, fn, ttl = 300) {
const cached = await redis.get(key);
if (cached) return JSON.parse(cached);
const data = await fn();
await redis.setex(key, ttl, JSON.stringify(data));
return data;
}
// 4. Use webhooks instead of polling
// ❌ Wrong: Poll for status
setInterval(async () => {
const payment = await x402.payments.get(paymentId);
if (payment.status === 'confirmed') {
// Handle confirmation
}
}, 1000); // API call every second!
// ✅ Correct: Use webhooks
app.post('/webhooks/402x', async (req, res) => {
if (req.body.type === 'payment.confirmed') {
// Handle immediately
}
res.sendStatus(200);
});Security Issues
Possible Fraud Detected
Symptoms:
- Payment flagged by fraud detection
- Delayed confirmation
- Manual review required
What This Means:
The payment triggered one or more fraud indicators:
- Unusual velocity (too many payments too fast)
- Geographic anomalies
- Device fingerprint mismatch
- Known fraud patterns
Actions:
javascript
// 1. Review payment details
const payment = await x402.payments.get(paymentId);
console.log('Fraud score:', payment.fraudScore);
console.log('Fraud reasons:', payment.fraudReasons);
// 2. For legitimate payments, contact support
// Email: [email protected] with payment ID
// 3. Adjust fraud settings (if too sensitive)
await x402.settings.update({
fraudDetection: {
sensitivity: 'medium', // 'low', 'medium', 'high'
autoReject: false, // Manual review instead
velocityLimit: {
maxPayments: 10,
windowSeconds: 60
}
}
});Environment Issues
Works in Sandbox, Fails in Production
Common Causes:
- Using Test API Key
javascript
// Check which key you're using
if (apiKey.startsWith('sk_test_')) {
console.warn('⚠️ Using TEST key in production!');
}- Different Configuration
javascript
// Ensure consistent config
const config = {
sandbox: {
apiKey: process.env.X402_SANDBOX_KEY,
webhookUrl: 'https://dev.example.com/webhooks'
},
production: {
apiKey: process.env.X402_PRODUCTION_KEY,
webhookUrl: 'https://example.com/webhooks'
}
};
const env = process.env.NODE_ENV;
const x402 = new X402(config[env]);- Network/Firewall Issues
bash
# Test production API connectivity
curl https://api.402x.io/health
# Should return: {"status": "ok"}Mobile/Browser Issues
Wallet Not Connecting on Mobile
Solutions:
javascript
// 1. Check mobile wallet support
const isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);
if (isMobile) {
// Use WalletConnect for mobile
await x402.connect({ provider: 'walletconnect' });
} else {
// Use MetaMask/injected for desktop
await x402.connect({ provider: 'injected' });
}
// 2. Deep link to mobile wallet
if (isMobile && !window.ethereum) {
const dappUrl = encodeURIComponent(window.location.href);
const metamaskDeepLink = `https://metamask.app.link/dapp/${dappUrl}`;
window.location.href = metamaskDeepLink;
}Getting Help
Still Stuck?
Before contacting support, gather:
- Error Information
javascript
{
error: 'Error message',
statusCode: 500,
requestId: 'req_abc123',
timestamp: Date.now(),
environment: 'production'
}- Reproduction Steps
- What you're trying to do
- Minimal code example
- Expected vs actual behavior
- Environment Details
- SDK version
- Programming language/runtime
- Browser (if applicable)
- Operating system
Contact:
Quick Links