How to Accept M-Pesa Payments on Your Website or App: A Practical Daraja API Guide

In Kenya, if your customers cannot pay with M-Pesa, many of them will not pay at all. Whether you run an online shop, a school collecting fees or a property business collecting rent, adding M-Pesa to your website or app removes friction and gets you paid faster.
This guide explains how M-Pesa integration works, what you need before you start, and the mistakes that cause most integrations to fail. The first half is for business owners; the second half is for developers.
Part 1: For business owners
What is the Daraja API?
Daraja is Safaricom's official platform that lets software talk to M-Pesa. Through it, your website or app can send a payment request directly to a customer's phone, confirm when they have paid, and update your records automatically. No more checking messages and matching transaction codes by hand.
The most popular option: STK Push (Lipa Na M-Pesa Online)
With STK Push, the customer enters their phone number on your website and clicks "Pay". A prompt appears on their phone asking for their M-Pesa PIN. Once they enter it, the money goes to your Paybill or Till number and your system is notified instantly.
What you need before integration
• A Paybill or Till number registered to your business through Safaricom
• Business registration documents for the go-live approval process
• A website or app with secure hosting (HTTPS) so Safaricom can send payment confirmations to it
• A developer familiar with Daraja, or the time to learn it
Part 2: For developers
Step 1: Create a Daraja account and app
Sign up on the Safaricom developer portal and create an app to get your Consumer Key and Consumer Secret. You will build and test everything in the sandbox environment first using Safaricom's test credentials.
Step 2: Get an access token
Every API call needs an OAuth access token. You generate it by sending your Consumer Key and Secret (Base64 encoded) to the OAuth endpoint. Tokens expire after about an hour, so cache them and refresh before expiry rather than requesting a new one on every payment.
Step 3: Send the STK Push request
The password is a Base64 encoding of your shortcode, passkey and a timestamp in the format YYYYMMDDHHmmss. A simplified Node.js example:
const timestamp = getTimestamp(); // e.g. 20261004143000
const password = Buffer.from(SHORTCODE + PASSKEY + timestamp).toString('base64');
const res = await fetch(STK_PUSH_URL, {
method: 'POST',
headers: { Authorization: Bearer ${accessToken}, 'Content-Type': 'application/json' },
body: JSON.stringify({
BusinessShortCode: SHORTCODE,
Password: password,
Timestamp: timestamp,
TransactionType: 'CustomerPayBillOnline', // 'CustomerBuyGoodsOnline' for Till
Amount: 1500,
PartyA: '2547XXXXXXXX',
PartyB: SHORTCODE,
PhoneNumber: '2547XXXXXXXX',
CallBackURL: 'https://yourdomain.co.ke/api/mpesa/callback',
AccountReference: 'INV-1024',
TransactionDesc: 'Payment for INV-1024'
})
});
Step 4: Handle the callback
The STK Push response only tells you the request was accepted, not that the customer paid. The real result arrives later at your CallBackURL. Your callback handler should:
• Check the ResultCode (0 means success; other codes mean cancelled, timed out or insufficient funds)
• Match the CheckoutRequestID to the pending order in your database
• Save the M-Pesa receipt number and mark the order as paid
• Respond quickly and process heavier tasks (emails, receipts) in the background
Step 5: Go live
Once testing is complete, apply to go live through the Daraja portal. Safaricom will issue production credentials and a passkey for your shortcode. Update your environment variables, switch to the production URLs, and run a few small real transactions before announcing it to customers.
Common mistakes to avoid
• Trusting the first response. Never mark an order as paid until the callback confirms it.
• No fallback for missed callbacks. Callbacks occasionally fail. Use the Transaction Status or STK Query API to check payments that stay pending too long.
• Wrong phone number format. Numbers must be in the 2547XXXXXXXX format. Normalise user input that starts with 07 or +254.
• Exposing credentials. Keep keys and passkeys on the server, never in front-end code or public repositories.
• Callback URL not reachable. It must be public and use HTTPS. Localhost will not work; use a tunnelling tool during development.
Do it yourself or hire help?
A basic STK Push integration is achievable for an experienced developer, but production-ready payments need proper error handling, reconciliation, security and reporting. If payments are central to your business, it is worth getting it right the first time.
Need M-Pesa integrated into your website, app or business system? We build secure, tested payment integrations for Kenyan businesses. [Contact us today]