npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

safaricom-mpesa-node

v1.0.3

Published

Modern, comprehensive M-Pesa SDK for Node.js - Safaricom Kenya

Readme

Safaricom M-Pesa Node.js SDK

npm version License: MIT

Modern, comprehensive M-Pesa SDK for Node.js - Safaricom Kenya. A production-ready package for integrating M-Pesa payments into your Node.js applications.

Author: Patrick Mugo
Company: Codezuka Systems LTD
License: MIT

📦 Installation

npm install safaricom-mpesa-node

🚀 Quick Start

import { Mpesa } from 'safaricom-mpesa-node';

const mpesa = new Mpesa({
  consumerKey: 'YOUR_KEY',
  consumerSecret: 'YOUR_SECRET',
  businessShortCode: '174379',
  passkey: 'YOUR_PASSKEY',
  environment: 'sandbox',
  callbackUrl: 'https://your-domain.com/callback/stk',
});

const result = await mpesa.stkPush({
  phoneNumber: '0712345678',
  amount: 1,
  accountReference: 'INV001',
  description: 'Payment for invoice',
});

🚀 Features

  • Complete M-Pesa API Coverage - All 9+ endpoints implemented
  • TypeScript First - Full type definitions and IntelliSense support
  • Zero Dependencies - Minimal, lightweight, fast
  • Dual Module Support - Works with both ESM and CommonJS
  • Smart Defaults - Auto-format phone numbers, generate passwords, timestamps
  • Express Middleware - Built-in middleware for callback handling
  • Multi-Account Support - Handle multiple M-Pesa accounts
  • Auto-Retry Logic - Automatic retry with exponential backoff
  • Comprehensive Validation - Input validation with helpful error messages
  • Logging System - Sanitized logging with configurable levels
  • CLI Tool - Test and validate your configuration
  • Batch Operations - Send multiple B2C payments efficiently
  • Production Ready - Error handling, retries, logging, validation

📦 Installation

npm install safaricom-mpesa-node

or

yarn add safaricom-mpesa-node

or

pnpm add safaricom-mpesa-node

⚡ Quick Start

1. Install the package

npm install safaricom-mpesa-node

2. Initialize M-Pesa

import { Mpesa } from 'safaricom-mpesa-node';

const mpesa = new Mpesa({
  consumerKey: 'YOUR_CONSUMER_KEY',
  consumerSecret: 'YOUR_CONSUMER_SECRET',
  businessShortCode: '174379',
  passkey: 'YOUR_PASSKEY',
  environment: 'sandbox', // or 'production'
  callbackUrl: 'https://your-domain.com/callback/stk',
});

3. Initiate STK Push

const result = await mpesa.stkPush({
  phoneNumber: '0712345678', // Auto-formatted to 254712345678
  amount: 1,
  accountReference: 'INV001',
  description: 'Payment for invoice',
});

console.log('CheckoutRequestID:', result.CheckoutRequestID);

That's it! The SDK handles phone number formatting, password generation, timestamp creation, and all the complex details automatically.

📚 API Reference

STK Push (Lipa na M-Pesa Online)

Initiate a payment request to the customer's phone.

const result = await mpesa.stkPush({
  phoneNumber: '0712345678', // Required - Auto-formatted
  amount: 100, // Required - Minimum 1, whole number only
  accountReference: 'INV001', // Required
  description: 'Payment for invoice', // Optional
  callbackUrl: 'https://...', // Optional if set in config
});

STK Query

Query the status of an STK Push transaction.

const result = await mpesa.stkQuery({
  checkoutRequestID: 'ws_CO_19122023123456789',
});

C2B (Customer to Business)

Register URLs

await mpesa.c2bRegister({
  validationUrl: 'https://your-domain.com/c2b/validation',
  confirmationUrl: 'https://your-domain.com/c2b/confirmation',
  responseType: 'Completed', // or 'Cancelled'
});

Simulate Payment (Sandbox Only)

if (mpesa.isSandbox()) {
  await mpesa.simulateC2B({
    phoneNumber: '254712345678',
    amount: 100,
    billRefNumber: 'INV001',
  });
}

B2C (Business to Customer)

Send payments to customers.

const result = await mpesa.b2c({
  phoneNumber: '0712345678',
  amount: 1000,
  commandId: 'BusinessPayment', // or 'SalaryPayment', 'PromotionPayment'
  remarks: 'Salary payment',
  occasion: 'Monthly salary',
});

B2B (Business to Business)

Business to business payments.

