M-PESA Integration
Send instant SMS notifications when you receive payments through M-PESA. This integration helps you confirm payments to customers in real-time, improving trust and customer satisfaction.
Use Case: Automatically send payment confirmation SMS to customers when they complete M-PESA transactions.
How It Works
When a customer makes an M-PESA payment to your business, you can automatically send them an SMS confirmation with transaction details. This is particularly useful for:
- E-commerce payment confirmations
- Service payment receipts
- Bill payment notifications
- Donation confirmations
- Subscription payments
Prerequisites
Before you begin, make sure you have:
- An active SMSLeopard account with API credentials (Get started here)
- M-PESA API credentials from Safaricom (Daraja API)
- A server to handle M-PESA callbacks (Node.js, PHP, Python, etc.)
Integration Steps
Step 1: Set Up M-PESA Callback URL
First, configure your M-PESA callback URL to receive payment notifications. This endpoint will handle incoming payment confirmations from Safaricom.
// Example using Express.js (Node.js)
const express = require('express');
const axios = require('axios');
const app = express();
app.use(express.json());
// M-PESA callback endpoint
app.post('/mpesa/callback', async (req, res) => {
const { Body } = req.body;
if (Body.stkCallback.ResultCode === 0) {
// Payment successful
const callbackMetadata = Body.stkCallback.CallbackMetadata.Item;
const amount = callbackMetadata.find(item => item.Name === 'Amount').Value;
const phoneNumber = callbackMetadata.find(item => item.Name === 'PhoneNumber').Value;
const mpesaReceiptNumber = callbackMetadata.find(item => item.Name === 'MpesaReceiptNumber').Value;
// Send SMS confirmation
await sendPaymentConfirmationSMS(phoneNumber, amount, mpesaReceiptNumber);
}
res.json({ ResultCode: 0, ResultDesc: 'Success' });
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`M-PESA callback server running on port ${PORT}`);
});
Step 2: Create SMS Sending Function
Create a function to send SMS notifications using the SMSLeopard API:
const axios = require('axios');
async function sendPaymentConfirmationSMS(phoneNumber, amount, receiptNumber) {
const apiKey = process.env.SMSLEOPARD_API_KEY;
const apiSecret = process.env.SMSLEOPARD_API_SECRET;
const sourceAddress = 'YOUR_SENDER_ID'; // Your approved sender ID
// Format phone number (ensure it starts with country code)
const formattedPhone = phoneNumber.startsWith('254')
? phoneNumber
: '254' + phoneNumber.substring(1);
const message = `Payment of KES ${amount} received successfully. M-PESA Ref: ${receiptNumber}. Thank you for your payment!`;
try {
const response = await axios.post(
'https://api.smsleopard.com/v1/sms/send',
{
source: sourceAddress,
destination: [formattedPhone],
message: message
},
{
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${apiKey}:${apiSecret}`
}
}
);
console.log('SMS sent successfully:', response.data);
return response.data;
} catch (error) {
console.error('Error sending SMS:', error.response?.data || error.message);
throw error;
}
}Step 3: Handle M-PESA C2B Payments
For Customer-to-Business (C2B) payments, set up a validation and confirmation endpoint:
// C2B Validation endpoint
app.post('/mpesa/validation', (req, res) => {
// Validate the payment request
console.log('C2B Validation:', req.body);
res.json({
ResultCode: 0,
ResultDesc: 'Accepted'
});
});
// C2B Confirmation endpoint
app.post('/mpesa/confirmation', async (req, res) => {
const {
TransAmount,
MSISDN,
TransID,
FirstName,
LastName
} = req.body;
console.log('Payment confirmed:', req.body);
// Send SMS confirmation
const message = `Dear ${FirstName} ${LastName}, we have received your payment of KES ${TransAmount}. Transaction ID: ${TransID}. Thank you!`;
await sendCustomSMS(MSISDN, message);
res.json({
ResultCode: 0,
ResultDesc: 'Success'
});
});
async function sendCustomSMS(phoneNumber, message) {
const apiKey = process.env.SMSLEOPARD_API_KEY;
const apiSecret = process.env.SMSLEOPARD_API_SECRET;
const sourceAddress = 'YOUR_SENDER_ID';
const formattedPhone = phoneNumber.startsWith('254')
? phoneNumber
: '254' + phoneNumber.substring(1);
try {
await axios.post(
'https://api.smsleopard.com/v1/sms/send',
{
source: sourceAddress,
destination: [formattedPhone],
message: message
},
{
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${apiKey}:${apiSecret}`
}
}
);
} catch (error) {
console.error('Error sending SMS:', error.response?.data || error.message);
}
}
Best Practices
Message Templates
Create clear and professional message templates:
// Payment confirmation
const templates = {
payment: (amount, ref) =>
`Payment of KES ${amount} received. Ref: ${ref}. Thank you!`,
paymentWithBalance: (amount, ref, balance) =>
`Received KES ${amount}. Ref: ${ref}. Account balance: KES ${balance}. Thank you!`,
failedPayment: (amount) =>
`Payment of KES ${amount} could not be processed. Please try again or contact support.`
};Error Handling
Always implement robust error handling:
async function sendPaymentConfirmationSMS(phoneNumber, amount, receiptNumber) {
try {
await sendSMS(phoneNumber, amount, receiptNumber);
} catch (error) {
// Log error for monitoring
console.error('SMS sending failed:', error);
// Implement retry logic
setTimeout(async () => {
try {
await sendSMS(phoneNumber, amount, receiptNumber);
} catch (retryError) {
// Log to error tracking service
console.error('SMS retry failed:', retryError);
}
}, 5000); // Retry after 5 seconds
}
}Phone Number Formatting
Ensure phone numbers are properly formatted:
function formatPhoneNumber(phone) {
// Remove any spaces or special characters
phone = phone.replace(/[\s\-\(\)]/g, '');
// Handle Kenyan numbers
if (phone.startsWith('0')) {
return '254' + phone.substring(1);
} else if (phone.startsWith('254')) {
return phone;
} else if (phone.startsWith('+254')) {
return phone.substring(1);
}
return phone;
}Testing Your Integration
Test with M-PESA Sandbox
Use the M-PESA sandbox environment to test your integration:
- Register your callback URLs in the Daraja sandbox
- Use test credentials provided by Safaricom
- Initiate test transactions using sandbox phone numbers
- Verify SMS delivery in the SMSLeopard dashboard
Sample Test Script
// test_integration.js
async function testMPESAIntegration() {
const testData = {
phoneNumber: "254712345678",
amount: 100,
receiptNumber: "TEST123456",
};
console.log("Testing SMS sending...");
try {
await sendPaymentConfirmationSMS(
testData.phoneNumber,
testData.amount,
testData.receiptNumber
);
console.log("✓ Test SMS sent successfully");
} catch (error) {
console.error("✗ Test failed:", error);
}
}
testMPESAIntegration();Monitoring and Analytics
Track your integration performance in the SMSLeopard dashboard:
- Delivery rates
- Failed messages
- Response times
- Cost per message
Security Considerations
Important: Always validate M-PESA callbacks to ensure they're from Safaricom's servers.
- Store API credentials securely as environment variables
- Use HTTPS for all callback endpoints
- Implement IP whitelisting for M-PESA callback URLs
- Validate callback signatures if provided by M-PESA
- Log all transactions for audit purposes