Confirming a payment
Two independent signals. Use both, and never credit on the redirect alone.
#The redirect tells you what to ask about
The payer is sent back on their own, when the payment is settled. Three seconds after the page says "Payment received" it follows redirect_url, counting down out loud, with the link still there for anyone who taps first or has scripting off.
Every other ending offers the link and moves nobody. Overpaid, underpaid and expired each show "Return to you" without a countdown: an overpayment is yours to refund and a shortfall is still owed, so the screen that explains it stays until the payer leaves it. They were offered nothing at all before 12 Sep 2026, which left a payer stranded and you without them.
The URL carries reference= in every case, so the page they land on can call GET /payments/{reference} and confirm before crediting anybody. Do not credit on the redirect alone: it says the payer came back, not that the money did, and it is sent on an overpayment and a shortfall too.
redirect_url on POST /payments is shown as a return link on the success page. From 30 Aug 2026 it carries reference:
https://acme.ng/thanks -> https://acme.ng/thanks?reference=CHK123
https://acme.ng/thanks?order=A17 -> https://acme.ng/thanks?order=A17&reference=CHK123It used to be rendered bare, which made it useless for the thing a redirect is for: your page had no way to know which payment had completed, so it could not confirm anything and had to either trust a webhook that may not have landed yet, or rely on a session of its own, which fails the moment a payer finishes on a different device from the one they started on.
Your own query string is preserved, and a reference you set yourself is not overwritten. https only: anything else is dropped rather than rendered.
#Then confirm it server side
Never credit on the redirect alone. It is a URL in a browser: anybody can type it. It tells you which payment to ask about, nothing more.
GET /v1/payments/CHK123
Authorization: Bearer sk_live_…Scoped to your own merchant and mode, so a reference from anywhere else is 404 not_found rather than readable. Credit on what this returns, never on what the browser said.
#And still take the webhook
collection.success is the signal that arrives whether or not the payer ever comes back to your site, which on a bank transfer is common: they finish in their banking app and close the tab. A shop that only credits on redirect loses those customers silently.
The two carry the same reference, so handling both and de-duplicating on it is the whole integration:
| Redirect | Webhook | |
|---|---|---|
| Arrives | as soon as the payer clicks back | when the money lands |
| Reliable | no, the payer may never return | yes, retried |
| Trustworthy | no, confirm it | yes, signed |
| Use it to | know what to ask about | credit |
Test both with the outcomes in Test mode: making a payment happen.
The signature recipe and the retry schedule are on webhooks. Both outcomes are testable end to end with test mode.