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 πŸ™

Β© 2026 – Pkg Stats / Ryan Hefner

mpesa-ke

v1.1.1

Published

πŸ‡°πŸ‡ͺ The simplest M-Pesa SDK for JavaScript & TypeScript. STK Push, C2B, B2C, and more β€” zero dependencies.

Readme

πŸ‡°πŸ‡ͺ mpesa-ke

The simplest M-Pesa SDK for JavaScript & TypeScript.

STK Push, C2B, B2C, and more β€” zero dependencies, works everywhere.

MIT License Node.js TypeScript


Why mpesa-ke?

| Feature | mpesa-ke | Others | |---|---|---| | Zero dependencies | βœ… Native fetch() | ❌ Axios, request, etc. | | TypeScript first | βœ… Full types | ⚠️ Partial or none | | All Daraja APIs | βœ… STK, C2B, B2C, B2B, QR, Tax, Balance, Status, Reversal | ⚠️ Usually STK only | | Framework helpers | βœ… Next.js & Express | ❌ DIY | | Retry & Debug | βœ… Built-in retry + debug logging | ❌ DIY | | Security Credentials | βœ… Auto-encrypts with Safaricom cert | ❌ Manual OpenSSL | | 5-minute setup | βœ… Install β†’ configure β†’ done | ❌ Hours of setup |

Quick Start

Install

npm install mpesa-ke

Your First STK Push (5 lines)

import { Mpesa } from 'mpesa-ke';

const mpesa = new Mpesa({
  consumerKey: 'your_consumer_key',
  consumerSecret: 'your_consumer_secret',
  businessShortCode: '174379',
  passKey: 'your_passkey',
  environment: 'sandbox',
  callbackUrl: 'https://yourdomain.com/callback',
});

const result = await mpesa.stkPush({
  phoneNumber: '0712345678',
  amount: 100,
  accountReference: 'Order123',
  transactionDesc: 'Payment for order',
});

console.log('Check your phone! ID:', result.CheckoutRequestID);

Using Environment Variables

import { createMpesa } from 'mpesa-ke';

// Reads from MPESA_CONSUMER_KEY, MPESA_CONSUMER_SECRET, etc.
const mpesa = createMpesa();

API Reference

new Mpesa(config)

| Parameter | Type | Required | Description | |---|---|---|---| | consumerKey | string | βœ… | OAuth Consumer Key from Daraja | | consumerSecret | string | βœ… | OAuth Consumer Secret | | businessShortCode | string | βœ… | Your Paybill or Till number | | passKey | string | βœ… | Lipa Na M-Pesa Online Passkey | | environment | 'sandbox' \| 'production' | βœ… | API environment | | callbackUrl | string | βœ… | Default URL for async results | | timeout | number | ❌ | Request timeout in ms (default: 30000) | | initiatorName | string | ❌ | Initiator name for B2C/B2B operations | | initiatorPassword | string | ❌ | Initiator password (auto-encrypted) | | certificatePath | string | ❌ | Path to Safaricom production cert | | debug | boolean | ❌ | Log all API requests (default: false) | | retries | number | ❌ | Retry on 5xx errors (default: 0) |


mpesa.stkPush(request) β€” Lipa Na M-Pesa Online

Send a payment prompt to the customer's phone.

const result = await mpesa.stkPush({
  phoneNumber: '0712345678',    // Any Kenyan format works
  amount: 100,                   // KES (whole numbers)
  accountReference: 'Order123',  // Max 12 chars
  transactionDesc: 'Payment',   // Max 13 chars
  callbackUrl: 'https://...',   // Optional override
});

Returns: { MerchantRequestID, CheckoutRequestID, ResponseCode, ResponseDescription, CustomerMessage }


mpesa.stkQuery(request) β€” Query STK Status

Check if a customer completed the STK push payment.

const status = await mpesa.stkQuery({
  checkoutRequestId: result.CheckoutRequestID,
});

if (status.ResultCode === '0') {
  console.log('Payment successful!');
}

mpesa.c2bRegisterUrl(request) β€” Register C2B URLs

Tell M-Pesa where to send payment confirmations.

await mpesa.c2bRegisterUrl({
  validationUrl: 'https://yourdomain.com/mpesa/validate',
  confirmationUrl: 'https://yourdomain.com/mpesa/confirm',
  responseType: 'Completed',
});

mpesa.b2cPayment(request) β€” Send Money to Customer

await mpesa.b2cPayment({
  phoneNumber: '0712345678',
  amount: 500,
  commandId: 'BusinessPayment',
  remarks: 'Refund for order #123',
});

mpesa.accountBalance() β€” Query Account Balance

const balance = await mpesa.accountBalance();

mpesa.transactionStatus(request) β€” Query Transaction

const status = await mpesa.transactionStatus({
  transactionId: 'OEI2AK4Q16',
});

mpesa.reversal(request) β€” Reverse Transaction

await mpesa.reversal({
  transactionId: 'OEI2AK4Q16',
  amount: 100,
  remarks: 'Wrong transaction',
});

mpesa.b2bPayment(request) β€” Business to Business

await mpesa.b2bPayment({
  receiverShortCode: '600000',
  amount: 1000,
  commandId: 'BusinessPayBill',
  accountReference: 'INV001',
  remarks: 'Payment for services',
});

mpesa.dynamicQR(request) β€” Generate QR Code

const result = await mpesa.dynamicQR({
  merchantName: 'My Shop',
  refNo: 'Order123',
  amount: 500,
  transactionType: 'BG', // Buy Goods
  creditPartyIdentifier: '174379',
});
console.log(result.QRCode); // Base64-encoded QR image

