# Create a payment

> Creates a payment and returns the checkout_url to send your customer to.

`POST https://api.paybeta.ng/api/v1/payments`

Authentication: `P-API-KEY` header.

Send the customer to the returned `checkout_url` (redirect), or open it as a popup with `new PaybetaPop().resumeTransaction(id)`, using the payment's `id`.

- `mode` decides whether this is a test payment (no real money) or a live one. See [Test mode](/checkout-test-mode).
- Sending the same `reference` and amount again returns the existing payment (`200 OK`), so a timed-out request is safe to retry. The same `reference` with another amount is refused (`409 Conflict`).
- Keep the `id` or your `reference` to [verify the payment](/checkout-verify-payment) later.

**More examples** (pick them in the Request menu): a payment where the customer pays the fee, a Paybeta wallet-only payment, and a live payment.

## The payment object

| Field | Description |
| --- | --- |
| `id` | Paybeta's id for the payment (`GTW-…`). |
| `reference` | Your reference. |
| `status` | `pending`, `successful`, `failed` or `expired`. |
| `amount` | The amount you asked for, in naira. |
| `fee` | Paybeta's fee. |
| `pass_charge` | Whether the fee is added on top for the customer. |
| `charge_amount` | What the customer pays: `amount`, or `amount + fee` with `pass_charge`. |
| `amount_paid` | What the customer paid, once paid; `null` before. |
| `merchant_amount` | What you receive. |
| `mode` / `livemode` | `test` or `live`. |
| `channel` | How it was paid (`card`, `transfer`, `bank`, `ussd`, `wallet`); `null` until paid. |
| `customer`, `description`, `metadata` | As you sent them. |
| `recurring`, `authorization` | Saved cards; not available yet. |
| `checkout_url` | The payment page. |
| `failure_reason` | Why the last attempt was declined, if it was. |
| `expires_at`, `paid_at`, `created_at` | Times (ISO 8601, West Africa Time, +01:00). |

## Body

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `mode` | string | yes | `test` (sandbox, no real money) or `live`. |
| `reference` | string | yes | Your unique reference for this payment. Reusing it with the same amount returns the existing payment. |
| `amount` | number | yes | In naira. Minimum ₦100. |
| `currency` | string | no | `NGN` (the only currency). |
| `customer` | object | yes | Who is paying. |
| `customer.email` | string | yes | The customer's email. |
| `customer.name` | string | no | The customer's name. |
| `customer.phone` | string | no | The customer's phone number. |
| `description` | string | no | Shown to the customer on the payment page. |
| `redirect_url` | string | no | Where the customer goes after paying (https). |
| `webhook_url` | string | no | Where the webhook goes (public https). Without it, the one saved in the API console is used. |
| `channels` | string | no | The payment options to offer: `card`, `transfer`, `bank`, `ussd`, `wallet`. Options switched off for your account are left out. |
| `pass_charge` | boolean | no | `true` adds Paybeta's fee on top for the customer, so you receive the full amount. Without it, your console default applies. |
| `metadata` | object | no | Your own data (up to 4KB), returned on the payment and its webhook. |
| `expires_in` | integer | no | Minutes until the payment page expires: 5 to 10080. Default 60. |
| `recurring` | boolean | no | `true` saves the card for later charges (card only). Not available yet. |

## Request: Request body

```json
{
  "mode": "test",
  "reference": "ORDER-{{$timestamp}}",
  "amount": 5000,
  "currency": "NGN",
  "description": "Pro plan · monthly",
  "customer": {
    "email": "ada@example.com",
    "name": "Ada Lovelace",
    "phone": "08012345678"
  },
  "redirect_url": "https://shop.example.com/thanks",
  "webhook_url": "https://shop.example.com/webhooks/paybeta",
  "channels": ["card", "transfer", "bank", "ussd", "wallet"],
  "pass_charge": false,
  "metadata": {
    "cart_id": 9
  },
  "expires_in": 60
}
```

## Request: Customer pays the fee

```json
{
  "mode": "test",
  "reference": "ORDER-{{$timestamp}}",
  "amount": 5000,
  "description": "Pro plan · monthly",
  "customer": {
    "email": "ada@example.com"
  },
  "channels": ["card", "transfer"],
  "pass_charge": true
}
```

## Request: Paybeta wallet only

```json
{
  "mode": "test",
  "reference": "ORDER-{{$timestamp}}",
  "amount": 5000,
  "currency": "NGN",
  "description": "Wallet payment test",
  "customer": {
    "email": "ada@example.com",
    "name": "Ada Lovelace",
    "phone": "08012345678"
  },
  "redirect_url": "https://shop.example.com/thanks",
  "channels": ["wallet"]
}
```

## Request: Live payment

