Safaricom's Daraja API is the official developer interface to M-PESA. Through it you can charge customers (STK Push), receive paybill or till payments (C2B), pay out earnings (B2C), and check balances or transactions. The documentation has improved sharply since Daraja 2.0 in 2018, but the integration is still where most Kenyan e-commerce projects get stuck. This guide walks through the core flow, the common failure modes, and the plugin shortcuts that mean you may not need to write Daraja code yourself at all.
Daraja in one paragraph
Daraja is a REST API. You authenticate with consumer key + consumer secret to get a short-lived OAuth token, then call endpoints with that token. Every endpoint that triggers a customer action returns asynchronously: Safaricom calls back to a URL you provide once the customer confirms (or doesn't). Your job is to expose a public callback endpoint, parse the result, and update your order state. That's the pattern for STK Push, C2B, and B2C.
The endpoints you actually use
- STK Push (Lipa na M-PESA Online). Triggers a payment prompt on the customer's phone. They enter PIN, you receive a callback. By far the most common integration for e-commerce.
- C2B (Customer-to-Business). Customers pay your paybill or till using their normal M-PESA menu. Safaricom sends a notification to your registered URL. Use this when STK Push isn't suitable (offline channels, recurring rent, schools).
- B2C (Business-to-Customer). You pay out to a customer's M-PESA number, refunds, withdrawals from a wallet you operate, salary disbursement.
- B2B (Business-to-Business). Move funds between two paybill or till accounts you control. Less common; mostly used by aggregators.
- Account Balance, Transaction Status, Reversal. Operational endpoints for reconciliation and dispute handling.
Sandbox vs production
Daraja has a sandbox environment with test credentials and pre-defined test phone numbers. Sign up at developer.safaricom.co.ke, create an app, and pick the products (STK Push, C2B, B2C) you need. The sandbox returns near-instant successful callbacks for the test phone numbers. Real handsets do not work in sandbox.
Going live requires a separate production application. You'll need a registered paybill or till and Safaricom's go-live approval. The approval is administrative, usually 5-15 business days, and involves submitting a security questionnaire and a callback URL on a TLS-secured public domain.
STK Push: the typical e-commerce flow
- Customer clicks Pay. Your front-end captures their M-PESA phone number and the amount.
- Your server calls Daraja STK Push. You sign the request with your paybill, passkey, timestamp, and a CallBackURL on your server.
- Customer receives a prompt. Their phone shows "Pay KES X to [Merchant], Enter PIN."
- Customer enters PIN (or cancels, or times out). Daraja completes the flow and POSTs the result to your CallBackURL within seconds to a few minutes.
- Your CallBackURL parses the result. Look at the ResultCode, 0 means success, anything else is a failure or cancellation. Update the order state and notify the customer.
Shopify integration
Shopify does not natively support M-PESA. The standard route is a third-party app:
- iPay Shopify app. Aggregator-style, covers M-PESA, Airtel Money, cards. Settles to a Kenyan bank account. Fees ~3% per transaction.
- PesaPal Shopify app. Similar aggregator, supports M-PESA and cards. Built specifically for Kenya, Tanzania, and Uganda.
- Manual order processing. Some Shopify merchants accept "manual" orders, then send the customer the paybill via email and verify the M-PESA SMS code before fulfilling. Cheap and zero-integration but doesn't scale.
WooCommerce integration
WooCommerce on WordPress has more open M-PESA plugin support because anyone can publish a plugin. Common options:
- WooCommerce M-PESA STK Push by Osen Concepts. Free, well-maintained, handles STK Push and callback verification.
- Lipa Na M-PESA for WooCommerce. Paid plugin, supports STK Push, C2B confirmation, and dashboard reporting.
- PesaPal WooCommerce Gateway. Aggregator route, same trade-offs as the Shopify version.
Plugin choice mostly comes down to support. Free plugins evolve slowly when Safaricom changes the API; paid plugins typically ship a fix within days.
Custom stack, Node, Python, PHP, Go
For a custom site or a backend service, the integration is roughly 200-400 lines of code. Mature open-source SDKs cover the main languages:
- Node.js:
mpesa-nodeanddaraja-nodeon npm. - Python:
django-darajafor Django, ormpesa-pythonfor Flask/FastAPI. - PHP:
safaricom/mpesa-phpvia Composer. - Go:
billowdev/mpesa-goand several community modules. - Java/Kotlin: Spring Boot starters from community contributors.
Each SDK abstracts authentication, request signing, and callback parsing. Even with an SDK, you'll spend time on three areas: the callback URL infrastructure, idempotency for duplicate callbacks, and reconciliation against the Safaricom transaction statement.
Callback URL, the part everyone underestimates
Safaricom requires a public, TLS-secured callback URL. Local development is awkward because callbacks must hit a publicly reachable host. Solutions:
- ngrok or Cloudflare Tunnel for local development, exposes your localhost to the internet over HTTPS.
- A staging environment on Vercel, Render, Fly.io, or your VPS for pre-production testing.
- Idempotent callback handling. Safaricom may resend a callback if your server returns non-200. Always look up by transaction ID first; do not double-process.
Reconciliation, the silent killer
STK Push callbacks fail occasionally, network blips, Safaricom outages, your server restarts mid-callback. The mitigation is daily reconciliation against Safaricom's Daraja transaction statement endpoint. Pull the last 24 hours of transactions, compare against your local records, and flag mismatches.
For low-volume sites this can be manual. For higher volume, build an automated daily job that emails any mismatch to your operations team.
Common failure modes
- Customer enters wrong PIN, gives up. Callback ResultCode 1032 (cancelled) or 1037 (timeout). Your order should stay pending, not fail outright, many customers retry within minutes.
- Customer has insufficient balance. ResultCode 1 with descriptive message. Order fails; offer them an alternative or a retry.
- Daraja times out the synchronous call. The STK Push request times out after 30 seconds. The customer's phone may still receive the prompt; rely on the callback.
- Duplicate callback. Safaricom occasionally resends. Idempotency on the MpesaReceiptNumber is essential.
- Wrong CallBackURL after a deploy. If you forget to update the URL in Daraja, callbacks 404 silently. Always log incoming callback bodies on a permanent store.
Security checklist
- Never put consumer secret in client-side code.
- Validate the source IP of incoming callbacks against Safaricom's published ranges.
- Verify the amount and the account match the order before fulfilling.
- Rotate consumer key/secret if a developer leaves your team.
- Rate-limit STK Push requests per IP and per phone number to prevent fraud.
What it costs
Daraja itself is free. Safaricom charges merchant fees on the transactions. For Buy Goods (till) the merchant fee is paid by the merchant; for Paybill the customer pays. See our M-PESA for Business charges 2026 for the full picture.
Build vs buy decision
Use a plugin or aggregator if: standard checkout flow, no recurring billing, no custom payout logic. The 1-3% aggregator fee is cheaper than ongoing engineering time.
Build directly on Daraja if: you need custom invoice numbers, you operate multiple paybills, you do recurring billing, you settle to multiple bank accounts, you process refunds programmatically, or you handle volume above ~5,000 transactions per month where aggregator fees become material.
Resources
- Safaricom Daraja developer portal
- Daraja API documentation
- Apply for an M-PESA paybill or till
- M-PESA for Business charges 2026
- How M-PESA paybills actually work
Related developer references
Curated external sources we cite. Open in a new tab.