mpesa.c2bSimulate(request) β€” Simulate C2B (Sandbox)

Test your C2B callbacks without real money:

await mpesa.c2bSimulate({
  amount: 100,
  phoneNumber: '0712345678',
  billRefNumber: 'Test001',
});

mpesa.taxRemittance(request) β€” Tax Payment (KRA)

await mpesa.taxRemittance({
  amount: 5000,
  accountReference: 'KRA_PIN',
  receiverShortCode: '572572',
  remarks: 'Tax remittance',
});

Security Credentials

For B2C, B2B, Account Balance, Transaction Status, and Reversal APIs, Safaricom requires an encrypted security credential. mpesa-ke handles this automatically:

const mpesa = new Mpesa({
  // ... other config
  initiatorName: 'testapi',
  initiatorPassword: 'your_password',
  // For production, provide the cert path:
  // certificatePath: './certs/production.cer',
});

// Now B2C/B2B calls auto-encrypt the credential:
await mpesa.b2cPayment({ phoneNumber: '0712345678', amount: 500 });

Retry & Debug Mode

const mpesa = new Mpesa({
  // ... other config
  retries: 3,    // Retry up to 3 times on 5xx errors (exponential backoff)
  debug: true,   // Log all API requests and responses
});

Framework Integrations

Next.js (App Router)

Handle M-Pesa callbacks with one line:

// app/api/mpesa/callback/route.ts
import { createStkCallbackRoute } from 'mpesa-ke/nextjs';

export const POST = createStkCallbackRoute({
  onResult: async (result) => {
    if (result.success) {
      // Payment successful β€” update your database
      console.log('Receipt:', result.mpesaReceiptNumber);
      console.log('Amount:', result.amount);
      console.log('Phone:', result.phoneNumber);
    }
  },
});

Express / Fastify

import express from 'express';
import { createStkCallbackHandler } from 'mpesa-ke/express';

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

app.post('/mpesa/callback', createStkCallbackHandler({
  onResult: async (result) => {
    if (result.success) {
      console.log('Payment received!', result.mpesaReceiptNumber);
    }
  },
}));

Parse Callbacks Manually

If you need full control:

import { parseStkCallback } from 'mpesa-ke';

// In any handler
const result = parseStkCallback(requestBody);
console.log(result.success);          // boolean
console.log(result.mpesaReceiptNumber); // string | null
console.log(result.amount);           // number | null
console.log(result.phoneNumber);      // string | null

Phone Number Handling

mpesa-ke automatically formats Kenyan phone numbers. All these work:

mpesa.stkPush({ phoneNumber: '0712345678', ... });     // βœ…
mpesa.stkPush({ phoneNumber: '+254712345678', ... });   // βœ…
mpesa.stkPush({ phoneNumber: '254712345678', ... });    // βœ…
mpesa.stkPush({ phoneNumber: '712345678', ... });       // βœ…
mpesa.stkPush({ phoneNumber: '0712-345-678', ... });    // βœ…

Error Handling

mpesa-ke throws typed errors for easy handling:

import { MpesaAuthError, MpesaApiError, MpesaValidationError } from 'mpesa-ke';

try {
  await mpesa.stkPush({ ... });
} catch (error) {
  if (error instanceof MpesaValidationError) {
    // Invalid input (bad phone number, missing field, etc.)
    console.log('Field:', error.field);
  } else if (error instanceof MpesaAuthError) {
    // Authentication failed (bad keys)
    console.log('Status:', error.statusCode);
  } else if (error instanceof MpesaApiError) {
    // Safaricom API error
    console.log('Error code:', error.errorCode);
    console.log('Response:', error.response);
  }
}

Environment Variables

| Variable | Description | Required | |---|---|---| | MPESA_CONSUMER_KEY | OAuth Consumer Key | βœ… | | MPESA_CONSUMER_SECRET | OAuth Consumer Secret | βœ… | | MPESA_BUSINESS_SHORTCODE | Paybill / Till number | βœ… | | MPESA_PASSKEY | STK Push passkey | βœ… | | MPESA_ENVIRONMENT | sandbox or production | ❌ (default: sandbox) | | MPESA_CALLBACK_URL | Callback URL for results | βœ… | | MPESA_INITIATOR_NAME | Initiator for B2C/B2B | ❌ | | MPESA_INITIATOR_PASSWORD | Auto-encrypted credential | ❌ | | MPESA_CERTIFICATE_PATH | Path to production cert | ❌ | | MPESA_DEBUG | Set true for debug logs | ❌ | | MPESA_RETRIES | Retry count (default: 0) | ❌ |

Getting Safaricom API Credentials

  1. Go to developer.safaricom.co.ke
  2. Create an account and log in
  3. Create a new app and get your Consumer Key and Consumer Secret
  4. For STK Push, use the test credentials:
    • Shortcode: 174379
    • Passkey: bfb279f9aa9bdbcf158e97dd71a467cd2e0c893059b10f78e6b72ada1ed2c919
  5. For production, go through the Safaricom Go Live process

Requirements

  • Node.js 18+ (uses native fetch())
  • Works with: Node.js, Bun, Deno, Cloudflare Workers

Contributing

Contributions are welcome! See CONTRIBUTING.md for guidelines.

License

MIT Β© Onyango Don Omondi


Built with ❀️ in Kenya πŸ‡°πŸ‡ͺ

If this saved you time, give it a ⭐ on GitHub!