Troubleshooting Guide
Common issues and solutions for Xpay integrations, with step-by-step debugging instructions.
Quick Support: For urgent issues, join our Discord or email support@xpay.sh with your error details.
Common Issues
Payment Verification Failures
Issue: “Payment verification failed” errors
Symptoms:
- 402 Payment Required responses when payment was made
PaymentRequiredErrorexceptions in your application- Valid payments being rejected
Common Causes:
-
Incorrect payment amount
Expected: 0.10 USDC Received: 0.099 USDC (missing gas/precision) -
Stale payment timestamps
Payment timestamp: 2024-01-01T10:00:00Z Current time: 2024-01-01T10:06:00Z Max age: 300 seconds (5 minutes) - EXPIRED -
Wrong facilitator URL
Expected: https://facilitator.base.org Used: https://facilitator.ethereum.org
Solutions:
// 1. Add payment tolerance for precision issues
const paywall = new XpayPaywall({
receivingWallet: '0x...',
verification: {
tolerance: 0.001, // Allow 0.1% variance
maxAge: 600 // Extend to 10 minutes if needed
}
})
// 2. Debug payment verification
async function debugPaymentVerification(req: Request, expectedPrice: number) {
const headers = req.headers
console.log('Payment Headers:', {
'x-payment-amount': headers['x-payment-amount'],
'x-payment-hash': headers['x-payment-hash'],
'x-payment-timestamp': headers['x-payment-timestamp'],
'x-facilitator-url': headers['x-facilitator-url']
})
try {
const verification = await paywall.verifyPayment(headers, {
price: expectedPrice,
tolerance: 0.001,
maxAge: 600
})
console.log('Verification Result:', verification)
return verification
} catch (error) {
console.error('Verification Error:', {
error: error.message,
expectedPrice,
receivedAmount: headers['x-payment-amount'],
timeDiff: Date.now() - new Date(headers['x-payment-timestamp']).getTime()
})
throw error
}
}
// 3. Implement retry logic for temporary failures
async function verifyWithRetry(headers: any, options: any, retries = 3) {
for (let i = 0; i < retries; i++) {
try {
return await paywall.verifyPayment(headers, options)
} catch (error) {
if (i === retries - 1 || !isRetryableError(error)) {
throw error
}
console.log(`Payment verification failed, retrying... (${i + 1}/${retries})`)
await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)))
}
}
}
function isRetryableError(error: any): boolean {
return [
'NETWORK_ERROR',
'TIMEOUT',
'SERVICE_UNAVAILABLE'
].includes(error.code)
}Agent Spending Limit Issues
Issue: Agents hitting limits unexpectedly
Symptoms:
SpendingLimitErrorexceptions- Agents pausing automatically
- API calls being rejected despite sufficient budget
Debugging Steps:
// 1. Check current agent status and spending
async function debugAgentSpending(agentId: string) {
const agent = await smartProxy.getAgent(agentId)
console.log('Agent Status:', {
id: agent.id,
status: agent.status,
totalSpent: agent.totalSpent,
totalCalls: agent.totalCalls,
limits: {
daily: agent.dailyLimit,
perCall: agent.perCallLimit,
monthly: agent.monthlyLimit
},
utilization: {
daily: (agent.totalSpent / agent.dailyLimit) * 100,
monthly: agent.monthlyLimit ? (agent.totalSpent / agent.monthlyLimit) * 100 : null
}
})
// Check recent transactions
const transactions = await smartProxy.getTransactionHistory({
agentIds: [agentId],
limit: 10
})
console.log('Recent Transactions:', transactions.transactions.map(tx => ({
id: tx.id,
amount: tx.amount,
status: tx.status,
createdAt: new Date(tx.createdAt)
})))
return { agent, transactions }
}
// 2. Reset daily limits if needed
async function resetDailySpending(agentId: string) {
try {
await smartProxy.resetDailySpending(agentId)
console.log(`Daily spending reset for agent ${agentId}`)
} catch (error) {
console.error('Failed to reset daily spending:', error)
}
}
// 3. Implement graceful limit handling
async function makeRequestWithLimitHandling(agentId: string, apiCall: () => Promise<Response>) {
try {
return await smartProxy.protectedFetch(apiCall, { agentId })
} catch (error) {
if (error instanceof SpendingLimitError) {
switch (error.limitType) {
case 'daily':
console.log(`Agent ${agentId} hit daily limit. Next reset: ${error.resetTime}`)
// Queue for tomorrow or increase limit
await handleDailyLimitExceeded(agentId, error)
break
case 'per_request':
console.log(`Request too expensive for agent ${agentId}`)
// Try with smaller request or increase per-call limit
await handlePerRequestLimitExceeded(agentId, error)
break
case 'monthly':
console.log(`Agent ${agentId} hit monthly limit`)
// Pause agent and notify administrators
await handleMonthlyLimitExceeded(agentId, error)
break
}
}
throw error
}
}Connection and Network Issues
Issue: Timeouts and connection errors
Symptoms:
- Request timeouts
- “Connection refused” errors
- Intermittent failures
Solutions:
// 1. Configure robust timeout and retry settings
const smartProxy = new SmartProxy({
endpoint: 'https://smart-proxy-abc123.xpay.sh',
apiKey: 'xpay_fw_...',
// Network configuration
timeout: 30000, // 30 second timeout
retries: 3, // Retry up to 3 times
retryDelay: 1000, // Start with 1 second delay
// Connection pooling
keepAlive: true,
maxSockets: 50,
// Custom retry logic
retryCondition: (error) => {
return error.code === 'TIMEOUT' ||
error.code === 'ECONNRESET' ||
(error.response && error.response.status >= 500)
}
})
// 2. Implement circuit breaker pattern
class CircuitBreaker {
private failures = 0
private lastFailTime = 0
private state: 'closed' | 'open' | 'half-open' = 'closed'
constructor(
private threshold = 5,
private timeout = 60000,
private monitorPeriod = 120000
) {}
async execute<T>(operation: () => Promise<T>): Promise<T> {
if (this.state === 'open') {
if (Date.now() - this.lastFailTime >= this.timeout) {
this.state = 'half-open'
} else {
throw new Error('Circuit breaker is open')
}
}
try {
const result = await operation()
this.onSuccess()
return result
} catch (error) {
this.onFailure()
throw error
}
}
private onSuccess() {
this.failures = 0
this.state = 'closed'
}
private onFailure() {
this.failures++
this.lastFailTime = Date.now()
if (this.failures >= this.threshold) {
this.state = 'open'
}
}
}
// 3. Add comprehensive error handling
async function robustApiCall(agentId: string, url: string, options: any) {
const circuitBreaker = new CircuitBreaker()
return await circuitBreaker.execute(async () => {
try {
return await smartProxy.protectedFetch(url, {
...options,
agentId,
timeout: 30000
})
} catch (error) {
// Enhanced error logging
console.error('API call failed:', {
agentId,
url,
error: error.message,
code: error.code,
status: error.status,
timestamp: new Date().toISOString()
})
// Specific error handling
if (error.code === 'TIMEOUT') {
throw new Error('Request timed out - try reducing payload size or increasing timeout')
} else if (error.code === 'ECONNRESET') {
throw new Error('Connection reset - check network connectivity')
} else if (error.status === 503) {
throw new Error('Service unavailable - Xpay service may be temporarily down')
}
throw error
}
})
}Authentication Issues
Issue: API key and authentication errors
Symptoms:
- 401 Unauthorized responses
- “Invalid API key” errors
- Intermittent authentication failures
Debugging:
// 1. Validate API key format and permissions
function validateApiKey(apiKey: string) {
if (!apiKey) {
throw new Error('API key is required')
}
if (!apiKey.startsWith('xpay_')) {
throw new Error('Invalid API key format - must start with "xpay_"')
}
if (apiKey.includes('test_') && process.env.NODE_ENV === 'production') {
throw new Error('Test API key used in production environment')
}
if (apiKey.includes('live_') && process.env.NODE_ENV !== 'production') {
console.warn('Live API key used in non-production environment')
}
}
// 2. Test API key connectivity
async function testApiKeyConnectivity(apiKey: string) {
try {
const smartProxy = new SmartProxy({
endpoint: process.env.XPAY_SMART_PROXY_ENDPOINT!,
apiKey
})
// Make a simple API call to test connectivity
await smartProxy.ping()
console.log('✅ API key is valid and service is reachable')
return true
} catch (error) {
console.error('❌ API key test failed:', error.message)
if (error.status === 401) {
console.log('🔑 Check your API key in the Xpay dashboard')
} else if (error.status === 403) {
console.log('🚫 API key lacks required permissions')
} else if (error.code === 'ENOTFOUND') {
console.log('🌐 Check your smart proxy endpoint URL')
}
return false
}
}
// 3. Implement API key rotation
class ApiKeyManager {
private currentKey: string
private backupKey?: string
constructor(primaryKey: string, backupKey?: string) {
this.currentKey = primaryKey
this.backupKey = backupKey
}
async executeWithFallback<T>(operation: (apiKey: string) => Promise<T>): Promise<T> {
try {
return await operation(this.currentKey)
} catch (error) {
if (error.status === 401 && this.backupKey) {
console.log('Primary API key failed, trying backup key')
try {
const result = await operation(this.backupKey)
// Swap keys if backup works
this.currentKey = this.backupKey
this.backupKey = undefined
console.log('Backup API key worked, keys rotated')
return result
} catch (backupError) {
console.error('Both API keys failed')
}
}
throw error
}
}
}Performance Issues
Issue: Slow response times and high latency
Symptoms:
- Requests taking longer than expected
- Timeout errors under load
- Poor application performance
Performance Optimization:
// 1. Implement request caching
class PerformanceOptimizedSmartProxy {
private cache = new Map<string, CachedResponse>()
private smartProxy: SmartProxy
constructor(config: any) {
this.smartProxy = new SmartProxy(config)
}
async cachedProtectedFetch(
url: string,
options: any,
cacheOptions?: { ttl?: number, key?: string }
) {
const cacheKey = cacheOptions?.key || this.generateCacheKey(url, options)
const cached = this.cache.get(cacheKey)
if (cached && Date.now() - cached.timestamp < (cacheOptions?.ttl || 300000)) {
console.log(`Cache hit for ${cacheKey}`)
return cached.response
}
const response = await this.smartProxy.protectedFetch(url, options)
// Cache successful responses
if (response.ok) {
this.cache.set(cacheKey, {
response: response.clone(),
timestamp: Date.now()
})
}
return response
}
private generateCacheKey(url: string, options: any): string {
return `${url}:${JSON.stringify(options)}`
}
}
// 2. Connection pooling and keep-alive
const httpAgent = new require('https').Agent({
keepAlive: true,
keepAliveMsecs: 30000,
maxSockets: 50,
maxFreeSockets: 10,
timeout: 30000
})
const optimizedSmartProxy = new SmartProxy({
endpoint: 'https://smart-proxy-abc123.xpay.sh',
apiKey: 'xpay_fw_...',
httpAgent
})
// 3. Request batching for multiple operations
class BatchingSmartProxy {
private pendingRequests: Array<{
request: any
resolve: (value: any) => void
reject: (error: any) => void
}> = []
private batchTimer?: NodeJS.Timeout
constructor(
private smartProxy: SmartProxy,
private batchSize = 10,
private batchDelay = 100
) {}
async protectedFetch(url: string, options: any): Promise<Response> {
return new Promise((resolve, reject) => {
this.pendingRequests.push({
request: { url, options },
resolve,
reject
})
if (this.pendingRequests.length >= this.batchSize) {
this.processBatch()
} else if (!this.batchTimer) {
this.batchTimer = setTimeout(() => this.processBatch(), this.batchDelay)
}
})
}
private async processBatch() {
if (this.batchTimer) {
clearTimeout(this.batchTimer)
this.batchTimer = undefined
}
const batch = this.pendingRequests.splice(0, this.batchSize)
try {
const batchRequests = batch.map(item => ({
agentId: item.request.options.agentId,
url: item.request.url,
options: item.request.options
}))
const results = await this.smartProxy.batchRequests(batchRequests)
batch.forEach((item, index) => {
const result = results[index]
if (result.success) {
item.resolve(result.response)
} else {
item.reject(result.error)
}
})
} catch (error) {
batch.forEach(item => item.reject(error))
}
}
}
// 4. Performance monitoring
class PerformanceMonitor {
private metrics: Map<string, number[]> = new Map()
async monitoredRequest(name: string, operation: () => Promise<any>) {
const start = performance.now()
try {
const result = await operation()
const duration = performance.now() - start
this.recordMetric(name, duration)
if (duration > 5000) {
console.warn(`Slow request detected: ${name} took ${duration}ms`)
}
return result
} catch (error) {
const duration = performance.now() - start
console.error(`Failed request: ${name} failed after ${duration}ms`, error)
throw error
}
}
private recordMetric(name: string, duration: number) {
if (!this.metrics.has(name)) {
this.metrics.set(name, [])
}
const durations = this.metrics.get(name)!
durations.push(duration)
// Keep only last 100 measurements
if (durations.length > 100) {
durations.shift()
}
}
getPerformanceReport(): Record<string, any> {
const report: Record<string, any> = {}
for (const [name, durations] of this.metrics) {
const avg = durations.reduce((sum, d) => sum + d, 0) / durations.length
const max = Math.max(...durations)
const min = Math.min(...durations)
report[name] = {
average: avg,
max,
min,
count: durations.length
}
}
return report
}
}Frequently Asked Questions
General Questions
Q: What is the difference between test and live API keys?
A: Test API keys (prefixed with xpay_test_) work with testnet USDC and don’t process real payments. Live API keys (prefixed with xpay_live_) process real mainnet USDC transactions. Always use test keys during development.
Q: How long do payments take to confirm?
A: Most payments confirm within 2-5 seconds on Base network. However, during network congestion, it may take up to 30 seconds. Always implement appropriate timeout handling.
Q: Can I use Xpay with other cryptocurrencies besides USDC?
A: Currently, Xpay only supports USDC on Base network. Support for other stablecoins is planned for future releases.
Agent Management
Q: What happens if an agent’s wallet runs out of funds?
A: The agent will receive InsufficientFundsError exceptions. Implement monitoring to track wallet balances and top up before they run empty:
async function checkWalletBalance(agentId: string) {
const agent = await smartProxy.getAgent(agentId)
const balance = await getWalletBalance(agent.walletAddress)
if (balance < agent.dailyLimit * 0.1) { // 10% buffer
await notifications.send({
type: 'low_balance',
message: `Agent ${agentId} wallet balance is low: ${balance} USDC`,
agentId,
balance
})
}
}Q: How do I handle agents that are consistently hitting limits?
A: Analyze spending patterns and either:
- Increase limits if the usage is legitimate
- Optimize the agent’s API usage patterns
- Implement dynamic limit adjustments based on performance
async function analyzeLimitHits(agentId: string) {
const analytics = await smartProxy.getSpendingAnalytics({
agentIds: [agentId],
timeframe: '7d'
})
const hitRate = analytics.limitHits / analytics.totalCalls
if (hitRate > 0.1) { // More than 10% of calls hit limits
console.log(`Agent ${agentId} frequently hits limits - consider optimization`)
return {
recommendation: 'optimize_usage',
hitRate,
suggestedActions: [
'Review API call patterns',
'Implement caching',
'Increase limits if usage is legitimate'
]
}
}
}Payment Processing
Q: Why am I getting “Payment amount mismatch” errors?
A: This usually happens due to:
- Gas fee calculations affecting the final amount
- Network precision differences
- Exchange rate fluctuations
Always include tolerance in payment verification:
const verification = await paywall.verifyPayment(headers, {
price: 0.10,
tolerance: 0.001 // Allow 0.1% variance
})Q: How do I handle partial payments or refunds?
A: Xpay doesn’t currently support partial payments. For refunds, you’ll need to implement your own refund logic:
async function issueRefund(transactionId: string, amount: number) {
// Create a new transaction record
const refund = await db.query(`
INSERT INTO transactions (id, type, amount, status, original_transaction_id)
VALUES ($1, 'refund', $2, 'pending', $3)
`, [generateId(), amount, transactionId])
// Process refund through your payment system
// This is outside of Xpay's scope
}Integration Issues
Q: How do I test my integration without spending real money?
A: Use the test environment:
const testSmartProxy = new SmartProxy({
endpoint: 'https://smart-proxy-test-abc123.xpay.sh',
apiKey: 'xpay_test_...',
network: 'base-sepolia' // Testnet
})
// Get testnet USDC from faucets
// All transactions use test tokensQ: Can I run multiple agents with the same wallet?
A: While technically possible, it’s not recommended for production:
- Nonce conflicts can cause transaction failures
- Difficult to track spending per agent
- Security implications
Instead, create separate wallets for each agent:
async function createAgentWithNewWallet() {
const wallet = generateNewWallet()
const agent = await smartProxy.createAgent({
id: generateAgentId(),
name: 'New Agent',
walletAddress: wallet.address,
kmsKeyId: await encryptPrivateKey(wallet.privateKey),
dailyLimit: 50,
perCallLimit: 2
})
return { agent, wallet }
}Debugging Tools
Q: How can I debug payment flows in detail?
A: Enable debug logging and use the built-in diagnostic tools:
// Enable debug mode
const smartProxy = new SmartProxy({
endpoint: 'https://smart-proxy-abc123.xpay.sh',
apiKey: 'xpay_fw_...',
debug: true, // Enables detailed logging
logLevel: 'debug'
})
// Use diagnostic endpoint
async function runDiagnostics(agentId: string) {
const diagnostics = await smartProxy.runDiagnostics(agentId)
console.log('Diagnostics Report:', {
agentStatus: diagnostics.agent.status,
walletBalance: diagnostics.wallet.balance,
networkConnectivity: diagnostics.network.reachable,
apiKeyValid: diagnostics.auth.valid,
lastTransaction: diagnostics.transactions.latest
})
return diagnostics
}
// Test specific payment scenarios
async function testPaymentScenario() {
const testResult = await smartProxy.testPayment({
agentId: 'test-agent',
amount: 0.10,
dryRun: true // Don't actually process payment
})
console.log('Payment Test Result:', testResult)
}Q: How do I monitor my integration’s health?
A: Implement comprehensive monitoring:
// Health check endpoint
app.get('/health/xpay', async (req, res) => {
try {
const checks = await Promise.all([
smartProxy.ping(),
checkDatabaseConnection(),
checkWalletBalances(),
checkRecentTransactionSuccess()
])
res.json({
status: 'healthy',
timestamp: new Date().toISOString(),
checks: {
xpayService: checks[0],
database: checks[1],
wallets: checks[2],
transactions: checks[3]
}
})
} catch (error) {
res.status(503).json({
status: 'unhealthy',
error: error.message
})
}
})
// Automated monitoring
setInterval(async () => {
try {
const health = await checkXpayHealth()
if (!health.healthy) {
await alerting.send('Xpay integration unhealthy', health)
}
} catch (error) {
await alerting.send('Xpay health check failed', error)
}
}, 60000) // Check every minuteGetting Help
Support Channels
- Discord Community: discord.gg/xpay - Fastest response for general questions
- Email Support: support@xpay.sh - For account and billing issues
- GitHub Issues: github.com/xpaysh/xpay-docs - For documentation issues
- Emergency Support: For production outages, use emergency contact provided in your dashboard
What to Include in Support Requests
When reporting issues, include:
- Error details: Full error message and stack trace
- Code snippet: Minimal reproducible example
- Environment: Node.js version, package versions, OS
- Agent/Transaction IDs: For specific payment issues
- Timeline: When the issue started occurring
- Expected vs actual behavior: Clear description of the problem
Status Page
Monitor Xpay service status at: status.xpay.sh
Subscribe to updates to stay informed about:
- Service outages
- Planned maintenance
- Performance issues
- New feature releases
Still having issues? Our team is here to help! Join our Discord community or reach out to support@xpay.sh.