> ## Documentation Index
> Fetch the complete documentation index at: https://chariow.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Best Practices

> Production-ready guidelines for integrating with the Chariow Checkout API

Follow these best practices to build robust, secure, and reliable integrations with the Chariow API, especially when handling checkout flows.

## Checkout API Best Practices

### Always Handle All Response States

The checkout API returns different states that require different handling. Never assume a checkout will always require payment.

```javascript theme={null}
async function handleCheckout(checkoutData) {
  const response = await fetch('https://api.chariow.com/v1/checkout', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${API_KEY}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(checkoutData)
  });

  const result = await response.json();

  switch (result.data.step) {
    case 'awaiting_payment':
      // Redirect to payment - most common case
      return { action: 'redirect', url: result.data.payment.checkout_url };

    case 'completed':
      // Free product - sale completed immediately
      return { action: 'success', saleId: result.data.purchase.id };

    case 'already_purchased':
      // Customer already owns this product
      return { action: 'already_owned', message: result.data.message };

    default:
      // Handle unexpected states gracefully
      console.error('Unexpected checkout state:', result.data.step);
      return { action: 'error', message: 'Unexpected response from payment system' };
  }
}
```

### Validate Data Before Submitting

Reduce failed checkouts by validating customer data on your end before calling the API.

```javascript theme={null}
function validateCheckoutData(data) {
  const errors = [];

  // Email validation
  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
  if (!data.email || !emailRegex.test(data.email)) {
    errors.push('Valid email is required');
  }

  // Name validation
  if (!data.first_name || data.first_name.length > 50) {
    errors.push('First name is required (max 50 characters)');
  }
  if (!data.last_name || data.last_name.length > 50) {
    errors.push('Last name is required (max 50 characters)');
  }

  // Phone validation - numeric only
  const phoneNumber = data.phone?.number?.replace(/\D/g, '');
  if (!phoneNumber || phoneNumber.length < 6) {
    errors.push('Valid phone number is required');
  }

  // Country code validation
  const validCountryCodes = ['US', 'CA', 'GB', 'FR', 'DE', 'ES', 'IT', /* ... */];
  if (!data.phone?.country_code || !validCountryCodes.includes(data.phone.country_code)) {
    errors.push('Valid country code is required');
  }

  return {
    valid: errors.length === 0,
    errors
  };
}
```

### Store Sale IDs Immediately

Always persist the sale ID as soon as you receive it. This is critical for customer support, refunds, and order tracking.

```javascript theme={null}
async function processCheckout(checkoutData, orderId) {
  const result = await initiateCheckout(checkoutData);

  if (result.data.purchase?.id) {
    // Store immediately - before any redirect
    await database.orders.update(orderId, {
      chariow_sale_id: result.data.purchase.id,
      checkout_initiated_at: new Date()
    });
  }

  return result;
}
```

### Use Pulses for Reliable Sale Confirmation

Never rely solely on redirect URLs to confirm purchases. Payment pages can be closed, redirects can fail, and customers may not return to your site.

<Warning>
  The `redirect_url` is for user experience only. Always use [Pulses](/en/guides/pulses) (webhooks) for reliable sale confirmation.
</Warning>

```javascript theme={null}
// Set up a Pulse endpoint for sale confirmation
app.post('/webhooks/chariow', async (req, res) => {
  const signature = req.headers['x-chariow-signature'];

  // Verify webhook signature
  if (!verifySignature(req.body, signature)) {
    return res.status(401).send('Invalid signature');
  }

  const { event, data } = req.body;

  switch (event) {
    case 'sale.completed':
      await fulfillOrder(data.id);
      await sendConfirmationEmail(data.customer.email);
      break;

    case 'sale.refunded':
      await revokeAccess(data.id);
      await sendRefundEmail(data.customer.email);
      break;
  }

  res.status(200).send('OK');
});
```

***

## Security Best Practices

### Never Expose API Keys in Client Code

API keys should only be used server-side. Never include them in JavaScript bundles, mobile apps, or any client-facing code.

<Tabs>
  <Tab title="Correct">
    ```javascript theme={null}
    // Server-side (Node.js)
    const API_KEY = process.env.CHARIOW_API_KEY;

    app.post('/api/checkout', async (req, res) => {
      const response = await fetch('https://api.chariow.com/v1/checkout', {
        method: 'POST',
        headers: {
          'Authorization': `Bearer ${API_KEY}`,
          'Content-Type': 'application/json'
        },
        body: JSON.stringify(req.body)
      });

      res.json(await response.json());
    });
    ```
  </Tab>

  <Tab title="Incorrect">
    ```javascript theme={null}
    // Client-side - NEVER DO THIS
    const API_KEY = 'sk_live_abc123'; // EXPOSED!

    fetch('https://api.chariow.com/v1/checkout', {
      headers: { 'Authorization': `Bearer ${API_KEY}` }
    });
    ```
  </Tab>
