# Checkout API

> Accept card, bank transfer, bank account (GTBank), USSD and Paybeta wallet payments on Paybeta’s hosted payment page.

Create a payment with one API call, send your customer to the `checkout_url` it returns, and Paybeta handles the rest on its hosted payment page. You get a signed webhook when the payment completes, and you can check any payment's status at any time.

## How it works

1. **Create a payment** from your server: `POST /api/v1/payments` with the amount, your `reference` and the customer's email.
2. **Send the customer to `checkout_url`**: redirect them, or open the payment page in a popup with `new PaybetaPop().resumeTransaction(id)`.
3. **Get the webhook** at your `webhook_url` when the payment succeeds or fails.
4. **Verify the payment** (`GET /api/v1/payments/{reference}`) before you deliver.

## Base URL

| | |
| --- | --- |
| **Base URL** | `https://checkout.paybeta.ng` |
| **Test and live** | Same host. Each payment's `mode` (`test` or `live`) picks which. |
| **Currency** | Naira (`NGN`) only. Amounts are in naira, not kobo. |

## Authentication

Send your **live** Paybeta API key in the `P-API-KEY` header, from the Paybeta API console. The same live key creates test and live payments; your test keys don't work here.

```http
P-API-KEY: YOUR_LIVE_API_KEY
Content-Type: application/json
Accept: application/json
```

Your live **secret key** is only used to check webhook signatures. Keep both keys on your server, never in an app or browser.

## Responses

Every response has the same shape: `status`, `message` and `data`. On errors, `status` is `failed`, `message` is the first problem, and `data` is `null`.

```json
{
  "status": "successful",
  "message": "Payment created",
  "data": { "id": "GTW-K3J7Q2M9X4ZP8L1N5R6T0V2B3C4D", "status": "pending", "...": "..." }
}
```

| HTTP status | When |
| --- | --- |
| `201 Created` | A new payment was created. |
| `200 OK` | The payment you asked for, including a repeated create with the same `reference` and amount. |
| `400 Bad Request` | A field is missing or invalid. |
| `401 Unauthorized` | A bad or missing key, a test key (only your live API key works here), or an account without API access. |
| `404 Not Found` | No payment with that reference or id. |
| `409 Conflict` | The `reference` was already used for a different amount. |

## Safe retries

Your `reference` makes creating a payment safe to retry. Sending the same `reference` and amount again returns the existing payment with `200 OK` instead of creating a second one, so a request that timed out can simply be sent again. The same `reference` with a different amount is refused with `409 Conflict`.

## Payment options

Customers can pay by **card**, **bank transfer**, **bank account** (GTBank), **USSD** or **Paybeta wallet**. Paybeta and your console settings (Account Settings › Payment gateway) decide which are on. Use `channels` to offer only some of them; an option that's switched off is left out even if `channels` lists it.

## Fees and settlement

Paybeta's fee is taken from the amount by default: the customer pays `amount` and you receive `merchant_amount`. With `pass_charge: true` the fee is added on top instead: the customer pays `charge_amount` (`amount + fee`) and you receive the full `amount`. Without `pass_charge`, your console default applies.

Settlement is the next business day, into your Paybeta wallet by default, or to your approved bank account if you chose that in the console.
