mpesa-ke
v1.1.1
Published
π°πͺ The simplest M-Pesa SDK for JavaScript & TypeScript. STK Push, C2B, B2C, and more β zero dependencies.
Maintainers
Readme
π°πͺ mpesa-ke
The simplest M-Pesa SDK for JavaScript & TypeScript.
STK Push, C2B, B2C, and more β zero dependencies, works everywhere.
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-keYour 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 imagempesa.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 | nullPhone 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
- Go to developer.safaricom.co.ke
- Create an account and log in
- Create a new app and get your Consumer Key and Consumer Secret
- For STK Push, use the test credentials:
- Shortcode:
174379 - Passkey:
bfb279f9aa9bdbcf158e97dd71a467cd2e0c893059b10f78e6b72ada1ed2c919
- Shortcode:
- 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!