```json
{
  "mode": "live",
  "reference": "ORDER-{{$timestamp}}",
  "amount": 5000,
  "currency": "NGN",
  "description": "Pro plan · monthly",
  "customer": {
    "email": "ada@example.com",
    "name": "Ada Lovelace",
    "phone": "08012345678"
  },
  "redirect_url": "https://shop.example.com/thanks",
  "webhook_url": "https://shop.example.com/webhooks/paybeta",
  "channels": ["card", "transfer", "bank", "ussd", "wallet"],
  "pass_charge": false,
  "metadata": {
    "cart_id": 9
  },
  "expires_in": 60
}
```

## Response 201: Created

```json
{
  "status": "successful",
  "message": "Payment created",
  "data": {
    "id": "GTW-K3J7Q2M9X4ZP8L1N5R6T0V2B3C4D",
    "reference": "ORDER-1001",
    "status": "pending",
    "amount": 5000,
    "fee": 70,
    "pass_charge": false,
    "charge_amount": 5000,
    "amount_paid": null,
    "merchant_amount": 4930,
    "mode": "test",
    "livemode": false,
    "currency": "NGN",
    "channel": null,
    "description": "Pro plan · monthly",
    "customer": {
      "email": "ada@example.com",
      "name": "Ada Lovelace",
      "phone": "08012345678"
    },
    "metadata": {
      "cart_id": 9
    },
    "recurring": false,
    "authorization": null,
    "checkout_url": "https://checkout.paybeta.ng/pay/GTW-K3J7Q2M9X4ZP8L1N5R6T0V2B3C4D",
    "provider": "squad",
    "provider_reference": null,
    "failure_reason": null,
    "expires_at": "2026-10-30T13:00:00+01:00",
    "paid_at": null,
    "created_at": "2026-10-30T12:00:00+01:00"
  }
}
```

## Response 201: Customer pays the fee

```json
{
  "status": "successful",
  "message": "Payment created",
  "data": {
    "id": "GTW-K3J7Q2M9X4ZP8L1N5R6T0V2B3C4D",
    "reference": "ORDER-1001",
    "status": "pending",
    "amount": 5000,
    "fee": 70,
    "pass_charge": true,
    "charge_amount": 5070,
    "amount_paid": null,
    "merchant_amount": 5000,
    "mode": "test",
    "livemode": false,
    "currency": "NGN",
    "channel": null,
    "description": "Pro plan · monthly",
    "customer": {
      "email": "ada@example.com",
      "name": null,
      "phone": null
    },
    "metadata": null,
    "recurring": false,
    "authorization": null,
    "checkout_url": "https://checkout.paybeta.ng/pay/GTW-K3J7Q2M9X4ZP8L1N5R6T0V2B3C4D",
    "provider": "squad",
    "provider_reference": null,
    "failure_reason": null,
    "expires_at": "2026-10-30T13:00:00+01:00",
    "paid_at": null,
    "created_at": "2026-10-30T12:00:00+01:00"
  }
}
```

## Response 200: Same reference again

```json
{
  "status": "successful",
  "message": "Payment already exists for this reference",
  "data": {
    "id": "GTW-K3J7Q2M9X4ZP8L1N5R6T0V2B3C4D",
    "reference": "ORDER-1001",
    "status": "pending",
    "amount": 5000,
    "fee": 70,
    "pass_charge": false,
    "charge_amount": 5000,
    "amount_paid": null,
    "merchant_amount": 4930,
    "mode": "test",
    "livemode": false,
    "currency": "NGN",
    "channel": null,
    "description": "Pro plan · monthly",
    "customer": {
      "email": "ada@example.com",
      "name": "Ada Lovelace",
      "phone": "08012345678"
    },
    "metadata": {
      "cart_id": 9
    },
    "recurring": false,
    "authorization": null,
    "checkout_url": "https://checkout.paybeta.ng/pay/GTW-K3J7Q2M9X4ZP8L1N5R6T0V2B3C4D",
    "provider": "squad",
    "provider_reference": null,
    "failure_reason": null,
    "expires_at": "2026-10-30T13:00:00+01:00",
    "paid_at": null,
    "created_at": "2026-10-30T12:00:00+01:00"
  }
}
```

## Response 400: Validation error

```json
{
  "status": "failed",
  "message": "The amount field is required.",
  "data": null
}
```

## Response 409: Same reference, different amount

```json
{
  "status": "failed",
  "message": "Duplicate reference: a payment with this reference already exists for a different amount.",
  "data": {
    "id": "GTW-K3J7Q2M9X4ZP8L1N5R6T0V2B3C4D",
    "reference": "ORDER-1001",
    "amount": 5000,
    "status": "pending"
  }
}
```
