DocsAdvanced guidesError Handling

Error Handling

When integrating with lomi., it’s essential to handle errors gracefully to provide a smooth payment experience for your customers. lomi. uses conventional HTTP response codes and returns meaningful error codes and messages that you can use to troubleshoot issues and inform your customers about the status of their transactions.

Error Response Format

All errors follow this format:

{
  "error": {
    "message": "Error description",
    "code": 400,
    "details": [
      {
        "path": "amount",
        "message": "Amount must be a positive number"
      }
    ]
  }
}

HTTP Status Codes

  • 400 Bad Request - Invalid parameters or validation failed
  • 401 Unauthorized - Missing or invalid API key
  • 403 Forbidden - Valid API key but insufficient permissions
  • 404 Not Found - Resource doesn’t exist
  • 429 Too Many Requests - Rate limit exceeded
  • 500 Internal Server Error - Something went wrong on our end

Validation Errors

When request validation fails:

try {
  const session = await lomi.checkoutSessions.create({
    amount: -100,  // Invalid: must be positive
    currency: 'INVALID',  // Invalid: must be 3 letters
    provider_codes: []  // Invalid: must not be empty
  });
} catch (error) {
  if (error.validationErrors) {
    // Validation failed with specific field errors
    error.validationErrors.errors.forEach(err => {
      console.error(`${err.path}: ${err.message}`);
    });
  }
}

Authentication Errors

When API key validation fails:

try {
  const session = await lomi.checkoutSessions.create({
    // ... parameters
  });
} catch (error) {
  if (error.statusCode === 401) {
    // Check common authentication issues
    if (!process.env.LOMI_API_KEY) {
      console.error('API key not configured');
    } else if (process.env.LOMI_API_KEY.startsWith('test_')) {
      console.error('Using test key in production');
    } else {
      console.error('Invalid API key');
    }
  }
}

Rate Limit Errors

When you exceed the rate limit (100 requests per 15 minutes):

try {
  const response = await fetch('https://api.lomi.africa/v1/checkout/sessions', {
    method: 'POST',
    headers: {
      'x-api-key': process.env.LOMI_API_KEY
    }
  });
 
  // Check rate limit headers
  console.log({
    remaining: response.headers.get('X-RateLimit-Remaining'),
    reset: response.headers.get('X-RateLimit-Reset')
  });
} catch (error) {
  if (error.statusCode === 429) {
    // Implement exponential backoff
    const retryAfter = parseInt(error.headers['retry-after'] || '60');
    await new Promise(resolve => setTimeout(resolve, retryAfter * 1000));
    // Retry request
  }
}

Provider Errors

When payment provider operations fail:

try {
  const session = await lomi.checkoutSessions.create({
    // ... parameters
  });
} catch (error) {
  switch (error.code) {
    case 'provider_not_available':
      console.error('Payment provider is temporarily unavailable');
      break;
    case 'provider_declined':
      console.error('Payment declined by provider:', error.message);
      break;
    case 'invalid_phone':
      console.error('Invalid phone number format');
      break;
  }
}

Best Practices

  1. Graceful Degradation

    • Always provide fallback options
    • Show user-friendly error messages
    • Log detailed errors for debugging
  2. Retry Strategy

    async function withRetry(fn, maxRetries = 3) {
      for (let i = 0; i < maxRetries; i++) {
        try {
          return await fn();
        } catch (error) {
          if (error.statusCode === 429 || error.statusCode >= 500) {
            // Wait longer between each retry
            await new Promise(resolve => 
              setTimeout(resolve, Math.pow(2, i) * 1000)
            );
            continue;
          }
          throw error;
        }
      }
    }
  3. Error Monitoring

    • Log all errors with context
    • Track error rates and patterns
    • Set up alerts for critical errors

Next Steps