<!-- ZenPays documentation · https://docs.zenpayz.com/docs/guides/error-handling -->

# Error Handling

Learn how to handle errors in the ZenPays SDK.

## Error Types

The SDK provides specific error classes for different scenarios:

| Error Class | HTTP Status | Description |
|-------------|-------------|-------------|
| `ZenPaysError` | Various | Base error class |
| `AuthenticationError` | 401 | Invalid API key |
| `AuthorizationError` | 403 | Insufficient permissions |
| `NotFoundError` | 404 | Resource not found |
| `ValidationError` | 400 | Invalid input data |
| `RateLimitError` | 429 | Rate limit exceeded |
| `NetworkError` | - | Network connectivity issues |
| `PaymentError` | 402 | Payment processing failed |
| `ConfigurationError` | - | SDK misconfiguration |

## Basic Error Handling

<Tabs groupId="language">
  <TabItem value="javascript" label="JavaScript" default>

```typescript
import {
  AuthenticationError,
  NotFoundError,
  ValidationError,
  ZenPaysError,
} from '@zenxdigitalholdings/zenpays'

try {
  await zenpays.payments.createPaymentIntent({
    amount: 1000,
    currency: 'USD',
  })
}
catch (error) {
  if (error instanceof AuthenticationError) {
    console.error('Invalid API key. Check your credentials.')
  }
  else if (error instanceof ValidationError) {
    console.error('Validation failed:', error.message)
    console.error('Fields:', error.fields)
  }
  else if (error instanceof NotFoundError) {
    console.error('Resource not found')
  }
  else if (error instanceof ZenPaysError) {
    console.error(`API Error [${error.code}]: ${error.message}`)
    console.error('Status:', error.status)
    console.error('Details:', error.details)
  }
  else {
    throw error // Re-throw unexpected errors
  }
}
```

  </TabItem>
  <TabItem value="python" label="Python">

```python
from zenpays import ZenPays
from zenpays.errors import (
    AuthenticationError,
    NotFoundError,
    ValidationError,
    ZenPaysError,
)

try:
    zenpays.payments.create_payment_intent({
        "amount": 1000,
        "currency": "USD",
    })
except AuthenticationError:
    print("Invalid API key. Check your credentials.")
except ValidationError as e:
    print(f"Validation failed: {e}")
    print(f"Fields: {e.fields}")
except NotFoundError:
    print("Resource not found")
except ZenPaysError as e:
    print(f"API Error [{e.code}]: {e}")
    print(f"Status: {e.status}")
    print(f"Details: {e.details}")
except Exception as e:
    raise  # Re-raise unexpected errors
```

  </TabItem>
</Tabs>

## Error Properties

All ZenPays errors include:

```typescript
interface ZenPaysError extends Error {
  message: string // Human-readable message
  code?: string // Error code (e.g., 'VALIDATION_ERROR')
  status?: number // HTTP status code
  details?: string // Additional details
}
```

## Rate Limiting

Handle rate limits gracefully:

<Tabs groupId="language">
  <TabItem value="javascript" label="JavaScript" default>

```typescript
import { RateLimitError } from '@zenxdigitalholdings/zenpays'

async function makeRequestWithRetry(fn: () => Promise<any>, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await fn()
    }
    catch (error) {
      if (error instanceof RateLimitError && i < maxRetries - 1) {
        const delay = error.retryAfter ?? (2 ** i * 1000)
        console.log(`Rate limited. Retrying in ${delay}ms...`)
        await new Promise(resolve => setTimeout(resolve, delay))
      }
      else {
        throw error
      }
    }
  }
}

// Usage
const intent = await makeRequestWithRetry(() =>
  zenpays.payments.createPaymentIntent({ amount: 1000, currency: 'USD' })
)
```

  </TabItem>
  <TabItem value="python" label="Python">

```python
import time
from zenpays.errors import RateLimitError

def make_request_with_retry(fn, max_retries=3):
    for i in range(max_retries):
        try:
            return fn()
        except RateLimitError as e:
            if i < max_retries - 1:
                delay = e.retry_after if e.retry_after else (2 ** i * 1000) / 1000
                print(f"Rate limited. Retrying in {delay}s...")
                time.sleep(delay)
            else:
                raise

# Usage
intent = make_request_with_retry(
    lambda: zenpays.payments.create_payment_intent({"amount": 1000, "currency": "USD"})
)
```

  </TabItem>
