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

# Step 4: Best Practices

> Essential security and error handling for production

## Security

### 1. Protect Your API Keys

<Warning>
  **Never** expose API keys in frontend code or public repositories!
</Warning>

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

<CodeGroup>
  ```javascript Node.js theme={null}
  const apiKey = process.env.ONECLICK_API_KEY;
  ```

  ```python Python theme={null}
  import os
  api_key = os.getenv('ONECLICK_API_KEY')
  ```

  ```php PHP theme={null}
  <?php
  $apiKey = getenv('ONECLICK_API_KEY');
  ?>
  ```
</CodeGroup>

### 2. Backend Only

Always call Navio API from your backend, never from frontend:

```javascript theme={null}
// ❌ WRONG - Frontend
fetch("https://api.oneclickdz.com/v3/ocpay/createLink", {
  headers: { "X-Access-Token": "YOUR_KEY" }, // Exposed to users!
});

// ✅ CORRECT - Backend
// Frontend calls YOUR API, your backend calls OneClick
fetch("/api/create-payment", { method: "POST" });
```

### 3. Validate Inputs

```javascript theme={null}
function validateOrderData(data) {
  if (!Number.isInteger(data.amount)) {
    throw new Error("Amount must be integer");
  }

  if (data.amount < 500 || data.amount > 500000) {
    throw new Error("Amount must be between 500 and 500,000 DZD");
  }

  if (!data.title || data.title.trim().length === 0) {
    throw new Error("Title is required");
  }

  return data;
}
```

### 4. Verify Before Fulfillment

<Warning>
  Always verify payment status on backend before fulfilling orders!
</Warning>

```javascript theme={null}
async function fulfillOrder(orderId) {
  const order = await db.orders.findOne({ id: orderId });

  // CRITICAL: Verify payment status
  const response = await fetch(
    `https://api.oneclickdz.com/v3/ocpay/checkPayment/${order.paymentRef}`,
    {
      headers: { "X-Access-Token": process.env.ONECLICK_API_KEY },
    }
  );

  const data = await response.json();

  if (data.data.status !== "CONFIRMED") {
    throw new Error("Payment not confirmed");
  }

  // Safe to fulfill
  await processOrder(order);
}
```

## Error Handling

### Handle API Errors

<CodeGroup>
  ```javascript Node.js theme={null}
  async function createPaymentSafe(orderId, amount, title) {
    try {
      const response = await fetch(
        "https://api.oneclickdz.com/v3/ocpay/createLink",
        {
          method: "POST",
          headers: {
            "Content-Type": "application/json",
            "X-Access-Token": process.env.ONECLICK_API_KEY,
          },
          body: JSON.stringify({
            productInfo: { title, amount },
          }),
        }
      );

      if (!response.ok) {
        if (response.status === 403) {
          throw new Error("Merchant not validated");
        }
        throw new Error("API request failed");
      }

      const data = await response.json();

      if (!data.success) {
        throw new Error(data.error?.message || "Unknown error");
      }

      return data.data;
    } catch (error) {
      console.error("Payment creation failed:", error);
      throw error;
    }
  }
  ```

  ```python Python theme={null}
  def create_payment_safe(order_id, amount, title):
      try:
          response = requests.post(
              'https://api.oneclickdz.com/v3/ocpay/createLink',
              headers={
                  'Content-Type': 'application/json',
                  'X-Access-Token': os.getenv('ONECLICK_API_KEY')
              },
              json={
                  'productInfo': {'title': title, 'amount': amount}
              }
          )

          if response.status_code == 403:
              raise Exception('Merchant not validated')

          response.raise_for_status()

          data = response.json()

          if not data['success']:
              raise Exception(data['error'].get('message', 'Unknown error'))

          return data['data']

      except Exception as e:
          print(f'Payment creation failed: {e}')
          raise
  ```

  ```php PHP theme={null}
  <?php
  function createPaymentSafe($orderId, $amount, $title) {
      try {
          $ch = curl_init('https://api.oneclickdz.com/v3/ocpay/createLink');
          curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
          curl_setopt($ch, CURLOPT_POST, true);
          curl_setopt($ch, CURLOPT_HTTPHEADER, [
              'Content-Type: application/json',
              'X-Access-Token: ' . getenv('ONECLICK_API_KEY')
          ]);
          curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
              'productInfo' => ['title' => $title, 'amount' => $amount]
          ]));

          $response = curl_exec($ch);
          $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);

          if ($httpCode === 403) {
              throw new Exception('Merchant not validated');
          }

          if ($httpCode !== 200) {
              throw new Exception('API request failed');
          }

          $data = json_decode($response, true);

          if (!$data['success']) {
              throw new Exception($data['error']['message'] ?? 'Unknown error');
          }

          return $data['data'];

      } catch (Exception $e) {
          error_log('Payment creation failed: ' . $e->getMessage());
          throw $e;
      }
  }
  ?>
  ```
</CodeGroup>

## Testing

### Use Sandbox

Test with sandbox keys before production:

```bash theme={null}
# Development
ONECLICK_API_KEY=sk_sandbox_abc123...

