Skip to content

Error Handling Guide

Learn how to handle errors gracefully in your Nivatio integration.

API Error Format

All API errors return a consistent JSON format:

{
  "statusCode": 400,
  "message": "Invalid order amount",
  "error": "Bad Request",
  "code": "invalid_amount"
}

Common Error Codes

Client Errors (4xx)

Code HTTP Status Description Solution
invalid_amount 400 Amount is not valid Ensure amount uses 6 decimals (100000 = 100.000)
invalid_currency 400 Currency not supported Use NUSD
unauthorized 401 Invalid API key or JWT Check credentials
forbidden 403 Insufficient permissions Check API key permissions
not_found 404 Order or resource not found Verify the ID
rate_limit 429 Too many requests Implement exponential backoff

Server Errors (5xx)

Code HTTP Status Description Solution
internal_error 500 Internal server error Retry with exponential backoff
service_unavailable 503 Service temporarily unavailable Retry after delay

Handling Errors in Code

JavaScript/TypeScript

try {
  const order = await client.orders.create({
    amount: 100000,
    currency: 'NUSD'
  });
  console.log('Order created:', order.id);
} catch (error) {
  if (error.statusCode === 400) {
    console.error('Bad request:', error.message);
    // Show user-friendly error
  } else if (error.statusCode === 401) {
    console.error('Unauthorized - check API key');
    // Refresh credentials
  } else if (error.statusCode === 429) {
    console.error('Rate limited - retry later');
    // Implement retry with backoff
  } else {
    console.error('Unexpected error:', error);
  }
}

Python

from nivatio import NivatioError

try:
    order = client.orders.create(
        amount=100000,
        currency='NUSD'
    )
    print(f"Order created: {order.id}")
except NivatioError as e:
    if e.status_code == 400:
        print(f"Bad request: {e.message}")
    elif e.status_code == 401:
        print("Unauthorized - check API key")
    elif e.status_code == 429:
        print("Rate limited - retry later")
    else:
        print(f"Unexpected error: {e}")

Retry Strategy

Exponential Backoff

Implement retries for transient errors (429, 500, 503):

async function createOrderWithRetry(data, maxRetries = 3) {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      return await client.orders.create(data);
    } catch (error) {
      if (error.statusCode === 429 || error.statusCode >= 500) {
        if (attempt === maxRetries) throw error;

        // Exponential backoff: 1s, 2s, 4s, 8s...
        const delay = Math.pow(2, attempt - 1) * 1000;
        console.log(`Retry attempt ${attempt} after ${delay}ms`);
        await new Promise(resolve => setTimeout(resolve, delay));
      } else {
        throw error; // Don't retry client errors (except 429)
      }
    }
  }
}

Webhook Error Handling

Always Return 200

Nivatio expects a 200 status code within 5 seconds:

app.post('/webhook', (req, res) => {
  try {
    const event = req.body;

    // Process event asynchronously
    processEventAsync(event).catch(err => {
      console.error('Error processing event:', err);
      // Log but don't fail the webhook
    });

    // Acknowledge immediately
    res.json({ received: true });
  } catch (error) {
    // Still return 200 to prevent retries
    console.error('Webhook error:', error);
    res.json({ received: true, error: 'Processing async' });
  }
});

Idempotency

Prevent duplicate processing:

const processedEvents = new Set();

app.post('/webhook', (req, res) => {
  const event = req.body;

  // Check if already processed
  if (processedEvents.has(event.eventId)) {
    return res.json({ received: true, reason: 'duplicate' });
  }

  // Process event...

  // Mark as processed
  processedEvents.add(event.eventId);

  // Clean up old events after 24 hours
  setTimeout(() => {
    processedEvents.delete(event.eventId);
  }, 24 * 60 * 60 * 1000);

  res.json({ received: true });
});

Payment-Specific Errors

Transaction Failures

app.post('/webhook', (req, res) => {
  const event = req.body;

  if (event.type === 'payment.failed') {
    const order = event.data;

    // Log failure reason
    console.error(`Payment ${order.id} failed:`, order.failureReason);

    // Notify customer
    notifyCustomer(order, `Payment failed: ${order.failureReason}`);

    // Update your database
    updateOrderStatus(order.id, 'FAILED');
  }

  res.json({ received: true });
});

Timeout Handling

// Orders expire after a configurable timeout
if (event.type === 'payment.expired') {
  const order = event.data;
  console.log(`Payment ${order.id} expired`);
  // Clean up your side
}

Logging Best Practices

What to Log

// Good: Log relevant context
console.log('Order created', {
  orderId: order.id,
  amount: order.amount,
  currency: order.currency,
  status: order.status
});

// Bad: Don't log sensitive data
console.log('API Key:', apiKey); // Never do this!

Structured Logging

function log(level, message, data) {
  console.log(JSON.stringify({
    timestamp: new Date().toISOString(),
    level,
    message,
    ...data
  }));
}

log('info', 'Order created', { orderId: order.id, amount: order.amount });
log('error', 'Payment failed', { orderId: order.id, reason: error.message });

Monitoring & Alerting

Key Metrics to Monitor

  1. Payment success rate - % of successful payments
  2. Webhook delivery rate - % of webhooks delivered successfully
  3. API error rate - % of API calls returning errors
  4. Payment latency - Time from creation to confirmation

Setting Up Alerts

// Alert if success rate drops below 95%
if (successRate < 0.95) {
  sendAlert({
    title: 'Low Payment Success Rate',
    message: `Success rate dropped to ${(successRate * 100).toFixed(2)}%`,
    severity: 'high'
  });
}

Next Steps