---
title: Use Cases
description: Practical recipes that map real business needs to the right ZevPay Checkout integration, with links into the detailed reference for each step.
---

# Use Cases

This page maps common business needs to a concrete integration path. Each recipe is short on purpose: it tells you which pieces to use and links straight to the reference for the details. If you are brand new, start with the [Quick Start](/guide/quick-start), then come back here to find the shape that fits what you are building.

A quick orientation on the three checkout styles, since most recipes pick one:

| Style | You host | We host | Best for |
|-------|----------|---------|----------|
| [Inline](/checkout/inline) | Your page + our SDK | The payment widget | Keeping customers on your site, no backend required |
| [Standard](/checkout/standard) | Nothing | The whole payment page | Fastest server-to-server integration |
| [Invoice](/checkout/invoice) | Nothing | A shareable, itemised payment link that becomes a receipt | Billing a specific customer for a specific amount |

Everything below settles the same way and reports back through the same [webhooks](/webhooks/events). You learn one settlement and reconciliation model once, then reuse it across every recipe.

## Accept a one-time payment on your site

**When:** a customer checks out an order and you want them to pay now.

1. On your server, call [initialize](/api/initialize-session) with the `amount` (in kobo), the customer `email`, a `callback_url`, and your own `reference`.
2. Redirect the customer to the returned `checkout_url` ([Standard Checkout](/checkout/standard)).
3. When they finish, we redirect them back to your `callback_url` with a `status` and your `merchant_reference`.
4. **Confirm from your server** with [verify](/api/verify-session) before you fulfil. The callback alone is not proof of payment.
5. Fulfil once, keyed on the reference, when [`charge.success`](/webhooks/events#charge-success) arrives (or verify returns `completed`).

Prefer to keep the customer on your own page? Use [Inline Checkout](/checkout/inline) instead: the flow is the same, but the widget renders in your page.

## Accept payments without a backend

**When:** you have a static site or a frontend-only app and no server to hold a secret key.

Use [Inline Checkout](/checkout/inline) with your **public key** (`pk_*`). The SDK calls [initialize](/api/initialize-session) directly from the browser, and you lock the key to your domains with [domain whitelisting](/guide/authentication#domain-whitelisting). Never ship a secret key to the browser. Because you have no server to receive webhooks, reconcile by having your backend (even a serverless function) call [verify](/api/verify-session), or add a webhook endpoint when you are ready.

## Send a payable invoice

**When:** you are billing a known customer for a known amount, and you want a link you can email or paste into a chat.

1. Create the invoice with line items, optional tax, and a due date ([Invoice Checkout](/checkout/invoice)).
2. Share the returned payment link. The hosted page collects the payment and becomes a receipt once paid.
3. Fulfil on the [`invoice.paid`](/webhooks/events) webhook. Partial payments (if enabled) fire [`invoice.payment_received`](/webhooks/events) with `is_partial` and `is_fully_paid` flags, and now carry the invoice's `line_items` and `metadata` so you can reconcile without a follow-up fetch.

## Collect a balance settled outside ZevPay

**When:** part of an invoice was paid somewhere we cannot see (cash, a transfer straight to your own bank, or platform credit), and you need to collect the rest online.

An invoice only ever records money that reached you through us, so a part-settled invoice cannot represent the remainder. The same secret key drives standard checkout, so:

1. Cancel the invoice so its old full-amount link stops working.
2. Open a [Standard Checkout](/checkout/standard) session for exactly the outstanding amount.
3. Reconcile the `charge.success` back to your invoice using `merchant_reference` and `metadata`.

This is the exact pattern our own products use. See the full walkthrough in [Collecting a balance settled outside ZevPay](/checkout/invoice#collecting-a-balance-settled-outside-zevpay).

## Bill on a schedule

**When:** subscriptions, memberships, or any recurring charge.

We do not store cards for off-session charging, so recurring billing is customer-initiated each cycle:

1. At the start of each cycle, create a fresh [invoice](/checkout/invoice) (or [session](/checkout/standard)) for that period, with a `reference` unique to the cycle, for example `SUB-{customer}-{yyyy-mm}`.
2. Email the invoice link, or if the customer is on your site, redirect them through checkout.
3. Advance the subscription when [`invoice.paid`](/webhooks/events) (or [`charge.success`](/webhooks/events)) arrives for that cycle's reference.

A per-cycle reference keeps each period idempotent: retrying the same cycle never creates a second charge.

## Reconcile every payment reliably

**When:** always. This is the backbone under every recipe above.

- **Webhooks are the source of truth.** Fulfil on [`charge.success`](/webhooks/events#charge-success) / [`invoice.paid`](/webhooks/events), not on the browser redirect.
- **Verify signatures** on every webhook against the raw request body, see [Verifying Signatures](/webhooks/signatures).
- **Be idempotent.** Key your fulfilment on the reference. We retry failed deliveries with backoff, so your endpoint will occasionally see the same event twice.
- **Confirm server-side** with [verify](/api/verify-session) before fulfilling if you are acting on a redirect, and as a fallback if a webhook is delayed.
- **Every response is wrapped** in `{ "success": true, "data": {...} }`. Read your fields off `data`.

## Map payments back to your orders

**When:** you need each payment to point at one of your records.

- Set your own `reference` (your order id) on [initialize](/api/initialize-session) or the invoice. We echo it back as `merchant_reference` in the webhook and on the callback redirect (on `success`, `cancelled`, and `expired`).
- Attach anything else you need in `metadata`. We return it verbatim in the webhook, in [verify](/api/verify-session), and on the transaction in your dashboard.

Between `merchant_reference` and `metadata`, you never have to store our identifiers to find your way back to an order.

## Testing safely against live

We do not offer a full sandbox yet. Most integrations are built against production, so treat testing as a first-class part of your rollout:

- **Use [test keys](/guide/test-mode)** (`sk_test_` / `pk_test_`) where available. They hit the same endpoints and exercise the same code paths as live keys.
- **Start with the smallest real amount** on live keys for a true end-to-end check, then refund out of band.
- **Build reconciliation before you go live.** A working webhook handler with signature verification and idempotency is what makes a first real payment safe.
- **Never fulfil on the redirect alone.** Always confirm with a webhook or [verify](/api/verify-session).
- **Roll out behind a flag** so you can enable live checkout for a small cohort first.

## Security checklist

A short list worth revisiting before launch:

- Secret keys (`sk_*`) live only on your server. Public keys (`pk_*`) are the only keys that belong in a browser, and only with domain whitelisting.
- Verify every webhook signature against the raw bytes.
- Treat callback query params as untrusted hints, never as proof of payment.
- Store the reference you care about at create time so a delayed or replayed event still maps home.

## Related

- [Quick Start](/guide/quick-start)
- [Standard Checkout](/checkout/standard) · [Inline Checkout](/checkout/inline) · [Invoice Checkout](/checkout/invoice)
- [Webhook Events](/webhooks/events) · [Verifying Signatures](/webhooks/signatures)
- [Authentication](/guide/authentication) · [Test Mode](/guide/test-mode)
