Paystack M-Pesa uses a checkout popup or redirect where the customer enters their phone number, then receives an STK push from Paystack's shortcode. Direct Daraja triggers the STK push immediately from your own paybill with no redirect. The Daraja path has fewer steps and keeps the customer on your site, but Paystack gives you multi-method checkout and managed error handling out of the box.
Both Paystack and Daraja end at the same place: a green M-Pesa popup on the customer's phone asking them to enter their PIN. The difference is everything that happens before and after that popup.
With direct Daraja, the flow is short:
The customer never leaves your page. There is no redirect, no popup from another domain, no extra form to fill out. You already have their phone number (they entered it during checkout or registration), and the STK push fires from your code the moment they click "Pay."
With Paystack Checkout (the standard flow), the path has more steps:
That is three extra steps: the redirect, the payment method selection, and the phone number entry. Each extra step is a chance for the customer to drop off.
Paystack gives you two ways to present the checkout:
Redirect checkout: The customer leaves your site entirely and lands on a checkout.paystack.com page. After payment, they are redirected back to your callback URL. This is the simplest to implement (just redirect to the URL Paystack gives you), but it is also the most disruptive to the customer experience. The customer sees a different domain, a different design, and has to trust that they are still in the right place.
Inline checkout (Paystack Inline JS): A modal pops up over your page. The customer stays on your site, and the Paystack checkout appears as an overlay. This is better for conversion because the customer does not feel like they have left your store. But it is still a Paystack-branded modal, and the customer still needs to select a payment method and enter their phone number.
With direct Daraja, there is no redirect and no modal. The payment happens in the background. You can show whatever UI you want while the customer waits for the STK push. A custom loading spinner, a countdown timer, a "check your phone" message with your own branding. You control every pixel.
There is a third Paystack option that gets closer to the Daraja experience: the Charge API. With the Charge API, you collect the phone number in your own form and send it directly to Paystack's API. No redirect, no popup. The STK push fires from Paystack's end, and you handle the "waiting" UI yourself. This is the best option if you want to use Paystack but need a seamless checkout experience. The trade-off is that the STK push still shows Paystack's shortcode on the customer's phone, not your paybill.
Get weekly developer tips
Join 25,000+ developers. Practical guides, job tips, and new content — straight to your inbox.
No spam. Unsubscribe anytime.
This is where customers get confused, and where business owners sometimes have strong feelings.
Direct Daraja STK push: The M-Pesa popup on the customer's phone shows your business name and your paybill number. If your paybill is 123456 and your business name is "Kwetu Store," the customer sees:
Pay KES 1,500.00 to
Kwetu Store
Paybill: 123456
Account: ORD-20260720-001
Enter M-PESA PIN
This is familiar. The customer knows they are paying your business. If they have paid your paybill before, they recognize it.
Paystack STK push: The M-Pesa popup shows Paystack's aggregator shortcode and Paystack's business name (or a variation registered with Safaricom). The customer sees something like:
Pay KES 1,500.00 to
Paystack Payments
Paybill: XXXXXX
Enter M-PESA PIN
For customers who have used Paystack before, this is fine. For customers who have never heard of Paystack, this can cause a moment of hesitation. "I was buying from Kwetu Store. Why does it say Paystack?" That hesitation can cause a drop-off, especially for first-time online shoppers or less tech-savvy customers.
How much this matters depends on your customer base. If your customers are tech-savvy urban professionals who have used multiple online payment platforms, the Paystack branding is not a problem. If your customers are in smaller towns, less familiar with online payments, and trust your brand specifically, the unfamiliar name on the STK push could cause them to cancel the payment.
There is no technical fix for this. It is a consequence of using an aggregator model. Paystack collects the payment on their paybill and settles it to you later. Daraja lets the payment go to your paybill directly.
M-Pesa STK push has a timeout. If the customer does not enter their PIN within a certain window (typically around 30 to 60 seconds, depending on Safaricom's current configuration), the request expires. How you handle that timeout differs between the two approaches.
Direct Daraja timeout handling:
You have full control. After sending the STK push request, you can:
Your callback URL receives the result from Safaricom, and you decide what to show the customer based on the result code. If the result code indicates a timeout, you can retry immediately or prompt the customer to try again.
Paystack timeout handling:
Paystack manages the timeout internally. When you use Paystack Checkout (redirect or inline), the checkout page shows a "waiting for payment" state and eventually times out. The customer sees a message on the Paystack page. With the Charge API, Paystack returns a pay_offline status and you need to wait for the webhook. If the payment times out, you receive a webhook indicating the failure, or you can poll the Verify Transaction endpoint.
The difference is control. With Daraja, you decide the timeout duration, the polling interval, the retry logic, and the customer-facing messages. With Paystack, you accept Paystack's timeout behavior and respond to the webhook events they send you.
For a deep dive into timeout patterns across different providers, see M-Pesa STK Push Timeout Handling Compared Across Providers.
When a payment fails, the quality of the error information determines how quickly you can debug it and how clearly you can communicate the problem to the customer.
Daraja error codes: Safaricom returns specific result codes in the callback. These codes tell you exactly what went wrong:
1032: Request cancelled by user (they tapped "Cancel" on the STK push).1037: Timeout. The customer did not respond in time.2001: Wrong PIN.1: Insufficient funds.1025: Invalid or inactive number.With these codes, you can show precise messages: "You cancelled the payment. Would you like to try again?" or "The payment timed out. Tap Pay to get another prompt." This level of specificity improves the customer experience and your support team's ability to help.
Paystack error handling: Paystack abstracts Safaricom's result codes into its own status system. A failed M-Pesa payment comes back as a failed charge with a gateway_response message. These messages are cleaner and more user-friendly, but they lose some specificity. You might see "Transaction failed" where Daraja would have told you "Insufficient funds." Paystack's error documentation covers the possible responses, but you are working with Paystack's interpretation of the underlying failure, not the raw Safaricom code.
For debugging, the Daraja path gives you more information. For customer-facing messages, Paystack's abstractions are often good enough. The question is whether "good enough" is enough for your product. If you are building a payments product where understanding exactly why each transaction failed matters (analytics, fraud detection, automated retry logic), the raw Daraja codes are more useful.
Every step in a payment flow is a potential drop-off. This is not theory. It is measurable.
The Daraja advantage: The customer stays on your page. They click "Pay," they see the STK push, they enter their PIN. Three steps, no context switching, no new domains, no payment method selection (because M-Pesa is the only option). If your entire customer base pays with M-Pesa, this is the shortest possible path to completed payment.
The Paystack overhead: Redirect checkout adds at least two extra steps (navigate to Paystack page, select payment method). Inline checkout reduces the redirect friction but still adds the payment method selection and phone number entry steps. The Charge API eliminates the redirect entirely but still requires you to build the waiting UI.
When does the extra redirect not matter?
When does the redirect matter a lot?
For strategies on optimizing Kenyan checkout conversion, see Why Kenyan Checkout Conversion Depends on Mobile Money Placement.
After the customer enters their PIN, both systems need to tell your server what happened. The mechanisms are different.
Daraja callbacks: Safaricom sends a POST request to your callback URL with the transaction result. This happens once. If your server is down, you miss it. There is no built-in retry mechanism. You need to build your own polling logic using the Transaction Status API to catch missed callbacks. The callback payload includes the Safaricom receipt number, the result code, and the transaction details.
Paystack webhooks: Paystack sends a POST request to your webhook URL with the charge.success event (or charge.failed). Paystack retries failed webhook deliveries on a schedule. You verify the webhook using the x-paystack-signature header. As a backup, you can also call the Verify Transaction endpoint to check the payment status.
Paystack's webhook system is more robust out of the box. The signature verification is well-documented, the retry behavior is predictable, and the event payload is consistent across all payment methods. With Daraja, you need to build that reliability yourself: a callback endpoint that handles failures gracefully, a sweeper job that polls for missed callbacks, and validation logic for the callback payload.
If you are using Paystack's Charge API (no redirect), there is a middle ground. You submit the charge, receive a pay_offline status, and then either wait for the webhook or poll the Verify endpoint. This gives you more control over the user experience while still benefiting from Paystack's webhook infrastructure.
Here is what the initiation looks like in code for each approach.
Direct Daraja STK Push (Node.js):
// Daraja: Lipa na M-Pesa Online (STK Push)
const timestamp = new Date().toISOString().replace(/[-T:.Z]/g, '').slice(0, 14);
const password = Buffer.from(
shortcode + passkey + timestamp
).toString('base64');
const response = await fetch(
'https://api.safaricom.co.ke/mpesa/stkpush/v1/processrequest',
{
method: 'POST',
headers: {
Authorization: `Bearer ${accessToken}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
BusinessShortCode: shortcode,
Password: password,
Timestamp: timestamp,
TransactionType: 'CustomerPayBillOnline',
Amount: 1500,
PartyA: '254712345678',
PartyB: shortcode,
PhoneNumber: '254712345678',
CallBackURL: 'https://yoursite.com/api/mpesa/callback',
AccountReference: 'ORD-20260720-001',
TransactionDesc: 'Payment for order ORD-20260720-001',
}),
}
);
// Response tells you if the push was sent, not if payment succeeded
const data = await response.json();
// data.ResponseCode === '0' means the push was sent successfully
Paystack Charge API (Node.js):
// Paystack: Charge API for M-Pesa
const response = await fetch('https://api.paystack.co/charge', {
method: 'POST',
headers: {
Authorization: 'Bearer sk_live_YOUR_SECRET_KEY',
'Content-Type': 'application/json',
},
body: JSON.stringify({
email: 'customer@example.com',
amount: 150000, // KES 1,500 in cents
currency: 'KES',
mobile_money: {
phone: '0712345678',
provider: 'mpesa',
},
}),
});
const data = await response.json();
// data.data.status === 'pay_offline' means the push was sent
// Wait for the charge.success webhook
Notice the differences:
The Paystack code is shorter and simpler. The Daraja code requires more setup but gives you direct access to Safaricom's parameters.
For many products, the differences in this article are academic. Here is when you should not spend time agonizing over this choice:
The choice matters when you are processing high volume, when M-Pesa is your dominant or only payment method, when your customers are sensitive to unfamiliar branding on the STK push, or when you need maximum control over the payment experience for competitive reasons.
For the broader decision framework covering cost, channels, and engineering resources beyond the STK push experience, see Paystack vs Daraja: When to Use a Gateway and When to Go Direct.
KES 2,999
The definitive guide to building payment systems with Paystack — from your first transaction to production-grade payment infrastructure across Africa. 9 modules, 47 lessons. Free preview available.
Start Paystack Course