</Tabs>

## Network Errors

Handle network connectivity issues:

<Tabs groupId="language">
  <TabItem value="javascript" label="JavaScript" default>

```typescript
import { NetworkError } from '@zenxdigitalholdings/zenpays'

try {
  await zenpays.payments.createPaymentIntent({
    amount: 1000,
    currency: 'USD',
  })
}
catch (error) {
  if (error instanceof NetworkError) {
    if (error.message === 'Request timeout') {
      console.error('Request timed out. Try again later.')
    }
    else {
      console.error('Network error. Check your connection.')
    }
  }
}
```

  </TabItem>
  <TabItem value="python" label="Python">

```python
from zenpays.errors import NetworkError

try:
    zenpays.payments.create_payment_intent({
        "amount": 1000,
        "currency": "USD",
    })
except NetworkError as e:
    if str(e) == "Request timeout":
        print("Request timed out. Try again later.")
    else:
        print("Network error. Check your connection.")
```

  </TabItem>
</Tabs>

## Payment Errors

Handle payment-specific errors:

<Tabs groupId="language">
  <TabItem value="javascript" label="JavaScript" default>

```typescript
import { PaymentError } from '@zenxdigitalholdings/zenpays'

try {
  await zenpays.payments.confirmPayment('pi_xxx', {
    customerDetails: { ... },
    paymentMethodDetails: { ... },
  })
} catch (error) {
  if (error instanceof PaymentError) {
    console.error('Payment failed:', error.message)
    console.error('Intent ID:', error.paymentIntentId)
    // Show user-friendly message
  }
}
```

  </TabItem>
  <TabItem value="python" label="Python">

```python
from zenpays.errors import PaymentError

try:
    zenpays.payments.confirm_payment("pi_xxx", {
        "customer_details": { ... },
        "payment_method_details": { ... },
    })
except PaymentError as e:
    print(f"Payment failed: {e}")
    print(f"Intent ID: {e.payment_intent_id}")
    # Show user-friendly message
```

  </TabItem>
</Tabs>

## Validation Errors

Handle validation errors with field-level details:

<Tabs groupId="language">
  <TabItem value="javascript" label="JavaScript" default>

```typescript
import { ValidationError } from '@zenxdigitalholdings/zenpays'

try {
  await zenpays.customers.create({
    email: 'invalid-email',
    name: '',
  })
}
catch (error) {
  if (error instanceof ValidationError) {
    console.error('Validation errors:')
    if (error.fields) {
      for (const [field, messages] of Object.entries(error.fields)) {
        console.error(`  ${field}: ${messages.join(', ')}`)
      }
    }
  }
}
```

  </TabItem>
  <TabItem value="python" label="Python">

```python
from zenpays.errors import ValidationError

try:
    zenpays.customers.create({
        "email": "invalid-email",
        "name": "",
    })
except ValidationError as e:
    print("Validation errors:")
    if e.fields:
        for field, messages in e.fields.items():
            print(f"  {field}: {', '.join(messages)}")
```

  </TabItem>
</Tabs>

## Channel Limit Errors

Handle payment method amount limit violations:

<Tabs groupId="language">
  <TabItem value="javascript" label="JavaScript" default>

```typescript
import { ChannelLimitError } from '@zenxdigitalholdings/zenpays'

try {
  await zenpays.payments.confirmPayment('pi_xxx', {
    customerDetails: { ... },
    paymentMethodDetails: { ... },
  })
} catch (error) {
  if (error instanceof ChannelLimitError) {
    const { amount, currency, paymentMethod, minimumAmount, maximumAmount } = error.limits ?? {}
    console.error(`Amount ${amount} ${currency} is out of range for ${paymentMethod}`)
    console.error(`Allowed range: ${minimumAmount} - ${maximumAmount} ${currency}`)
    // Prompt user to adjust the amount or select a different payment method
  }
}
```

  </TabItem>
  <TabItem value="python" label="Python">