const result = await mpesa.b2b({
  receiverShortCode: '123456',
  amount: 5000,
  commandId: 'BusinessPayBill', // or 'BusinessBuyGoods'
  remarks: 'Payment for services',
  accountReference: 'INV001',
});

Account Balance

Query your M-Pesa account balance.

const result = await mpesa.accountBalance({
  // Optional: Override partyA, identifierType, etc.
});

Transaction Status

Query the status of any transaction.

const result = await mpesa.transactionStatus({
  transactionID: 'ABC123XYZ',
  // or
  originatorConversationID: 'CONV123',
});

Reversal

Reverse an erroneous transaction.

const result = await mpesa.reversal({
  transactionID: 'ABC123XYZ',
  amount: 100,
  remarks: 'Erroneous transaction',
});

Batch Operations

Send multiple B2C payments efficiently.

const results = await mpesa.b2cBatch(
  [
    { phoneNumber: '0712345678', amount: 100, remarks: 'Salary' },
    { phoneNumber: '0723456789', amount: 200, remarks: 'Bonus' },
    { phoneNumber: '0734567890', amount: 300, remarks: 'Commission' },
  ],
  {
    onProgress: (completed, total) => {
      console.log(`Progress: ${completed}/${total}`);
    },
    onError: (error, item) => {
      console.error('Failed:', item, error);
    },
    delay: 1000, // Delay between requests (ms)
  }
);

🔄 Callback Handling

Express.js Middleware

import express from 'express';
import { Mpesa } from 'safaricom-mpesa-node';

const app = express();
app.use(express.json());

const mpesa = new Mpesa({ /* config */ });

// STK Push callback
app.post(
  '/callback/stk',
  mpesa.middleware.stkCallback(),
  (req, res) => {
    const { ResultCode, ResultDesc } = req.mpesa;

    if (ResultCode === 0) {
      const amount = req.mpesa.getAmount();
      const phone = req.mpesa.getPhoneNumber();
      const mpesaRef = req.mpesa.getTransactionId();

      // Process successful payment
      console.log(`Payment received: ${amount} from ${phone}`);
    } else {
      // Handle failed payment
      console.log(`Payment failed: ${ResultDesc}`);
    }

    res.json({ success: true });
  }
);

// C2B Validation
app.post(
  '/c2b/validation',
  mpesa.middleware.c2bValidation(),
  (req, res) => {
    const payment = req.mpesa;
    // Validate and respond
    res.json({
      ResultCode: 0,
      ResultDesc: 'Accepted',
    });
  }
);

// C2B Confirmation
app.post(
  '/c2b/confirmation',
  mpesa.middleware.c2bConfirmation(),
  (req, res) => {
    const payment = req.mpesa;
    // Process payment
    res.json({ success: true });
  }
);

// B2C Result
app.post(
  '/callback/b2c',
  mpesa.middleware.b2cResult(),
  (req, res) => {
    const result = req.mpesa;
    const amount = result.getAmount();
    const phone = result.getPhoneNumber();
    // Process result
    res.json({ success: true });
  }
);

Manual Parsing (Next.js, etc.)

// STK Push callback
const transaction = mpesa.parseSTKCallback(req.body);
const amount = transaction.getAmount();
const phone = transaction.getPhoneNumber();

// C2B callback
const c2bPayment = mpesa.parseC2BConfirmation(req.body);

// B2C result
const b2cResult = mpesa.parseB2CResult(req.body);

⚙️ Configuration

Full Configuration Options

const mpesa = new Mpesa({
  // Required
  consumerKey: 'YOUR_KEY',
  consumerSecret: 'YOUR_SECRET',
  businessShortCode: '174379',
  passkey: 'YOUR_PASSKEY',
  environment: 'sandbox', // or 'production'

  // Optional
  name: 'main-account', // For multi-account identification
  initiatorName: 'initiator', // For B2C, B2B, Reversal, etc.
  securityCredential: 'credential', // For B2C, B2B, Reversal, etc.

  // Default callback URLs
  callbackUrl: 'https://your-domain.com/callback/stk',
  validationUrl: 'https://your-domain.com/c2b/validation',
  confirmationUrl: 'https://your-domain.com/c2b/confirmation',
  timeoutUrl: 'https://your-domain.com/callback/timeout',
  resultUrl: 'https://your-domain.com/callback/result',

  // Logging
  logging: {
    enabled: true,
    level: 'info', // 'error' | 'warn' | 'info' | 'debug'
    sanitize: true, // Remove sensitive data from logs
    customLogger: winston, // Optional custom logger
  },

  // Retry configuration
  retry: {
    enabled: true,
    maxRetries: 3,
    retryDelay: 1000, // Initial delay in ms
  },

  timeout: 30000, // Request timeout in ms
  validateRequests: true, // Auto-validate inputs
});

