Logo
API Reference

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

PropertyTypeDescription
codestringA short string identifying the error
messagestringA human-readable message describing the error
metadataobjectOptional 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

CodeDescription
INVALID_CREDENTIALSInvalid API key, signature, or expired timestamp
UNAUTHORIZEDMissing required authentication headers
FORBIDDENIntegration is not active or environment mismatch

Validation Errors

CodeDescription
VALIDATION_ERRORInvalid request parameters
INVALID_AMOUNTAmount must be a valid decimal number
INVALID_CURRENCYCurrency must be 'THB'
INVALID_REFERENCE_IDReference ID format is invalid
INVALID_BANK_CODEBank code must be a valid 3-digit Thai bank code
MISSING_REQUIRED_FIELDA required field is missing

Resource Errors

CodeDescription
RESOURCE_NOT_FOUNDThe requested resource was not found
DUPLICATE_REFERENCE_IDReference ID already exists for this merchant
INSUFFICIENT_BALANCEMerchant account has insufficient balance

Payment Errors

CodeDescription
PAYMENT_NOT_CANCELLABLEPayment cannot be cancelled in its current state
PAYMENT_NOT_CAPTURABLEPayment cannot be captured in its current state
PAYMENT_NOT_REFUNDABLEPayment cannot be refunded in its current state

Rate Limiting Errors

CodeDescription
RATE_LIMIT_EXCEEDEDToo many requests in a short period

Server Errors

CodeDescription
INTERNAL_ERRORAn internal server error occurred
SERVICE_UNAVAILABLEThe 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;
    }
  }
}

On this page