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

# Security Best Practices

> Keep your integration secure and protect your customers

Keep your integration secure and protect your customers.

## API Key Management

You have two API keys. Treat them like passwords.

| Key                  | Header            | Purpose                                               |
| :------------------- | :---------------- | :---------------------------------------------------- |
| **Vault API Key**    | `X-API-KEY`       | Authenticates with Ozura's secure card storage system |
| **Merchant API Key** | `X-OZURA-API-KEY` | Identifies your merchant account                      |

<Note>
  **Why two keys?** The Vault API Key connects to the secure card tokenization system. The Merchant API Key ties transactions to your specific merchant account. Both are required for security.

  **Where to find them:** Your Vault API Key lives in Developers → Vault Key. Your Merchant API Key is in Developers → API Keys.
</Note>

<Info>
  **What about the Vault Pub Key (`X-Pub-Key`)?** If you've used [OzElements](/sdks/elements/overview), you may be familiar with the Vault Pub Key required for direct tokenization. With Checkout, you **do not** need to provide or manage this key — Checkout handles tokenization and pub key authentication internally. Your integration only requires the two keys listed above.
</Info>

### Do's and Don'ts

| Do                                         | Don't                          |
| :----------------------------------------- | :----------------------------- |
| Store keys in environment variables        | Hardcode keys in your code     |
| Rotate keys if compromised                 | Commit keys to version control |
| Restrict key access to needed team members | Share keys via email or chat   |

## Server-Side Only

<Warning>
  **Never expose your API keys to the browser.**
</Warning>

Create sessions from your server, not from client-side JavaScript:

```javascript theme={null}
// ✅ CORRECT: Server-side (Node.js, Next.js API route, etc.)
app.post('/create-checkout', async (req, res) => {
  const response = await fetch('https://checkout.ozura.com/api/sessions/create', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-API-KEY': process.env.VAULT_API_KEY,        // Server env var
      'X-OZURA-API-KEY': process.env.MERCHANT_API_KEY // Server env var
    },
    body: JSON.stringify({
      merchantId: process.env.MERCHANT_ID,
      amount: req.body.amount,
      // ...
    })
  });
  
  const data = await response.json();
  res.json({ checkoutUrl: data.data.checkoutUrl });
});
```

```javascript theme={null}
// ❌ WRONG: Client-side (browser JavaScript)
// API keys would be visible to anyone viewing page source!
fetch('https://checkout.ozura.com/api/sessions/create', {
  headers: {
    'X-API-KEY': 'exposed_key_123',  // NEVER DO THIS
  }
});
```

## Verify Payments

**Always verify payments server-side before fulfilling orders.**

A malicious user could attempt to visit your success URL directly. Always check the session status:

```javascript theme={null}
// On your success page handler
app.get('/success', async (req, res) => {
  const { sessionId } = req.query;
  
  // Check session status
  // No headers required - session ID acts as access token
  const response = await fetch(
    `https://checkout.ozura.com/api/sessions/${sessionId}`
  );
  
  const data = await response.json();
  
  if (data.data.session.status === 'completed') {
    // ✅ Payment verified by Ozura
    // ⚠️ YOUR RESPONSIBILITY: Implement your fulfillment logic
    await updateOrderStatus(sessionId, 'paid'); // YOUR implementation
  } else {
    // Not completed - don't fulfill
    res.redirect('/payment-issue');
  }
});
```

## Session Security

### Time Limits

Sessions expire after 30 minutes. This provides a secure window for payment while giving customers adequate time to complete checkout.

### One-Time Use

Each session can only be completed once. After payment:

* The session status becomes `completed`
* Back button won't work (session is completed — single-use)
* The checkout page shows "already paid"

## Use HTTPS in Production

All API requests to Ozura are made over HTTPS automatically.

Your redirect URLs (`successUrl`, `cancelUrl`, `errorUrl`) should use HTTPS in production:

```json theme={null}
{
  "successUrl": "https://mysite.com/success",
  "cancelUrl": "https://mysite.com/cancel",
  "errorUrl": "https://mysite.com/error"
}
```

<Note>
  **Local development:** You can use `http://localhost:3000` for testing. HTTPS is only required for production URLs.
</Note>

## PCI Compliance

Ozura Checkout is designed for PCI compliance:

* **Card data never touches your servers** – Customers enter card details directly on our secure checkout page
* **Tokenization** – Card data is tokenized before processing
* **Encryption** – All data is encrypted in transit and at rest
* **Vault API Key stays on your server** – When you create a session, Checkout internally mints a short-lived, session-bound tokenization credential. The checkout page uses this credential to tokenize card data in the browser. Your Vault API Key is never sent to the customer's browser — it is only used in your server-to-server session creation call.

By using Ozura Checkout (hosted page), you significantly reduce your PCI compliance scope.

## Refunds

Refunds are handled through the OzuraPay API, not through the Checkout widget.