Environment Variables

Create a .env file:

MPESA_CONSUMER_KEY=your_key
MPESA_CONSUMER_SECRET=your_secret
MPESA_BUSINESS_SHORTCODE=174379
MPESA_PASSKEY=your_passkey
MPESA_ENVIRONMENT=sandbox
MPESA_INITIATOR_NAME=your_initiator
MPESA_SECURITY_CREDENTIAL=your_credential

📱 Phone Number Handling

The SDK automatically formats phone numbers to M-Pesa format (254XXXXXXXXX):

  • 0712345678254712345678
  • 712345678254712345678
  • 254712345678254712345678 (already correct)
  • +254712345678254712345678

Only Safaricom numbers are accepted (prefixes: 070, 071, 072, 074, 011, 079).

🛠️ CLI Tool

The package includes a CLI tool for testing and validation:

# Install globally
npm install -g safaricom-mpesa-node

# Or use with npx
npx safaricom-mpesa-node <command>

Available Commands

# Test STK Push
safaricom-mpesa-node test stk-push --phone 0712345678 --amount 1

# Test C2B (sandbox only)
safaricom-mpesa-node test c2b --phone 0712345678 --amount 10

# Test B2C
safaricom-mpesa-node test b2c --phone 0712345678 --amount 100

# Query balance
safaricom-mpesa-node balance

# Query transaction status
safaricom-mpesa-node status --transaction-id ABC123

# Validate configuration
safaricom-mpesa-node validate

# Generate .env.example
safaricom-mpesa-node init

🔐 Error Handling

The SDK provides custom error classes for better error handling:

import {
  MpesaValidationError,
  MpesaAPIError,
  MpesaAuthError,
  MpesaNetworkError,
  MpesaTimeoutError,
} from 'safaricom-mpesa-node';

try {
  await mpesa.stkPush({ /* ... */ });
} catch (error) {
  if (error instanceof MpesaValidationError) {
    console.error('Validation error:', error.field, error.suggestion);
  } else if (error instanceof MpesaAPIError) {
    console.error('API error:', error.code, error.message);
  } else if (error instanceof MpesaAuthError) {
    console.error('Auth error:', error.message);
  } else if (error instanceof MpesaNetworkError) {
    console.error('Network error:', error.message);
  }
}

🔄 Multi-Account Support

Handle multiple M-Pesa accounts:

const mainPaybill = new Mpesa({
  name: 'main-paybill',
  consumerKey: 'KEY1',
  consumerSecret: 'SECRET1',
  businessShortCode: '174379',
  passkey: 'PASSKEY1',
  environment: 'sandbox',
});

const tillAccount = new Mpesa({
  name: 'till-account',
  consumerKey: 'KEY2',
  consumerSecret: 'SECRET2',
  businessShortCode: '123456',
  passkey: 'PASSKEY2',
  environment: 'sandbox',
});

// Use independently
await mainPaybill.stkPush({ /* ... */ });
await tillAccount.b2c({ /* ... */ });

📊 Logging

Configure logging with sanitization:

const mpesa = new Mpesa({
  // ... other config
  logging: {
    enabled: true,
    level: 'debug', // 'error' | 'warn' | 'info' | 'debug'
    sanitize: true, // Remove sensitive data
    customLogger: winston, // Optional custom logger
  },
});

Logs automatically sanitize:

  • Phone numbers: 2547****5678
  • Passwords/Keys: ****
  • Tokens: First/last 4 chars only

🧪 Testing

Sandbox Credentials

For testing, use the M-Pesa sandbox environment:

  • Consumer Key & Secret: Get from Safaricom Developer Portal
  • Business ShortCode: 174379 (test paybill)
  • Passkey: Get from developer portal
  • Test Phone Numbers: Any valid Safaricom number

Test Flow

  1. Set environment: 'sandbox' in your config
  2. Use sandbox credentials
  3. Test with real phone numbers (you'll receive actual prompts)
  4. Switch to production when ready

📖 Examples

See the examples/ directory for complete examples:

  • examples/express/ - Express.js application
  • examples/nextjs/ - Next.js application
  • examples/basic/ - Basic usage
  • examples/multi-account/ - Multi-account setup

📝 License

MIT License - see LICENSE file for details.

👨‍💻 Author

Patrick Mugo
Codezuka Systems LTD

🙏 Acknowledgments

Built with ❤️ for the Kenyan developer community.

📚 Resources


Need help?