Skip to content

Error Handling

Learn how to handle errors from the Pixlpay API effectively.

Error Response Format

All errors follow a consistent structure:

json
{
  "success": false,
  "error": "ERROR_CODE",
  "message": "Human-readable error message"
}

For validation errors:

json
{
  "success": false,
  "error": "VALIDATION_ERROR",
  "message": "The given data was invalid",
  "errors": {
    "url": ["The url field is required."],
    "events": ["The events field must be an array."]
  }
}

HTTP Status Codes

CodeMeaningAction
200SuccessRequest completed successfully
201CreatedResource created successfully
400Bad RequestFix the request and retry
401UnauthorizedCheck authentication token
403ForbiddenToken lacks required scope
404Not FoundResource doesn't exist
422Validation ErrorFix validation errors and retry
429Rate LimitedWait and retry with backoff
500Server ErrorRetry later or contact support

Common Errors

Authentication Errors (401)

json
{
  "success": false,
  "error": "Unauthorized",
  "message": "Invalid or missing API token"
}

Solutions:

  • Check the Authorization header format: Bearer YOUR_TOKEN
  • Verify the token hasn't been revoked
  • Check the token hasn't expired

Permission Errors (403)

json
{
  "success": false,
  "error": "Forbidden",
  "message": "Token does not have the required scope: orders:write"
}

Solutions:

  • Add the required scope to your token
  • Create a new token with appropriate scopes

Not Found Errors (404)

json
{
  "success": false,
  "error": "Not Found",
  "message": "Order not found"
}

Solutions:

  • Verify the resource ID is correct
  • Ensure the resource belongs to your store
  • Check if the resource was deleted

Validation Errors (422)

json
{
  "success": false,
  "error": "VALIDATION_ERROR",
  "message": "The given data was invalid",
  "errors": {
    "url": ["The url must be a valid URL."],
    "events": ["The selected events.0 is invalid."]
  }
}

Solutions:

  • Check the errors object for specific field issues
  • Refer to API documentation for valid values
  • Ensure required fields are provided

Rate Limit Errors (429)

json
{
  "success": false,
  "error": "Too Many Requests",
  "message": "Rate limit exceeded. Retry after 60 seconds."
}

Headers:

X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1700745660

Solutions:

  • Implement exponential backoff
  • Cache responses where appropriate
  • Increase token rate limit if needed

Business Logic Errors (400)

json
{
  "success": false,
  "error": "ORDER_ALREADY_COMPLETED",
  "message": "Order is already completed"
}

Common codes:

  • ORDER_ALREADY_COMPLETED - Order already fulfilled
  • INVALID_STATE - Resource in invalid state for operation
  • LIMIT_EXCEEDED - Plan limit reached

Error Handling Best Practices

1. Always Check Success Flag

javascript
async function getProducts() {
  const response = await fetch(`${BASE_URL}/products`, {
    headers: { Authorization: `Bearer ${token}` }
  });

  const data = await response.json();

  if (!data.success) {
    throw new APIError(data.error, data.message, response.status);
  }

  return data.data;
}

2. Implement Retry Logic

javascript
async function fetchWithRetry(url, options, maxRetries = 3) {
  let lastError;

  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      const response = await fetch(url, options);

      // Don't retry client errors (4xx except 429)
      if (response.status >= 400 && response.status < 500 && response.status !== 429) {
        const data = await response.json();
        throw new APIError(data.error, data.message, response.status);
      }

      // Handle rate limiting
      if (response.status === 429) {
        const retryAfter = response.headers.get('Retry-After') || 60;
        await sleep(retryAfter * 1000);
        continue;
      }

      // Retry server errors
      if (response.status >= 500) {
        throw new Error(`Server error: ${response.status}`);
      }

      return await response.json();
    } catch (error) {
      lastError = error;

      if (error instanceof APIError) {
        throw error; // Don't retry client errors
      }

      // Exponential backoff
      const delay = Math.min(1000 * Math.pow(2, attempt), 30000);
      await sleep(delay);
    }
  }

  throw lastError;
}

3. Handle Validation Errors

javascript
function handleValidationErrors(errors) {
  const messages = [];

  for (const [field, fieldErrors] of Object.entries(errors)) {
    for (const error of fieldErrors) {
      messages.push(`${field}: ${error}`);
    }
  }

  return messages;
}

try {
  await createWebhook({ url: 'invalid-url' });
} catch (error) {
  if (error.code === 'VALIDATION_ERROR') {
    const messages = handleValidationErrors(error.errors);
    console.error('Validation failed:', messages.join(', '));
  }
}

4. Log Errors Appropriately

javascript
function logAPIError(error, context) {
  console.error('API Error', {
    code: error.code,
    message: error.message,
    status: error.status,
    endpoint: context.endpoint,
    method: context.method,
    timestamp: new Date().toISOString(),
  });

  // Send to monitoring service
  monitoring.captureException(error, {
    tags: { api: 'pixlpay' },
    extra: context,
  });
}

5. Create an Error Class

javascript
class PixlpayError extends Error {
  constructor(code, message, status, errors = null) {
    super(message);
    this.name = 'PixlpayError';
    this.code = code;
    this.status = status;
    this.errors = errors;
  }

  isValidationError() {
    return this.code === 'VALIDATION_ERROR';
  }

  isRateLimited() {
    return this.status === 429;
  }

  isAuthError() {
    return this.status === 401 || this.status === 403;
  }

  isRetryable() {
    return this.status >= 500 || this.status === 429;
  }
}

Complete Example

javascript
class PixlpayClient {
  constructor(token, baseUrl) {
    this.token = token;
    this.baseUrl = baseUrl;
  }

  async request(method, endpoint, data = null) {
    const url = `${this.baseUrl}${endpoint}`;
    const options = {
      method,
      headers: {
        'Authorization': `Bearer ${this.token}`,
        'Accept': 'application/json',
        'Content-Type': 'application/json',
      },
    };

    if (data) {
      options.body = JSON.stringify(data);
    }

    let lastError;
    for (let attempt = 1; attempt <= 3; attempt++) {
      try {
        const response = await fetch(url, options);
        const responseData = await response.json();

        if (!responseData.success) {
          throw new PixlpayError(
            responseData.error,
            responseData.message,
            response.status,
            responseData.errors
          );
        }

        return responseData;
      } catch (error) {
        lastError = error;

        if (error instanceof PixlpayError) {
          if (!error.isRetryable()) {
            throw error;
          }
        }

        // Exponential backoff
        await new Promise(r => setTimeout(r, 1000 * Math.pow(2, attempt)));
      }
    }

    throw lastError;
  }

  async getProducts() {
    return this.request('GET', '/products');
  }

  async fulfillOrder(orderId) {
    return this.request('POST', `/orders/${orderId}/fulfill`);
  }
}

// Usage
const client = new PixlpayClient(token, 'https://yourstore.pixlpay.net/api/external/v1');

try {
  const products = await client.getProducts();
  console.log('Products:', products.data);
} catch (error) {
  if (error.isAuthError()) {
    console.error('Authentication failed - check your token');
  } else if (error.isValidationError()) {
    console.error('Validation failed:', error.errors);
  } else {
    console.error('API error:', error.message);
  }
}

Built for game developers, by game developers.