Skip to content
On this page

Guides

Overview

Payjar collects a bank transfer. Use a popup on your website, or create the payment from your server.

Payjar collects a payment by bank transfer. You tell Payjar the amount and the customer's email. Payjar shows a bank account. When the transfer arrives, Payjar marks the payment as paid and tells your server.

There are two ways to do this.

New here? Get started covers the account, the keys, and a first test payment.

  • Popup. Add one script to your website. One JavaScript call opens the payment window.
  • API. Your server or mobile app creates the payment, shows the account number, and checks when it is paid. Use this for iOS, Android, and Flutter.

Base URL: https://app.supa.cash. All API paths start with /supapay/v1. The popup script is https://app.supa.cash/js/payjar.js.

Browser
<script src="https://app.supa.cash/js/payjar.js"></script>
<script>
  Payjar.checkout({
    key: 'pk_test_your_public_key',
    email: '[email protected]',
    amount: 500000,
    reference: 'order_1001',
    onSuccess: function (payment) {
      console.log('Paid', payment.reference, payment.status);
    }
  });
</script>

amount: 500000 is ₦5,000. key is your public key. That call is enough to open the window. The full button, callbacks, and React setup are on Popup checkout.

What you need#

  1. A Payjar account and a store.
  2. A public key for the website popup.
  3. A secret key for your server, mobile backend, and webhooks.

No keys yet?

Register a store. The dashboard shows your test public key and test secret key.

Register
KeyStarts withWhere it goes
Test public keypk_test_Your website
Live public keypk_live_Your website, after the store is live
Test secret keysk_test_Your server only
Live secret keysk_live_Your server only

The public key is safe in a browser. The secret key stays on your server. A mobile app should call your server, and your server should call Payjar with the secret key.

Test keys and live keys are separate. A test payment never moves real money.

Amounts are in kobo#

Send the price in kobo. ₦1 is 100.

PriceSend this
₦505000
₦1,000100000
₦5,000500000
₦2,500.50250050

Payjar returns every money field in kobo too: amount, fee, charged_amount, and settled_amount.

The customer always transfers a whole number of naira. Payjar rounds the transfer up to the next naira when it needs to.

A payment, in one object#

This is the object you will see in API responses, in the popup onSuccess callback, and inside a webhook. Money fields are kobo.

JSON
{
  "id": 1042,
  "reference": "order_1001",
  "access_code": "a1b2c3d4e5f6789012345678abcdef01",
  "amount": 500000,
  "fee": 5000,
  "charged_amount": 500000,
  "settled_amount": 495000,
  "fee_paid_by": "merchant",
  "currency": "NGN",
  "status": "awaiting_transfer",
  "environment": "test",
  "customer_id": 88,
  "customer_email": "[email protected]",
  "customer_name": "Ada Lovelace",
  "customer_phone": "08031234567",
  "metadata": {
    "order_id": "1001"
  },
  "bank": {
    "bank_name": "9JAPAY MFB",
    "account_name": "SUPAPAY CHECKOUT",
    "account_number": "0123456789",
    "amount": 500000,
    "currency": "NGN"
  },
  "paid_at": null,
  "expires_at": "2026-11-03T17:07:00+01:00",
  "created_at": "2026-11-03T16:37:00+01:00",
  "checkout_url": "https://app.supa.cash/supapay/v1/checkout/frame/a1b2c3d4e5f6789012345678abcdef01",
  "script_url": "https://app.supa.cash/js/payjar.js"
}
FieldMeaning
referenceYour order id. You choose it.
access_codeA private code for this checkout session.
amountThe price you asked for, in kobo.
feePayjar's fee, in kobo.
charged_amountWhat the customer must transfer, in kobo.
settled_amountWhat your store receives, in kobo.
fee_paid_bymerchant or customer.
statusWhere the payment is right now. See the status table.
bankThe account the customer transfers to. bank.amount is the same value as charged_amount.
expires_atThe transfer must arrive before this time. A new payment lasts 30 minutes.
checkout_urlA hosted payment page. Open it in a browser or a mobile web view.
script_urlThe popup script.
metadataYour own JSON object, returned unchanged.

checkout_url and script_url are included when you create or verify one payment. They are left off the list endpoint and off webhooks.

Fees#

A new store charges a 1% fee, paid by the store. Your store can be set up with a different rate, a flat fee, a fee cap, or with the customer paying the fee. Read the numbers from the response. You do not need to calculate them yourself.

Store pays the fee. You charge ₦5,000. The customer transfers ₦5,000. The fee is ₦50. You receive ₦4,950.

FieldKoboNaira
amount500000₦5,000
fee5000₦50
charged_amount500000₦5,000
settled_amount495000₦4,950

Customer pays the fee. You charge ₦1,000 at 1%. The customer transfers ₦1,011. You receive ₦1,000.

FieldKoboNaira
amount100000₦1,000
fee1100₦11
charged_amount101100₦1,011
settled_amount100000₦1,000

The extra naira in that second example is the whole-naira rounding. The customer transfers charged_amount. Anything else will not match the payment.

Status values#

StatusMeaning
awaiting_transferPayjar is waiting for the transfer. This is the normal status right after you create a payment.
pendingThe payment exists and is not paid yet.
successThe transfer arrived. You can deliver the order.
expiredThe 30 minutes ran out before the transfer arrived.
failedThe payment failed.
cancelledThe payment was cancelled.

References#

Send your own reference, such as an order id. It can be up to 100 characters.

Use a new reference for each order. If you send a reference that is already paid, Payjar returns that paid payment and does not start a new one. If you send a reference that is still unpaid, Payjar refreshes the account number and the 30 minute timer, then returns the same reference.

If you leave reference out, Payjar creates one. It looks like auto_ followed by 12 letters and numbers.

Response envelope#

A successful Payjar response looks like this:

JSON
{
  "success": true,
  "message": "Payment initialized",
  "options": {
    "notifications_count": 0,
    "payment": {}
  }
}

Read your data from options. notifications_count is always there. You can ignore it.

A failed Payjar response looks like this:

JSON
{
  "success": false,
  "message": "Payment not found.",
  "options": {
    "notifications_count": 0
  }
}

Validation errors use a different shape and HTTP 422. See Errors.

Authentication#

Send the key on every create, verify, list, and simulate call.

HTTP
Authorization: Bearer sk_test_your_secret_key
Accept: application/json
Content-Type: application/json

You can also send the key in the x-api-key or api-key header. Prefer Authorization: Bearer.

The popup status check and the hosted payment page do not use your key. They use the access_code from the payment.