Safaricom M-Pesa
Collect payments with Safaricom M-Pesa
SAFARICOM collects M-Pesa payments with your own Daraja API keys. Centipid
Billing sends an STK push to the payer’s phone, the payer enters their M-Pesa
PIN, and money settles into your own paybill or till.
Available to ISPs in Kenya (KE), alongside Kopo Kopo, Bank, Paybill, Till, Paystack, PesaPal, Relworx, PayPal, and DPO.
If you do not have Daraja API keys, use Paybill or Till instead — they send the same STK push using the platform’s Daraja credentials, and money still settles into your own paybill or till.
Before you start
- Have an active Safaricom paybill or Buy Goods till registered to your business.
- Create an app on the Daraja portal and collect its consumer key, consumer secret, and passkey. The passkey is normally emailed to the address registered in Daraja.
- Confirm your Daraja app is on production credentials, not sandbox.
- Confirm your workspace has a public HTTPS domain — Safaricom must be able to reach it to deliver callbacks, and callback registration is refused without one.
Configure the gateway
Open Settings → Payments → M-Pesa Paybill / Till Number (API keys)
Select the gateway from the payment marketplace to open its form.
Choose the collection method
Paybill — the payer pays your paybill and enters an account number. Till — the payer pays your till number (Buy Goods). The rest of the form changes with this choice.
Enter the number and the shortcode
In paybill mode, Paybill number and M-Pesa shortcode are the same value, so the shortcode field mirrors the paybill and is read-only. In till mode, enter your Till number and the separate Daraja M-Pesa shortcode — the shortcode must be different from the till number, or the switch is refused.
Enter the Daraja credentials
Consumer key, consumer secret, and passkey are all required and stored as secrets. They are not echoed back into the form after saving.
Register the callback URLs (paybill only)
See Callback URLs below.
Run a controlled test payment
Pay a small amount from the captive portal. Verify the STK prompt arrives, the payment lands in the ledger, and the subscriber’s package is applied.
Credentials
| Field | Required | Notes |
|---|---|---|
| Collection method | Yes | Paybill or Till. Decides which fields below apply. |
| Paybill number | Yes, in paybill mode | Digits only, at least 4, maximum 20. |
| Till number | Yes, in till mode | Digits only, maximum 20. |
| M-Pesa shortcode | Yes | Digits only. In paybill mode it must equal the paybill (the form fills it for you). In till mode it is the separate Daraja shortcode and must differ from the till number. |
| Consumer key | Yes | Secret. From your Daraja app. |
| Consumer secret | Yes | Secret. From your Daraja app. |
| Passkey | Yes | Secret. Required for STK push; usually emailed to the address registered in Daraja. |
Saving validates the form; it does not prove the credentials work at Safaricom. Only a test payment does that.
Callback URLs
Settings → Payments shows two URLs once your workspace has a domain:
| Name | Path |
|---|---|
| Confirmation URL | https://<your-host>/api/safaricom/callback |
| Validation URL | https://<your-host>/api/safaricom/validation |
In paybill mode the form also shows a Register callbacks button, which registers both URLs with Safaricom’s C2B v2 API for you. It is enabled only when the configuration is saved and complete — while there are unsaved edits it is disabled with “Save your changes before registering callbacks.”
Till mode does not offer registration: Buy Goods STK results return on the callback URL sent with each request, so there is nothing to register.
What the payer must provide
- A Safaricom phone number, which receives the STK prompt.
- An account number, in paybill mode. Centipid Billing sends the subscriber’s account reference with the request, so the payer does not type it.
The payer authorizes on their handset, so no Safaricom domain has to be added to the captive walled garden.
How a payment completes
Request
Centipid Billing requests a Daraja access token and posts the STK push. Both calls run inside the payer’s checkout request and are deliberately short — a 5-second connect timeout, 10 seconds for the token, and 10 seconds for the push acknowledgement — so Safaricom degradation fails fast instead of hanging the captive page.
Persistence
The acknowledged request is written locally with its
MerchantRequestIDandCheckoutRequestID. If that write fails, it is retried on a durable queue rather than dropped: the push already reached the payer, so the record has to survive for the callback to be matched to a checkout.Authorization
The payer enters their M-Pesa PIN. Money moves into your own paybill or till.
Confirmation
Safaricom calls back —
/api/safaricom/paybill/callbackin paybill mode, or/api/till-number/callbackin till mode. The callback is processed asynchronously, then the payment is recorded and the subscriber’s package applied.
Troubleshooting
| Symptom | What it means | What to do |
|---|---|---|
| “Unable to process payment. Please try again later.” at checkout | The token request or the STK push failed, or Safaricom was unreachable within the timeouts above. | Check the Daraja consumer key, secret, passkey, and shortcode. Confirm the credentials are production, not sandbox. |
| “Unable to process payment without a resolved payment user.” | Safaricom accepted the push, but the checkout could not be tied to a subscriber. | Raise with support with the phone number and the time — the payer may have been charged. |
| “Safaricom authentication failed. Check the saved consumer key and secret.” when registering callbacks | Daraja refused the credentials. | Re-copy both values from the Daraja app. Confirm the app is live. |
| “Safaricom rejected the callback registration. Reason: The saved paybill or shortcode is not recognized…” | The shortcode is not one Daraja recognizes for this app. | Verify the paybill in Daraja and correct it in Settings → Payments. |
| “…The callback URLs may already be registered…” | Safaricom already holds URLs for this shortcode. | Check the active URLs in Daraja, or ask Safaricom to reset them. |
| Callback registration reports both URLs were rejected | Safaricom would not accept one or both URLs. | Confirm your workspace domain is a public HTTPS address that resolves from outside your network. |
| “Safaricom could not be reached. Try again.” | The registration request could not connect. | Check outbound connectivity from the application host, then retry. |
| STK prompt never arrives | The push was acknowledged but not delivered. | Confirm the number is on Safaricom and can receive STK, and that the paybill or till is active. |
| Payer paid, service not applied | The callback did not arrive or did not match a checkout. | See Payment troubleshooting. |
For every gateway’s fields in one place, see the credential reference.
