Popular Integrations
M-Pesa

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:

  1. Register your callback URLs in the Daraja sandbox
  2. Use test credentials provided by Safaricom
  3. Initiate test transactions using sandbox phone numbers
  4. 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