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

# Best Practices

> Follow these guidelines to build a robust integration

## Overview

Follow these guidelines to build a robust integration and provide the best experience for your customers.

## Security

### Protect Your API Keys

Never expose API keys in client-side code or version control:

```javascript theme={null}
// ✅ Good - Use environment variables
const apiKey = process.env.CHEQPAY_API_KEY;

// ❌ Bad - Never hardcode
const apiKey = 'sk_live_abc123...';
```

### Environment Variables

Store credentials securely:

```bash theme={null}
# .env file (add to .gitignore)
CHEQPAY_API_KEY=sk_live_abc123xyz
```

```javascript theme={null}
// Load with dotenv
require('dotenv').config();

const config = {
  apiKey: process.env.CHEQPAY_API_KEY
};
```

<Warning>
  Add `.env` to your `.gitignore` file. Never commit API keys to version control.
</Warning>

### PCI Compliance

<AccordionGroup>
  <Accordion title="Never Store Card Numbers" icon="credit-card">
    Never log, store, or display full card numbers. Use Cheqpay's tokenization to keep card data secure.
  </Accordion>

  <Accordion title="Use HTTPS Everywhere" icon="lock">
    Always use HTTPS for payment pages and API requests. Never accept card data over HTTP.
  </Accordion>

  <Accordion title="Implement CSP Headers" icon="shield-halved">
    Use Content Security Policy headers to prevent XSS attacks on payment pages.
  </Accordion>

  <Accordion title="Rotate API Keys" icon="key">
    Regularly rotate API keys (every 90 days recommended). Update and test before deactivating old keys.
  </Accordion>
</AccordionGroup>

## Optimize Success Rates

### Include Device Information

Send device data with every payment to improve approval rates:

```javascript theme={null}
function getDeviceInformation() {
  return {
    ipAddress: req.ip,
    userAgent: req.headers['user-agent'],
    httpBrowserLanguage: req.headers['accept-language'],
    httpBrowserScreenWidth: req.body.screenWidth,
    httpBrowserScreenHeight: req.body.screenHeight,
    httpBrowserColorDepth: req.body.colorDepth,
    httpBrowserTimeDifference: new Date().getTimezoneOffset(),
    httpBrowserJavaEnabled: req.body.javaEnabled
  };
}

const payment = {
  // ... payment data
  deviceInformation: getDeviceInformation()
};
```

### Save Payment Methods

Returning customers with saved cards have 3-5% higher approval rates:

```javascript theme={null}
// Save card during first payment
{
  paymentMethod: {
    type: 'card',
    options: { card: { /* ... */ } },
    persist: true  // Save for future use
  }
}

// Use saved card for subsequent payments
{
  paymentMethod: {
    type: 'payment_method_id',
    paymentMethodId: savedCard.id,
    cvc: '123'  // Still collect CVC
  }
}
```

### Include Billing Address

Billing address verification improves approval rates:

```javascript theme={null}
{
  billingAddress: {
    address: 'Av. Reforma 123',
    city: 'Mexico City',
    state: 'CDMX',
    postalCode: '01000',
    country: 'MX'
  }
}
```

### Consistent Customer Data

Use the same customer information across all payments:

```javascript theme={null}
// ✅ Good - Consistent data
{
  customer: {
    externalId: 'user_123',  // Always the same
    email: 'maria@example.com',
    firstName: 'María',
    lastName: 'González'
  }
}

// ❌ Bad - Changing data
{
  customer: {
    email: 'maria.gonzalez@example.com',  // Different email
    firstName: 'Maria',  // Different spelling
    // ...
  }
}
```

## Idempotency

Always use `externalId` to prevent duplicate charges:

```javascript theme={null}
// ✅ Good - Use your order/transaction ID
{
  externalId: `order_${orderId}`,
  amount: 10000,
  // ...
}

// ❌ Bad - Random or missing ID
{
  amount: 10000,
  // Missing externalId
}
```

### Safe Retries

With idempotency, you can safely retry failed requests:

```javascript theme={null}
async function createPaymentSafely(orderData) {
  const paymentData = {
    externalId: `order_${orderData.id}`,
    amount: orderData.total,
    currency: 'MXN',
    // ...
  };

  // If this fails and you retry, the same externalId
  // prevents duplicate charges
  return await createPaymentWithRetry(paymentData);
}
```

