M-Pesa integration comes up in almost every Kenyan software project. The Daraja API is reasonably well-documented, but there's a consistent gap between reading the documentation and having a reliable integration running in production. Here's what to understand before you start.
The Daraja API
Daraja is Safaricom's API platform — the name means "bridge" in Swahili. It exposes M-Pesa functionality as HTTP-based APIs. The main ones businesses use are:
- STK Push (M-Pesa Express / Lipa Na M-Pesa Online) — Your system sends a payment request that appears as a pop-up prompt on the customer's phone. They enter their PIN to confirm. This is what most e-commerce platforms and apps use to collect payment.
- C2B (Customer to Business) — Register your Paybill or Till number and receive real-time notifications when a customer manually sends money. The callback fires as soon as payment is confirmed.
- B2C (Business to Customer) — Send money from your M-Pesa business account to a customer's number. Used for refunds, commission payouts, and salary disbursements.
- Transaction Status — Query the current state of a specific transaction by its M-Pesa receipt number. Essential for reconciliation and handling edge cases.
- Account Balance — Query your M-Pesa business account balance programmatically.
For most businesses, you need STK Push and Transaction Status to start. C2B is required if you want customers to pay via Paybill without an app prompt. B2C is for outgoing payments.
What you need before you can go live
To integrate directly with Daraja, you need:
- A registered M-Pesa business account (Paybill or Till Number) with Safaricom
- A Safaricom Developer Portal account at developer.safaricom.co.ke
- A live application approved through the portal's go-live process
- A publicly accessible HTTPS callback URL for receiving payment notifications
The go-live process involves a technical review. Safaricom will test your integration — not just the success path — and expects you to demonstrate correct handling of timeouts, cancelled payments, and insufficient funds. Plan for this review in your project timeline.
Sandbox vs production
The Daraja sandbox at developer.safaricom.co.ke lets you test without real money. Important differences to know:
Credentials change at go-live. Your Consumer Key and Consumer Secret in sandbox are different from your production credentials. Every environment you deploy to needs the right set — mixing them up is a common deployment error.
Test phone numbers are fixed. In sandbox, you can only use Safaricom's published test numbers (e.g., 254708374149). Attempting to use real Safaricom lines will fail.
Callback URLs must be public even in sandbox. Localhost won't receive callbacks. During development, use a tunnelling tool like ngrok or Cloudflare Tunnel to expose your local server to the internet for testing.
Sandbox accepts things production rejects. Test your error cases explicitly: what happens when the user cancels, when the transaction times out, when the user has insufficient funds. These scenarios often behave differently in production.
The four most common mistakes
Not handling the timeout properly. STK Push has a 30-second user response window. If the user doesn't respond — their phone is off, they dismiss the prompt, or connectivity drops — Safaricom sends a timeout callback to your server, not a payment confirmation. Your system must handle this state: mark the order as pending, not failed, and provide a way for the user to retry or for you to check transaction status manually.
Assuming the callback is the final word. Callbacks can occasionally be delayed or arrive out of order under network load. Build a reconciliation step that queries Transaction Status for any payment that hasn't received a definitive callback within a reasonable window. Don't rely solely on callbacks for financial records.
Hardcoding credentials. Consumer keys and access tokens expire and must be regenerated. Store credentials in environment variables or a secrets manager, not in your codebase. Access tokens in particular expire every hour — your code must request a new one automatically.
Slow callback URL response. When your server receives a callback, it must respond with a 200 status immediately — before doing any processing. If your server takes more than a few seconds to respond, Safaricom marks the callback as failed and may retry. Queue the callback for asynchronous processing and respond 200 right away.
Direct integration vs aggregator
Integrating Daraja directly gives you full control, no per-transaction fees beyond Safaricom's standard rates, and direct access to all API features. But it requires more development time, ongoing credential management, and handling of edge cases and reconciliation yourself.
Payment aggregators — Pesapal, IntaSend, PayHero, Flutterwave — wrap the Daraja API and provide a simpler integration surface with a dashboard, webhooks, and support. They charge a percentage per transaction (typically 1–3.5%), which adds up at volume, but the simplified integration and reduced operational overhead often justify the cost at smaller transaction volumes.
If your development team is small, your payment volumes are moderate, or you need to get to market quickly, start with an aggregator. You can move to direct Daraja integration later when volume and complexity justify it. The decision is a cost-versus-time trade-off, not a quality one.
PAYMENTS INTEGRATION
Need help integrating M-Pesa?
We build and maintain M-Pesa integrations — STK Push, B2C, C2B — and handle reconciliation, error handling, and ongoing Daraja API changes.
See our payments service →