safaricom-mpesa-node
v1.0.3
Published
Modern, comprehensive M-Pesa SDK for Node.js - Safaricom Kenya
Maintainers
Readme
Safaricom M-Pesa Node.js SDK
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-nodeor
yarn add safaricom-mpesa-nodeor
pnpm add safaricom-mpesa-node⚡ Quick Start
1. Install the package
npm install safaricom-mpesa-node2. 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):
0712345678→254712345678712345678→254712345678254712345678→254712345678(already correct)+254712345678→254712345678
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
- Set
environment: 'sandbox'in your config - Use sandbox credentials
- Test with real phone numbers (you'll receive actual prompts)
- Switch to
productionwhen ready
📖 Examples
See the examples/ directory for complete examples:
examples/express/- Express.js applicationexamples/nextjs/- Next.js applicationexamples/basic/- Basic usageexamples/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?
- 📖 Check the documentation above
- 📧 Email: [email protected]
