Error Codes and Handling
This guide provides comprehensive information about error handling in the Orsunpay API, including error codes, retry strategies, and best practices.
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
| Status | Name | Description | When to Retry |
|---|
400 | Bad Request | Invalid request parameters | ❌ Never |
401 | Unauthorized | Invalid or missing API key | ❌ Never |
403 | Forbidden | Insufficient permissions | ❌ Never |
404 | Not Found | Resource doesn’t exist | ❌ Never |
409 | Conflict | Resource conflict (e.g., duplicate) | ❌ Never |
422 | Unprocessable Entity | Valid request, business rule violation | ❌ Never |
429 | Too Many Requests | Rate limit exceeded | ✅ After delay |
500 | Internal Server Error | Server error | ✅ With backoff |
502 | Bad Gateway | Upstream service error | ✅ With backoff |
503 | Service Unavailable | Service temporarily down | ✅ With backoff |
504 | Gateway Timeout | Request 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.