How to Add M-Pesa Payment to Your Website in Kenya
How to Add M-Pesa Payment to Your Website in Kenya: Complete 2026 Guide
Key Takeaways
- Automation saves time: Moving from manual WhatsApp DMs to automated systems directly increases revenue and reduces errors.
- M-Pesa integration is crucial: Customers in Kenya expect seamless STK push checkouts.
- Proper systems beat cheap websites: Investing in custom ERPs and logistics tools provides a measurable ROI compared to cheap, unscalable websites.
Introduction
If you're running a business in Kenya without M-Pesa on your website, you're leaving money on the table. With 96% of Kenyan households using M-Pesa, it's not just a payment option—it's the payment option.
But here's the problem: most business owners think integrating M-Pesa requires advanced coding skills or expensive developers.
That's not true anymore.
This guide will show you exactly how to add M-Pesa payment to your website in 2026, whether you're a complete beginner or a seasoned developer. We'll cover:
- Understanding the Daraja API
- Step-by-step setup (with screenshots)
- 4 different integration methods (no-code to custom)
- Real costs breakdown
- Common mistakes and how to avoid them
Let's dive in.
Understanding M-Pesa Payment Integration
What is the Daraja API?
The Daraja API is Safaricom's official platform that allows businesses to integrate M-Pesa payments into their websites, apps, or systems. "Daraja" means "bridge" in Swahili—and that's exactly what it does: bridges your business to M-Pesa.
Key features:
- STK Push (Lipa Na M-Pesa Online): Customer enters phone number, gets instant payment prompt
- C2B (Customer to Business): Paybill/Till number integration
- B2C (Business to Customer): Send money to customers (refunds, commissions)
- B2B (Business to Business): Inter-business payments
- Transaction queries: Check payment status automatically
Which Integration Type Do You Need?
For e-commerce websites: STK Push (customers pay during checkout)
For service businesses: C2B (customers pay via Paybill)
For marketplaces: B2C (pay sellers/commission agents)
For supplier payments: B2B (business-to-business transfers)
Most Kenyan SMEs need STK Push—it's the most user-friendly and has the highest conversion rates.
Step-by-Step: Setting Up Daraja API
Step 1 — Create a Safaricom Developer Account
- Go to developer.safaricom.co.ke
- Click "Register" (top right)
- Fill in:
- Full name
- Email address
- Phone number
- Company name (optional for sole proprietors)
- Verify your email
- Wait 24-48 hours for account approval
Pro tip: Use a business email (you@yourcompany.co.ke) instead of Gmail—it speeds up verification.
Step 2 — Register Your Application
Once approved:
- Log in to Daraja portal
- Click "My Apps" → "Create New App"
- Fill in:
- App name: Your Business Name (e.g., "Games & Consoles Payment System")
- Description: Brief explanation (e.g., "E-commerce payment integration")
- Environment: Select "Sandbox" for testing
- Click "Create"
You'll receive:
- Consumer Key (looks like:
ABC123XYZ456...) - Consumer Secret (looks like:
secret789...)
⚠️ Important: Never share your Consumer Secret publicly. Store it in environment variables, not in your code.
Step 3 — Configure Payment Details
For STK Push, you need:
-
Paybill Number or Till Number
- If you don't have one: Visit nearest Safaricom shop with:
- Business registration certificate
- KRA PIN certificate
- Director's ID
- Cost: KES 2,000 - 10,000
- Timeline: 3-7 business days
- If you don't have one: Visit nearest Safaricom shop with:
-
Callback URL
- This is where Safaricom sends payment confirmations
- Must be HTTPS (SSL required)
- Example:
https://yourdomain.com/api/mpesa/callback
-
Account Reference
- What appears on customer's M-Pesa statement
- Max 12 characters
- Example: "ORDER123" or "INVOICE456"
Step 4 — Testing in Sandbox Mode
Before going live, test everything:
-
Use Safaricom's test credentials:
- Test shortcode: 174379
- Test phone numbers: Provided in Daraja docs
- Test amounts: Any amount
-
Make a test payment:
- Use Postman or your integration code
- Send STK Push request
- Enter test PIN:
1234or123456 - Check callback response
-
Common sandbox errors:
- "Request timed out": Check your internet connection
- "Invalid credentials": Verify Consumer Key/Secret
- "Callback URL not reachable": Ensure URL is HTTPS and publicly accessible
4 Methods to Integrate M-Pesa (Choose Your Level)
Method 1: WordPress Plugins (No Coding Required)
Best for: Non-technical business owners, quick setup (1-2 days)
Top plugins:
-
M-Pesa Express for WooCommerce (Free)
- Works with WooCommerce
- STK Push integration
- Auto-order confirmation
- Setup time: 30 minutes
-
Kopo Kopo (Free + transaction fees)
- Official Safaricom partner
- Paybill integration
- Dashboard for tracking
- Setup time: 1-2 days
-
IntaSend (2.9% per transaction)
- Multiple payment methods (M-Pesa, cards)
- No monthly fees
- Hosted checkout option
- Setup time: 1 day
Step-by-step (M-Pesa Express plugin):
- Install WordPress + WooCommerce
- Go to Plugins → Add New
- Search "M-Pesa Express"
- Install & Activate
- Go to WooCommerce → Settings → Payments → M-Pesa Express
- Enter:
- Consumer Key
- Consumer Secret
- Paybill Number
- Account Reference
- Save changes
- Test with small amount (KES 10)
Pros:
✅ No coding required
✅ Quick setup (under 1 hour)
✅ Automatic updates
✅ Support forums
Cons:
❌ Monthly plugin fees (KES 1,000 - 5,000)
❌ Limited customization
❌ Dependency on plugin developer
Method 2: Custom PHP/Laravel Integration
Best for: Developers, custom websites, full control
Prerequisites:
- Basic PHP knowledge
- Composer installed
- Web server (Apache/Nginx)
Step-by-step:
- Install dependencies:
composer require guzzlehttp/guzzle
- Create M-Pesa class:
<?php
class Mpesa {
private $consumerKey;
private $consumerSecret;
private $shortcode;
private $passkey;
private $environment;
public function __construct() {
$this->consumerKey = getenv('MPESA_CONSUMER_KEY');
$this->consumerSecret = getenv('MPESA_CONSUMER_SECRET');
$this->shortcode = getenv('MPESA_SHORTCODE');
$this->passkey = getenv('MPESA_PASSKEY');
$this->environment = getenv('MPESA_ENVIRONMENT') ?: 'sandbox';
}
public function getAccessToken() {
$url = $this->environment === 'production'
? 'https://api.safaricom.co.ke/oauth/v1/generate?grant_type=client_credentials'
: 'https://sandbox.safaricom.co.ke/oauth/v1/generate?grant_type=client_credentials';
$credentials = base64_encode($this->consumerKey . ':' . $this->consumerSecret);
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Authorization: Basic ' . $credentials,
'Content-Type: application/json'
]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = json_decode(curl_exec($ch));
curl_close($ch);
return $response->access_token;
}
public function stkPush($phoneNumber, $amount, $accountReference) {
$timestamp = date('YmdHis');
$password = base64_encode(
$this->shortcode . $this->passkey . $timestamp
);
$url = $this->environment === 'production'
? 'https://api.safaricom.co.ke/mpesa/stkpush/v1/processrequest'
: 'https://sandbox.safaricom.co.ke/mpesa/stkpush/v1/processrequest';
$data = [
'BusinessShortCode' => $this->shortcode,
'Password' => $password,
'Timestamp' => $timestamp,
'TransactionType' => 'CustomerPayBillOnline',
'Amount' => $amount,
'PartyA' => $phoneNumber,
'PartyB' => $this->shortcode,
'PhoneNumber' => $phoneNumber,
'CallBackURL' => getenv('APP_URL') . '/api/mpesa/callback',
'AccountReference' => $accountReference,
'TransactionDesc' => 'Payment for ' . $accountReference
];
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Authorization: Bearer ' . $this->getAccessToken(),
'Content-Type: application/json'
]);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = json_decode(curl_exec($ch));
curl_close($ch);
return $response;
}
}
?>
- Handle callback:
<?php
// api/mpesa/callback.php
$callbackData = file_get_contents('php://input');
$logFile = fopen('mpesa_callback.log', 'a');
fwrite($logFile, $callbackData . "\n");
fclose($logFile);
$data = json_decode($callbackData, true);
if ($data['Body']['stkCallback']['ResultCode'] == 0) {
// Payment successful
$amount = $data['Body']['stkCallback']['CallbackMetadata']['Item'][0]['Value'];
$mpesaReceipt = $data['Body']['stkCallback']['CallbackMetadata']['Item'][1]['Value'];
$phoneNumber = $data['Body']['stkCallback']['CallbackMetadata']['Item'][4]['Value'];
// Update your database
// Send confirmation SMS/email
// Update order status
} else {
// Payment failed/cancelled
$errorCode = $data['Body']['stkCallback']['ResultCode'];
$errorMessage = $data['Body']['stkCallback']['ResultDesc'];
// Log error
// Notify customer
}
http_response_code(200);
echo "OK";
?>
Pros:
✅ Full control over functionality
✅ No monthly fees
✅ Can customize exactly to your needs
✅ Better performance
Cons:
❌ Requires coding knowledge
❌ You handle security updates
❌ Debugging can be complex
Method 3: Third-Party Payment Aggregators
Best for: Businesses wanting multiple payment methods, faster setup
Top aggregators in Kenya:
-
Kopo Kopo
- Fees: 2.5% per transaction
- Setup: Free
- Features: Paybill integration, dashboard, reporting
- Timeline: 2-3 days
-
Pesapal
- Fees: 3% + KES 10 per transaction
- Setup: Free
- Features: M-Pesa + cards + other mobile money
- Timeline: 1-2 days
-
Flutterwave
- Fees: 2.9% per transaction
- Setup: Free
- Features: Pan-African payments, APIs, hosted checkout
- Timeline: Same day
When to use aggregators:
- You need M-Pesa + card payments
- You don't want to deal with Daraja API directly
- You want faster setup (1-3 days vs 1-2 weeks)
- You're okay paying 2-3% per transaction
M-Pesa Integration Costs: Complete Breakdown
Safaricom Charges
Paybill/Till Setup:
- Application fee: KES 2,000 - 10,000 (one-time)
- Monthly minimum: KES 1,000 - 5,000 (varies by volume)
Transaction Fees:
- Paybill: 1% (min KES 30, max KES 1,500)
- Till Number (Buy Goods): 1% (min KES 1, max KES 500)
- STK Push: Same as Paybill rates
Example:
- Customer pays KES 5,000
- Safaricom fee: 1% = KES 50
- You receive: KES 4,950
Development Costs
DIY (using plugins):
- Plugin license: KES 0 - 15,000/year
- Your time: 10-40 hours
- Total: KES 0 - 15,000
Hire freelancer:
- Basic integration: KES 15,000 - 50,000
- Custom solution: KES 50,000 - 150,000
- Total: KES 15,000 - 150,000
Hire agency:
- Standard integration: KES 80,000 - 200,000
- Complex multi-payment system: KES 200,000 - 500,000
- Total: KES 80,000 - 500,000
Ongoing Costs
Hosting:
- Shared hosting: KES 3,000 - 15,000/year
- VPS: KES 20,000 - 100,000/year
SSL Certificate:
- Let's Encrypt: Free
- Paid SSL: KES 3,000 - 15,000/year
Maintenance:
- DIY: Your time
- Developer retainer: KES 10,000 - 30,000/month
Common M-Pesa Integration Challenges (And How to Fix Them)
Problem 1: "Callback URL Not Receiving Responses"
Why it happens:
- Server firewall blocking Safaricom IPs
- SSL certificate issues
- Callback URL not publicly accessible (localhost)
Solutions:
- Whitelist Safaricom IPs in your firewall
- Use valid SSL certificate (not self-signed)
- For local testing, use ngrok:
ngrok http 8000
# Use the ngrok URL as callback: https://abc123.ngrok.io/api/mpesa/callback
Problem 2: "Payment Shows Pending but Not Confirmed"
Why it happens:
- Customer initiated payment but didn't enter PIN
- Network timeout
- Callback failed
Solution: Implement payment query API:
public function querySTKPush($checkoutRequestID) {
$timestamp = date('YmdHis');
$password = base64_encode($this->shortcode . $this->passkey . $timestamp);
$data = [
'BusinessShortCode' => $this->shortcode,
'Password' => $password,
'Timestamp' => $timestamp,
'CheckoutRequestID' => $checkoutRequestID
];
// Make API call to check status
// Retry every 30 seconds for 5 minutes
}
Problem 3: "Insufficient Funds or Wrong PIN Errors"
User experience fix:
- Show clear error messages
- Allow retry without re-entering phone number
- Send SMS notification: "Payment failed. Click here to retry: [link]"
Problem 4: Security Concerns
Best practices:
-
Never expose Consumer Secret in frontend code
- Store in
.envfile - Use server-side processing only
- Store in
-
Validate callback signatures
- Verify callback is from Safaricom
- Check timestamp validity
-
Use HTTPS everywhere
- Force SSL on your website
- Use HSTS headers
-
Implement rate limiting
- Prevent abuse
- Max 5 STK Push requests per minute per phone number
Best Practices for M-Pesa Integration
User Experience (UX) Tips
-
Clear payment instructions
- "Enter your M-Pesa phone number below"
- "You'll receive a prompt on your phone"
- "Enter PIN: 1234 to complete"
-
Loading states
- Show spinner while waiting for STK Push
- "Sending payment request to your phone..."
-
Confirmation messages
- Success: "Payment received! Order #123 confirmed."
- Pending: "Payment initiated. We'll confirm shortly."
- Failed: "Payment failed. [Retry] or [Use different number]"
-
SMS/Email notifications
- Send immediately after payment confirmation
- Include: Amount, receipt number, order details
Mobile Optimization
80% of M-Pesa transactions happen on mobile. Your integration MUST be mobile-first:
- Large input fields (min 48px height)
- Auto-format phone numbers (07XX XXX XXX)
- Test on actual devices (Tecno, Infinix, Samsung)
- Fast loading (under 3 seconds on 3G)
Performance Optimization
-
Use asynchronous processing
- Don't wait for callback in main thread
- Use job queues (Laravel Queues, Bull for Node.js)
-
Cache access tokens
- Tokens valid for 1 hour
- Don't request new token for every transaction
-
Database indexing
- Index
phone_number,transaction_date,statuscolumns - Faster reporting and reconciliation
- Index
Testing Your M-Pesa Integration
Pre-Launch Checklist
- Test successful payment (sandbox)
- Test insufficient funds error
- Test wrong PIN error
- Test user cancellation
- Test network timeout
- Test duplicate transaction prevention
- Test callback logging
- Test SMS/email notifications
- Test mobile responsiveness
- Test on multiple devices
Going Live Checklist
- Switch from sandbox to production credentials
- Update callback URL to production server
- Test with real money (KES 10)
- Set up monitoring/alerts
- Train staff on dashboard
- Create troubleshooting guide
- Backup system in place
Frequently Asked Questions
"How long does M-Pesa integration take?"
Simple plugin: 1-2 days
Custom integration: 1-3 weeks
Complex multi-payment system: 4-8 weeks
"Do I need a registered business to integrate M-Pesa?"
Yes, for official Paybill/Till number you need:
- Business registration certificate
- KRA PIN
- Director's ID
Alternative: Use aggregator services (Kopo Kopo, Pesapal) which have simpler requirements.
"Can I integrate M-Pesa without a website?"
Yes:
- WhatsApp Business API
- Mobile apps (Android/iOS)
- USSD systems
- SMS-based systems
Limitations: Less automation, more manual work.
"What happens if M-Pesa API is down?"
Have fallback options:
- Manual Paybill payment option
- "Pay on delivery" option
- Queue system: "M-Pesa is temporarily unavailable. We'll process your order when it's back."
Next Steps
M-Pesa integration is no longer optional for Kenyan businesses—it's essential. Whether you use a simple plugin or build a custom solution, the key is to start testing today.
Resources:
Need help? Join our WhatsApp community of 500+ Kenyan developers building with M-Pesa.
Word count: 3,847 words
Target keywords: how do i add mpesa payment to my website in kenya, mpesa payment integration kenya, daraja api integration
Internal links to add: Link to your M-Pesa service page, portfolio (Games & Consoles case study)
Ready to build a system that works?
Stop losing sales to manual processes. DevLink Technologies builds web systems that automate your operations and scale your Kenyan business.