Skip to main content

Error Codes and Handling

This guide provides comprehensive information about error handling in the Orsunpay API, including error codes, retry strategies, and best practices.

Error Response Format

All API errors follow a consistent format:
{
  "error": {
    "code": "error_code",
    "message": "Human-readable error description",
    "details": {
      "field": "Additional context about the error",
      "suggestion": "Recommended action to resolve"
    }
  }
}

HTTP Status Codes

StatusNameDescriptionWhen to Retry
400Bad RequestInvalid request parameters❌ Never
401UnauthorizedInvalid or missing API key❌ Never
403ForbiddenInsufficient permissions❌ Never
404Not FoundResource doesn’t exist❌ Never
409ConflictResource conflict (e.g., duplicate)❌ Never
422Unprocessable EntityValid request, business rule violation❌ Never
429Too Many RequestsRate limit exceeded✅ After delay
500Internal Server ErrorServer error✅ With backoff
502Bad GatewayUpstream service error✅ With backoff
503Service UnavailableService temporarily down✅ With backoff
504Gateway TimeoutRequest timeout✅ With backoff

Authentication Errors

authentication_required

{
  "error": {
    "code": "authentication_required",
    "message": "No API key provided",
    "details": {
      "suggestion": "Include 'Authorization: Bearer your_api_key' header"
    }
  }
}

invalid_api_key

{
  "error": {
    "code": "invalid_api_key",
    "message": "The provided API key is invalid",
    "details": {
      "suggestion": "Verify your API key is correct and active"
    }
  }
}

insufficient_permissions

{
  "error": {
    "code": "insufficient_permissions",
    "message": "API key does not have permission for this operation",
    "details": {
      "required_permission": "transactions:write"
    }
  }
}

Validation Errors

invalid_request

{
  "error": {
    "code": "invalid_request",
    "message": "Missing required parameter: amount",
    "details": {
      "field": "amount",
      "location": "body"
    }
  }
}

invalid_parameter_value

{
  "error": {
    "code": "invalid_parameter_value",
    "message": "Currency must be a valid ISO 4217 code",
    "details": {
      "field": "currency",
      "provided": "US",
      "expected": "USD"
    }
  }
}

parameter_out_of_range

{
  "error": {
    "code": "parameter_out_of_range",
    "message": "Amount must be between 100 and 100000",
    "details": {
      "field": "amount",
      "min": 100,
      "max": 100000,
      "provided": 50
    }
  }
}

Resource Errors

resource_not_found

{
  "error": {
    "code": "resource_not_found", 
    "message": "Transaction tr_invalid123 not found",
    "details": {
      "resource_type": "transaction",
      "resource_id": "tr_invalid123"
    }
  }
}

resource_already_exists

{
  "error": {
    "code": "resource_already_exists",
    "message": "Customer with buyerId 'customer_123' already exists",
    "details": {
      "field": "buyerId",
      "existing_id": "cu_clkj3h2n0000qjr8x4c5h6e7b"
    }
  }
}

Business Logic Errors

transaction_not_capturable

{
  "error": {
    "code": "transaction_not_capturable",
    "message": "Transaction must be in PROCESSING status to capture",
    "details": {
      "current_status": "SUCCESS",
      "required_status": "PROCESSING"
    }
  }
}

insufficient_balance

{
  "error": {
    "code": "insufficient_balance",
    "message": "Merchant account has insufficient balance for payout",
    "details": {
      "available_balance": 1000,
      "requested_amount": 5000
    }
  }
}

payment_method_not_supported

{
  "error": {
    "code": "payment_method_not_supported",
    "message": "Payment method 'crypto' not available in country 'BR'",
    "details": {
      "payment_method": "crypto",
      "country": "BR",
      "available_methods": ["card", "pix", "boleto"]
    }
  }
}

Rate Limiting Errors

rate_limit_exceeded

{
  "error": {
    "code": "rate_limit_exceeded",
    "message": "Too many requests. Please retry after some time.",
    "details": {
      "limit": 1000,
      "window": "1 hour",
      "retry_after": 60
    }
  }
}

Provider Errors

provider_error

{
  "error": {
    "code": "provider_error",
    "message": "Payment provider returned an error",
    "details": {
      "provider": "stripe",
      "provider_code": "card_declined",
      "provider_message": "Your card was declined"
    }
  }
}

provider_unavailable

{
  "error": {
    "code": "provider_unavailable",
    "message": "Payment provider is temporarily unavailable",
    "details": {
      "provider": "paypal",
      "estimated_recovery": "2023-11-15T11:00:00.000Z"
    }
  }
}

Idempotency Errors

idempotency_conflict

{
  "error": {
    "code": "idempotency_conflict",
    "message": "Request conflicts with previous request using same idempotency key",
    "details": {
      "idempotency_key": "user_123_order_456",
      "original_request_id": "req_abc123",
      "conflict_field": "amount"
    }
  }
}

Retry Strategies

Exponential Backoff

async function retryWithBackoff(apiCall, maxRetries = 3) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      return await apiCall();
    } catch (error) {
      // Don't retry client errors (4xx)
      if (error.status >= 400 && error.status < 500) {
        throw error;
      }
      
      // Don't retry on last attempt
      if (attempt === maxRetries - 1) {
        throw error;
      }
      
      // Exponential backoff: 1s, 2s, 4s
      const delay = Math.pow(2, attempt) * 1000;
      await new Promise(resolve => setTimeout(resolve, delay));
    }
  }
}