</Tabs>

### Validate Webhook Signatures

Always verify that incoming webhooks are genuinely from Chariow by checking the signature.

```javascript theme={null}
const crypto = require('crypto');

function verifySignature(payload, signature, secret) {
  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(JSON.stringify(payload))
    .digest('hex');

  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expectedSignature)
  );
}
```

### Use Environment Variables

Store all sensitive configuration in environment variables, never in code.

```bash theme={null}
# .env file (never commit this)
CHARIOW_API_KEY=sk_live_your_api_key
CHARIOW_WEBHOOK_SECRET=whsec_your_webhook_secret
```

```javascript theme={null}
// Load from environment
const config = {
  apiKey: process.env.CHARIOW_API_KEY,
  webhookSecret: process.env.CHARIOW_WEBHOOK_SECRET
};
```

***

## Error Handling Best Practices

### Implement Comprehensive Error Handling

Handle all possible error scenarios gracefully to provide a good user experience.

```javascript theme={null}
async function safeCheckout(checkoutData) {
  try {
    const response = await fetch('https://api.chariow.com/v1/checkout', {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${API_KEY}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(checkoutData)
    });

    // Handle HTTP errors
    if (!response.ok) {
      const error = await response.json();

      switch (response.status) {
        case 401:
          console.error('API key invalid');
          return { error: 'Payment system configuration error' };

        case 404:
          return { error: 'Product not found or unavailable' };

        case 422:
          // Validation errors - return field-specific messages
          return { error: 'Validation failed', fields: error.errors };

        case 429:
          return { error: 'Too many requests. Please try again shortly.' };

        default:
          return { error: 'Payment system unavailable. Please try again.' };
      }
    }

    return await response.json();

  } catch (networkError) {
    // Handle network failures
    console.error('Network error:', networkError);
    return { error: 'Unable to connect to payment system. Check your connection.' };
  }
}
```

### Display User-Friendly Error Messages

Map API errors to helpful messages that guide users to fix issues.

```javascript theme={null}
const errorMessages = {
  'email': 'Please enter a valid email address',
  'phone.number': 'Please enter a valid phone number (digits only)',
  'phone.country_code': 'Please select your country',
  'address': 'Please enter your shipping address',
  'city': 'Please enter your city',
  'state': 'Please enter your state or region',
  'country': 'Please select your country',
  'zip': 'Please enter your postal code',
  'discount_code': 'This discount code is invalid or has expired'
};

function getFieldError(fieldName, apiErrors) {
  if (apiErrors[fieldName]) {
    return errorMessages[fieldName] || apiErrors[fieldName][0];
  }
  return null;
}
```

### Log Errors for Debugging

Maintain detailed logs for troubleshooting while keeping sensitive data secure.

```javascript theme={null}
function logCheckoutError(error, checkoutData) {
  // Remove sensitive data before logging
  const safeData = {
    product_id: checkoutData.product_id,
    email: maskEmail(checkoutData.email),
    timestamp: new Date().toISOString(),
    error: error.message || error
  };

  console.error('Checkout failed:', JSON.stringify(safeData));

  // Send to monitoring service
  monitoring.captureError(error, { context: safeData });
}

function maskEmail(email) {
  const [local, domain] = email.split('@');
  return `${local.slice(0, 2)}***@${domain}`;
}
```

***

## Performance Best Practices

### Implement Request Timeouts

Don't let API calls hang indefinitely. Set reasonable timeouts.

```javascript theme={null}
async function checkoutWithTimeout(checkoutData, timeoutMs = 30000) {
  const controller = new AbortController();
  const timeout = setTimeout(() => controller.abort(), timeoutMs);

  try {
    const response = await fetch('https://api.chariow.com/v1/checkout', {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${API_KEY}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(checkoutData),
      signal: controller.signal
    });

    return await response.json();
  } finally {
    clearTimeout(timeout);
  }
}
```

### Cache Product Data

Reduce API calls by caching product information that doesn't change frequently.

