How a Pesapal API 3.0 payment works
- Get a token with your consumer key and secret. It lasts at most 5 minutes.
- Register your IPN URL once per environment, and keep the
ipn_id. - Submit the order with your own
id, the amount, currency,callback_url,notification_id(theipn_id) and the customer's email or phone. Save the order first. - Send the customer to the
redirect_urlin the response. They choose how to pay on Pesapal's page. - Pesapal redirects them to your
callback_urland calls your IPN URL, both withOrderTrackingId,OrderMerchantReferenceandOrderNotificationType(CALLBACKURLorIPNCHANGE). Neither includes the status. - Check the status with GetTransactionStatus, and settle the order from that answer.
Base URLs
Sandbox: https://cybqa.pesapal.com/pesapalv3. Live: https://pay.pesapal.com/v3. The two have separate keys and separate IPN registrations. Pesapal publishes demo keys for the sandbox, linked from its authentication docs.
Transaction statuses
| status_code | payment_status_description | What to do |
|---|---|---|
1 | COMPLETED | Check merchant_reference, amount and currency, then fulfil once. |
2 | FAILED | Mark unpaid; let the customer try again with a new order id. |
3 | REVERSED | If you fulfilled it, treat the payment as taken back. |
0 | INVALID | Not paid. Keep checking, and expire the order if it never completes. |
Every status, and what Pesapal's error object looks like, is in the Pesapal error reference.
What the docs don't say
- The phone number format for
billing_address.phone_number. Send the email address too when you can. - What your server must answer to an IPN. The Node.js guide answers 200 with the values it received, and runs a reconcile job so a missed IPN never leaves an order stuck.
- A list of error codes. Errors come as an object with
type,codeandmessage: log all three.
Frequently asked questions
What is notification_id in Pesapal SubmitOrderRequest?
It is the ipn_id returned when you register your IPN URL with POST /api/URLSetup/RegisterIPN. Register once per environment (sandbox and live are separate) and send the same ipn_id with every order.
How long is a Pesapal API 3.0 token valid?
At most 5 minutes, according to Pesapal. The response includes expiryDate in UTC. Cache the token and renew it shortly before it expires.
Why doesn't the Pesapal IPN include the payment status?
Pesapal says the callback and IPN calls leave the status out for security reasons. They give you the OrderTrackingId; call GetTransactionStatus with it and use that answer.
What characters can the order id contain?
Letters, numbers, hyphens, underscores, full stops and colons, up to 50 characters. Spaces and symbols such as @ # $ % & / \ ? = are not allowed, and the id must be unique for every order request.
Can I cancel or refund a Pesapal order from the API?
Yes. CancelOrder works only for failed or pending payments, once. RefundRequest works only for COMPLETED payments, once, in the same currency; mobile money is refunded in full only, cards in full or in part, and you must approve the refund.
Related
- Pesapal API 3.0 in Node.js: a full integration with a cached token, safe IPN handling, reconciliation and tests.
- Pesapal API 3.0 in Laravel and in Django and in Next.js: the same design, with feature tests.
- Pesapal error and status reference.
- Comparing African payment APIs and the mobile money in Kenya page.
Endpoints and fields checked November 2026 against Pesapal's API 3.0 documentation at developer.pesapal.com.