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.
<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#
- A Payjar account and a store.
- A public key for the website popup.
- 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.
| Key | Starts with | Where it goes |
|---|---|---|
| Test public key | pk_test_ | Your website |
| Live public key | pk_live_ | Your website, after the store is live |
| Test secret key | sk_test_ | Your server only |
| Live secret key | sk_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.
| Price | Send this |
|---|---|
| ₦50 | 5000 |
| ₦1,000 | 100000 |
| ₦5,000 | 500000 |
| ₦2,500.50 | 250050 |
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.
{
"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"
}| Field | Meaning |
|---|---|
reference | Your order id. You choose it. |
access_code | A private code for this checkout session. |
amount | The price you asked for, in kobo. |
fee | Payjar's fee, in kobo. |
charged_amount | What the customer must transfer, in kobo. |
settled_amount | What your store receives, in kobo. |
fee_paid_by | merchant or customer. |
status | Where the payment is right now. See the status table. |
bank | The account the customer transfers to. bank.amount is the same value as charged_amount. |
expires_at | The transfer must arrive before this time. A new payment lasts 30 minutes. |
checkout_url | A hosted payment page. Open it in a browser or a mobile web view. |
script_url | The popup script. |
metadata | Your 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.
| Field | Kobo | Naira |
|---|---|---|
amount | 500000 | ₦5,000 |
fee | 5000 | ₦50 |
charged_amount | 500000 | ₦5,000 |
settled_amount | 495000 | ₦4,950 |
Customer pays the fee. You charge ₦1,000 at 1%. The customer transfers ₦1,011. You receive ₦1,000.
| Field | Kobo | Naira |
|---|---|---|
amount | 100000 | ₦1,000 |
fee | 1100 | ₦11 |
charged_amount | 101100 | ₦1,011 |
settled_amount | 100000 | ₦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#
| Status | Meaning |
|---|---|
awaiting_transfer | Payjar is waiting for the transfer. This is the normal status right after you create a payment. |
pending | The payment exists and is not paid yet. |
success | The transfer arrived. You can deliver the order. |
expired | The 30 minutes ran out before the transfer arrived. |
failed | The payment failed. |
cancelled | The 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:
{
"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:
{
"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.
Authorization: Bearer sk_test_your_secret_key
Accept: application/json
Content-Type: application/jsonYou 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.
