Error Codes
Understanding and handling ShadhinPay API errors
This guide explains the error codes returned by the ShadhinPay API and how to handle them.
All errors follow this structure:
{
"success": false,
"error": {
"code": "INVALID_AMOUNT",
"message": "Amount must be greater than 10 BDT",
"details": {
"field": "amount",
"min_value": 10
}
}
}
| Code | Description |
|---|
200 | Success |
201 | Created |
400 | Bad Request - Invalid parameters |
401 | Unauthorized - Invalid credentials |
403 | Forbidden - Insufficient permissions |
404 | Not Found - Resource doesn't exist |
409 | Conflict - Resource already exists |
422 | Unprocessable - Validation failed |
429 | Too Many Requests - Rate limited |
500 | Internal Server Error |
| Code | HTTP | Description |
|---|
MISSING_CLIENT_ID | 401 | Client-Id header is required |
INVALID_CLIENT_ID | 401 | Client-Id is invalid |
MISSING_BUSINESS_ID | 401 | Business-Id header is required |
INVALID_BUSINESS_ID | 401 | Business-Id is invalid |
MISSING_SECRET_KEY | 401 | Secret-Key header is required |
INVALID_SECRET_KEY | 401 | Secret-Key is invalid |
BUSINESS_NOT_AUTHORIZED | 403 | Business doesn't have access |
| Code | HTTP | Description |
|---|
INVALID_AMOUNT | 400 | Amount is invalid or below minimum |
INVALID_CURRENCY | 400 | Currency not supported |
INVALID_PHONE | 400 | Phone number format is invalid |
PAYMENT_NOT_FOUND | 404 | Payment doesn't exist |
PAYMENT_ALREADY_COMPLETED | 409 | Payment was already processed |
PAYMENT_EXPIRED | 410 | Payment link has expired |
REFUND_EXCEEDS_AMOUNT | 400 | Refund amount exceeds payment |
REFUND_NOT_ALLOWED | 400 | Payment cannot be refunded |
| Code | HTTP | Description |
|---|
INVOICE_NOT_FOUND | 404 | Invoice doesn't exist |
INVOICE_ALREADY_PAID | 409 | Invoice was already paid |
INVALID_LINE_ITEMS | 400 | Line items are invalid |
INVALID_DUE_DATE | 400 | Due date is in the past |
| Code | HTTP | Description |
|---|
RATE_LIMIT_EXCEEDED | 429 | Too many requests |
When rate limited, check the Retry-After header:
{
"success": false,
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Too many requests. Please retry after 60 seconds",
"details": {
"retry_after": 60
}
}
}
async function createPayment(paymentData) {
try {
const response = await fetch('/api/v1/payments', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Client-Id': CLIENT_ID,
'Business-Id': BUSINESS_ID,
'Secret-Key': SECRET_KEY,
},
body: JSON.stringify(paymentData),
});
const data = await response.json();
if (!data.success) {
// Handle specific error codes
switch (data.error.code) {
case 'INVALID_AMOUNT':
console.error('Amount too low:', data.error.details.min_value);
break;
case 'RATE_LIMIT_EXCEEDED':
const retryAfter = data.error.details.retry_after;
console.log(`Rate limited. Retry in ${retryAfter}s`);
break;
default:
console.error('Error:', data.error.message);
}
return null;
}
return data.data;
} catch (error) {
console.error('Network error:', error);
return null;
}
}
import requests
import time
def create_payment(payment_data):
try:
response = requests.post(
'https://api.shadhinpay.com/api/v1/payments',
headers={
'Content-Type': 'application/json',
'Client-Id': CLIENT_ID,
'Business-Id': BUSINESS_ID,
'Secret-Key': SECRET_KEY,
},
json=payment_data
)
data = response.json()
if not data['success']:
error = data['error']
if error['code'] == 'RATE_LIMIT_EXCEEDED':
retry_after = error['details']['retry_after']
time.sleep(retry_after)
return create_payment(payment_data) # Retry
raise Exception(f"{error['code']}: {error['message']}")
return data['data']
except requests.exceptions.RequestException as e:
print(f"Network error: {e}")
return None
- Always check
success - Don't assume requests succeed
- Handle specific codes - Different errors need different handling
- Implement retries - For rate limits and temporary failures
- Log errors - Store error details for debugging
- Show user-friendly messages - Don't expose raw error codes to users