daraja-package
v1.11.0
Published
A simple, zero-dependency implementation of the Safaricom M-Pesa Daraja API for Node.js
Maintainers
Readme
daraja-package
A zero-dependency Node.js wrapper for Safaricom's M-Pesa Daraja API — STK Push (Lipa na M-Pesa Online), C2B, B2C, B2B, Reversals, Transaction Status, Account Balance, Dynamic QR, and the full Bill Manager suite.
M-Pesa is a mobile money service used across East Africa that lets people send/receive money, pay bills, and borrow small amounts entirely over a phone, with or without a bank account. This package lets your Node.js backend talk to Safaricom's Daraja API to initiate and manage those payments programmatically.
You need a Daraja app (consumer key/secret) from the Safaricom Developer Portal before you can use this package.
[!NOTE] Using B2C, B2B, Reversal, AccountBalance, or TransactionStatus? Those methods need a
SecurityCredential— eitherinitiatorPassword+certificate, or a pre-generatedsecurityCredential, as extra constructor options beyond the basics below — see Setting up SecurityCredential.
Requirements
- Node.js 18+ (the package uses the runtime's built-in
fetch; no HTTP client dependency) - A Daraja account with a Consumer Key, Consumer Secret, and Lipa na M-Pesa online PassKey
- A publicly reachable HTTPS callback URL (see Exposing a local server with ngrok)
Installation
npm install daraja-packageQuick start
import mpesa from "daraja-package";
import * as dotenv from "dotenv";
dotenv.config();
const mobilePay = mpesa({
callbackURL: "https://example.com/mpesa/callback",
consumerSecret: process.env.CONSUMER_SECRET,
consumerKey: process.env.CONSUMER_KEY,
passKey: process.env.PASS_KEY,
shortcode: process.env.SHORTCODE, // Daraja BusinessShortCode, required for express()
mpesaBaseUrl: "DEV" // "DEV" (sandbox, default) | "PRODUCTION"
});
try {
const result = await mobilePay.express({
phone: "712345678", // no leading 0 or 254
amount: "20", // KES
tillOrPayBillNumber: "174379", // Till or PayBill number
account_reference: "Order1234", // max 13 characters
transaction_desc: "Buying apples and oranges",
merchantType: "PAYBILL" // "PAYBILL" (default) or "TILL"
});
console.log(result);
} catch (error) {
console.error("STK push failed:", error.message);
}Every method returns a Promise. On a non-2xx response from Daraja, or on invalid input, the promise rejects with an Error — always wrap calls in try/catch or attach .catch().
Configuration
mpesa({ ... }) accepts:
| Option | Type | Required | Description |
|---------------------|--------|----------|--------------|
| callbackURL | string | Yes | HTTPS URL Daraja will call with the transaction result. |
| consumerKey | string | Yes | Daraja app Consumer Key. |
| consumerSecret | string | Yes | Daraja app Consumer Secret. |
| passKey | string | Yes | Lipa na M-Pesa Online PassKey, used to derive the STK Push password. |
| shortcode | string | Only for the methods listed below | Your Daraja BusinessShortCode/organization shortcode. Read directly by express(), expressPushQuery(), customerToBusiness(), customerToBusinessv1(), billManagerInvoiceOptin(), billManagerReconciliation(), and billManagerUpdateOnBoardingDetails() instead of being passed per call. |
| mpesaBaseUrl | string | No | "DEV" (sandbox, default) or "PRODUCTION" (live). Any other/omitted value falls back to sandbox. |
| initiatorPassword | string | Only for SecurityCredential methods, unless securityCredential is set | Password for your Daraja Initiator/API-operator username. |
| certificate | string | Only for SecurityCredential methods, unless securityCredential is set | Safaricom's public certificate — PEM content, or a filesystem path to it. |
| securityCredential | string | Only for SecurityCredential methods, as an alternative to initiatorPassword/certificate | A ready-made, base64 SecurityCredential generated via the Daraja Developer Portal's Test Credentials menu. Takes priority over initiatorPassword/certificate when set. |
The constructor throws synchronously if passKey, consumerSecret, consumerKey, or callbackURL is missing — validate your env vars are loaded before calling mpesa(...). initiatorPassword/certificate/securityCredential are validated lazily, only when a method that needs them is called. shortcode is not validated at all — if you omit it and call a method that reads it, Daraja will reject the request with a missing/invalid ShortCode/BusinessShortCode error, so make sure it's set before going live.
Setting up SecurityCredential (B2C, B2B, Reversal, AccountBalance, TransactionStatus)
These five method families send a SecurityCredential field that Daraja requires to be your Initiator password, RSA-encrypted (PKCS#1 v1.5) with Safaricom's public certificate, then base64-encoded — a different mechanism from the STK Push password used by express(). There are two ways to provide it:
Option 1: let the package encrypt it for you
This package computes the RSA encryption for you via crypto.publicEncrypt, but you must supply the two secret ingredients yourself:
initiatorPassword— the password for your Daraja Initiator (API operator) username. This is set/reset from your Daraja app's production go-live settings, or provided as sandbox test credentials by Safaricom. It is not your Consumer Key/Secret or STK PassKey.certificate— Safaricom's public certificate. Download it from your Daraja app's certificate page (sandbox and production have separate certificates). Pass either the raw PEM content or a path to the file:
import { readFileSync } from "node:fs";
const mobilePay = mpesa({
callbackURL: "https://example.com/mpesa/callback",
consumerSecret: process.env.CONSUMER_SECRET,
consumerKey: process.env.CONSUMER_KEY,
passKey: process.env.PASS_KEY,
initiatorPassword: process.env.INITIATOR_PASSWORD,
certificate: "./certs/sandbox-cert.cer", // or: readFileSync("./certs/sandbox-cert.cer", "utf8")
});
await mobilePay.reversal({ /* ... */ });If certificate is omitted, this package falls back to its own bundled certificate — SandboxCertificate.cer when mpesaBaseUrl is "DEV", ProductionCertificate.cer when "PRODUCTION". Safaricom has a documented history of the certificate downloadable from the Daraja portal not matching what their sandbox actually accepts (see safaricom/mpesa-node-library#17) — if generateSecurityCredential() runs without error but Daraja still rejects your request with "Initiator Information is invalid", re-download a fresh certificate directly from your Daraja portal account and pass it explicitly rather than relying on the bundled one.
Note that RSA PKCS#1 v1.5 padding caps the plaintext at 245 bytes for a 2048-bit key (256 − 11 bytes of padding overhead) — if your Initiator password is unusually long, encryption will fail client-side with a data too large for key size error. Real Daraja Initiator passwords are short, human-chosen strings, so this should only come up if the wrong value is being passed as initiatorPassword.
Option 2: supply a pre-generated SecurityCredential
If you'd rather not manage the certificate/initiatorPassword flow yourself, generate the SecurityCredential directly on the Daraja Developer Portal's Test Credentials menu (it encrypts your M-PESA Organization Portal initiator password for you) and pass the result as securityCredential:
const mobilePay = mpesa({
callbackURL: "https://example.com/mpesa/callback",
consumerSecret: process.env.CONSUMER_SECRET,
consumerKey: process.env.CONSUMER_KEY,
passKey: process.env.PASS_KEY,
securityCredential: process.env.SECURITY_CREDENTIAL, // from Daraja Portal's Test Credentials menu
});
await mobilePay.reversal({ /* ... */ });When securityCredential is set, it's used as-is — no local RSA encryption happens, and initiatorPassword/certificate aren't needed at all.
Error handling
All async methods on the returned instance either resolve with the parsed JSON response body, or reject with an Error whose message includes the HTTP status and, where available, Daraja's own error description:
try {
await mobilePay.reversal({ /* ... */ });
} catch (error) {
// error.message e.g.:
// "Daraja request to /mpesa/reversal/v1/request failed with status 400: Invalid Amount"
console.error(error.message);
}Input validation (e.g. an account_reference longer than 13 characters) throws before any network call is made, with a description of what's wrong.
API reference
generateToken()express(options)— STK Push / Lipa na M-Pesa OnlineexpressPushQuery(options)reversal(options)customerToBusiness(options)— C2B Register URL v2customerToBusinessv1(options)— C2B Register URL v1transactionStatus(options)accountBalance(options)businessToCustomer(options)— B2CbusinessToBusinessBuyGoods(options)businessToBusinessPaybill(options)businessToCustomerAccountTopup(options)dynamicQRCode(options)billManagerInvoiceOptin(options)billManagerSingleInvoicing(options)billManagerBulkInvoicing(invoices)billManagerReconciliation(options)billManagerCancelSingleInvoicing(externalReference)billManagerCancelBulkInvoicing(references)billManagerUpdateOnBoardingDetails(options)billManagerUpdateSingleInvoicing(options)billManagerUpdateBulkInvoicing(invoices)
Full JSDoc for every method (with typedefs for each response shape) is also available directly in package/Mpesa.js and via your editor's IntelliSense/autocomplete once the package is installed.
generateToken()
Fetches a fresh OAuth access token using your consumer key/secret. Called internally by every other method — you normally don't need to call this yourself.
const token = await mobilePay.generateToken();Returns: Promise<string> — the bearer token.
express(options)
Lipa na M-Pesa Online (STK Push) — prompts the customer's phone for their M-Pesa PIN to complete a payment.
BusinessShortCode (and the STK password derived from it) on this request is taken from the constructor's shortcode option, not from tillOrPayBillNumber — make sure shortcode is set on mpesa({ ... }) before calling this method.
| Param | Type | Description |
|---|---|---|
| phone | string | Subscriber number without leading 0/254, e.g. "712345678". |
| amount | string | number | Amount in KES. |
| tillOrPayBillNumber | string | Your Till or PayBill number. Used as PartyB only — the actual destination of the payment, which may differ from the constructor's shortcode. |
| account_reference | string | Shown to the customer in the STK prompt. Max 13 characters. |
| transaction_desc | string | Extra description sent with the request. |
| merchantType | string | Optional, default "PAYBILL". "PAYBILL" or "TILL" — selects whether Daraja's TransactionType is sent as CustomerPayBillOnline or CustomerBuyGoodsOnline. |
Returns: { MerchantRequestID, CheckoutRequestID, ResponseCode, ResponseDescription, CustomerMessage }
await mobilePay.express({
phone: "712345678",
amount: "20",
tillOrPayBillNumber: "174379",
account_reference: "Order1234",
transaction_desc: "Buying apples and oranges",
merchantType: "TILL" // omit or "PAYBILL" for a PayBill number
});expressPushQuery(options)
Checks the status of a previously initiated STK Push. Uses the constructor's shortcode as BusinessShortCode, matching what express() used to initiate the push.
| Param | Type | Description |
|---|---|---|
| CheckoutRequestID | string | Returned by express(). |
Returns: { ResponseCode, ResponseDescription, MerchantRequestID, CheckoutRequestID, ResultCode, ResultDesc }
reversal(options)
Reverses a completed C2B transaction.
| Param | Type | Description |
|---|---|---|
| api_username | string | Initiator username, 5–20 alphanumeric characters. |
| api_shortcode | string | Organization shortcode receiving the reversal (ReceiverParty). |
| amount | string | number | Amount to reverse; must be > 0. |
| remarks | string | Max 100 characters. |
| timeoutURL | string | Valid URL, called if Daraja times out. |
| resultURL | string | Valid URL, called with the final result. |
| transactionID | string | The M-Pesa transaction receipt to reverse. |
Returns: { OriginatorConversationID, ConversationID, ResponseCode, ResponseDescription }
Requires a SecurityCredential — either
initiatorPassword+certificate, or a pre-generatedsecurityCredential— see Setting up SecurityCredential.
customerToBusiness(options)
Registers validation/confirmation URLs for the constructor's shortcode (C2B Register URL, v2).
| Param | Type | Description |
|---|---|---|
| ConfirmationURL | string | Called after a successful payment. |
| ValidationURL | string | Called before a payment is accepted (if validation is enabled). |
| ResponseType | "Completed" | "Cancelled" | Default "Completed". Determines the default action if your ValidationURL is unreachable. |
Returns: { OriginatorCoversationID, ResponseCode, ResponseDescription }
Note: in sandbox you can re-register freely; in production this is effectively a one-time call per shortcode.
customerToBusinessv1(options)
Same as customerToBusiness, against the v1 Register URL endpoint. Same parameters and return shape.
transactionStatus(options)
Secondary reconciliation for a transaction when no callback was received.
| Param | Type | Description |
|---|---|---|
| api_username | string | Initiator username. |
| transaction_id | string | M-Pesa receipt number, or use OriginalConversationID. |
| short_code | string | Organization shortcode (PartyA). |
| MSISDN | string | Optional, used as PartyA if short_code is absent. |
| remarks | string | |
| occasion | string | Default "OK". |
| result_url | string | |
| queue_timeout_url | string | |
| OriginalConversationID | string | |
Returns: { OriginatorConversationID, ConversationID, ResponseCode, ResponseDescription }
Requires a SecurityCredential — either
initiatorPassword+certificate, or a pre-generatedsecurityCredential— see Setting up SecurityCredential.
accountBalance(options)
Queries the M-Pesa account balance for a shortcode.
| Param | Type | Description |
|---|---|---|
| api_username | string | Initiator username. |
| short_code | string | Organization shortcode (PartyA). |
| queueTimeoutURL | string | |
| resultURL | string | |
| remarks | string | Default "ok". |
Returns: { OriginatorConversationID, ConversationID, ResponseCode, ResponseDescription }
Requires a SecurityCredential — either
initiatorPassword+certificate, or a pre-generatedsecurityCredential— see Setting up SecurityCredential.
businessToCustomer(options)
B2C — bulk disbursement from your business account to a customer's phone.
| Param | Type | Description |
|---|---|---|
| OriginatorConversationID | string | Your own idempotency/reference ID. |
| api_username | string | Initiator username. |
| amount | string | number | |
| short_code | string | Your organization shortcode (PartyA). |
| remarks | string | Default "ok". |
| customer_phone_number | string | Format 2547XXXXXXXX. |
| queue_timeout_url | string | |
| result_url | string | |
| occassion | string | Default "payout". |
Returns: { ConversationID, OriginatorConversationID, ResponseCode, ResponseDescription }
Requires a SecurityCredential — either
initiatorPassword+certificate, or a pre-generatedsecurityCredential— see Setting up SecurityCredential.
businessToBusinessBuyGoods(options)
Pays another business's Till/Buy Goods number from your business account.
| Param | Type | Description |
|---|---|---|
| api_username | string | Initiator username. |
| api_short_code | string | Your shortcode (PartyA). |
| recipient_short_code | string | Recipient Till number (PartyB). |
| amount | string | number | |
| account_reference | string | Account number shown to the recipient. |
| requester_phone_number | string | |
| remarks | string | Default "OK". |
| timeoutURL | string | |
| resultURL | string | |
Returns: { OriginatorConversationID, ConversationID, ResponseCode, ResponseDescription }
Requires a SecurityCredential — either
initiatorPassword+certificate, or a pre-generatedsecurityCredential— see Setting up SecurityCredential.
businessToBusinessPaybill(options)
Pays another business's PayBill number from your business account.
| Param | Type | Description |
|---|---|---|
| api_username | string | Initiator username. |
| api_short_code | string | Your shortcode (PartyA). |
| amount | string | number | |
| recipient_short_code | string | Recipient PayBill (PartyB). |
| account_reference | string | |
| requester_phone_number | string | |
| remarks | string | Default "OK". |
| queue_timeout_url | string | |
| result_url | string | |
Returns: { OriginatorConversationID, ConversationID, ResponseCode, ResponseDescription }
Requires a SecurityCredential — either
initiatorPassword+certificate, or a pre-generatedsecurityCredential— see Setting up SecurityCredential.
businessToCustomerAccountTopup(options)
Tops up a customer/merchant account from your business account.
| Param | Type | Description |
|---|---|---|
| api_username | string | Initiator username. |
| api_short_code | string | Your shortcode (PartyA). |
| amount | string | number | |
| customer_short_code | string | Recipient shortcode (PartyB). |
| customer_mobile_number | string | |
| account_number | string | |
| remarks | string | Default "OK". |
| queue_timeout_url | string | |
| result_url | string | |
Returns: { OriginatorConversationID, ConversationID, ResponseCode, ResponseDescription }
Requires a SecurityCredential — either
initiatorPassword+certificate, or a pre-generatedsecurityCredential— see Setting up SecurityCredential. The exactCommandIDDaraja expects for this flow varies by product configuration; this method defaults to"BusinessPayBill"— confirm against your app's Daraja docs before relying on it in production.
dynamicQRCode(options)
Generates a scannable Dynamic QR code for Buy Goods, PayBill, Withdraw, or Send Money.
| Param | Type | Description |
|---|---|---|
| your_business_name | string | |
| reference_value | string | |
| amount | string | number | |
| transaction_type | "BG" | "WA" | "PB" | "SM" | "SB" | Buy Goods / Withdraw Agent / PayBill / Send Money / Send to Business. |
| credit_party_identifier | string | Mobile number, Till, PayBill, etc., matching transaction_type. |
| qr_code_size | number | Default 300 (pixels, square image). |
Returns: { ResponseCode, RequestID, ResponseDescription, QRCode }
billManagerInvoiceOptin(options)
Onboards the constructor's shortcode to Bill Manager. Required once before using the other Bill Manager methods.
| Param | Type | Description |
|---|---|---|
| email | string | |
| phone | string | Format 07XXXXXXXX. |
| send_reminders | boolean | Sent as 1/0. |
| logo | string | Optional JPEG/JPG. |
| callbackURL | string | |
Returns: { app_key, resmsg, rescode }
billManagerSingleInvoicing(options)
Sends a single e-invoice via SMS to a customer.
| Param | Type | Description |
|---|---|---|
| external_reference | string | |
| customer_full_name | string | |
| customer_phone_number | string | |
| billed_period | string | |
| invoice_title | string | |
| due_date | string | |
| account_reference | string | |
| amount | string | number | |
| invoice_items | { itemName, amount }[] | Optional line items. |
Returns: { Status_Message, resmsg, rescode }
billManagerBulkInvoicing(invoices)
Sends up to 1000 e-invoices in one call.
| Param | Type | Description |
|---|---|---|
| invoices | Invoice[] | Array of the same shape as billManagerSingleInvoicing's options (camelCase Daraja field names — see JSDoc). |
Returns: { Status_Message, resmsg, rescode }
billManagerReconciliation(options)
Acknowledges a payment pushed to you by Bill Manager. shortCode is the constructor's shortcode.
| Param | Type | Description |
|---|---|---|
| transaction_id | string | |
| amount | string | number | KES. |
| customer_phone_number | string | Format 2547XXXXXXXX. |
| date_created | string | YYYY-MM-DD. |
| account_number | string | |
Returns: { resmsg, rescode }
billManagerCancelSingleInvoicing(externalReference)
Recalls a previously sent invoice.
| Param | Type | Description |
|---|---|---|
| externalReference | string | The reference used when the invoice was created. |
Returns: { Status_Message, resmsg, rescode, errors }
billManagerCancelBulkInvoicing(references)
Recalls multiple invoices at once.
| Param | Type | Description |
|---|---|---|
| references | { externalReference: string }[] | |
Returns: { Status_Message, resmsg, rescode, errors }
billManagerUpdateOnBoardingDetails(options)
Updates the details you supplied during billManagerInvoiceOptin for the constructor's shortcode.
| Param | Type | Description |
|---|---|---|
| email | string | |
| phone | string | Format 07XXXXXXXX. |
| send_reminders | boolean | Sent as 1/0. |
| logo | string | |
| callback_url | string | |
Returns: { resmsg, rescode }
billManagerUpdateSingleInvoicing(options)
Updates a previously sent single invoice. Same shape as billManagerSingleInvoicing, using customer_phone instead of customer_phone_number and account_number instead of account_reference.
billManagerUpdateBulkInvoicing(invoices)
Bulk equivalent of billManagerUpdateSingleInvoicing.
| Param | Type | Description |
|---|---|---|
| invoices | Invoice[] | |
Known limitations
businessToCustomerAccountTopup'sCommandIDis a best-effort"BusinessPayBill"default; Safaricom's exact expected value can vary by product setup — verify against your app's Daraja documentation.- Phone numbers passed to
express()andbusinessToCustomer()are expected without a leading0or254(the package prepends254itself); passing an already-prefixed number will double it up. - No built-in retry, rate limiting, or webhook signature verification for your
callbackURL/ConfirmationURL/resultURLhandlers — implement that on your own server.
Exposing a local server with ngrok
Daraja requires requests to be made from a secure HTTPS TLS connection.
ngrok is the easiest way to get a public HTTPS URL for your local dev server so Daraja can reach your callbackURL.
# Windows
choco install ngrok
# macOS
brew install ngrok
# Linux (APT)
curl -s https://ngrok-agent.s3.amazonaws.com/ngrok.asc | \
sudo gpg --dearmor -o /etc/apt/keyrings/ngrok.gpg && \
echo "deb [signed-by=/etc/apt/keyrings/ngrok.gpg] https://ngrok-agent.s3.amazonaws.com buster main" | \
sudo tee /etc/apt/sources.list.d/ngrok.list && \
sudo apt update && sudo apt install ngrokngrok config add-authtoken TOKEN # requires a free ngrok.com account
ngrok http 8000 # forwards a public HTTPS URL to localhost:8000Use the printed https://*.ngrok-free.app forwarding URL as your callbackURL while testing against the sandbox.
For AI coding assistants
See llms.txt for a compact, machine-readable summary of this package's API intended for LLM tooling and coding agents.
Contributing
Issues and PRs are welcome at the GitHub repository. The published package lives in package/ — that's where Mpesa.js, index.js, and package.json live and get versioned/published independently of the repo root.
License
MIT
