Why M-Pesa Integration Matters

M-Pesa processes over $300 billion in transactions annually and has over 51 million active users across Africa. If your business operates in Kenya β€” or anywhere M-Pesa is active β€” failing to integrate it isn't just inconvenient. It's leaving money on the table.

In this guide, we'll walk through everything you need to know about integrating M-Pesa's Daraja API into your website or application, from sandbox setup to production go-live.

Understanding the Daraja API

Safaricom's Daraja API is the official developer interface for M-Pesa. It supports several transaction types:

  • STK Push (Lipa Na M-Pesa Online) β€” Initiates a payment prompt directly on the customer's phone. The most common integration for e-commerce.
  • C2B (Customer to Business) β€” Accepts payments where the customer initiates from their phone (Paybill / Buy Goods).
  • B2C (Business to Customer) β€” Sends money from your business to a customer's phone. Used for refunds, disbursements, and payouts.
  • B2B (Business to Business) β€” Transfers between business accounts.
  • Transaction Status API β€” Queries the status of any transaction.
  • Account Balance API β€” Checks your M-Pesa business account balance.

Step 1: Register on the Daraja Developer Portal

Head to developer.safaricom.co.ke and create a developer account. Once registered:

  1. Create a new app to get your Consumer Key and Consumer Secret
  2. Enable the APIs you need (Lipa Na M-Pesa Online, C2B, B2C, etc.)
  3. Note down your sandbox shortcode: 174379 (test only)
  4. Note your sandbox passkey for STK Push tests

Step 2: Get an OAuth Token

Before every API call, you need a Bearer token. Here's how to get one in Node.js:

const getOAuthToken = async () => {
  const credentials = Buffer.from(
    `${process.env.MPESA_CONSUMER_KEY}:${process.env.MPESA_CONSUMER_SECRET}`
  ).toString('base64');

  const response = await fetch(
    'https://sandbox.safaricom.co.ke/oauth/v1/generate?grant_type=client_credentials',
    { headers: { Authorization: `Basic ${credentials}` } }
  );

  const data = await response.json();
  return data.access_token;
};

Step 3: Initiate an STK Push

STK Push is the most customer-friendly integration. It sends a payment prompt directly to the user's phone:

const stkPush = async (phone, amount, reference) => {
  const token = await getOAuthToken();
  const timestamp = new Date().toISOString().replace(/[^0-9]/g, '').slice(0, 14);
  const password = Buffer.from(
    `${shortcode}${passkey}${timestamp}`
  ).toString('base64');

  const response = await fetch(
    'https://sandbox.safaricom.co.ke/mpesa/stkpush/v1/processrequest',
    {
      method: 'POST',
      headers: {
        Authorization: `Bearer ${token}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        BusinessShortCode: shortcode,
        Password: password,
        Timestamp: timestamp,
        TransactionType: 'CustomerPayBillOnline',
        Amount: amount,
        PartyA: phone,   // Format: 254712345678
        PartyB: shortcode,
        PhoneNumber: phone,
        CallBackURL: 'https://yourdomain.com/api/mpesa/callback',
        AccountReference: reference,
        TransactionDesc: 'Payment',
      }),
    }
  );
  return response.json();
};

Step 4: Handle the Callback

M-Pesa will POST the transaction result to your CallBackURL. Here's how to handle it:

app.post('/api/mpesa/callback', (req, res) => {
  const { Body } = req.body;
  const { stkCallback } = Body;

  if (stkCallback.ResultCode === 0) {
    // Payment successful
    const metadata = stkCallback.CallbackMetadata.Item;
    const amount = metadata.find(i => i.Name === 'Amount').Value;
    const mpesaRef = metadata.find(i => i.Name === 'MpesaReceiptNumber').Value;
    const phone = metadata.find(i => i.Name === 'PhoneNumber').Value;

    // Update your database, fulfill the order, etc.
    console.log(`Payment of KES ${amount} received. Ref: ${mpesaRef}`);
  } else {
    // Payment failed or cancelled
    console.log('Payment failed:', stkCallback.ResultDesc);
  }

  res.json({ ResultCode: 0, ResultDesc: 'Accepted' });
});

Step 5: Going Live

To go live, you need Safaricom's approval. The process typically takes 1–2 weeks:

  1. Complete your sandbox testing thoroughly β€” Safaricom will review your implementation
  2. Submit a go-live request via the Daraja portal with your business details
  3. Provide your Paybill number or till number (obtained separately from Safaricom)
  4. Submit your CallbackURL β€” it must be HTTPS and publicly accessible
  5. Switch your base URL from sandbox.safaricom.co.ke to api.safaricom.co.ke

Common Mistakes to Avoid

  • Not formatting phone numbers correctly β€” always use 254712345678 format, not 0712345678
  • Callback URLs must be HTTPS in production β€” use ngrok for local testing
  • Not handling duplicate callbacks β€” M-Pesa may send the same callback multiple times
  • Not storing the CheckoutRequestID β€” you'll need it to query transaction status
  • Token caching β€” OAuth tokens are valid for 1 hour; cache them to avoid rate limiting

Need Help with M-Pesa Integration?

M-Pesa integration is one of our core competencies at Trabicon. We've done it dozens of times and can have your integration production-ready in days, not weeks. Get in touch and let's talk.