Inline checkout
A payment in a popup on your own page. One script, your public key, no redirect.
A payment started on the merchant's own page with their public key, shown in a Paymonetra popup over that page instead of a redirect. Bank transfer only for now. Beta: switched on per business, so ask us to turn it on for yours.
#Quick start
<script src="https://pay.paymonetra.com/inline.js"></script>
<button id="pay">Pay with Paymonetra</button>
<script>
document.getElementById("pay").onclick = function () {
MonetraPay.pay({
key: "pk_live_...", // your public key, never the sk_ one
amount: 5000, // naira
reference: "ORDER-1042", // your order id; one per attempt
email: "ada@example.com", // optional; asked in the popup if missing
name: "Ada Obi", // optional; asked with the email
description: "Wallet top-up", // optional, shown under the amount
onSuccess: function (tx) { /* ask YOUR server to verify tx.reference */ },
onClose: function (tx) { /* tx.status says how it ended */ },
onError: function (err) { /* err.code, err.message, err.field */ }
});
};
</script>MonetraPay.pay() also returns a Promise that resolves with the same tx as onClose, and rejects only when the call itself is wrong (no key, a secret key, no amount, a window already open). MonetraPay.close() closes it.
The public key is visible to anyone, so a payer can start a payment for any amount and a browser can be told anything. Before you credit, ship or unlock, your server must confirm with your secret key:
GET https://paymonetra.com/api/gateway/v1/payments/{tx.reference}
Authorization: Bearer sk_live_...and check status is paid and amount is what you charged, or act on the collection.success webhook, which fires for inline payments exactly as for any checkout. onSuccess is for updating the screen.
#Callbacks
| callback | when | argument |
|---|---|---|
onLoad | the popup is showing the payment | tx |
onSuccess | the payment arrived (once) | tx with status: "paid" |
onClose | the popup closed, for any reason | tx |
onError | the payment could not start | { code, message, field } |
tx is { reference, status, amount, merchant_reference, mode }. reference is ours (the same one GET /payments/{reference} and the webhook use); merchant_reference is the reference you passed.
tx.status on close:
| status | meaning |
|---|---|
paid | received in full |
pending | the payer says they already sent it; wait for the webhook |
cancelled | the payer cancelled before paying (a late transfer still credits you) |
expired | the 20 minutes ran out |
underpaid / overpaid | arrived, but not the exact amount |
dismissed | closed before the payment window loaded |
failed | it never started; onError already said why |
#References
Pass your own reference (letters, numbers, . _ : -, up to 80). The same reference returns the same payment, so a double click or a page reload opens the payment already in progress rather than a second one, and its amount cannot be changed by calling again. Use a new reference for each new attempt (after a payment expires or is cancelled). A reference already used by a payment your server created cannot be reopened in the popup (invalid_request, field: reference).
#Which sites
- Live keys start payments only from the sites registered for the business, over https, and only those sites can show the popup. Exact hosts:
example.comdoes not coverpay.example.com. - Test keys work from any https page and from
localhost, once the business is switched on. The popup then shows test buttons that complete a payment and fire your test webhook. Try it athttps://paymonetra.com/inline-demo.htmlwith apk_test_key.
#Errors
| code | why |
|---|---|
invalid_key | no key, or a secret key passed (rotate it) |
unauthorized | the public key is unknown or revoked |
not_enabled | inline checkout is not switched on for the business |
origin_not_allowed | a live key on a site that is not registered, or a test key on a plain http page |
not_verified | live key, business not verified |
invalid_request | field names it: amount (at least NGN 100), email, reference |
amount_over_limit, too_many_pending | as for POST /payments |
rate_limited | more than 30 starts in 10 minutes from one address, or 600 an hour on one key |
network_error | the payer is offline |
#Under the hood
Nothing here needs calling directly. It is here for a page with its own CSP.
The script calls POST https://pay.paymonetra.com/api/gateway/v1/inline/payments with the public key and the fields above, then frames https://pay.paymonetra.com/{reference}?embed=1&o={your origin}, which sends Content-Security-Policy: frame-ancestors {your origin} only when that origin is allowed. The two talk with postMessage, checked by origin both ways. Nothing here needs calling directly.