```javascript theme={null}
const productCache = new Map();
const CACHE_TTL = 5 * 60 * 1000; // 5 minutes

async function getProduct(productId) {
  const cached = productCache.get(productId);

  if (cached && Date.now() - cached.timestamp < CACHE_TTL) {
    return cached.data;
  }

  const response = await fetch(`https://api.chariow.com/v1/products/${productId}`, {
    headers: { 'Authorization': `Bearer ${API_KEY}` }
  });

  const data = await response.json();

  productCache.set(productId, {
    data: data.data,
    timestamp: Date.now()
  });

  return data.data;
}
```

### Respect Rate Limits

Handle rate limiting gracefully with exponential backoff.

```javascript theme={null}
async function fetchWithRetry(url, options, maxRetries = 3) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    const response = await fetch(url, options);

    if (response.status === 429) {
      const retryAfter = response.headers.get('Retry-After') || Math.pow(2, attempt);
      console.log(`Rate limited. Retrying after ${retryAfter}s`);
      await sleep(retryAfter * 1000);
      continue;
    }

    return response;
  }

  throw new Error('Max retries exceeded');
}

function sleep(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}
```

***

## Testing Best Practices

### Test All Checkout Scenarios

Create comprehensive tests for every possible checkout outcome.

```javascript theme={null}
describe('Checkout Integration', () => {
  test('handles paid product checkout', async () => {
    const result = await handleCheckout({
      product_id: 'prd_paid_product',
      email: 'test@example.com',
      // ...
    });

    expect(result.action).toBe('redirect');
    expect(result.url).toContain('payment.chariow.com');
  });

  test('handles free product checkout', async () => {
    const result = await handleCheckout({
      product_id: 'prd_free_product',
      email: 'test@example.com',
      // ...
    });

    expect(result.action).toBe('success');
    expect(result.saleId).toBeDefined();
  });

  test('handles already purchased product', async () => {
    const result = await handleCheckout({
      product_id: 'prd_already_owned',
      email: 'existing@example.com',
      // ...
    });

    expect(result.action).toBe('already_owned');
  });

  test('handles invalid product gracefully', async () => {
    const result = await handleCheckout({
      product_id: 'prd_nonexistent',
      email: 'test@example.com',
      // ...
    });

    expect(result.error).toBeDefined();
  });

  test('handles shipping required products', async () => {
    const result = await handleCheckout({
      product_id: 'prd_physical',
      email: 'test@example.com',
      address: '123 Main St',
      city: 'New York',
      state: 'NY',
      country: 'US',
      zip: '10001',
      // ...
    });

    expect(result.action).toBe('redirect');
  });
});
```

### Use Staging Environment for Integration Tests

Always test against a staging environment before deploying to production.

```javascript theme={null}
const config = {
  development: {
    apiUrl: 'https://api.staging.chariow.com/v1',
    apiKey: process.env.CHARIOW_STAGING_KEY
  },
  production: {
    apiUrl: 'https://api.chariow.com/v1',
    apiKey: process.env.CHARIOW_LIVE_KEY
  }
};

const env = process.env.NODE_ENV || 'development';
const apiConfig = config[env];
```

***

## Checklist for Production

Before going live, ensure you've completed this checklist:

<AccordionGroup>
  <Accordion title="Security">
    * [ ] API keys stored in environment variables
    * [ ] API calls made server-side only
    * [ ] Webhook signatures verified
    * [ ] HTTPS used for all endpoints
    * [ ] Sensitive data not logged
  </Accordion>

  <Accordion title="Error Handling">
    * [ ] All HTTP status codes handled
    * [ ] Network errors caught
    * [ ] User-friendly error messages displayed
    * [ ] Errors logged for debugging
    * [ ] Rate limiting handled gracefully
  </Accordion>

  <Accordion title="Checkout Flow">
    * [ ] All checkout states handled (payment, completed, already\_purchased)
    * [ ] Sale IDs stored immediately
    * [ ] Pulses configured for sale confirmation
    * [ ] Customer data validated before submission
    * [ ] Shipping fields included when required
  </Accordion>

  <Accordion title="Testing">
    * [ ] Unit tests for checkout logic
    * [ ] Integration tests with staging API
    * [ ] All product types tested
    * [ ] Error scenarios tested
    * [ ] Load testing completed
  </Accordion>
</AccordionGroup>

***

## Next Steps

<CardGroup cols={2}>
  <Card title="Use Cases" icon="lightbulb" href="/en/guides/use-cases">
    Explore real-world integration examples
  </Card>

  <Card title="Checkout Guide" icon="cart-shopping" href="/en/guides/checkout">
    Learn the complete checkout flow
  </Card>

  <Card title="Pulses" icon="webhook" href="/en/guides/pulses">
    Set up webhooks for notifications
  </Card>

  <Card title="API Reference" icon="code" href="/api-reference/introduction">
    Explore the complete API
  </Card>
</CardGroup>