# Production
ONECLICK_API_KEY=sk_live_xyz789...
```

### Test Scenarios

Test these cases:

* ✅ Successful payment
* ✅ Failed payment
* ✅ Expired payment link (20 min)
* ✅ Customer returns without paying
* ✅ API errors

## Production Checklist

**Before going live:**

### Security

* [ ] API keys in environment variables
* [ ] No frontend API calls
* [ ] Input validation implemented
* [ ] HTTPS enabled

### Functionality

* [ ] Order creation works
* [ ] Payment links generate correctly
* [ ] Status checking works
* [ ] Order fulfillment only on CONFIRMED
* [ ] Cron job running

### Testing

* [ ] Sandbox testing complete
* [ ] All edge cases tested
* [ ] Error handling verified

### Monitoring

* [ ] Error logging enabled
* [ ] Can view pending orders
* [ ] Can manually check status

## Common Mistakes

<Warning>
  **Avoid these:**

  * ❌ Hardcoding API keys
  * ❌ Calling API from frontend
  * ❌ Not saving paymentRef
  * ❌ Fulfilling without verification
  * ❌ No error handling
  * ❌ Checking too frequently (respect rate limits)
  * ❌ Not testing in sandbox first
</Warning>

## Database Best Practices

Add indexes for performance:

```sql theme={null}
-- Speed up pending order queries
CREATE INDEX idx_orders_pending
ON orders(status, payment_ref)
WHERE status = 'PENDING';

-- Speed up paymentRef lookups
CREATE INDEX idx_payment_ref ON orders(payment_ref);
```

## Logging

Log important events:

```javascript theme={null}
// Log payment creation
console.log("[Payment] Created", {
  orderId,
  paymentRef,
  amount,
  timestamp: new Date(),
});

// Log status changes
console.log("[Payment] Status changed", {
  orderId,
  oldStatus: "PENDING",
  newStatus: "PAID",
  timestamp: new Date(),
});
```

## Next Steps

<CardGroup cols={2}>
  <Card title="API Reference" icon="book" href="/en/api-reference/ocpay/create-link">
    Complete API documentation
  </Card>

  {" "}

  <Card title="Contact Support" icon="headset" href="/en/contact">
    Get help from our team
  </Card>

  {" "}

  <Card title="Dashboard" icon="gauge" href="https://enterprise.oneclickdz.com/">
    Monitor your payments
  </Card>

  <Card title="Security Guide" icon="shield" href="/en/security-best-practices">
    Advanced security practices
  </Card>
</CardGroup>

## You're Ready! 🎉

You now have everything to accept payments with Navio. Start with sandbox, test thoroughly, then go live!
