Skip to content

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:

  1. 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);
});
  1. 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 OK

Check 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:

  1. 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'
  1. 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);
});
  1. 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-keys

429 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 ID

Integration 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 session

Performance 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:

  1. Using Test API Key
javascript
// Check which key you're using
if (apiKey.startsWith('sk_test_')) {
  console.warn('⚠️  Using TEST key in production!');
}
  1. 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]);
  1. 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:

  1. Error Information
javascript
{
  error: 'Error message',
  statusCode: 500,
  requestId: 'req_abc123',
  timestamp: Date.now(),
  environment: 'production'
}
  1. Reproduction Steps
  • What you're trying to do
  • Minimal code example
  • Expected vs actual behavior
  1. Environment Details
  • SDK version
  • Programming language/runtime
  • Browser (if applicable)
  • Operating system

Contact: