Pesapal Payments API
Live Base URL: https://payments.pesavoucher.com/api/v2/
No separate sandbox base URL is documented yet for these endpoints — contact support@pesavoucher.com for sandbox credentials before going live. Pesapal's own demo environment (
https://cybqa.pesapal.com/pesapalv3) is available if you're integrating directly — see Direct Pesapal API Reference below.
Live Testing Note To perform real live transactions or trigger a live test webhook, please contact us: support@pesavoucher.com Telegram: Join our channel → t.me/Dev_PesaVoucher
PesaVoucher's Pesapal integration lets customers pay via Pesapal's hosted checkout — cards, mobile money, and bank options, depending on what's enabled on the underlying Pesapal merchant account — inside an iframe you embed on your site. PesaVoucher handles the Pesapal v3 OAuth token, IPN registration, and order submission internally; you only need to call the endpoints below.
| Path | Who it's for | Endpoints |
|---|---|---|
| PesaVoucher Pesapal Endpoints (recommended) | Merchants using PesaVoucher's wrapper — no Pesapal account of your own required | initiate.php, callback.php, listen_pesapal.php |
| Direct Pesapal API (advanced) | Merchants with their own Pesapal merchant account integrating Pesapal v3 directly | Auth/RequestToken, URLSetup/RegisterIPN, Transactions/SubmitOrderRequest, Transactions/GetTransactionStatus |
This page documents the recommended path first, then the direct Pesapal API for reference.
Note: the example
initiate.phprequests in the Pesapal Postman collection don't show theX-API-KEY/X-API-SECRETheaders used by the other payment rails on this platform — onlyContent-Type: application/json, withmerchant_idpassed in the request body instead. Confirm with support whether header-based authentication is also required for your account before going live.
Endpoint: POST /initiate.php
Starts a Pesapal payment session and returns an iframe URL for the customer to complete payment.
curl -X POST https://payments.pesavoucher.com/api/v2/initiate.php \-H "Content-Type: application/json" \-d '{"merchant_id": "MCH123","order_id": "ORD-2026-0417","amount": 1500.00,"currency": "KES","description": "Order payment","phone_number": "254712345678","email_address": "customer@example.com","first_name": "Jane","last_name": "Doe"}'
| Parameter | Type | Required | Description |
|---|---|---|---|
merchant_id | string | Yes | Your PesaVoucher merchant identifier |
order_id | string | Yes | Your internal order/reference ID |
amount | float | Yes | Payment amount |
currency | string | Yes | Currency code — KES confirmed in examples |
description | string | Yes | Description shown on the Pesapal checkout |
phone_number | string | Yes | Customer phone, 254XXXXXXXXX format |
email_address | string | Yes | Customer email address |
first_name | string | Yes | Customer first name |
last_name | string | Yes | Customer last name |
callback_url | string | No | Override where the customer is redirected after payment. If omitted, PesaVoucher's default handler is used |
Note: one example in the collection calls this endpoint at
/pesapal/initiate.phpinstead of/initiate.phpwhen acallback_urloverride is supplied. Confirm the exact path for your account with support before going live.
{"redirect_url": "https://pay.pesapal.com/iframe/PesapalIframe3/Index?OrderTrackingId=9c5add4c-1234-5678-9abc-def012345678","order_tracking_id": "9c5add4c-1234-5678-9abc-def012345678"}
Additional fields (e.g. a success flag) may also be present — treat redirect_url and order_tracking_id as the fields you need.
Next step: Embed redirect_url in an iframe on your checkout page. The customer completes payment (card, mobile money, or bank, depending on what's enabled) inside the iframe.
Endpoint: GET /pesapal/callback.php
Pesapal redirects the customer's browser here after payment (or to your callback_url override, if supplied). PesaVoucher verifies the transaction status and updates its records.
GET /pesapal/callback.php?OrderTrackingId={order_tracking_id}&OrderMerchantReference={order_id}&OrderNotificationType=IPNCHANGE
Treat this as UX only, not your source of truth. The customer can close the browser before the redirect fires. Always confirm the final payment status from the IPN notification (below) — never credit a user based on the redirect alone.
Endpoint: POST /listen_pesapal.php
PesaVoucher registers this URL with Pesapal on your behalf and receives a notification whenever a transaction's status changes — this is the authoritative signal that a payment completed.
POST /listen_pesapal.php?OrderTrackingId={order_tracking_id}&OrderMerchantReference={order_id}&OrderNotificationType=IPNCHANGE
You don't need to register your own IPN URL with Pesapal — PesaVoucher already has. Deposit status updates are delivered to your own configured webhook URL (see Webhooks) in PesaVoucher's normalized format.
payment_status_description | Meaning |
|---|---|
COMPLETED | Payment successful — credit the user |
PENDING | Still processing — keep waiting |
FAILED | Payment failed — allow retry |
INVALID | Order/tracking ID not recognized |
REVERSED | Payment was reversed after completing |
If you have your own Pesapal merchant account and want to integrate with Pesapal v3 directly instead of using the PesaVoucher wrapper above, these are the underlying calls PesaVoucher itself makes.
Pesapal Production URL: https://pay.pesapal.com/v3 Pesapal Demo/Sandbox URL: https://cybqa.pesapal.com/pesapalv3
Endpoint: POST /api/Auth/RequestToken
curl -X POST https://pay.pesapal.com/v3/api/Auth/RequestToken \-H "Accept: application/json" \-H "Content-Type: application/json" \-d '{"consumer_key": "your_consumer_key","consumer_secret": "your_consumer_secret"}'
Returns a Bearer token — attach it as Authorization: Bearer {token} on every subsequent request.
Demo credentials: Pesapal's public demo consumer key/secret (
qkio1BGGYAXTu2JOfm7XSXNruoZsrqEW/osGQ364R49cXKeOYSpaOnT++rHs=) work against the demo URL above for early testing.
Endpoint: POST /api/URLSetup/RegisterIPN
Run once per environment. Returns an ipn_id — pass this as notification_id on every order you submit.
curl -X POST https://pay.pesapal.com/v3/api/URLSetup/RegisterIPN \-H "Authorization: Bearer {access_token}" \-H "Content-Type: application/json" \-d '{"ipn_notification_type": "POST","url": "https://payments.pesavoucher.com/api/v2/listen_pesapal.php"}'
Endpoint: GET /api/URLSetup/GetIpnList
Lists all IPN URLs registered on your Pesapal account.
Endpoint: POST /api/Transactions/SubmitOrderRequest
curl -X POST https://pay.pesapal.com/v3/api/Transactions/SubmitOrderRequest \-H "Authorization: Bearer {access_token}" \-H "Content-Type: application/json" \-d '{"id": "ORD-2026-0417","currency": "KES","amount": "1500.00","description": "PesaVoucher Test Payment","callback_url": "https://yoursite.com/payment/complete","notification_id": "{ipn_id}","language": "EN","terms_and_conditions_id": "","billing_address": {"phone_number": "254712345678","email_address": "test@pesavoucher.com","country_code": "KE","first_name": "Jane","middle_name": "","last_name": "Doe","line_1": "Nairobi","line_2": "","city": "Nairobi","state": "","postal_code": "","zip_code": ""}}'
Returns order_tracking_id and redirect_url — embed redirect_url in an iframe.
Endpoint: GET /api/Transactions/GetTransactionStatus?orderTrackingId={order_tracking_id}
curl "https://pay.pesapal.com/v3/api/Transactions/GetTransactionStatus?orderTrackingId=9c5add4c-1234-5678-9abc-def012345678" \-H "Authorization: Bearer {access_token}"
Returns payment_status_description — see the status table above. Use this to poll status directly if you're integrating with Pesapal yourself rather than relying on PesaVoucher's IPN forwarding.
- Register (or confirm PesaVoucher has already registered) your IPN URL
- Call Initiate Payment, embed the returned
redirect_urlin an iframe - Complete a test payment using Pesapal's demo credentials/environment
- Confirm you receive a webhook/IPN update on your configured URL with
COMPLETEDstatus - Test a failed/cancelled payment to confirm
FAILEDis handled correctly
- Embed
redirect_urlin an iframe rather than opening it in a new tab — this matches Pesapal's intended checkout UX - Treat the IPN/webhook notification as your source of truth; the browser redirect is UX only
- Store
order_tracking_idalongside your ownorder_idfor reconciliation - Handle all five
payment_status_descriptionvalues, includingREVERSED - Confirm with support whether
X-API-KEY/X-API-SECRETheaders are required forinitiate.phpon your account - Never expose your Pesapal
consumer_secretif you integrate directly