```python
from zenpays.errors import ChannelLimitError

try:
    zenpays.payments.confirm_payment("pi_xxx", {
        "customer_details": { ... },
        "payment_method_details": { ... },
    })
except ChannelLimitError as e:
    limits = e.limits or {}
    print(f"Amount {limits.get('amount')} {limits.get('currency')} is out of range for {limits.get('paymentMethod')}")
    print(f"Allowed range: {limits.get('minimumAmount')} - {limits.get('maximumAmount')} {limits.get('currency')}")
    # Prompt user to adjust the amount or select a different payment method
```

  </TabItem>
</Tabs>

## Best Practices

1. **Always catch errors** - Don't let errors crash your application
2. **Use specific error types** - Check for specific errors before generic ones
3. **Log error details** - Include code, status, and details for debugging
4. **Show user-friendly messages** - Don't expose technical details to users
5. **Implement retry logic** - Handle transient errors automatically
6. **Monitor errors** - Track error rates in production

## Example: Complete Error Handler

<Tabs groupId="language">
  <TabItem value="javascript" label="JavaScript" default>

```typescript
import {
  AuthenticationError,
  AuthorizationError,
  ChannelLimitError,
  NetworkError,
  NotFoundError,
  PaymentError,
  RateLimitError,
  ValidationError,
  ZenPaysError,
} from '@zenxdigitalholdings/zenpays'

function handleZenPaysError(error: unknown): {
  message: string
  shouldRetry: boolean
  retryDelay?: number
} {
  if (error instanceof AuthenticationError) {
    return { message: 'Invalid API key', shouldRetry: false }
  }

  if (error instanceof AuthorizationError) {
    return { message: 'Access denied', shouldRetry: false }
  }

  if (error instanceof NotFoundError) {
    return { message: 'Resource not found', shouldRetry: false }
  }

  if (error instanceof ValidationError) {
    return { message: error.message, shouldRetry: false }
  }

  if (error instanceof ChannelLimitError) {
    return { message: error.message, shouldRetry: false }
  }

  if (error instanceof RateLimitError) {
    return {
      message: 'Too many requests',
      shouldRetry: true,
      retryDelay: error.retryAfter ?? 5000,
    }
  }

  if (error instanceof NetworkError) {
    return { message: 'Network error', shouldRetry: true, retryDelay: 1000 }
  }

  if (error instanceof PaymentError) {
    return { message: 'Payment failed', shouldRetry: false }
  }

  if (error instanceof ZenPaysError) {
    return { message: error.message, shouldRetry: false }
  }

  return { message: 'An unexpected error occurred', shouldRetry: false }
}
```

  </TabItem>
  <TabItem value="python" label="Python">

```python
from typing import TypedDict, Optional
from zenpays.errors import (
    AuthenticationError,
    AuthorizationError,
    ChannelLimitError,
    NetworkError,
    NotFoundError,
    PaymentError,
    RateLimitError,
    ValidationError,
    ZenPaysError,
)

class ErrorResult(TypedDict):
    message: str
    should_retry: bool
    retry_delay: Optional[int]

def handle_zenpays_error(error: Exception) -> ErrorResult:
    if isinstance(error, AuthenticationError):
        return {"message": "Invalid API key", "should_retry": False, "retry_delay": None}

    if isinstance(error, AuthorizationError):
        return {"message": "Access denied", "should_retry": False, "retry_delay": None}

    if isinstance(error, NotFoundError):
        return {"message": "Resource not found", "should_retry": False, "retry_delay": None}

    if isinstance(error, ValidationError):
        return {"message": str(error), "should_retry": False, "retry_delay": None}

    if isinstance(error, ChannelLimitError):
        return {"message": str(error), "should_retry": False, "retry_delay": None}

    if isinstance(error, RateLimitError):
        return {
            "message": "Too many requests",
            "should_retry": True,
            "retry_delay": error.retry_after if hasattr(error, "retry_after") and error.retry_after else 5000,
        }

    if isinstance(error, NetworkError):
        return {"message": "Network error", "should_retry": True, "retry_delay": 1000}

    if isinstance(error, PaymentError):
        return {"message": "Payment failed", "should_retry": False, "retry_delay": None}

    if isinstance(error, ZenPaysError):
        return {"message": str(error), "should_retry": False, "retry_delay": None}

    return {"message": "An unexpected error occurred", "should_retry": False, "retry_delay": None}
```

  </TabItem>
</Tabs>
