Error Handling
One2Pays uses conventional HTTP response codes to indicate the success or failure of an API request. In general:
- 2xx codes indicate success
- 4xx codes indicate an error that failed given the information provided
- 5xx codes indicate an error with One2Pays's servers
Error Response Format
All errors return a JSON object with the following structure:
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Amount must be a valid decimal number",
"metadata": {
"field": "amount"
}
}
}Error Object Properties
| Property | Type | Description |
|---|---|---|
code | string | A short string identifying the error |
message | string | A human-readable message describing the error |
metadata | object | Optional additional error details (field names, etc.) |
HTTP Status Codes
200 - OK
Everything worked as expected.
201 - Created
Resource was created successfully.
400 - Bad Request
The request was unacceptable, often due to missing a required parameter or invalid data format.
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Amount must be a valid decimal number",
"metadata": {
"field": "amount"
}
}
}401 - Unauthorized
No valid authentication credentials provided, or authentication failed.
{
"success": false,
"error": {
"code": "INVALID_CREDENTIALS",
"message": "Invalid signature or expired timestamp"
}
}403 - Forbidden
Valid credentials but insufficient permissions, or integration is not active.
{
"success": false,
"error": {
"code": "FORBIDDEN",
"message": "Integration is not active"
}
}404 - Not Found
The requested resource doesn't exist.
{
"success": false,
"error": {
"code": "RESOURCE_NOT_FOUND",
"message": "Payment not found"
}
}409 - Conflict
The request conflicts with another request (perhaps due to using the same idempotency key or duplicate reference ID).
{
"success": false,
"error": {
"code": "DUPLICATE_REFERENCE_ID",
"message": "Reference ID already exists for this merchant"
}
}429 - Too Many Requests
Too many requests hit the API too quickly. We recommend an exponential backoff of your requests.
{
"success": false,
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Too many requests, please try again later",
"metadata": {
"limit": 100,
"remaining": 0,
"resetAt": "2024-01-01T00:01:00.000Z",
"retryAfter": 60
}
}
}500, 502, 503, 504 - Server Errors
Something went wrong on One2Pays's end. (These are rare.)
{
"success": false,
"error": {
"code": "INTERNAL_ERROR",
"message": "An unexpected error occurred"
}
}Error Codes
Authentication Errors
| Code | Description |
|---|---|
INVALID_CREDENTIALS | Invalid API key, signature, or expired timestamp |
UNAUTHORIZED | Missing required authentication headers |
FORBIDDEN | Integration is not active or environment mismatch |
Validation Errors
| Code | Description |
|---|---|
VALIDATION_ERROR | Invalid request parameters |
INVALID_AMOUNT | Amount must be a valid decimal number |
INVALID_CURRENCY | Currency must be 'THB' |
INVALID_REFERENCE_ID | Reference ID format is invalid |
INVALID_BANK_CODE | Bank code must be a valid 3-digit Thai bank code |
MISSING_REQUIRED_FIELD | A required field is missing |
Resource Errors
| Code | Description |
|---|---|
RESOURCE_NOT_FOUND | The requested resource was not found |
DUPLICATE_REFERENCE_ID | Reference ID already exists for this merchant |
INSUFFICIENT_BALANCE | Merchant account has insufficient balance |
Payment Errors
| Code | Description |
|---|---|
PAYMENT_NOT_CANCELLABLE | Payment cannot be cancelled in its current state |
PAYMENT_NOT_CAPTURABLE | Payment cannot be captured in its current state |
PAYMENT_NOT_REFUNDABLE | Payment cannot be refunded in its current state |
Rate Limiting Errors
| Code | Description |
|---|---|
RATE_LIMIT_EXCEEDED | Too many requests in a short period |
Server Errors
| Code | Description |
|---|---|
INTERNAL_ERROR | An internal server error occurred |
SERVICE_UNAVAILABLE | The service is temporarily unavailable |
Error Handling Best Practices
Tip
Always handle errors gracefully in your application and provide meaningful feedback to your users.
Example Error Handling
async function createPayment(paymentData: object) {
try {
const response = await fetch('https://api.example.com/api/v1/payments', {
method: 'POST',
headers: {
'X-API-Key': API_KEY,
'X-Timestamp': timestamp,
'X-Signature': signature,
'Content-Type': 'application/json',
},
body: JSON.stringify(paymentData),
});
const result = await response.json();
if (!result.success) {
// Handle specific error codes
switch (result.error.code) {
case 'VALIDATION_ERROR':
console.error('Validation error:', result.error.message);
// Show user-friendly validation message
break;
case 'INVALID_CREDENTIALS':
console.error('Authentication failed');
// Re-authenticate or show login prompt
break;
case 'RATE_LIMIT_EXCEEDED':
console.error('Rate limit exceeded, retrying...');
// Implement exponential backoff
const retryAfter = result.error.metadata?.retryAfter || 60;
await new Promise(resolve => setTimeout(resolve, retryAfter * 1000));
return createPayment(paymentData); // Retry
case 'DUPLICATE_REFERENCE_ID':
console.error('Duplicate reference ID');
// Handle duplicate (might be from retry)
break;
default:
console.error('Unexpected error:', result.error);
}
throw new Error(result.error.message);
}
return result.data;
} catch (error) {
console.error('Request failed:', error);
throw error;
}
}Retry Logic
For RATE_LIMIT_EXCEEDED and server errors (5xx), implement exponential backoff:
async function createPaymentWithRetry(paymentData, maxRetries = 3) {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await createPayment(paymentData);
} catch (error) {
if (error.code === 'RATE_LIMIT_EXCEEDED' && attempt < maxRetries) {
const delay = Math.pow(2, attempt) * 1000; // Exponential backoff
const retryAfter = error.metadata?.retryAfter || delay / 1000;
await new Promise(resolve => setTimeout(resolve, retryAfter * 1000));
continue;
}
throw error;
}
}
}