# Standard Checkout

Redirect customers to a hosted checkout page on ZevPay. Create a session on your server with your secret key, then redirect the customer to the checkout URL.

## How it works

1. Your server creates a checkout session via the API (using your secret key)
2. You redirect the customer to the returned `checkout_url`
3. Customer selects a payment method and completes payment on the ZevPay checkout page
4. Customer is redirected back to your `callback_url` with the payment status

## Integration

### Step 1: Create a session (server-side)

::: code-group

```js [Node.js]
const response = await fetch('https://api.zevpaycheckout.com/v1/checkout/session/initialize', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'x-api-key': 'sk_test_your_secret_key',
  },
  body: JSON.stringify({
    amount: 500000, // ₦5,000 in kobo
    currency: 'NGN',
    email: 'customer@example.com',
    customer_name: 'John Doe', // optional
    callback_url: 'https://yoursite.com/payment/callback',
    reference: 'order_12345', // optional
    metadata: { orderId: '12345' }, // optional
  }),
});

const { data } = await response.json();
// data.checkout_url → "https://secure.zevpaycheckout.com/{sessionId}"
// data.session_id  → save this for verification
```

```python [Python]
import requests

response = requests.post(
    "https://api.zevpaycheckout.com/v1/checkout/session/initialize",
    headers={
        "Content-Type": "application/json",
        "x-api-key": "sk_test_your_secret_key",
    },
    json={
        "amount": 500000,
        "currency": "NGN",
        "email": "customer@example.com",
        "customer_name": "John Doe",
        "callback_url": "https://yoursite.com/payment/callback",
        "reference": "order_12345",
    },
)

data = response.json()["data"]
checkout_url = data["checkout_url"]
session_id = data["session_id"]
```

```php [PHP]
$ch = curl_init('https://api.zevpaycheckout.com/v1/checkout/session/initialize');
curl_setopt_array($ch, [
  CURLOPT_POST => true,
  CURLOPT_HTTPHEADER => [
    'Content-Type: application/json',
    'x-api-key: sk_test_your_secret_key',
  ],
  CURLOPT_POSTFIELDS => json_encode([
    'amount' => 500000,
    'currency' => 'NGN',
    'email' => 'customer@example.com',
    'customer_name' => 'John Doe',
    'callback_url' => 'https://yoursite.com/payment/callback',
    'reference' => 'order_12345',
  ]),
  CURLOPT_RETURNTRANSFER => true,
]);

$response = json_decode(curl_exec($ch), true);
$checkoutUrl = $response['data']['checkout_url'];
$sessionId = $response['data']['session_id'];
```

:::

### Step 2: Redirect the customer

Redirect the customer to the `checkout_url` returned from the initialization call:

```js
// Browser redirect
window.location.href = data.checkout_url;
```

Or link directly:

```html
<a href="https://secure.zevpaycheckout.com/ecc48011-e36e-4741-9b99-657f4a1ee86e">
  Complete Payment
</a>
```

### Step 3: Handle the callback

After the customer completes (or cancels) payment, they are redirected to your `callback_url` with query parameters:

```
https://yoursite.com/payment/callback?status=success&reference=ZVP-CKO-S-abc123
```

**Callback query parameters:**

| Parameter | Values | Description |
|-----------|--------|-------------|
| `status` | `success` | Payment completed |
| `status` | `cancelled` | Customer cancelled/closed checkout |
| `status` | `expired` | Session expired |
| `reference` | string | Transaction reference (only on `success`) |

### Step 4: Verify the payment (server-side)

::: warning Always verify
Never trust the callback query parameters alone. Always verify payment on your server.
:::

```js
// Your callback route handler
app.get('/payment/callback', async (req, res) => {
  const { status, reference } = req.query;

  if (status === 'success') {
    // Verify with ZevPay
    const verification = await fetch(
      `https://api.zevpaycheckout.com/v1/checkout/session/${sessionId}/verify`,
      {
        headers: { 'x-api-key': 'sk_test_your_secret_key' },
      }
    );

    const { data } = await verification.json();

    if (data.status === 'completed') {
      // Payment confirmed — fulfill the order
      await fulfillOrder(data.reference, data.amount);
      res.redirect('/order/success');
    } else {
      res.redirect('/order/pending');
    }
  } else if (status === 'cancelled') {
    res.redirect('/checkout');
  } else if (status === 'expired') {
    res.redirect('/checkout?error=expired');
  }
});
```

## Initialization parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `amount` | integer | Yes | Amount in kobo |
| `currency` | string | Yes | Currency code (`"NGN"`) |
| `email` | string | Yes | Customer's email address |
| `callback_url` | string | Yes | URL to redirect after payment |
| `reference` | string | No | Your unique transaction reference |
| `customer_name` | string | No | Customer's name — displayed in the dashboard instead of "Anonymous" |
| `metadata` | object | No | Custom data attached to the session |
| `payment_methods` | string[] | No | Enabled methods: `["bank_transfer", "payid"]` |

## Checkout page states

| State | Description |
|-------|-------------|
| Loading | Fetching session details |
| Ready | Payment methods displayed, awaiting selection |
| Expired | Session has timed out, option to return to merchant |
| Not Found | Invalid session ID |
| Error | API or network error |

## Full example (Express.js)

```js
const express = require('express');
const app = express();

// Create checkout session
app.post('/create-checkout', async (req, res) => {
  const response = await fetch('https://api.zevpaycheckout.com/v1/checkout/session/initialize', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'x-api-key': process.env.ZEVPAY_SECRET_KEY,
    },
    body: JSON.stringify({
      amount: req.body.amount * 100, // Convert Naira to kobo
      currency: 'NGN',
      email: req.body.email,
      callback_url: `${req.protocol}://${req.get('host')}/payment/callback`,
      reference: `order_${Date.now()}`,
      metadata: { userId: req.user.id },
    }),
  });

  const { data } = await response.json();

  // Save session_id for later verification
  await saveSessionId(req.user.id, data.session_id);

  // Redirect to checkout
  res.redirect(data.checkout_url);
});

// Handle callback
app.get('/payment/callback', async (req, res) => {
  const { status, reference } = req.query;

  if (status !== 'success') {
    return res.redirect('/checkout?error=' + status);
  }

  // Verify payment
  const sessionId = await getSessionIdForUser(req.user.id);
  const verification = await fetch(
    `https://api.zevpaycheckout.com/v1/checkout/session/${sessionId}/verify`,
    { headers: { 'x-api-key': process.env.ZEVPAY_SECRET_KEY } }
  );

  const { data } = await verification.json();

  if (data.status === 'completed') {
    await fulfillOrder(data);
    res.redirect('/order/success');
  } else {
    res.redirect('/order/pending');
  }
});

app.listen(3000);
```

## Compared to Inline Checkout

| Aspect | Standard | Inline |
|--------|----------|--------|
| Session creation | Server-side (secret key) | Client-side (public key) |
| User experience | Full-page redirect | Modal overlay |
| Backend required | Yes | No |
| Callback method | URL redirect | JavaScript function |
| Best for | E-commerce, invoices | Quick checkouts, SPAs |

## Resources

- [API Reference](/api/overview) — Full endpoint documentation
- [Initialize Session](/api/initialize-session) — Session creation endpoint
- [Verify Session](/api/verify-session) — Verification endpoint
- [Inline Checkout](/sdks/inline) — Modal-based alternative
- [Webhook Events](/webhooks/events) — All event types