## Monitor Payment Status

Check payment status when needed using the GET endpoint. For production systems, consider implementing efficient status checking patterns to minimize unnecessary API calls.

## Error Handling

### Show User-Friendly Messages

```javascript theme={null}
function getCustomerMessage(error) {
  const friendlyMessages = {
    'DECLINED': 'Payment couldn\'t be processed. Please try a different card.',
    'EXPIRED_CARD': 'This card has expired. Please use a different card.',
    'INVALID_CARD': 'Unable to process this card. Please check your details.',
    'INSUFFICIENT_FUNDS': 'Payment couldn\'t be completed. Please try another payment method.',
    'AUTHENTICATION_FAILED': 'We couldn\'t verify your identity. Please try again.'
  };

  return friendlyMessages[error.code] ||
         'Unable to process payment. Please try again or contact support.';
}
```

### Implement Retry Logic

```javascript theme={null}
async function makePaymentWithRetry(data, maxRetries = 3) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      return await createPayment(data);
    } catch (error) {
      // Only retry server errors
      if (error.status >= 500 && attempt < maxRetries - 1) {
        const delay = Math.pow(2, attempt) * 1000;
        await sleep(delay);
        continue;
      }
      throw error;
    }
  }
}
```

## Performance Optimization

### Cache Customer Data

```javascript theme={null}
const customerCache = new Map();

async function getCustomer(customerId) {
  // Check cache first
  if (customerCache.has(customerId)) {
    return customerCache.get(customerId);
  }

  // Fetch from API
  const customer = await cheqpay.customers.get(customerId);

  // Cache for 5 minutes
  customerCache.set(customerId, customer);
  setTimeout(() => customerCache.delete(customerId), 5 * 60 * 1000);

  return customer;
}
```

### Process Refunds Asynchronously

```javascript theme={null}
// ✅ Good - Process async
app.post('/v2/orders/:id/refund', async (req, res) => {
  const order = await db.orders.findById(req.params.id);

  // Queue refund for background processing
  await queue.add('process-refund', {
    orderId: order.id,
    paymentOrderId: order.paymentOrderId,
    amount: req.body.amount
  });

  res.json({ message: 'Refund initiated' });
});

// ❌ Bad - Blocks response
app.post('/v2/orders/:id/refund', async (req, res) => {
  const order = await db.orders.findById(req.params.id);

  // This blocks the response
  const refund = await cheqpay.refunds.create({
    paymentOrderId: order.paymentOrderId,
    amount: req.body.amount
  });

  res.json({ refund });
});
```

## Logging and Monitoring

### Log Important Events

```javascript theme={null}
function logPaymentEvent(event, data) {
  logger.info(event, {
    timestamp: new Date().toISOString(),
    externalId: data.externalId,
    amount: data.amount,
    status: data.status,
    customerId: data.customerId,
    // DON'T log: card numbers, CVCs, full API keys
  });
}

// Log payment lifecycle
logPaymentEvent('payment.initiated', paymentData);
logPaymentEvent('payment.completed', paymentResponse);
logPaymentEvent('order.fulfilled', { orderId, paymentId });
```

### Monitor Key Metrics

```javascript theme={null}
const metrics = {
  paymentsCreated: 0,
  paymentsSucceeded: 0,
  paymentsFailed: 0,
  averageAmount: 0,
  averageProcessingTime: 0
};

function trackPayment(payment, duration) {
  metrics.paymentsCreated++;

  if (payment.status === 'COMPLETED') {
    metrics.paymentsSucceeded++;
  } else if (payment.status === 'FAILED') {
    metrics.paymentsFailed++;
  }

  metrics.averageAmount =
    (metrics.averageAmount + payment.amount) / 2;

  metrics.averageProcessingTime =
    (metrics.averageProcessingTime + duration) / 2;
}
```

### Set Up Alerts

