mpesa-ts-sdk
v1.0.4
Published
TypeScript M-Pesa SDK
Maintainers
Readme
mpesa-ts-sdk (TypeScript)
Comprehensive TypeScript SDK for Safaricom M-Pesa REST APIs. This repository contains:
- A TypeScript SDK (src/) with high-level service wrappers for STK, C2B, B2C, B2B, Account Balance, Reversal and Transaction Status.
- API client and token management utilities that handle OAuth token acquisition, caching and concurrent refresh protection.
- A PHP companion SDK and example tests at
mpesa-php-sdk/.
This README provides: installation, configuration, extended usage examples (for each service), testing instructions, webhook guidance, and developer notes.
Quick navigation
- About
- Install & quick run
- Configuration (MpesaConfig explained)
- Environment variables and secrets
- Detailed examples (STK, C2B, B2C, B2B, Account Balance, Transaction Status, Reversal)
- Token cache, locking and concurrency
- Webhooks & callbacks (best practices)
- Tests & examples (how to run)
- Development & docs generation
- Contributing
- Security
- License
About
The SDK purpose is to provide a predictable, typed and documented interface for calling M-Pesa endpoints. It favors explicit configuration, small surface area, and developer ergonomics (TSDoc, fluent builders, typed responses).
Core components
- Mpesa: top-level factory that holds configuration and provides accessors for services.
- MpesaConfig: typed configuration holder (credentials, endpoints, token cache path, debug flag).
- TokenManager: obtains, caches and refreshes OAuth tokens with filesystem cache + cross-process locking.
- ApiClient: small HTTP wrapper that injects Bearer tokens and retries once on 401.
- Service classes (in
src/Services) provide typed, fluent APIs for common workflows.
Install & quick run
Prerequisites: Node.js 14+ recommended, npm installed.
- Install dependencies:
npm install- (Optional) Install ts-node if you want to run TypeScript examples directly:
npm install -D ts-node typescript- Run the included STK example (uses the file
src/Tests/stk_push_test.ts):
# with ts-node (no build step)
npx ts-node src/Tests/stk_push_test.ts
# or compile and run
npx tsc
node dist/Tests/stk_push_test.jsConfiguration (MpesaConfig)
The SDK centralizes configuration in the MpesaConfig class. You normally create an Mpesa instance which internally creates a MpesaConfig for you, or you can construct a MpesaConfig directly and pass it to sdk.setConfig(...).
Key fields and behavior (what you need to know):
- consumerKey: your OAuth client id (from Safaricom)
- consumerSecret: your OAuth client secret
- environment: "sandbox" | "live" (affects base URL)
- baseUrl: resolved from environment by default (you can override with setBaseUrl)
- businessCode: your Business Short Code / Till number (used as PartyA/PartyB)
- passKey: used to compute STK Password (BusinessShortCode+PassKey+Timestamp)
- securityCredential: encrypted initiator credential (for certain B2B/B2C operations) — you can set it explicitly or call
setSecurityCredential(...)which encrypts an initiator password using a conservative AES operation insideMpesaConfig. - queueTimeoutUrl / resultUrl: URLs used for callback endpoints
- storeFile: filesystem path or stream-like URI used to persist the OAuth token cache (defaults to a temp file named from consumer key/secret)
- debug: boolean — when enabled some parts print helpful logs
Notes about getEncryptedFileName() and setSecurityCredential() implementations
getEncryptedFileName()produces a deterministic filename used when no storeFile is provided. It uses a local AES cipher to derive a name from your consumer key/secret (internal implementation detail).setSecurityCredential(initiatorPassword)encrypts the initiator password and stores it in the config. If you already have an encrypted credential from Safaricom you can calloverrideSecurityCredential(...).
Environment variables and secrets
It's strongly recommended to load the consumer keys, secrets and passkeys from environment variables rather than hardcoding them. Example .env (not included in repo):
MPESA_CONSUMER_KEY=your_consumer_key
MPESA_CONSUMER_SECRET=your_consumer_secret
MPESA_ENV=sandbox
MPESA_BUSINESS_CODE=174379
MPESA_PASS_KEY=your_pass_keyUse packages like dotenv in development to load .env safely.
Detailed examples (TypeScript)
The examples below assume you have an Mpesa instance configured.
Create the SDK instance
import { Mpesa } from "./src/Mpesa";
const sdk = new Mpesa(
process.env.MPESA_CONSUMER_KEY!,
process.env.MPESA_CONSUMER_SECRET!,
(process.env.MPESA_ENV as "sandbox" | "live") ?? "sandbox"
);
sdk.setDebug(process.env.DEBUG === "1");
sdk.setBusinessCode(process.env.MPESA_BUSINESS_CODE!);
sdk.setPassKey(process.env.MPESA_PASS_KEY!);- STK (Lipa Na M-Pesa)
- Build & execute an STK push
const stk = sdk.stk()
.setAmount(10)
.setPhoneNumber("2547xxxxxxxx")
.setCallbackUrl("https://example.com/mpesa/stk-callback")
.setTransactionType("CustomerPayBillOnline")
.setAccountReference("INV-1001")
.setTransactionDesc("Order 1001");
await stk.push();
console.log(stk.getResponse());- Poll for status (optional)
const checkout = stk.getCheckoutRequestId();
await stk.query(checkout);
console.log(stk.getResponse());- Customer-to-Business (C2B)
- Register confirmation/validation URLs (Sandbox requirement)
const c2b = sdk.customerToBusiness()
.setConfirmationUrl("https://example.com/mpesa/c2b/confirmation")
.setValidationUrl("https://example.com/mpesa/c2b/validation");
await c2b.registerUrl();
console.log(c2b.getResponse());- Simulate an inbound payment (sandbox)
await sdk.customerToBusiness()
.setCommandId("CustomerPayBillOnline")
.setBillRefNumber("TEST123")
.setAmount(10)
.setPhoneNumber("2547xxxxxxxx")
.simulate();- Business-to-Customer (B2C)
const b2c = sdk.businessToCustomer()
.setInitiatorName("api_initiator")
.setCommandId("BusinessPayment")
.setAmount(200)
.setPhoneNumber("2547xxxxxxxx")
.setRemarks("Payout");
const b2cResp = await b2c.paymentRequest();
console.log(b2cResp);- Account Balance
const ab = sdk.accountBalance()
.setInitiator("api_initiator")
.setIdentifierType("4")
.setRemarks("Daily balance");
const resp = await ab.accountBalance();
console.log(resp);- Transaction Status
await sdk.transactionStatus()
.setInitiator("initiator")
.setTransactionId("ABC123")
.setIdentifierType("4")
.checkTransactionStatus();- Reversal
await sdk.reversal()
.setInitiator("initiator")
.setTransactionId("ABC123")
.setRemarks("Mistaken payment")
.reverse();- B2B — BusinessPayBill and BusinessBuyGoods
Both endpoints are similar; use businessPayBill() or businessBuyGoods():
await sdk.businessPayBill()
.setInitiator("initiator")
.setSecurityCredential(process.env.MPESA_SECURITY_CREDENTIAL!)
.setPartyA("600000")
.setPartyB("600001")
.setAmount(1000)
.setQueueTimeoutUrl("https://example.com/timeout")
.setResultUrl("https://example.com/result")
.payBill();
await sdk.businessBuyGoods()
.setInitiator("initiator")
.setSecurityCredential(process.env.MPESA_SECURITY_CREDENTIAL!)
.setPartyA("600000")
.setPartyB("600001")
.setAmount(1000)
.buyGoods();Token cache and concurrency
The SDK uses TokenManager to cache an OAuth token into a small JSON file. Important details:
- Cache format: { token, expires_at, created_at } — unix timestamps (seconds).
- Expiry jitter: tokens are considered expired 60s before their actual expiry to avoid edge cases.
- Locking: a sidecar
.lockfile or deterministic tmp lock prevents concurrent refreshes across processes. If yourstoreFileis a stream URI (S3/etc.), the SDK synthesizes a deterministic temp lock filename. - Clearing cache:
sdk.clearTokenCache()removes cached token and lock.
If you deploy on multiple nodes, use a shared token cache or central token service to avoid many refreshes.
Webhooks / callbacks (best practices)
- Use HTTPS endpoints reachable by Safaricom sandbox/live. ngrok is useful during development.
- Log incoming requests (headers + body) and persist for auditing.
- Validate content where possible. The SDK does not perform webhook signature verification.
- Respond fast: Safaricom expects a quick acknowledgement; handle long-running work asynchronously.
Testing & example scripts
TypeScript STK example:
src/Tests/stk_push_test.ts— simple script for manual testing against sandbox.- Run with
npx ts-node src/Tests/stk_push_test.ts.
- Run with
PHP test example:
mpesa-php-sdk/src/Tests/stk_push_test.php.Suggested automated testing approach: use Jest with environment-based toggles (
MPESA_INTEGRATION_TEST=true) to guard live network calls. For unit tests, mockfetchand theApiClient.
Development & docs generation
- Type-check the project:
npx tsc --noEmit- Generate API docs (optional): use TypeDoc when ready:
npm install --save-dev typedoc
npx typedoc --out docs srcContributing
- Open an issue describing the change or bug.
- Create a branch
feature/your-topic. - Add tests and update docs.
- Open a PR and reference the issue.
Please follow existing code style and keep changes isolated.
Security & secrets
- DO NOT commit production credentials. Use environment variables or a secrets manager.
- The token cache and files may contain sensitive data — secure the filesystem (permissions, encryption at rest) in production.
License
MIT
Appendix A — mapping of services & primary methods
- STK:
stk().setAmount(...).setPhoneNumber(...).push()—getResponse(),query() - C2B:
customerToBusiness().registerUrl()and.simulate() - B2C:
businessToCustomer().paymentRequest() - Account Balance:
accountBalance().accountBalance() - Transaction Status:
transactionStatus().checkTransactionStatus() - Reversal:
reversal().reverse() - B2B:
businessPayBill().payBill()andbusinessBuyGoods().buyGoods()
If you'd like I can:
- Add example Jest integration tests guarded with environment flags
- Create a
docs/folder (TypeDoc-generated) and add sample HTML pages - Provide a
README-quickstart.mdtrimmed for copy/paste usage examples
Tell me which of the above follow-ups you'd like and I'll implement it.