// Usage
const transaction = await retryWithBackoff(async () => {
  return await orsunpay.transactions.create(transactionData);
});

Rate Limit Handling

async function handleRateLimit(apiCall) {
  try {
    return await apiCall();
  } catch (error) {
    if (error.status === 429) {
      const retryAfter = error.response?.headers?.['retry-after'] || 60;
      console.log(`Rate limited. Retrying after ${retryAfter} seconds`);
      
      await new Promise(resolve => 
        setTimeout(resolve, retryAfter * 1000)
      );
      
      return await apiCall();
    }
    throw error;
  }
}

Circuit Breaker Pattern

class CircuitBreaker {
  constructor(threshold = 5, timeout = 60000) {
    this.threshold = threshold;
    this.timeout = timeout;
    this.failures = 0;
    this.state = 'CLOSED'; // CLOSED, OPEN, HALF_OPEN
    this.nextAttempt = Date.now();
  }

  async call(apiCall) {
    if (this.state === 'OPEN') {
      if (Date.now() < this.nextAttempt) {
        throw new Error('Circuit breaker is OPEN');
      } else {
        this.state = 'HALF_OPEN';
      }
    }

    try {
      const result = await apiCall();
      this.onSuccess();
      return result;
    } catch (error) {
      this.onFailure();
      throw error;
    }
  }

  onSuccess() {
    this.failures = 0;
    this.state = 'CLOSED';
  }

  onFailure() {
    this.failures++;
    if (this.failures >= this.threshold) {
      this.state = 'OPEN';
      this.nextAttempt = Date.now() + this.timeout;
    }
  }
}

// Usage
const breaker = new CircuitBreaker();
const transaction = await breaker.call(async () => {
  return await orsunpay.transactions.create(transactionData);
});

Error Handling Best Practices

1. Implement Proper Error Handling

async function processPayment(paymentData) {
  try {
    const transaction = await orsunpay.transactions.create(paymentData);
    return { success: true, transaction };
  } catch (error) {
    console.error('Payment processing error:', {
      code: error.code,
      message: error.message,
      orderId: paymentData.orderId
    });

    // Handle specific error types
    switch (error.code) {
      case 'payment_method_not_supported':
        return {
          success: false,
          error: 'This payment method is not available in your country',
          suggestedMethods: error.details?.available_methods
        };
        
      case 'invalid_parameter_value':
        return {
          success: false,
          error: `Invalid ${error.details?.field}: ${error.message}`
        };
        
      case 'rate_limit_exceeded':
        // Implement retry logic or queue the request
        return {
          success: false,
          error: 'Too many requests. Please try again in a few minutes.',
          retryAfter: error.details?.retry_after
        };
        
      default:
        return {
          success: false,
          error: 'Payment processing failed. Please try again.'
        };
    }
  }
}

2. Log Errors Appropriately

function logError(error, context = {}) {
  const logData = {
    timestamp: new Date().toISOString(),
    error_code: error.code,
    error_message: error.message,
    http_status: error.status,
    request_id: error.request_id,
    ...context
  };

  // Don't log sensitive information
  delete logData.api_key;
  delete logData.customer_data;

  console.error('API Error:', logData);
  
  // Send to monitoring service
  if (error.status >= 500) {
    monitoring.alert('API Error', logData);
  }
}

3. Graceful Degradation

async function getPaymentMethods(country, currency) {
  try {
    const methods = await orsunpay.paymentMethods.list({
      country,
      currency
    });
    return methods.data;
  } catch (error) {
    console.warn('Failed to fetch payment methods:', error.message);
    
    // Fall back to default payment methods
    return [
      { id: 'card', name: 'Credit/Debit Card' },
      { id: 'paypal', name: 'PayPal' }
    ];
  }
}

4. User-Friendly Error Messages

function getUserFriendlyMessage(error) {
  const messages = {
    'authentication_required': 'Please log in to continue.',
    'insufficient_permissions': 'You don\'t have permission to perform this action.',
    'resource_not_found': 'The requested item could not be found.',
    'rate_limit_exceeded': 'Too many requests. Please wait a moment and try again.',
    'payment_method_not_supported': 'This payment method is not available.',
    'provider_error': 'Payment processing failed. Please try a different payment method.',
    'provider_unavailable': 'Payment service is temporarily unavailable. Please try again later.'
  };

  return messages[error.code] || 'An unexpected error occurred. Please try again.';
}

Monitoring and Alerting

Error Rate Monitoring

Set up alerts for:
  • Error rate > 5% over 5 minutes
  • Specific error codes (authentication, provider errors)
  • Rate limiting events
  • Circuit breaker state changes

Key Metrics

  • Error Rate: Percentage of requests resulting in errors
  • Error Distribution: Breakdown by error code and HTTP status
  • Recovery Time: Time to recover from errors
  • Retry Success Rate: Success rate of retry attempts
Always implement proper error handling and never expose sensitive information in error messages or logs.
Use idempotency keys for all state-changing operations to prevent duplicate processing in retry scenarios.