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 failed401
Unauthorized - Missing or invalid API key403
Forbidden - Valid API key but insufficient permissions404
Not Found - Resource doesn’t exist429
Too Many Requests - Rate limit exceeded500
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
-
Graceful Degradation
- Always provide fallback options
- Show user-friendly error messages
- Log detailed errors for debugging
-
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; } } }
-
Error Monitoring
- Log all errors with context
- Track error rates and patterns
- Set up alerts for critical errors