To migrate from Paystack to Daraja, you apply for Safaricom Daraja API credentials, set up your paybill or till number, replace Paystack Charge API calls with Daraja STK Push calls, replace Paystack webhooks with Daraja callbacks, and build or source a separate solution for card payments. The M-Pesa customer experience stays the same. The engineering and operational complexity increases.
Moving from Paystack to Daraja goes against the typical direction. Most teams add a gateway, not remove one. So why would you do this?
Cost at volume. Paystack charges a per-transaction fee for M-Pesa payments. Safaricom's Daraja has its own fee structure, which is set directly by Safaricom. At high transaction volumes (thousands of M-Pesa charges per day), the difference between Paystack's fee and Daraja's fee compounds into real money. If M-Pesa is 90% or more of your transactions, and your volume is high, the fee savings from going direct can be significant.
C2B Validation. This is the feature most people do not know about until they need it. With Daraja's C2B API, when a customer pays your paybill, Safaricom sends a validation request to your server before completing the transaction. You can approve or reject the payment in real time. Use cases: reject payments from unregistered customers, reject incorrect amounts, reject payments to wrong account references. Paystack does not expose this because Paystack sits between you and Safaricom.
Your own paybill branding. When a customer pays through Paystack, the STK push shows a Paystack-related shortcode. When they pay through Daraja, it shows your own paybill number and business name. For some industries (banking, insurance, government services), having your own business name on the M-Pesa prompt matters for trust and brand recognition.
B2C and B2B APIs. Daraja gives you direct access to Business-to-Customer (send money to a customer's M-Pesa), Business-to-Business (pay another business), Account Balance, and Transaction Status APIs. Paystack offers transfers, but through a different API with its own abstractions. If you need granular control over these operations, Daraja gives you the raw tools.
UX control. With Daraja, you control exactly when the STK push fires, what the TransactionDesc says, what the AccountReference is, and how you handle timeouts. With Paystack, you work within their abstraction layer. For most products, the abstraction is fine. For some, it is not.
This section is just as important as the previous one. Be honest with yourself about the trade-offs.
Card payments. Daraja does not process Visa, Mastercard, or any card payments. If you accept cards today through Paystack, you need a different plan for cards after migration. Your options:
Pesalink and bank transfers. Gone. Daraja does not handle bank-to-bank payments. If you have customers who pay via Pesalink, you need another provider for those.
Airtel Money. Daraja is Safaricom's API. It does not process Airtel Money. You need Airtel's own API or another provider that supports it.
Apple Pay. Only available through payment gateways. No Daraja equivalent.
The Paystack dashboard. Paystack gives you transaction search, settlement reports, customer management, and webhook delivery logs. With Daraja, your dashboard is the Safaricom portal (limited), your own M-Pesa statement downloads, and whatever admin tools you build yourself.
Webhook signature verification. Paystack webhooks include an HMAC-SHA512 signature that you verify against your secret key. This is a robust security mechanism. Daraja callbacks do not have a signature. You secure them through IP whitelisting (if Safaricom publishes their callback IPs), or by validating the CheckoutRequestID against your database. It is less clean.
Simpler auth. Paystack uses a secret key in the header. Daraja requires OAuth token generation (consumer key + consumer secret), token refresh (tokens expire), and passkey-based password generation for STK Push. More moving parts.
Multi-country support. If you ever plan to expand beyond Kenya, Daraja will not help you. Each country has its own mobile money API. Paystack gives you one API across multiple markets.
Get weekly developer tips
Join 25,000+ developers. Practical guides, job tips, and new content — straight to your inbox.
No spam. Unsubscribe anytime.
If you are migrating to Daraja, you need these credentials. Getting them takes time, so start the process well before you plan to migrate.
Step 1: Register on the Safaricom Developer Portal.
Go to developer.safaricom.co.ke and create an account. This gives you access to the sandbox environment and documentation.
Step 2: Create a sandbox app.
In the portal, create an app. You get sandbox Consumer Key and Consumer Secret. Use these to build and test your integration before going live.
Step 3: Apply for a paybill or till number.
If you do not already have one, apply through Safaricom Business. A paybill number is used for payments with an account reference (useful for matching payments to customers). A till number is used for simple payments without a reference (common in retail). The application process involves business registration documents and takes days to weeks.
Step 4: Go live.
Once you have a paybill or till, request live API access through the Safaricom portal. You submit your app for review. Safaricom verifies your callback URLs, tests your integration, and approves you. This process is not instant. Budget at least a week, sometimes longer.
Step 5: Get your live credentials.
After approval, you receive:
Store these securely. They are the keys to your M-Pesa integration. Treat them like you treat your Paystack secret key.
For the full Daraja setup process, see the M-Pesa integration hub.
Here is what your Paystack M-Pesa charge code looks like today, and what the Daraja replacement looks like.
Before: Paystack Charge API
async function chargeMpesaPaystack({ phone, amount, email, reference }) {
const res = await fetch('https://api.paystack.co/charge', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.PAYSTACK_SECRET_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
email,
amount, // Already in cents
currency: 'KES',
reference,
mobile_money: { phone, provider: 'mpesa' },
}),
});
return res.json();
}
After: Daraja STK Push
async function getDarajaToken() {
const credentials = Buffer.from(
`${process.env.DARAJA_CONSUMER_KEY}:${process.env.DARAJA_CONSUMER_SECRET}`
).toString('base64');
const res = await fetch(
'https://api.safaricom.co.ke/oauth/v1/generate?grant_type=client_credentials',
{ headers: { Authorization: `Basic ${credentials}` } }
);
const data = await res.json();
return data.access_token;
}
async function chargeMpesaDaraja({ phone, amount, reference }) {
const token = await getDarajaToken();
const timestamp = new Date()
.toISOString()
.replace(/[-T:.Z]/g, '')
.slice(0, 14);
const password = Buffer.from(
`${process.env.DARAJA_SHORTCODE}${process.env.DARAJA_PASSKEY}${timestamp}`
).toString('base64');
const res = await fetch(
'https://api.safaricom.co.ke/mpesa/stkpush/v1/processrequest',
{
method: 'POST',
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
BusinessShortCode: process.env.DARAJA_SHORTCODE,
Password: password,
Timestamp: timestamp,
TransactionType: 'CustomerPayBillOnline',
Amount: Math.round(amount / 100), // Convert cents to whole shillings
PartyA: phone,
PartyB: process.env.DARAJA_SHORTCODE,
PhoneNumber: phone,
CallBackURL: `${process.env.BASE_URL}/webhooks/daraja/stk`,
AccountReference: reference,
TransactionDesc: `Payment ${reference}`,
}),
}
);
return res.json();
}
Immediate things that hit you:
Paystack sends a charge.success event to your webhook URL with an HMAC signature for verification. Daraja sends a completely different JSON structure to your callback URL with no signature.
Before: Paystack webhook handler
app.post('/webhooks/paystack', express.json(), (req, res) => {
const hash = crypto
.createHmac('sha512', process.env.PAYSTACK_SECRET_KEY)
.update(JSON.stringify(req.body))
.digest('hex');
if (hash !== req.headers['x-paystack-signature']) {
return res.status(401).send('Unauthorized');
}
if (req.body.event === 'charge.success') {
const { reference, amount } = req.body.data;
confirmPayment(reference, amount);
}
res.status(200).send('OK');
});
After: Daraja STK callback handler
app.post('/webhooks/daraja/stk', express.json(), (req, res) => {
const callback = req.body?.Body?.stkCallback;
if (!callback) {
return res.status(400).json({ ResultCode: 1, ResultDesc: 'Bad request' });
}
const { CheckoutRequestID, ResultCode, ResultDesc } = callback;
if (ResultCode === 0) {
// Success
const items = callback.CallbackMetadata?.Item || [];
const amount = items.find(i => i.Name === 'Amount')?.Value;
const receipt = items.find(i => i.Name === 'MpesaReceiptNumber')?.Value;
const phone = items.find(i => i.Name === 'PhoneNumber')?.Value;
confirmDarajaPayment(CheckoutRequestID, {
amount: amount * 100, // Convert back to cents for your database
receipt,
phone,
});
} else {
// Failed: customer cancelled, insufficient funds, timeout, etc.
failDarajaPayment(CheckoutRequestID, ResultCode, ResultDesc);
}
// Daraja expects this exact response format
res.json({ ResultCode: 0, ResultDesc: 'Accepted' });
});
The big differences:
ResultCode: 0. If you return the wrong format, Daraja may retry the callback or log an error.Body.stkCallback.CallbackMetadata.Item, an array of key-value pairs. It is not a clean flat object like Paystack's webhook payload. You have to fish out each field by name.If you currently accept card payments through Paystack and you are migrating M-Pesa to Daraja, you still need a solution for cards. Here are your realistic options.
Option 1: Keep Paystack for cards only.
This is the simplest approach. You do not need to find a new card provider. You already have the integration. Just stop routing M-Pesa through Paystack and keep routing cards. Your checkout offers "Pay with M-Pesa" (goes to Daraja) and "Pay with Card" (goes to Paystack). This is the dual-provider setup from Running Paystack and Daraja Side by Side.
The downside: you are still paying Paystack fees on card transactions, and you still maintain the Paystack integration. But card volume is usually much lower than M-Pesa in Kenya, so the fee impact is small.
Option 2: Switch to another card provider.
If you want to fully remove Paystack, you need another provider that handles card payments in Kenya. Options include IntaSend, PesaPal, and Flutterwave. Each has different API designs, fee structures, and onboarding requirements. See Choosing Between Paystack, IntaSend, PesaPal, and Flutterwave for a comparison.
Option 3: Drop cards.
If card transactions are a tiny fraction of your revenue and the integration maintenance is not worth it, you can remove card payments entirely. This is a business decision. For some products (mass-market consumer apps in Kenya), M-Pesa covers 95%+ of payments and cards are not worth the complexity. For others (products with international or corporate customers), dropping cards is not an option.
Most teams migrating from Paystack to Daraja choose Option 1. It preserves card support with minimal additional work.
Here is the step-by-step plan. Timing: budget 4 to 8 weeks depending on how quickly you get Daraja go-live approval.
/webhooks/daraja/stk). Test it with sandbox callbacks.Going from Paystack to Daraja is going from a managed service to a self-managed integration. Here is what that means in practice.
Token management. Daraja OAuth tokens expire (typically after one hour). You need to either cache the token and refresh it before expiry, or request a new token for every API call. Paystack does not have this problem. Its secret key never expires.
Callback reliability. Paystack retries webhook deliveries and logs every attempt in the dashboard. Daraja's callback retry behavior is less predictable. If your server is briefly unreachable, you may miss a callback entirely. You need a polling mechanism (Transaction Status Query API) to catch missed callbacks.
Reconciliation. Paystack gives you settlement reports that match transactions to settlements. With Daraja, you download M-Pesa statements from the Safaricom portal and match them against your database manually or with custom code. This is more work.
Error handling. Daraja error responses are less descriptive than Paystack's. You will see cryptic error codes and messages that require cross-referencing Safaricom documentation. Build a mapping of common error codes to human-readable messages.
Testing environment. Paystack's test mode uses test keys and processes fake transactions cleanly. Daraja's sandbox works, but it has its quirks: different response formats than production, callbacks that sometimes do not fire, and test credentials that occasionally stop working. Test thoroughly, but expect the sandbox to surprise you.
Security. Without HMAC signature verification on callbacks, you need alternative security measures. IP whitelisting, CheckoutRequestID validation against your database, and HTTPS are your tools. None of them are as clean as Paystack's signature verification, but together they provide reasonable security.
None of this is insurmountable. Thousands of Kenyan businesses run on Daraja. But if you are coming from Paystack, the operational overhead increase is real. Make sure the benefits (cost savings, C2B validation, branding) justify the complexity for your specific situation.
Testing before migration:
Testing during migration (parallel period):
Rollback plan:
During the parallel period, rollback is trivial: set DARAJA_PERCENTAGE to 0 and all traffic goes back to Paystack. After full cutover, rollback means switching the router back to Paystack for M-Pesa. Keep your Paystack credentials active. Keep your Paystack webhook handler deployed. The cost of maintaining a dormant Paystack integration is near zero.
If you discover post-migration that the complexity is not worth the benefit, that is a legitimate outcome. Roll back to Paystack, accept the higher per-transaction cost, and spend your engineering time on something else. There is no shame in trying a migration, learning from it, and deciding the old approach was better for your situation.
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