Robust error handling ensures your integration stays reliable when things go wrong. Always handle errors gracefully — never expose raw API errors to your end users.
| Error | Cause | Resolution |
|---|---|---|
invalid token |
Expired or incorrect access token | Re-authenticate: POST to /authentication to get a fresh token |
invalid client_id |
Wrong client ID in credentials | Verify Client ID in your merchant dashboard |
invalid secret |
Wrong secret key | Re-copy secret key from dashboard. Regenerate if compromised. |
amount required |
Missing amount parameter | Include 'amount' as a decimal with 2 precision (e.g. 5000.00) |
currency invalid |
Unknown or lowercase currency code | Use ISO 4217 UPPERCASE codes: XAF, USD, EUR |
return_url required |
Missing return URL | Provide a full HTTPS return URL |
token not found |
Payment token expired or invalid | Tokens expire after the session ends. Initiate a new payment. |
<?php
use GuzzleHttp\Exception\ClientException;
use GuzzleHttp\Exception\ServerException;
try {
$response = $client->request('POST', '{{base_url}}/payment/create', $options);
$data = json_decode($response->getBody(), true);
if ($data['type'] === 'success') {
header('Location: ' . $data['data']['payment_url']);
exit;
}
// API returned an error in the JSON body
$errors = $data['message']['error'] ?? ['Payment initiation failed'];
// Show user-friendly message, log the full $data internally
} catch (ClientException $e) {
// 4xx error — bad request or auth failure
$errorBody = json_decode($e->getResponse()->getBody(), true);
$code = $e->getResponse()->getStatusCode();
// Log $errorBody, show friendly message to user
} catch (ServerException $e) {
// 5xx error — server side issue
// Log and show "please try again" message
} catch (\Exception $e) {
// Network error, timeout, etc.
// Log $e->getMessage(), show generic error
}
const initiatePayment = async (payload) => {
try {
const response = await fetch('{{base_url}}/payment/create', {
method: 'POST',
headers: {
'Authorization': 'Bearer {{access_token}}',
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify(payload),
});
const data = await response.json();
if (!response.ok) {
// HTTP 4xx or 5xx
const errorMsg = data?.message?.error?.[0] || 'Payment failed';
console.error('API Error:', response.status, errorMsg);
throw new Error(errorMsg);
}
if (data.type !== 'success') {
// HTTP 200 but logical error
const errorMsg = data?.message?.error?.[0] || 'Unknown error';
throw new Error(errorMsg);
}
return data.data; // { token, payment_url }
} catch (err) {
// Handle or rethrow — never show raw errors to users
console.error('Payment initiation error:', err.message);
throw err;
}
};
import requests
import logging
logger = logging.getLogger(__name__)
def initiate_payment(amount, currency, return_url, order_id):
try:
response = requests.post(
"{{base_url}}/payment/create",
json={
"amount": amount,
"currency": currency,
"return_url": return_url,
"custom": order_id,
},
headers={
"Authorization": "Bearer {{access_token}}",
"Accept": "application/json",
},
timeout=30,
)
response.raise_for_status() # raises for 4xx/5xx
data = response.json()
if data.get("type") != "success":
errors = data.get("message", {}).get("error", ["Unknown error"])
logger.error("Payment API error: %s", errors)
return None, errors[0]
return data["data"]["payment_url"], None
except requests.exceptions.HTTPError as e:
logger.error("HTTP error %s: %s", e.response.status_code, e.response.text)
return None, "Payment service unavailable. Please try again."
except requests.exceptions.RequestException as e:
logger.error("Network error: %s", str(e))
return None, "Network error. Please check your connection."