```javascript theme={null}
function checkHealthMetrics() {
  const successRate = metrics.paymentsSucceeded / metrics.paymentsCreated;

  if (successRate < 0.85) {  // Below 85% success
    sendAlert({
      severity: 'high',
      title: 'Low payment success rate',
      message: `Success rate: ${(successRate * 100).toFixed(2)}%`
    });
  }
}

// Check every 5 minutes
setInterval(checkHealthMetrics, 5 * 60 * 1000);
```

## Integration Checklist

Before going live, ensure you have:

<AccordionGroup>
  <Accordion title="Security" icon="shield-check">
    * ✅ API keys stored in environment variables
    * ✅ HTTPS used on all payment pages
    * ✅ No card data logged or stored
    * ✅ API key rotation schedule
  </Accordion>

  <Accordion title="Payment Processing" icon="credit-card">
    * ✅ Card payments working
    * ✅ 3D Secure authentication implemented
    * ✅ Device information included
    * ✅ Billing address collected
    * ✅ Payment methods saved for returning customers
  </Accordion>

  <Accordion title="Error Handling" icon="triangle-exclamation">
    * ✅ Customer-friendly error messages
    * ✅ Retry logic for temporary failures
    * ✅ Declined payment handling
    * ✅ Client-side validation
    * ✅ Error logging and monitoring
  </Accordion>

  <Accordion title="Customer Experience" icon="user">
    * ✅ Clear payment instructions
    * ✅ Order status updates
    * ✅ Email confirmations
    * ✅ Refund policy communicated
    * ✅ Support contact information
  </Accordion>

  <Accordion title="Operations" icon="gears">
    * ✅ Comprehensive logging
    * ✅ Metrics and monitoring
    * ✅ Alert thresholds set
    * ✅ Refund workflow tested
    * ✅ Support team trained
  </Accordion>
</AccordionGroup>

## Code Organization

### Separation of Concerns

```javascript theme={null}
// ✅ Good - Organized structure
// services/payment.service.js
class PaymentService {
  async createPayment(orderData) {
    const paymentData = this.buildPaymentRequest(orderData);
    const payment = await cheqpay.payments.create(paymentData);
    await this.recordPayment(payment);
    return payment;
  }
}

// controllers/payment.controller.js
class PaymentController {
  async processPayment(req, res) {
    const payment = await paymentService.createPayment(req.body);
    res.json({ payment });
  }
}

// handlers/payment.handler.js
class PaymentHandler {
  async handlePaymentCompleted(payment) {
    await orderService.fulfill(payment.externalId);
    await notificationService.sendConfirmation(payment.customerId);
  }
}
```

### Configuration Management

```javascript theme={null}
// config/cheqpay.config.js
const config = {
  sandbox: {
    baseUrl: 'https://api.sandbox.cheqpay.mx/pos',
    apiKey: process.env.CHEQPAY_SANDBOX_KEY
  },
  production: {
    baseUrl: 'https://prod.cheqpay.mx/pos',
    apiKey: process.env.CHEQPAY_PRODUCTION_KEY
  }
};

const env = process.env.NODE_ENV || 'sandbox';
module.exports = config[env];
```

## Testing

### Test Coverage

```javascript theme={null}
describe('Payment Service', () => {
  it('should create payment with valid data', async () => {
    const payment = await paymentService.createPayment(validData);
    expect(payment.status).toBe('COMPLETED');
  });

  it('should handle declined payments', async () => {
    await expect(
      paymentService.createPayment(declinedCardData)
    ).rejects.toThrow('DECLINED');
  });

  it('should prevent duplicate charges', async () => {
    const payment1 = await paymentService.createPayment(data);
    const payment2 = await paymentService.createPayment(data);
    expect(payment1.id).toBe(payment2.id);
  });
});
```

## Next Steps

<CardGroup cols={2}>
  <Card title="Testing Guide" icon="flask" href="/guides/testing">
    Test your integration thoroughly
  </Card>

  <Card title="Error Handling" icon="triangle-exclamation" href="/guides/error-handling">
    Handle errors gracefully
  </Card>

  <Card title="Going Live" icon="rocket" href="/guides/going-live">
    Production readiness checklist
  </Card>

  <Card title="API Reference" icon="book" href="/api-reference/introduction">
    Complete API documentation
  </Card>
</CardGroup>
