pesafy 0.3.1 → 0.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +3 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +0 -9
- package/dist/index.d.ts +0 -9
- package/dist/index.js +3 -4
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -288,7 +288,7 @@ async function registerC2BUrls(baseUrl, accessToken, request) {
|
|
|
288
288
|
ValidationURL: request.validationUrl
|
|
289
289
|
};
|
|
290
290
|
const { data } = await httpRequest(
|
|
291
|
-
`${baseUrl}/mpesa/c2b/
|
|
291
|
+
`${baseUrl}/mpesa/c2b/v1/registerurl`,
|
|
292
292
|
{
|
|
293
293
|
method: "POST",
|
|
294
294
|
headers: { Authorization: `Bearer ${accessToken}` },
|
|
@@ -302,7 +302,6 @@ async function registerC2BUrls(baseUrl, accessToken, request) {
|
|
|
302
302
|
async function simulateC2B(baseUrl, accessToken, request) {
|
|
303
303
|
const commandId = request.commandId ?? "CustomerPayBillOnline";
|
|
304
304
|
const isBuyGoods = commandId === "CustomerBuyGoodsOnline";
|
|
305
|
-
const billRef = isBuyGoods ? void 0 : request.billRefNumber ?? "";
|
|
306
305
|
const body = {
|
|
307
306
|
ShortCode: Number(request.shortCode),
|
|
308
307
|
CommandID: commandId,
|
|
@@ -310,8 +309,8 @@ async function simulateC2B(baseUrl, accessToken, request) {
|
|
|
310
309
|
// Daraja expects Msisdn as a number (not a quoted string)
|
|
311
310
|
Msisdn: msisdnToNumber(request.phoneNumber)
|
|
312
311
|
};
|
|
313
|
-
if (
|
|
314
|
-
body.BillRefNumber =
|
|
312
|
+
if (!isBuyGoods) {
|
|
313
|
+
body.BillRefNumber = request.billRefNumber ?? "";
|
|
315
314
|
}
|
|
316
315
|
const { data } = await httpRequest(
|
|
317
316
|
`${baseUrl}/mpesa/c2b/v2/simulate`,
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/utils/errors/error-factory.ts","../src/utils/http/client.ts","../src/core/auth/token-manager.ts","../src/core/encryption/security-credentials.ts","../src/mpesa/b2b/b2b.ts","../src/utils/phone.ts","../src/mpesa/stk-push/utils.ts","../src/mpesa/b2c/b2c.ts","../src/mpesa/c2b/register-url.ts","../src/mpesa/c2b/simulate.ts","../src/mpesa/c2b/types.ts","../src/mpesa/qr-code/dynamic-qr.ts","../src/mpesa/reversal/reversal.ts","../src/mpesa/stk-push/stk-push.ts","../src/mpesa/stk-push/stk-query.ts","../src/mpesa/stk-push/types.ts","../src/mpesa/transaction-status/query.ts","../src/mpesa/types.ts","../src/mpesa/index.ts","../src/express/index.ts","../src/mpesa/webhooks/retry.ts","../src/mpesa/webhooks/signature-verifier.ts","../src/mpesa/webhooks/webhook-handler.ts"],"names":["publicEncrypt"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Error creation utilities for Pesafy\n */\n\nimport type { ErrorCode, PesafyErrorOptions } from \"./types\";\n\nexport class PesafyError extends Error {\n readonly code: ErrorCode;\n readonly statusCode?: number;\n readonly response?: unknown;\n readonly requestId?: string;\n override readonly cause?: unknown;\n\n constructor(options: PesafyErrorOptions) {\n super(options.message);\n Object.defineProperty(this, \"name\", { value: \"PesafyError\" });\n this.code = options.code;\n this.statusCode = options.statusCode;\n this.response = options.response;\n this.requestId = options.requestId;\n this.cause = options.cause;\n\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, PesafyError);\n }\n }\n\n toJSON() {\n return {\n name: this.name,\n code: this.code,\n message: this.message,\n statusCode: this.statusCode,\n requestId: this.requestId,\n };\n }\n}\n\nexport function createError(options: PesafyErrorOptions): PesafyError {\n return new PesafyError(options);\n}\n","/**\n * HTTP client for Daraja API requests\n */\n\nimport { PesafyError } from \"../errors\";\nimport type { HttpRequestOptions, HttpResponse } from \"./types\";\n\nconst DEFAULT_TIMEOUT = 30000;\n\nexport async function httpRequest<T = unknown>(\n url: string,\n options: HttpRequestOptions = {}\n): Promise<HttpResponse<T>> {\n const {\n method = \"GET\",\n headers = {},\n body,\n timeout = DEFAULT_TIMEOUT,\n } = options;\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeout);\n\n try {\n const response = await fetch(url, {\n method,\n headers: {\n \"Content-Type\": \"application/json\",\n ...headers,\n },\n body: body ? JSON.stringify(body) : undefined,\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n let data: T;\n const text = await response.text();\n try {\n data = (text ? JSON.parse(text) : {}) as T;\n } catch {\n data = { raw: text } as T;\n }\n\n if (!response.ok) {\n // Include the full Daraja response body so callers can see exactly what\n // Safaricom returned: \"Invalid Access Token\", \"Bad Request - Invalid\n // Shortcode\", \"Wrong credentials\", etc. Previously this was swallowed.\n const bodyStr = text.length > 0 ? ` — ${text}` : \"\";\n throw new PesafyError({\n code: \"API_ERROR\",\n message: `Request failed with status ${response.status}${bodyStr}`,\n statusCode: response.status,\n response: data,\n });\n }\n\n return {\n data,\n status: response.status,\n headers: response.headers,\n };\n } catch (error) {\n clearTimeout(timeoutId);\n\n if (error instanceof PesafyError) throw error;\n if (error instanceof Error) {\n if (error.name === \"AbortError\") {\n throw new PesafyError({\n code: \"TIMEOUT\",\n message: `Request timed out after ${timeout}ms`,\n cause: error,\n });\n }\n throw new PesafyError({\n code: \"NETWORK_ERROR\",\n message: error.message,\n cause: error,\n });\n }\n throw new PesafyError({\n code: \"REQUEST_FAILED\",\n message: \"An unknown error occurred\",\n cause: error,\n });\n }\n}\n","/**\n * OAuth token manager for Daraja API\n * Token validity: 3600 seconds (1 hour)\n */\n\nimport { PesafyError } from \"../../utils/errors\";\nimport { httpRequest } from \"../../utils/http\";\nimport type { TokenResponse } from \"./types\";\n\nconst TOKEN_BUFFER_SECONDS = 60; // Refresh token 60s before expiry\n\nexport class TokenManager {\n private consumerKey: string;\n private consumerSecret: string;\n private baseUrl: string;\n private cachedToken: string | null = null;\n private tokenExpiresAt = 0;\n\n constructor(consumerKey: string, consumerSecret: string, baseUrl: string) {\n this.consumerKey = consumerKey;\n this.consumerSecret = consumerSecret;\n this.baseUrl = baseUrl;\n }\n\n private getAuthHeader(): string {\n const credentials = `${this.consumerKey}:${this.consumerSecret}`;\n const encoded = Buffer.from(credentials, \"utf-8\").toString(\"base64\");\n return `Basic ${encoded}`;\n }\n\n async getAccessToken(): Promise<string> {\n const now = Date.now() / 1000;\n if (this.cachedToken && this.tokenExpiresAt > now + TOKEN_BUFFER_SECONDS) {\n return this.cachedToken;\n }\n\n const url = `${this.baseUrl}/oauth/v1/generate?grant_type=client_credentials`;\n const response = await httpRequest<TokenResponse>(url, {\n method: \"GET\",\n headers: {\n Authorization: this.getAuthHeader(),\n },\n });\n\n const data = response.data;\n if (!data.access_token) {\n throw new PesafyError({\n code: \"AUTH_FAILED\",\n message: \"Failed to obtain access token\",\n response: data,\n });\n }\n\n this.cachedToken = data.access_token;\n this.tokenExpiresAt = now + (data.expires_in ?? 3600);\n return this.cachedToken;\n }\n\n clearCache(): void {\n this.cachedToken = null;\n this.tokenExpiresAt = 0;\n }\n}\n","/**\n * Security credential encryption for B2C, B2B, Reversal APIs\n * Uses RSA with PKCS#1.5 padding per Daraja API spec\n * Download certificates from: https://developer.safaricom.co.ke/APIs\n */\n\nimport { publicEncrypt } from \"node:crypto\";\nimport { PesafyError } from \"../../utils/errors\";\n\n/** Encrypt initiator password with M-Pesa public certificate (PEM format) */\nexport function encryptSecurityCredential(\n initiatorPassword: string,\n certificatePem: string\n): string {\n try {\n const passwordBuffer = Buffer.from(initiatorPassword, \"utf-8\");\n const encrypted = publicEncrypt(\n {\n key: certificatePem,\n padding: 1, // RSA_PKCS1_PADDING\n },\n passwordBuffer\n );\n return encrypted.toString(\"base64\");\n } catch (error) {\n throw new PesafyError({\n code: \"ENCRYPTION_FAILED\",\n message: \"Failed to encrypt security credential\",\n cause: error,\n });\n }\n}\n","/**\n * B2B - Business to Business payments\n * API: POST /mpesa/b2b/v1/paymentrequest\n */\n\nimport { httpRequest } from \"../../utils/http\";\nimport type { B2BRequest } from \"./types\";\n\nexport interface B2BResponse {\n OriginatorConversationID: string;\n ConversationID: string;\n ResponseCode: string;\n ResponseDescription: string;\n}\n\nexport async function processB2B(\n baseUrl: string,\n accessToken: string,\n securityCredential: string,\n initiatorName: string,\n request: B2BRequest\n): Promise<B2BResponse> {\n const body = {\n Initiator: initiatorName,\n SecurityCredential: securityCredential,\n CommandID: request.commandId ?? \"BusinessPayBill\",\n SenderIdentifierType: request.senderIdentifierType ?? 4,\n RecieverIdentifierType: request.receiverIdentifierType ?? 4,\n Amount: Math.round(request.amount),\n PartyA: request.shortCode,\n PartyB: request.receiverShortCode,\n AccountReference: request.accountReference ?? \"\",\n Remarks: request.remarks ?? \"B2B Payment\",\n QueueTimeOutURL: request.timeoutUrl,\n ResultURL: request.resultUrl,\n };\n\n const { data } = await httpRequest<B2BResponse>(\n `${baseUrl}/mpesa/b2b/v1/paymentrequest`,\n {\n method: \"POST\",\n headers: { Authorization: `Bearer ${accessToken}` },\n body,\n }\n );\n\n return data;\n}\n","/**\n * Shared phone number utilities for M-Pesa API calls.\n *\n * Two formatters are intentionally separate:\n * - formatSafaricomPhone → strict, for STK Push (only Safaricom/Airtel)\n * - formatKenyanMsisdn → permissive, for C2B simulate and B2C PartyB\n *\n * Daraja consistently expects 12-digit MSISDN without the '+' prefix.\n */\n\n/** Strips non-digit characters and normalises leading zeros / country code. */\nfunction toE164Kenya(phone: string): string {\n const digits = phone.replace(/\\D/g, \"\");\n\n if (digits.startsWith(\"254\")) return digits;\n if (digits.startsWith(\"0\")) return `254${digits.slice(1)}`;\n // Bare 9-digit local number (e.g. 712345678)\n if (digits.length === 9) return `254${digits}`;\n\n // Fallback — prepend country code and let the validator catch it\n return `254${digits}`;\n}\n\n/**\n * Strict formatter for STK Push.\n * Accepts Safaricom (2547xx) and Airtel Kenya (2541x) numbers only.\n * Throws a clear error on invalid input so Daraja never receives a bad MSISDN.\n */\nexport function formatSafaricomPhone(phone: string): string {\n const formatted = toE164Kenya(phone);\n\n // 2547xxxxxxxx (Safaricom) | 2541xxxxxxxx (Airtel Kenya)\n if (!/^254[71]\\d{8}$/.test(formatted)) {\n throw new Error(\n `Invalid Kenyan phone number: \"${phone}\". ` +\n \"Expected format: 07XXXXXXXX, 2547XXXXXXXX, or +2547XXXXXXXX.\"\n );\n }\n\n return formatted;\n}\n\n/**\n * Permissive formatter for C2B simulate and B2C PartyB.\n * Accepts any valid 12-digit Kenyan MSISDN (Safaricom, Airtel, Telkom, …).\n * Still throws if the result is structurally impossible.\n */\nexport function formatKenyanMsisdn(phone: string): string {\n const formatted = toE164Kenya(phone);\n\n // Must be exactly 12 digits and start with 254\n if (!/^254\\d{9}$/.test(formatted)) {\n throw new Error(\n `Invalid MSISDN: \"${phone}\". ` +\n \"Expected a 12-digit Kenyan number starting with 254.\"\n );\n }\n\n return formatted;\n}\n\n/**\n * Converts a formatted MSISDN string to the numeric form Daraja expects in\n * C2B simulate requests (\"Msisdn\": 254708374149 — no quotes in the JSON).\n */\nexport function msisdnToNumber(phone: string): number {\n return parseInt(formatKenyanMsisdn(phone), 10);\n}\n","/**\n * STK Push utilities\n *\n * Phone formatting delegates to the shared `formatSafaricomPhone` util which\n * validates Safaricom/Airtel numbers and throws a clear error on bad input.\n */\nexport { formatSafaricomPhone as formatPhoneNumber } from \"../../utils/phone\";\n\n/**\n * Generates the STK Push password.\n * Formula: Base64(Shortcode + Passkey + Timestamp)\n *\n * Uses btoa() instead of Buffer — works in Node.js, Bun, browsers, and\n * edge runtimes (Cloudflare Workers, Deno, etc.).\n *\n * IMPORTANT: The timestamp passed here MUST be the exact same value sent in\n * the request body's `Timestamp` field. Safaricom validates they match.\n * Always call getTimestamp() once and pass the result to both this function\n * and the request body — never call getTimestamp() twice.\n */\nexport function getStkPushPassword(\n shortCode: string,\n passKey: string,\n timestamp: string\n): string {\n const raw = `${shortCode}${passKey}${timestamp}`;\n return btoa(raw);\n}\n\n/**\n * Generates a Daraja-compatible timestamp: YYYYMMDDHHmmss\n * Call this once per request and reuse the value.\n */\nexport function getTimestamp(): string {\n const now = new Date();\n const pad = (n: number) => n.toString().padStart(2, \"0\");\n return [\n now.getFullYear(),\n pad(now.getMonth() + 1),\n pad(now.getDate()),\n pad(now.getHours()),\n pad(now.getMinutes()),\n pad(now.getSeconds()),\n ].join(\"\");\n}\n","/**\n * B2C - Business to Customer payments\n * API: POST /mpesa/b2c/v3/paymentrequest\n */\n\nimport { httpRequest } from \"../../utils/http\";\nimport { formatPhoneNumber } from \"../stk-push/utils\";\nimport type { B2CRequest } from \"./types\";\n\nexport interface B2CResponse {\n OriginatorConversationID: string;\n ConversationID: string;\n ResponseCode: string;\n ResponseDescription: string;\n}\n\nexport async function processB2C(\n baseUrl: string,\n accessToken: string,\n securityCredential: string,\n initiatorName: string,\n request: B2CRequest\n): Promise<B2CResponse> {\n const body = {\n OriginatorConversationID: `AG_${Date.now()}_${Math.random().toString(36).slice(2)}`,\n InitiatorName: initiatorName,\n SecurityCredential: securityCredential,\n CommandID: request.commandId ?? \"BusinessPayment\",\n Amount: Math.round(request.amount),\n PartyA: request.shortCode,\n PartyB: formatPhoneNumber(request.phoneNumber),\n Remarks: request.remarks ?? \"Payment\",\n QueueTimeOutURL: request.timeoutUrl,\n ResultURL: request.resultUrl,\n Occasion: request.occasion ?? \"\",\n };\n\n const { data } = await httpRequest<B2CResponse>(\n `${baseUrl}/mpesa/b2c/v3/paymentrequest`,\n {\n method: \"POST\",\n headers: { Authorization: `Bearer ${accessToken}` },\n body,\n }\n );\n\n return data;\n}\n","/**\n * C2B Register URLs\n * API: POST /mpesa/c2b/v2/registerurl\n *\n * Registers Validation and Confirmation callback URLs for a short code.\n * Sandbox: can be called multiple times.\n * Production: one-time registration; contact Safaricom to change.\n */\nimport { httpRequest } from \"../../utils/http\";\nimport type { C2BRegisterUrlRequest, C2BResponseType } from \"./types\";\n\n/**\n * Actual response shape from Daraja.\n * Note: Daraja's field name has a typo (\"CoversationID\" — missing the 'n').\n * We match it exactly so JSON parsing is lossless.\n */\nexport interface C2BRegisterUrlResponse {\n /** Global unique identifier for this registration request. Daraja typo: \"Coversation\" */\n OriginatorCoversationID: string;\n /** \"0\" = accepted */\n ResponseCode: string;\n ResponseDescription: string;\n}\n\nexport async function registerC2BUrls(\n baseUrl: string,\n accessToken: string,\n request: C2BRegisterUrlRequest\n): Promise<C2BRegisterUrlResponse> {\n const responseType: C2BResponseType = request.responseType ?? \"Completed\";\n\n const body = {\n ShortCode: request.shortCode,\n ResponseType: responseType,\n ConfirmationURL: request.confirmationUrl,\n ValidationURL: request.validationUrl,\n };\n\n const { data } = await httpRequest<C2BRegisterUrlResponse>(\n `${baseUrl}/mpesa/c2b/v2/registerurl`,\n {\n method: \"POST\",\n headers: { Authorization: `Bearer ${accessToken}` },\n body,\n }\n );\n\n return data;\n}\n","/**\n * C2B Simulate (sandbox only)\n * API: POST /mpesa/c2b/v2/simulate\n *\n * Simulates a customer paying a Paybill or Till number.\n * This endpoint is NOT available in production — use STK Push or Dynamic QR\n * for production payment initiation.\n */\n\nimport { httpRequest } from \"../../utils/http\";\nimport { msisdnToNumber } from \"../../utils/phone\";\nimport type { C2BCommandId, C2BSimulateRequest } from \"./types\";\n\n/**\n * Actual response shape from Daraja.\n * Note: Daraja's field name has a typo (\"CoversationID\" — missing the 'n').\n * We match it exactly so JSON parsing is lossless.\n * There is NO ConversationID field in this response (unlike B2C/B2B).\n */\nexport interface C2BSimulateResponse {\n /** Global unique identifier for this simulate request. Daraja typo: \"Coversation\" */\n OriginatorCoversationID: string;\n /** \"0\" = accepted */\n ResponseCode: string;\n ResponseDescription: string;\n}\n\nexport async function simulateC2B(\n baseUrl: string,\n accessToken: string,\n request: C2BSimulateRequest\n): Promise<C2BSimulateResponse> {\n const commandId: C2BCommandId = request.commandId ?? \"CustomerPayBillOnline\";\n const isBuyGoods = commandId === \"CustomerBuyGoodsOnline\";\n\n /**\n * BillRefNumber rules per Daraja docs:\n * - CustomerPayBillOnline: account reference (e.g. \"Test Ref\") — use passed value or empty string\n * - CustomerBuyGoodsOnline: must be null / omitted\n *\n * We omit the key entirely for BuyGoods rather than sending null, which can\n * cause a 400 \"Invalid Request Payload\" on some Daraja versions.\n */\n const billRef = isBuyGoods ? undefined : (request.billRefNumber ?? \"\");\n\n const body: Record<string, unknown> = {\n ShortCode: Number(request.shortCode),\n CommandID: commandId,\n Amount: Math.round(request.amount),\n // Daraja expects Msisdn as a number (not a quoted string)\n Msisdn: msisdnToNumber(request.phoneNumber),\n };\n\n if (billRef !== undefined) {\n body.BillRefNumber = billRef;\n }\n\n const { data } = await httpRequest<C2BSimulateResponse>(\n `${baseUrl}/mpesa/c2b/v2/simulate`,\n {\n method: \"POST\",\n headers: { Authorization: `Bearer ${accessToken}` },\n body,\n }\n );\n\n return data;\n}\n","/**\n * C2B (Customer to Business) types\n * API Reference: https://developer.safaricom.co.ke/APIs/CustomerToBusinessV2\n *\n * C2B v2 uses masked MSISDN (e.g. \"2547 ***** 126\").\n * C2B v1 used SHA-256 hashed MSISDN — do NOT use v1.\n */\n\n// ─── Command IDs ─────────────────────────────────────────────────────────────\n\n/**\n * The two valid C2B CommandID values:\n * - CustomerPayBillOnline → payment to a Paybill number\n * - CustomerBuyGoodsOnline → payment to a Till number\n */\nexport type C2BCommandId = \"CustomerPayBillOnline\" | \"CustomerBuyGoodsOnline\";\n\n/**\n * Default action when the ValidationURL is unreachable.\n * \"Completed\" → M-PESA auto-completes the transaction.\n * \"Cancelled\" → M-PESA auto-cancels the transaction.\n * Note: values are case-sensitive per Daraja docs.\n */\nexport type C2BResponseType = \"Completed\" | \"Cancelled\";\n\n// ─── Register URL ─────────────────────────────────────────────────────────────\n\nexport interface C2BRegisterUrlRequest {\n /** Paybill or Till short code */\n shortCode: string;\n /** URL that receives payment confirmation after a successful transaction */\n confirmationUrl: string;\n /** URL that receives validation requests (only when external validation is enabled) */\n validationUrl: string;\n responseType?: C2BResponseType;\n}\n\n// ─── C2B Simulate (sandbox only) ─────────────────────────────────────────────\n\nexport interface C2BSimulateRequest {\n /** Paybill or Till short code */\n shortCode: string;\n /**\n * Transaction type.\n * \"CustomerPayBillOnline\" → payment to a Paybill number (default)\n * \"CustomerBuyGoodsOnline\" → payment to a Till number\n */\n commandId?: C2BCommandId;\n /** Transaction amount (whole number, min 1 KES) */\n amount: number;\n /** Customer's phone number — any valid Kenyan MSISDN */\n phoneNumber: string;\n /**\n * Account reference for Paybill payments.\n * Must be null / omitted for CustomerBuyGoodsOnline.\n */\n billRefNumber?: string;\n}\n\n// ─── C2B Callback payload types ───────────────────────────────────────────────\n// Safaricom POSTs these to your ConfirmationURL / ValidationURL after payment.\n// v2 returns a masked MSISDN (e.g. \"2547 ***** 126\").\n\n/**\n * Shared fields present in both Validation and Confirmation callback payloads.\n */\nexport interface C2BCallbackPayloadBase {\n TransactionType: string;\n /** Unique M-PESA transaction ID (e.g. \"RKL51ZDR4F\") */\n TransID: string;\n /** 14-digit timestamp: YYYYMMDDHHmmss */\n TransTime: string;\n /** Transaction amount as a string (e.g. \"5.00\") */\n TransAmount: string;\n BusinessShortCode: string;\n /** Account reference; empty for Till transactions */\n BillRefNumber: string;\n InvoiceNumber: string;\n OrgAccountBalance: string;\n ThirdPartyTransID: string;\n /** v2: masked MSISDN e.g. \"2547 ***** 126\" */\n MSISDN: string;\n FirstName: string;\n MiddleName: string;\n LastName: string;\n}\n\n/** Payload posted to your ValidationURL (if external validation is enabled) */\nexport type C2BValidationPayload = C2BCallbackPayloadBase;\n\n/** Payload posted to your ConfirmationURL after successful payment */\nexport type C2BConfirmationPayload = C2BCallbackPayloadBase;\n\n/**\n * Union of both C2B callback types.\n * Use the discriminated `TransactionType` field if you need to distinguish them,\n * or register separate Express routes for each URL.\n */\nexport type C2BCallbackPayload = C2BValidationPayload | C2BConfirmationPayload;\n\n// ─── Validation response codes ────────────────────────────────────────────────\n\n/**\n * Codes your ValidationURL must return to Safaricom to accept or reject\n * a payment before it is processed.\n */\nexport const C2B_REJECTION_CODES = {\n /** Accept the transaction — M-PESA proceeds to process and confirm */\n ACCEPT: \"0\",\n /** Reject the transaction — M-PESA cancels and notifies the customer */\n REJECT: \"1\",\n} as const;\n\nexport type C2BRejectionCode =\n (typeof C2B_REJECTION_CODES)[keyof typeof C2B_REJECTION_CODES];\n","/**\n * Dynamic QR Code - Generate LIPA NA M-PESA QR codes\n * API: POST /mpesa/qrcode/v1/generate\n */\n\nimport { httpRequest } from \"../../utils/http\";\nimport type { DynamicQRRequest } from \"./types\";\n\nexport interface DynamicQRResponse {\n ResponseCode: string;\n RequestID: string;\n ResponseDescription: string;\n QRCode: string;\n}\n\nexport async function generateDynamicQR(\n baseUrl: string,\n accessToken: string,\n request: DynamicQRRequest\n): Promise<DynamicQRResponse> {\n const body = {\n MerchantName: request.merchantName,\n RefNo: request.refNo,\n Amount: Math.round(request.amount),\n TrxCode: request.trxCode,\n CPI: request.cpi,\n Size: request.size ?? \"300\",\n };\n\n const { data } = await httpRequest<DynamicQRResponse>(\n `${baseUrl}/mpesa/qrcode/v1/generate`,\n {\n method: \"POST\",\n headers: { Authorization: `Bearer ${accessToken}` },\n body,\n }\n );\n\n return data;\n}\n","/**\n * Transaction Reversal\n * API: POST /mpesa/reversal/v1/request\n */\n\nimport { httpRequest } from \"../../utils/http\";\nimport type { ReversalRequest } from \"./types\";\n\nexport interface ReversalResponse {\n OriginatorConversationID: string;\n ConversationID: string;\n ResponseCode: string;\n ResponseDescription: string;\n}\n\nexport async function processReversal(\n baseUrl: string,\n accessToken: string,\n securityCredential: string,\n initiatorName: string,\n request: ReversalRequest\n): Promise<ReversalResponse> {\n const body = {\n Initiator: initiatorName,\n SecurityCredential: securityCredential,\n CommandID: \"TransactionReversal\",\n TransactionID: request.transactionId,\n Amount: Math.round(request.amount),\n ReceiverParty: request.shortCode,\n RecieverIdentifierType: 4,\n ResultURL: request.resultUrl,\n QueueTimeOutURL: request.timeoutUrl,\n Remarks: request.remarks ?? \"Reversal\",\n Occasion: request.occasion ?? \"Reversal\",\n };\n\n const { data } = await httpRequest<ReversalResponse>(\n `${baseUrl}/mpesa/reversal/v1/request`,\n {\n method: \"POST\",\n headers: { Authorization: `Bearer ${accessToken}` },\n body,\n }\n );\n\n return data;\n}\n","/**\n * M-Pesa Express (STK Push) - Initiates payment prompt on customer's phone\n * API: POST /mpesa/stkpush/v1/processrequest\n */\n\nimport { httpRequest } from \"../../utils/http\";\nimport type { StkPushRequest, StkPushResponse } from \"./types\";\nimport { formatPhoneNumber, getStkPushPassword, getTimestamp } from \"./utils\";\n\nexport async function processStkPush(\n baseUrl: string,\n accessToken: string,\n request: StkPushRequest\n): Promise<StkPushResponse> {\n // Validate amount: Daraja requires a whole number ≥ 1 KES.\n // Math.round(0.3) = 0, which Daraja rejects — catch it here with a clear error.\n const amount = Math.round(request.amount);\n if (amount < 1) {\n throw new Error(\n `Amount must be at least KES 1 (got ${request.amount} which rounds to ${amount}).`\n );\n }\n\n // Generate timestamp ONCE — must be identical in both Password and Timestamp fields.\n // Safaricom validates that Base64(Shortcode+Passkey+Timestamp) matches the\n // Timestamp field; generating two separate timestamps causes auth failures.\n const timestamp = getTimestamp();\n\n // For CustomerBuyGoodsOnline (Till Number), PartyB is the till number.\n // For CustomerPayBillOnline (Paybill), PartyB is the shortcode.\n // Docs: https://developer.safaricom.co.ke/APIs/MpesaExpressSimulate\n const partyB = request.partyB ?? request.shortCode;\n\n const body = {\n BusinessShortCode: request.shortCode,\n Password: getStkPushPassword(request.shortCode, request.passKey, timestamp),\n Timestamp: timestamp,\n TransactionType: request.transactionType ?? \"CustomerPayBillOnline\",\n Amount: amount,\n PartyA: formatPhoneNumber(request.phoneNumber),\n PartyB: partyB,\n PhoneNumber: formatPhoneNumber(request.phoneNumber),\n CallBackURL: request.callbackUrl,\n AccountReference: request.accountReference.slice(0, 12),\n TransactionDesc: request.transactionDesc.slice(0, 13),\n };\n\n const { data } = await httpRequest<StkPushResponse>(\n `${baseUrl}/mpesa/stkpush/v1/processrequest`,\n {\n method: \"POST\",\n headers: { Authorization: `Bearer ${accessToken}` },\n body,\n }\n );\n\n return data;\n}\n","/**\n * STK Push Query - Check status of STK Push transaction\n * API: POST /mpesa/stkpushquery/v1/query\n */\n\nimport { httpRequest } from \"../../utils/http\";\nimport type { StkQueryRequest, StkQueryResponse } from \"./types\";\nimport { getStkPushPassword, getTimestamp } from \"./utils\";\n\nexport async function queryStkPush(\n baseUrl: string,\n accessToken: string,\n request: StkQueryRequest\n): Promise<StkQueryResponse> {\n // Generate timestamp ONCE — Password and Timestamp field must match exactly.\n const timestamp = getTimestamp();\n\n const body = {\n BusinessShortCode: request.shortCode,\n Password: getStkPushPassword(request.shortCode, request.passKey, timestamp),\n Timestamp: timestamp,\n CheckoutRequestID: request.checkoutRequestId,\n };\n\n const { data } = await httpRequest<StkQueryResponse>(\n `${baseUrl}/mpesa/stkpushquery/v1/query`,\n {\n method: \"POST\",\n headers: { Authorization: `Bearer ${accessToken}` },\n body,\n }\n );\n\n return data;\n}\n","/** STK Push (M-Pesa Express) types */\n\nexport type TransactionType =\n | \"CustomerPayBillOnline\"\n | \"CustomerBuyGoodsOnline\";\n\nexport interface StkPushRequest {\n amount: number;\n phoneNumber: string;\n callbackUrl: string;\n accountReference: string;\n transactionDesc: string;\n /** Business shortcode - Paybill or HO/store number for Till */\n shortCode: string;\n passKey: string;\n transactionType?: TransactionType;\n /**\n * The credit party receiving funds.\n * - CustomerPayBillOnline: omit (defaults to shortCode)\n * - CustomerBuyGoodsOnline: provide the Till Number\n * Docs: PartyB must be the Till Number when using Buy Goods.\n */\n partyB?: string;\n}\n\nexport interface StkPushResponse {\n MerchantRequestID: string;\n CheckoutRequestID: string;\n ResponseCode: string;\n ResponseDescription: string;\n CustomerMessage: string;\n}\n\nexport interface StkQueryRequest {\n checkoutRequestId: string;\n shortCode: string;\n passKey: string;\n}\n\nexport interface StkQueryResponse {\n ResponseCode: string;\n ResponseDescription: string;\n MerchantRequestID: string;\n CheckoutRequestID: string;\n /**\n * Daraja returns ResultCode as a NUMBER, not a string.\n * 0 = success\n * 1032 = cancelled by user\n * 1037 = timeout (customer did not respond)\n * 2001 = wrong PIN\n * etc.\n * Always compare with === 0, not === \"0\".\n */\n ResultCode: number;\n ResultDesc: string;\n}\n\n// ---------------------------------------------------------------------------\n// Callback payload types — Safaricom POSTs these to your CallBackURL\n// ---------------------------------------------------------------------------\n\n/** A single metadata item in a successful STK callback */\nexport interface StkCallbackMetadataItem {\n Name: \"Amount\" | \"MpesaReceiptNumber\" | \"TransactionDate\" | \"PhoneNumber\";\n /** Present on successful transactions; absent on failure */\n Value?: number | string;\n}\n\n/** Inner callback object for a SUCCESSFUL STK Push */\nexport interface StkCallbackSuccess {\n MerchantRequestID: string;\n CheckoutRequestID: string;\n /** 0 = success */\n ResultCode: 0;\n ResultDesc: string;\n CallbackMetadata: {\n Item: StkCallbackMetadataItem[];\n };\n}\n\n/** Inner callback object for a FAILED/CANCELLED STK Push */\nexport interface StkCallbackFailure {\n MerchantRequestID: string;\n CheckoutRequestID: string;\n /** Non-zero result codes: e.g. 1032 = cancelled by user */\n ResultCode: number;\n ResultDesc: string;\n CallbackMetadata?: never;\n}\n\nexport type StkCallbackInner = StkCallbackSuccess | StkCallbackFailure;\n\n/** Full wrapper that Safaricom POSTs to your CallBackURL */\nexport interface StkPushCallback {\n Body: {\n stkCallback: StkCallbackInner;\n };\n}\n\n/**\n * Type guard — narrows an StkCallbackInner to the success shape.\n * Usage:\n * if (isStkCallbackSuccess(callback.Body.stkCallback)) {\n * const receipt = getCallbackValue(callback, \"MpesaReceiptNumber\");\n * }\n */\nexport function isStkCallbackSuccess(\n cb: StkCallbackInner\n): cb is StkCallbackSuccess {\n return cb.ResultCode === 0;\n}\n\n/**\n * Extracts a named value from a successful callback's metadata items.\n * Returns undefined if the key is absent or the callback failed.\n */\nexport function getCallbackValue(\n callback: StkPushCallback,\n name: StkCallbackMetadataItem[\"Name\"]\n): string | number | undefined {\n const inner = callback.Body.stkCallback;\n if (!isStkCallbackSuccess(inner)) return undefined;\n return inner.CallbackMetadata.Item.find((i) => i.Name === name)?.Value;\n}\n","/**\n * Transaction Status Query\n * API: POST /mpesa/transactionstatus/v1/query\n */\n\nimport { httpRequest } from \"../../utils/http\";\nimport type { TransactionStatusRequest } from \"./types\";\n\nexport interface TransactionStatusResponse {\n OriginatorConversationID: string;\n ConversationID: string;\n ResponseCode: string;\n ResponseDescription: string;\n}\n\nexport async function queryTransactionStatus(\n baseUrl: string,\n accessToken: string,\n securityCredential: string,\n initiatorName: string,\n request: TransactionStatusRequest\n): Promise<TransactionStatusResponse> {\n const body = {\n Initiator: initiatorName,\n SecurityCredential: securityCredential,\n CommandID: \"TransactionStatusQuery\",\n TransactionID: request.transactionId,\n PartyA: request.shortCode,\n IdentifierType: request.identifierType ?? 4,\n ResultURL: request.resultUrl,\n QueueTimeOutURL: request.timeoutUrl,\n Remarks: \"Status\",\n Occasion: \"Query\",\n };\n\n const { data } = await httpRequest<TransactionStatusResponse>(\n `${baseUrl}/mpesa/transactionstatus/v1/query`,\n {\n method: \"POST\",\n headers: { Authorization: `Bearer ${accessToken}` },\n body,\n }\n );\n\n return data;\n}\n","export type Environment = \"sandbox\" | \"production\";\n\nexport const DARAJA_BASE_URLS = {\n sandbox: \"https://sandbox.safaricom.co.ke\",\n production: \"https://api.safaricom.co.ke\",\n} as const;\n\nexport interface MpesaConfig {\n consumerKey: string;\n consumerSecret: string;\n environment: Environment;\n /** Required for STK Push - Lipa Na M-Pesa passkey from Daraja portal */\n lipaNaMpesaShortCode?: string;\n lipaNaMpesaPassKey?: string;\n /** Required for B2C, B2B, Reversal - initiator name and password */\n initiatorName?: string;\n initiatorPassword?: string;\n /** PEM certificate for encrypting initiator password. Download from Daraja portal */\n certificatePath?: string;\n /** PEM certificate string (alternative to certificatePath) */\n certificatePem?: string;\n /** Pre-encrypted security credential (alternative to initiatorPassword + certificate) */\n securityCredential?: string;\n}\n","/**\n * M-Pesa Daraja API client\n */\n\nimport { TokenManager } from \"../core/auth\";\nimport { encryptSecurityCredential } from \"../core/encryption\";\nimport { type B2BRequest, processB2B } from \"./b2b\";\nimport { type B2CRequest, processB2C } from \"./b2c\";\nimport {\n type C2BRegisterUrlRequest,\n type C2BSimulateRequest,\n registerC2BUrls,\n simulateC2B,\n} from \"./c2b\";\nimport { type DynamicQRRequest, generateDynamicQR } from \"./qr-code\";\nimport { processReversal, type ReversalRequest } from \"./reversal\";\nimport {\n processStkPush,\n queryStkPush,\n type StkPushRequest,\n type StkQueryRequest,\n} from \"./stk-push\";\nimport {\n queryTransactionStatus,\n type TransactionStatusRequest,\n} from \"./transaction-status\";\nimport { DARAJA_BASE_URLS, type MpesaConfig } from \"./types\";\n\nexport class Mpesa {\n private config: MpesaConfig;\n private tokenManager: TokenManager;\n private baseUrl: string;\n\n constructor(config: MpesaConfig) {\n this.config = config;\n this.baseUrl = DARAJA_BASE_URLS[config.environment];\n this.tokenManager = new TokenManager(\n config.consumerKey,\n config.consumerSecret,\n this.baseUrl\n );\n }\n\n private async getToken(): Promise<string> {\n return this.tokenManager.getAccessToken();\n }\n\n private async getSecurityCredential(): Promise<string> {\n if (this.config.securityCredential) return this.config.securityCredential;\n if (!this.config.initiatorPassword) {\n throw new Error(\n \"Security credential required: provide securityCredential or (initiatorPassword + certificatePath/certificatePem)\"\n );\n }\n let cert: string;\n if (this.config.certificatePem) {\n cert = this.config.certificatePem;\n } else if (this.config.certificatePath) {\n cert = await Bun.file(this.config.certificatePath).text();\n } else {\n throw new Error(\n \"certificatePath or certificatePem required for B2C/B2B/Reversal\"\n );\n }\n return encryptSecurityCredential(this.config.initiatorPassword, cert);\n }\n\n /** STK Push (M-Pesa Express) - Initiate payment on customer phone */\n async stkPush(request: Omit<StkPushRequest, \"shortCode\" | \"passKey\">) {\n const shortCode = this.config.lipaNaMpesaShortCode ?? \"\";\n const passKey = this.config.lipaNaMpesaPassKey ?? \"\";\n if (!shortCode || !passKey) {\n throw new Error(\n \"lipaNaMpesaShortCode and lipaNaMpesaPassKey required for STK Push\"\n );\n }\n const token = await this.getToken();\n return processStkPush(this.baseUrl, token, {\n ...request,\n shortCode,\n passKey,\n });\n }\n\n /** STK Query - Check STK Push transaction status */\n async stkQuery(request: Omit<StkQueryRequest, \"shortCode\" | \"passKey\">) {\n const shortCode = this.config.lipaNaMpesaShortCode ?? \"\";\n const passKey = this.config.lipaNaMpesaPassKey ?? \"\";\n if (!shortCode || !passKey) {\n throw new Error(\n \"lipaNaMpesaShortCode and lipaNaMpesaPassKey required for STK Query\"\n );\n }\n const token = await this.getToken();\n return queryStkPush(this.baseUrl, token, {\n ...request,\n shortCode,\n passKey,\n });\n }\n\n /** B2C - Send money to customer */\n async b2c(request: B2CRequest) {\n const initiator = this.config.initiatorName ?? \"\";\n const securityCred = await this.getSecurityCredential();\n if (!initiator) throw new Error(\"initiatorName required for B2C\");\n const token = await this.getToken();\n return processB2C(this.baseUrl, token, securityCred, initiator, request);\n }\n\n /** B2B - Send money to business */\n async b2b(request: B2BRequest) {\n const initiator = this.config.initiatorName ?? \"\";\n const securityCred = await this.getSecurityCredential();\n if (!initiator) throw new Error(\"initiatorName required for B2B\");\n const token = await this.getToken();\n return processB2B(this.baseUrl, token, securityCred, initiator, request);\n }\n\n /** C2B - Register validation/confirmation URLs */\n async c2bRegisterUrls(request: C2BRegisterUrlRequest) {\n const token = await this.getToken();\n return registerC2BUrls(this.baseUrl, token, request);\n }\n\n /** C2B - Simulate payment (sandbox only) */\n async c2bSimulate(request: C2BSimulateRequest) {\n const token = await this.getToken();\n return simulateC2B(this.baseUrl, token, request);\n }\n\n /** Dynamic QR - Generate LIPA NA M-PESA QR code */\n async qrCode(request: DynamicQRRequest) {\n const token = await this.getToken();\n return generateDynamicQR(this.baseUrl, token, request);\n }\n\n /** Transaction Status - Query transaction status */\n async transactionStatus(request: TransactionStatusRequest) {\n const initiator = this.config.initiatorName ?? \"\";\n const securityCred = await this.getSecurityCredential();\n if (!initiator)\n throw new Error(\"initiatorName required for Transaction Status\");\n const token = await this.getToken();\n return queryTransactionStatus(\n this.baseUrl,\n token,\n securityCred,\n initiator,\n request\n );\n }\n\n /** Reversal - Reverse a transaction */\n async reversal(request: ReversalRequest) {\n const initiator = this.config.initiatorName ?? \"\";\n const securityCred = await this.getSecurityCredential();\n if (!initiator) throw new Error(\"initiatorName required for Reversal\");\n const token = await this.getToken();\n return processReversal(\n this.baseUrl,\n token,\n securityCred,\n initiator,\n request\n );\n }\n}\n","/**\n * Express-friendly helpers for M-Pesa Express (STK Push) and C2B simulate.\n *\n * NOTE: This module does NOT depend on Express at runtime. You pass in an\n * existing Express Router instance, and we attach handlers to it.\n */\n\nimport type { NextFunction, Request, Response, Router } from \"express\";\n\nimport { Mpesa } from \"../mpesa\";\nimport type { MpesaConfig } from \"../mpesa/types\";\nimport { PesafyError } from \"../utils/errors\";\n\nexport interface MpesaExpressConfig extends MpesaConfig {\n /**\n * Full callback URL that Safaricom will call after STK Push completes.\n * Example (sandbox):\n * https://your-domain.ngrok.io/api/mpesa/express/callback\n */\n callbackUrl: string;\n}\n\nexport function createMpesaExpressClient(config: MpesaExpressConfig): {\n mpesa: Mpesa;\n} {\n if (!config.consumerKey || !config.consumerSecret) {\n throw new PesafyError({\n code: \"INVALID_CREDENTIALS\",\n message: \"consumerKey and consumerSecret are required\",\n });\n }\n\n if (!config.lipaNaMpesaShortCode || !config.lipaNaMpesaPassKey) {\n throw new PesafyError({\n code: \"VALIDATION_ERROR\",\n message:\n \"lipaNaMpesaShortCode and lipaNaMpesaPassKey are required for STK Push\",\n });\n }\n\n if (!config.callbackUrl) {\n throw new PesafyError({\n code: \"VALIDATION_ERROR\",\n message: \"callbackUrl is required for STK Push callbacks\",\n });\n }\n\n const mpesa = new Mpesa(config);\n return { mpesa };\n}\n\ninterface StkPushBody {\n amount: number;\n phoneNumber: string;\n accountReference?: string;\n transactionDesc?: string;\n}\n\ninterface StkQueryBody {\n checkoutRequestId: string;\n}\n\nfunction normalizePhone(phone: string): string {\n const digits = phone.replace(/\\D/g, \"\");\n if (digits.startsWith(\"254\")) return digits;\n if (digits.startsWith(\"0\")) return `254${digits.slice(1)}`;\n return digits;\n}\n\nfunction sendError(res: Response, error: unknown): void {\n if (error instanceof PesafyError) {\n const status = error.statusCode ?? 400;\n res.status(status).json({\n error: error.code,\n message: error.message,\n statusCode: status,\n });\n return;\n }\n\n res.status(500).json({\n error: \"REQUEST_FAILED\",\n message: \"Unexpected error while processing M-Pesa request\",\n });\n}\n\nexport function createMpesaExpressRouter(\n router: Router,\n config: MpesaExpressConfig\n): Router {\n const { mpesa } = createMpesaExpressClient(config);\n\n // STK Push – initiate payment on customer phone\n router.post(\n \"/mpesa/express/stk-push\",\n async (req: Request, res: Response, next: NextFunction) => {\n try {\n const body = req.body as StkPushBody;\n\n if (!body || typeof body.amount !== \"number\" || body.amount <= 0) {\n throw new PesafyError({\n code: \"VALIDATION_ERROR\",\n message: \"amount must be a positive number\",\n });\n }\n\n if (!body.phoneNumber) {\n throw new PesafyError({\n code: \"VALIDATION_ERROR\",\n message: \"phoneNumber is required\",\n });\n }\n\n const phoneNumber = normalizePhone(body.phoneNumber);\n\n const result = await mpesa.stkPush({\n amount: body.amount,\n phoneNumber,\n callbackUrl: config.callbackUrl,\n accountReference:\n body.accountReference ??\n `PESAFY-${Date.now().toString(36).toUpperCase()}`,\n transactionDesc: body.transactionDesc ?? \"Payment\",\n });\n\n res.status(200).json(result);\n } catch (error) {\n if (res.headersSent) return next(error);\n sendError(res, error);\n }\n }\n );\n\n // STK Query – check status of an STK Push\n router.post(\n \"/mpesa/express/stk-query\",\n async (req: Request, res: Response, next: NextFunction) => {\n try {\n const body = req.body as StkQueryBody;\n\n if (!body || !body.checkoutRequestId) {\n throw new PesafyError({\n code: \"VALIDATION_ERROR\",\n message: \"checkoutRequestId is required\",\n });\n }\n\n const result = await mpesa.stkQuery({\n checkoutRequestId: body.checkoutRequestId,\n });\n\n res.status(200).json(result);\n } catch (error) {\n if (res.headersSent) return next(error);\n sendError(res, error);\n }\n }\n );\n\n // C2B simulate – sandbox only\n router.post(\n \"/mpesa/c2b/simulate\",\n async (req: Request, res: Response, next: NextFunction) => {\n try {\n const result = await mpesa.c2bSimulate(req.body);\n res.status(200).json(result);\n } catch (error) {\n if (res.headersSent) return next(error);\n sendError(res, error);\n }\n }\n );\n\n // C2B register URLs – sandbox only\n router.post(\n \"/mpesa/c2b/register-url\",\n async (req: Request, res: Response, next: NextFunction) => {\n try {\n const result = await mpesa.c2bRegisterUrls(req.body);\n res.status(200).json(result);\n } catch (error) {\n if (res.headersSent) return next(error);\n sendError(res, error);\n }\n }\n );\n\n return router;\n}\n","/**\n * Webhook retry mechanism with exponential backoff\n * For at-least-once delivery guarantee\n */\n\nexport interface RetryOptions {\n maxRetries?: number;\n initialDelay?: number;\n maxDelay?: number;\n backoffMultiplier?: number;\n maxRetryDuration?: number; // milliseconds (30 days default)\n}\n\nconst DEFAULT_OPTIONS: Required<RetryOptions> = {\n maxRetries: Infinity,\n initialDelay: 1000, // 1 second\n maxDelay: 3600000, // 1 hour\n backoffMultiplier: 2,\n maxRetryDuration: 30 * 24 * 60 * 60 * 1000, // 30 days\n};\n\nexport interface RetryResult<T> {\n success: boolean;\n data?: T;\n attempts: number;\n error?: Error;\n}\n\nexport async function retryWithBackoff<T>(\n fn: () => Promise<T>,\n options: RetryOptions = {}\n): Promise<RetryResult<T>> {\n const opts = { ...DEFAULT_OPTIONS, ...options };\n let delay = opts.initialDelay;\n let attempts = 0;\n const startTime = Date.now();\n\n while (attempts < opts.maxRetries) {\n attempts++;\n\n // Check if we've exceeded max retry duration\n if (Date.now() - startTime > opts.maxRetryDuration) {\n return {\n success: false,\n attempts,\n error: new Error(\"Max retry duration exceeded\"),\n };\n }\n\n try {\n const data = await fn();\n return { success: true, data, attempts };\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n\n // Don't retry on 4xx errors (client errors)\n if (err.message.includes(\"4\")) {\n return { success: false, attempts, error: err };\n }\n\n // Wait before retrying\n if (attempts < opts.maxRetries) {\n await new Promise((resolve) => setTimeout(resolve, delay));\n delay = Math.min(delay * opts.backoffMultiplier, opts.maxDelay);\n }\n }\n }\n\n return {\n success: false,\n attempts,\n error: new Error(\"Max retries exceeded\"),\n };\n}\n","/**\n * Webhook signature verification\n * Note: Daraja API doesn't provide webhook signatures in the same way as Stripe\n * Instead, verify by whitelisting IPs: 196.201.214.200, 196.201.214.206, etc.\n * This utility helps parse and validate webhook payloads\n */\n\nimport type { B2CWebhook, C2BWebhook, StkPushWebhook } from \"./types\";\n\nexport interface WebhookVerificationOptions {\n /** IP whitelist - verify request comes from Safaricom */\n allowedIPs?: string[];\n /** Optional: verify request headers match expected pattern */\n verifyHeaders?: boolean;\n}\n\nconst SAFARICOM_IPS = [\n \"196.201.214.200\",\n \"196.201.214.206\",\n \"196.201.213.114\",\n \"196.201.214.207\",\n \"196.201.214.208\",\n \"196.201.213.44\",\n \"196.201.212.127\",\n \"196.201.212.138\",\n \"196.201.212.129\",\n \"196.201.212.136\",\n \"196.201.212.74\",\n \"196.201.212.69\",\n];\n\nexport function verifyWebhookIP(\n requestIP: string,\n allowedIPs: string[] = SAFARICOM_IPS\n): boolean {\n return allowedIPs.includes(requestIP);\n}\n\nexport function parseStkPushWebhook(body: unknown): StkPushWebhook | null {\n try {\n const parsed = body as StkPushWebhook;\n if (parsed.Body?.stkCallback) return parsed;\n return null;\n } catch {\n return null;\n }\n}\n\nexport function parseB2CWebhook(body: unknown): B2CWebhook | null {\n try {\n const parsed = body as B2CWebhook;\n if (parsed.Result?.ResultCode !== undefined) return parsed;\n return null;\n } catch {\n return null;\n }\n}\n\nexport function parseC2BWebhook(body: unknown): C2BWebhook | null {\n try {\n const parsed = body as C2BWebhook;\n if (parsed.TransID && parsed.TransAmount) return parsed;\n return null;\n } catch {\n return null;\n }\n}\n","/**\n * Webhook event handler utilities\n */\n\nimport {\n parseB2CWebhook,\n parseC2BWebhook,\n parseStkPushWebhook,\n verifyWebhookIP,\n} from \"./signature-verifier\";\nimport type {\n B2CWebhook,\n C2BWebhook,\n StkPushWebhook,\n WebhookEventType,\n} from \"./types\";\n\nexport interface WebhookHandlerOptions {\n /** IP address of incoming request */\n requestIP?: string;\n /** Custom IP whitelist */\n allowedIPs?: string[];\n /** Skip IP verification (for testing) */\n skipIPCheck?: boolean;\n}\n\nexport interface WebhookHandlerResult<T = unknown> {\n success: boolean;\n eventType: WebhookEventType | null;\n data: T | null;\n error?: string;\n}\n\nexport function handleWebhook(\n body: unknown,\n options: WebhookHandlerOptions = {}\n): WebhookHandlerResult {\n // Verify IP if provided\n if (!options.skipIPCheck && options.requestIP) {\n if (!verifyWebhookIP(options.requestIP, options.allowedIPs)) {\n return {\n success: false,\n eventType: null,\n data: null,\n error: \"IP address not whitelisted\",\n };\n }\n }\n\n // Try to parse as STK Push\n const stkPush = parseStkPushWebhook(body);\n if (stkPush) {\n return {\n success: true,\n eventType: \"stk_push\",\n data: stkPush,\n };\n }\n\n // Try to parse as B2C\n const b2c = parseB2CWebhook(body);\n if (b2c) {\n return {\n success: true,\n eventType: \"b2c\",\n data: b2c,\n };\n }\n\n // Try to parse as C2B\n const c2b = parseC2BWebhook(body);\n if (c2b) {\n return {\n success: true,\n eventType: \"c2b\",\n data: c2b,\n };\n }\n\n return {\n success: false,\n eventType: null,\n data: null,\n error: \"Unknown webhook format\",\n };\n}\n\nexport function extractTransactionId(\n webhook: StkPushWebhook | B2CWebhook | C2BWebhook\n): string | null {\n if (\"Body\" in webhook && webhook.Body?.stkCallback) {\n const items = webhook.Body.stkCallback.CallbackMetadata?.Item;\n const mpesaReceipt = items?.find(\n (item) => item.Name === \"MpesaReceiptNumber\"\n );\n return mpesaReceipt ? String(mpesaReceipt.Value) : null;\n }\n if (\"Result\" in webhook && webhook.Result?.TransactionID) {\n return webhook.Result.TransactionID;\n }\n if (\"TransID\" in webhook) {\n return webhook.TransID;\n }\n return null;\n}\n\nexport function extractAmount(\n webhook: StkPushWebhook | B2CWebhook | C2BWebhook\n): number | null {\n if (\"Body\" in webhook && webhook.Body?.stkCallback) {\n const items = webhook.Body.stkCallback.CallbackMetadata?.Item;\n const amount = items?.find((item) => item.Name === \"Amount\");\n return amount ? Number(amount.Value) : null;\n }\n if (\"Result\" in webhook && webhook.Result?.ResultParameters) {\n const params = webhook.Result.ResultParameters.ResultParameter;\n const amount = params?.find((p) => p.Key === \"Amount\");\n return amount ? Number(amount.Value) : null;\n }\n if (\"TransAmount\" in webhook) {\n return Number.parseFloat(webhook.TransAmount);\n }\n return null;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/utils/errors/error-factory.ts","../src/utils/http/client.ts","../src/core/auth/token-manager.ts","../src/core/encryption/security-credentials.ts","../src/mpesa/b2b/b2b.ts","../src/utils/phone.ts","../src/mpesa/stk-push/utils.ts","../src/mpesa/b2c/b2c.ts","../src/mpesa/c2b/register-url.ts","../src/mpesa/c2b/simulate.ts","../src/mpesa/c2b/types.ts","../src/mpesa/qr-code/dynamic-qr.ts","../src/mpesa/reversal/reversal.ts","../src/mpesa/stk-push/stk-push.ts","../src/mpesa/stk-push/stk-query.ts","../src/mpesa/stk-push/types.ts","../src/mpesa/transaction-status/query.ts","../src/mpesa/types.ts","../src/mpesa/index.ts","../src/express/index.ts","../src/mpesa/webhooks/retry.ts","../src/mpesa/webhooks/signature-verifier.ts","../src/mpesa/webhooks/webhook-handler.ts"],"names":["publicEncrypt"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Error creation utilities for Pesafy\n */\n\nimport type { ErrorCode, PesafyErrorOptions } from \"./types\";\n\nexport class PesafyError extends Error {\n readonly code: ErrorCode;\n readonly statusCode?: number;\n readonly response?: unknown;\n readonly requestId?: string;\n override readonly cause?: unknown;\n\n constructor(options: PesafyErrorOptions) {\n super(options.message);\n Object.defineProperty(this, \"name\", { value: \"PesafyError\" });\n this.code = options.code;\n this.statusCode = options.statusCode;\n this.response = options.response;\n this.requestId = options.requestId;\n this.cause = options.cause;\n\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, PesafyError);\n }\n }\n\n toJSON() {\n return {\n name: this.name,\n code: this.code,\n message: this.message,\n statusCode: this.statusCode,\n requestId: this.requestId,\n };\n }\n}\n\nexport function createError(options: PesafyErrorOptions): PesafyError {\n return new PesafyError(options);\n}\n","/**\n * HTTP client for Daraja API requests\n */\n\nimport { PesafyError } from \"../errors\";\nimport type { HttpRequestOptions, HttpResponse } from \"./types\";\n\nconst DEFAULT_TIMEOUT = 30000;\n\nexport async function httpRequest<T = unknown>(\n url: string,\n options: HttpRequestOptions = {}\n): Promise<HttpResponse<T>> {\n const {\n method = \"GET\",\n headers = {},\n body,\n timeout = DEFAULT_TIMEOUT,\n } = options;\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeout);\n\n try {\n const response = await fetch(url, {\n method,\n headers: {\n \"Content-Type\": \"application/json\",\n ...headers,\n },\n body: body ? JSON.stringify(body) : undefined,\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n let data: T;\n const text = await response.text();\n try {\n data = (text ? JSON.parse(text) : {}) as T;\n } catch {\n data = { raw: text } as T;\n }\n\n if (!response.ok) {\n // Include the full Daraja response body so callers can see exactly what\n // Safaricom returned: \"Invalid Access Token\", \"Bad Request - Invalid\n // Shortcode\", \"Wrong credentials\", etc. Previously this was swallowed.\n const bodyStr = text.length > 0 ? ` — ${text}` : \"\";\n throw new PesafyError({\n code: \"API_ERROR\",\n message: `Request failed with status ${response.status}${bodyStr}`,\n statusCode: response.status,\n response: data,\n });\n }\n\n return {\n data,\n status: response.status,\n headers: response.headers,\n };\n } catch (error) {\n clearTimeout(timeoutId);\n\n if (error instanceof PesafyError) throw error;\n if (error instanceof Error) {\n if (error.name === \"AbortError\") {\n throw new PesafyError({\n code: \"TIMEOUT\",\n message: `Request timed out after ${timeout}ms`,\n cause: error,\n });\n }\n throw new PesafyError({\n code: \"NETWORK_ERROR\",\n message: error.message,\n cause: error,\n });\n }\n throw new PesafyError({\n code: \"REQUEST_FAILED\",\n message: \"An unknown error occurred\",\n cause: error,\n });\n }\n}\n","/**\n * OAuth token manager for Daraja API\n * Token validity: 3600 seconds (1 hour)\n */\n\nimport { PesafyError } from \"../../utils/errors\";\nimport { httpRequest } from \"../../utils/http\";\nimport type { TokenResponse } from \"./types\";\n\nconst TOKEN_BUFFER_SECONDS = 60; // Refresh token 60s before expiry\n\nexport class TokenManager {\n private consumerKey: string;\n private consumerSecret: string;\n private baseUrl: string;\n private cachedToken: string | null = null;\n private tokenExpiresAt = 0;\n\n constructor(consumerKey: string, consumerSecret: string, baseUrl: string) {\n this.consumerKey = consumerKey;\n this.consumerSecret = consumerSecret;\n this.baseUrl = baseUrl;\n }\n\n private getAuthHeader(): string {\n const credentials = `${this.consumerKey}:${this.consumerSecret}`;\n const encoded = Buffer.from(credentials, \"utf-8\").toString(\"base64\");\n return `Basic ${encoded}`;\n }\n\n async getAccessToken(): Promise<string> {\n const now = Date.now() / 1000;\n if (this.cachedToken && this.tokenExpiresAt > now + TOKEN_BUFFER_SECONDS) {\n return this.cachedToken;\n }\n\n const url = `${this.baseUrl}/oauth/v1/generate?grant_type=client_credentials`;\n const response = await httpRequest<TokenResponse>(url, {\n method: \"GET\",\n headers: {\n Authorization: this.getAuthHeader(),\n },\n });\n\n const data = response.data;\n if (!data.access_token) {\n throw new PesafyError({\n code: \"AUTH_FAILED\",\n message: \"Failed to obtain access token\",\n response: data,\n });\n }\n\n this.cachedToken = data.access_token;\n this.tokenExpiresAt = now + (data.expires_in ?? 3600);\n return this.cachedToken;\n }\n\n clearCache(): void {\n this.cachedToken = null;\n this.tokenExpiresAt = 0;\n }\n}\n","/**\n * Security credential encryption for B2C, B2B, Reversal APIs\n * Uses RSA with PKCS#1.5 padding per Daraja API spec\n * Download certificates from: https://developer.safaricom.co.ke/APIs\n */\n\nimport { publicEncrypt } from \"node:crypto\";\nimport { PesafyError } from \"../../utils/errors\";\n\n/** Encrypt initiator password with M-Pesa public certificate (PEM format) */\nexport function encryptSecurityCredential(\n initiatorPassword: string,\n certificatePem: string\n): string {\n try {\n const passwordBuffer = Buffer.from(initiatorPassword, \"utf-8\");\n const encrypted = publicEncrypt(\n {\n key: certificatePem,\n padding: 1, // RSA_PKCS1_PADDING\n },\n passwordBuffer\n );\n return encrypted.toString(\"base64\");\n } catch (error) {\n throw new PesafyError({\n code: \"ENCRYPTION_FAILED\",\n message: \"Failed to encrypt security credential\",\n cause: error,\n });\n }\n}\n","/**\n * B2B - Business to Business payments\n * API: POST /mpesa/b2b/v1/paymentrequest\n */\n\nimport { httpRequest } from \"../../utils/http\";\nimport type { B2BRequest } from \"./types\";\n\nexport interface B2BResponse {\n OriginatorConversationID: string;\n ConversationID: string;\n ResponseCode: string;\n ResponseDescription: string;\n}\n\nexport async function processB2B(\n baseUrl: string,\n accessToken: string,\n securityCredential: string,\n initiatorName: string,\n request: B2BRequest\n): Promise<B2BResponse> {\n const body = {\n Initiator: initiatorName,\n SecurityCredential: securityCredential,\n CommandID: request.commandId ?? \"BusinessPayBill\",\n SenderIdentifierType: request.senderIdentifierType ?? 4,\n RecieverIdentifierType: request.receiverIdentifierType ?? 4,\n Amount: Math.round(request.amount),\n PartyA: request.shortCode,\n PartyB: request.receiverShortCode,\n AccountReference: request.accountReference ?? \"\",\n Remarks: request.remarks ?? \"B2B Payment\",\n QueueTimeOutURL: request.timeoutUrl,\n ResultURL: request.resultUrl,\n };\n\n const { data } = await httpRequest<B2BResponse>(\n `${baseUrl}/mpesa/b2b/v1/paymentrequest`,\n {\n method: \"POST\",\n headers: { Authorization: `Bearer ${accessToken}` },\n body,\n }\n );\n\n return data;\n}\n","/**\n * Shared phone number utilities for M-Pesa API calls.\n *\n * Two formatters are intentionally separate:\n * - formatSafaricomPhone → strict, for STK Push (only Safaricom/Airtel)\n * - formatKenyanMsisdn → permissive, for C2B simulate and B2C PartyB\n *\n * Daraja consistently expects 12-digit MSISDN without the '+' prefix.\n */\n\n/** Strips non-digit characters and normalises leading zeros / country code. */\nfunction toE164Kenya(phone: string): string {\n const digits = phone.replace(/\\D/g, \"\");\n\n if (digits.startsWith(\"254\")) return digits;\n if (digits.startsWith(\"0\")) return `254${digits.slice(1)}`;\n // Bare 9-digit local number (e.g. 712345678)\n if (digits.length === 9) return `254${digits}`;\n\n // Fallback — prepend country code and let the validator catch it\n return `254${digits}`;\n}\n\n/**\n * Strict formatter for STK Push.\n * Accepts Safaricom (2547xx) and Airtel Kenya (2541x) numbers only.\n * Throws a clear error on invalid input so Daraja never receives a bad MSISDN.\n */\nexport function formatSafaricomPhone(phone: string): string {\n const formatted = toE164Kenya(phone);\n\n // 2547xxxxxxxx (Safaricom) | 2541xxxxxxxx (Airtel Kenya)\n if (!/^254[71]\\d{8}$/.test(formatted)) {\n throw new Error(\n `Invalid Kenyan phone number: \"${phone}\". ` +\n \"Expected format: 07XXXXXXXX, 2547XXXXXXXX, or +2547XXXXXXXX.\"\n );\n }\n\n return formatted;\n}\n\n/**\n * Permissive formatter for C2B simulate and B2C PartyB.\n * Accepts any valid 12-digit Kenyan MSISDN (Safaricom, Airtel, Telkom, …).\n * Still throws if the result is structurally impossible.\n */\nexport function formatKenyanMsisdn(phone: string): string {\n const formatted = toE164Kenya(phone);\n\n // Must be exactly 12 digits and start with 254\n if (!/^254\\d{9}$/.test(formatted)) {\n throw new Error(\n `Invalid MSISDN: \"${phone}\". ` +\n \"Expected a 12-digit Kenyan number starting with 254.\"\n );\n }\n\n return formatted;\n}\n\n/**\n * Converts a formatted MSISDN string to the numeric form Daraja expects in\n * C2B simulate requests (\"Msisdn\": 254708374149 — no quotes in the JSON).\n */\nexport function msisdnToNumber(phone: string): number {\n return parseInt(formatKenyanMsisdn(phone), 10);\n}\n","/**\n * STK Push utilities\n *\n * Phone formatting delegates to the shared `formatSafaricomPhone` util which\n * validates Safaricom/Airtel numbers and throws a clear error on bad input.\n */\nexport { formatSafaricomPhone as formatPhoneNumber } from \"../../utils/phone\";\n\n/**\n * Generates the STK Push password.\n * Formula: Base64(Shortcode + Passkey + Timestamp)\n *\n * Uses btoa() instead of Buffer — works in Node.js, Bun, browsers, and\n * edge runtimes (Cloudflare Workers, Deno, etc.).\n *\n * IMPORTANT: The timestamp passed here MUST be the exact same value sent in\n * the request body's `Timestamp` field. Safaricom validates they match.\n * Always call getTimestamp() once and pass the result to both this function\n * and the request body — never call getTimestamp() twice.\n */\nexport function getStkPushPassword(\n shortCode: string,\n passKey: string,\n timestamp: string\n): string {\n const raw = `${shortCode}${passKey}${timestamp}`;\n return btoa(raw);\n}\n\n/**\n * Generates a Daraja-compatible timestamp: YYYYMMDDHHmmss\n * Call this once per request and reuse the value.\n */\nexport function getTimestamp(): string {\n const now = new Date();\n const pad = (n: number) => n.toString().padStart(2, \"0\");\n return [\n now.getFullYear(),\n pad(now.getMonth() + 1),\n pad(now.getDate()),\n pad(now.getHours()),\n pad(now.getMinutes()),\n pad(now.getSeconds()),\n ].join(\"\");\n}\n","/**\n * B2C - Business to Customer payments\n * API: POST /mpesa/b2c/v3/paymentrequest\n */\n\nimport { httpRequest } from \"../../utils/http\";\nimport { formatPhoneNumber } from \"../stk-push/utils\";\nimport type { B2CRequest } from \"./types\";\n\nexport interface B2CResponse {\n OriginatorConversationID: string;\n ConversationID: string;\n ResponseCode: string;\n ResponseDescription: string;\n}\n\nexport async function processB2C(\n baseUrl: string,\n accessToken: string,\n securityCredential: string,\n initiatorName: string,\n request: B2CRequest\n): Promise<B2CResponse> {\n const body = {\n OriginatorConversationID: `AG_${Date.now()}_${Math.random().toString(36).slice(2)}`,\n InitiatorName: initiatorName,\n SecurityCredential: securityCredential,\n CommandID: request.commandId ?? \"BusinessPayment\",\n Amount: Math.round(request.amount),\n PartyA: request.shortCode,\n PartyB: formatPhoneNumber(request.phoneNumber),\n Remarks: request.remarks ?? \"Payment\",\n QueueTimeOutURL: request.timeoutUrl,\n ResultURL: request.resultUrl,\n Occasion: request.occasion ?? \"\",\n };\n\n const { data } = await httpRequest<B2CResponse>(\n `${baseUrl}/mpesa/b2c/v3/paymentrequest`,\n {\n method: \"POST\",\n headers: { Authorization: `Bearer ${accessToken}` },\n body,\n }\n );\n\n return data;\n}\n","/**\n * C2B Register URLs\n * API: POST /mpesa/c2b/v1/registerurl ← v1 (NOT v2 — v2 does not exist for this endpoint)\n *\n * Registers Validation and Confirmation callback URLs for a short code.\n * Sandbox: can be called multiple times (though Daraja sandbox is flaky — retry on 500).\n * Production: one-time registration; contact Safaricom to change.\n *\n * ⚠️ Common mistake: using /v2/registerurl causes persistent 500.003.1001 errors.\n * The Daraja Postman collection and official docs both confirm the path is /v1/registerurl.\n */\nimport { httpRequest } from \"../../utils/http\";\nimport type { C2BRegisterUrlRequest, C2BResponseType } from \"./types\";\n\n/**\n * Actual response shape from Daraja.\n * Note: Daraja's field name has a typo (\"CoversationID\" — missing the 'n').\n * We match it exactly so JSON parsing is lossless.\n */\nexport interface C2BRegisterUrlResponse {\n /** Global unique identifier for this registration request. Daraja typo: \"Coversation\" */\n OriginatorCoversationID: string;\n /** \"0\" = accepted */\n ResponseCode: string;\n ResponseDescription: string;\n}\n\nexport async function registerC2BUrls(\n baseUrl: string,\n accessToken: string,\n request: C2BRegisterUrlRequest\n): Promise<C2BRegisterUrlResponse> {\n const responseType: C2BResponseType = request.responseType ?? \"Completed\";\n\n const body = {\n ShortCode: request.shortCode,\n ResponseType: responseType,\n ConfirmationURL: request.confirmationUrl,\n ValidationURL: request.validationUrl,\n };\n\n // ✅ FIXED: /v1/registerurl (was incorrectly /v2/registerurl)\n // The v2 path does not exist and will always return 500.003.1001.\n const { data } = await httpRequest<C2BRegisterUrlResponse>(\n `${baseUrl}/mpesa/c2b/v1/registerurl`,\n {\n method: \"POST\",\n headers: { Authorization: `Bearer ${accessToken}` },\n body,\n }\n );\n\n return data;\n}\n","/**\n * C2B Simulate (sandbox only)\n * API: POST /mpesa/c2b/v2/simulate\n *\n * Simulates a customer paying a Paybill or Till number.\n * This endpoint is NOT available in production — use STK Push or Dynamic QR\n * for production payment initiation.\n */\nimport { httpRequest } from \"../../utils/http\";\nimport { msisdnToNumber } from \"../../utils/phone\";\nimport type { C2BCommandId, C2BSimulateRequest } from \"./types\";\n\n/**\n * Actual response shape from Daraja.\n * Note: Daraja's field name has a typo (\"CoversationID\" — missing the 'n').\n * We match it exactly so JSON parsing is lossless.\n * There is NO ConversationID field in this response (unlike B2C/B2B).\n */\nexport interface C2BSimulateResponse {\n /** Global unique identifier for this simulate request. Daraja typo: \"Coversation\" */\n OriginatorCoversationID: string;\n /** \"0\" = accepted */\n ResponseCode: string;\n ResponseDescription: string;\n}\n\nexport async function simulateC2B(\n baseUrl: string,\n accessToken: string,\n request: C2BSimulateRequest\n): Promise<C2BSimulateResponse> {\n const commandId: C2BCommandId = request.commandId ?? \"CustomerPayBillOnline\";\n const isBuyGoods = commandId === \"CustomerBuyGoodsOnline\";\n\n /**\n * BillRefNumber (AccountReference) rules — Daraja v2 sandbox behaviour:\n *\n * CustomerPayBillOnline:\n * - Required. Use the passed billRefNumber or fall back to empty string \"\".\n * - Empty string is accepted; it just means no account reference.\n *\n * CustomerBuyGoodsOnline:\n * - The field MUST be omitted entirely from the request body.\n * - Sending any value (even \"0\", \"\", or null) triggers:\n * 500.003.1001 \"The element AccountReference is invalid.\"\n * - ✅ FIX: conditionally include the field only for Paybill.\n */\n const body: Record<string, unknown> = {\n ShortCode: Number(request.shortCode),\n CommandID: commandId,\n Amount: Math.round(request.amount),\n // Daraja expects Msisdn as a number (not a quoted string)\n Msisdn: msisdnToNumber(request.phoneNumber),\n };\n\n // ✅ FIXED: Only include BillRefNumber for Paybill — omit entirely for Buy Goods\n if (!isBuyGoods) {\n body.BillRefNumber = request.billRefNumber ?? \"\";\n }\n\n const { data } = await httpRequest<C2BSimulateResponse>(\n `${baseUrl}/mpesa/c2b/v2/simulate`,\n {\n method: \"POST\",\n headers: { Authorization: `Bearer ${accessToken}` },\n body,\n }\n );\n\n return data;\n}\n","/**\n * C2B (Customer to Business) types\n * API Reference: https://developer.safaricom.co.ke/APIs/CustomerToBusinessV2\n *\n * C2B v2 uses masked MSISDN (e.g. \"2547 ***** 126\").\n * C2B v1 used SHA-256 hashed MSISDN — do NOT use v1.\n */\n\n// ─── Command IDs ─────────────────────────────────────────────────────────────\n\n/**\n * The two valid C2B CommandID values:\n * - CustomerPayBillOnline → payment to a Paybill number\n * - CustomerBuyGoodsOnline → payment to a Till number\n */\nexport type C2BCommandId = \"CustomerPayBillOnline\" | \"CustomerBuyGoodsOnline\";\n\n/**\n * Default action when the ValidationURL is unreachable.\n * \"Completed\" → M-PESA auto-completes the transaction.\n * \"Cancelled\" → M-PESA auto-cancels the transaction.\n * Note: values are case-sensitive per Daraja docs.\n */\nexport type C2BResponseType = \"Completed\" | \"Cancelled\";\n\n// ─── Register URL ─────────────────────────────────────────────────────────────\n\nexport interface C2BRegisterUrlRequest {\n /** Paybill or Till short code */\n shortCode: string;\n /** URL that receives payment confirmation after a successful transaction */\n confirmationUrl: string;\n /** URL that receives validation requests (only when external validation is enabled) */\n validationUrl: string;\n responseType?: C2BResponseType;\n}\n\n// ─── C2B Simulate (sandbox only) ─────────────────────────────────────────────\n\nexport interface C2BSimulateRequest {\n /** Paybill or Till short code */\n shortCode: string;\n /**\n * Transaction type.\n * \"CustomerPayBillOnline\" → payment to a Paybill number (default)\n * \"CustomerBuyGoodsOnline\" → payment to a Till number\n */\n commandId?: C2BCommandId;\n /** Transaction amount (whole number, min 1 KES) */\n amount: number;\n /** Customer's phone number — any valid Kenyan MSISDN */\n phoneNumber: string;\n /**\n * Account reference for Paybill payments.\n * Must be null / omitted for CustomerBuyGoodsOnline.\n */\n billRefNumber?: string;\n}\n\n// ─── C2B Callback payload types ───────────────────────────────────────────────\n// Safaricom POSTs these to your ConfirmationURL / ValidationURL after payment.\n// v2 returns a masked MSISDN (e.g. \"2547 ***** 126\").\n\n/**\n * Shared fields present in both Validation and Confirmation callback payloads.\n */\nexport interface C2BCallbackPayloadBase {\n TransactionType: string;\n /** Unique M-PESA transaction ID (e.g. \"RKL51ZDR4F\") */\n TransID: string;\n /** 14-digit timestamp: YYYYMMDDHHmmss */\n TransTime: string;\n /** Transaction amount as a string (e.g. \"5.00\") */\n TransAmount: string;\n BusinessShortCode: string;\n /** Account reference; empty for Till transactions */\n BillRefNumber: string;\n InvoiceNumber: string;\n OrgAccountBalance: string;\n ThirdPartyTransID: string;\n /** v2: masked MSISDN e.g. \"2547 ***** 126\" */\n MSISDN: string;\n FirstName: string;\n MiddleName: string;\n LastName: string;\n}\n\n/** Payload posted to your ValidationURL (if external validation is enabled) */\nexport type C2BValidationPayload = C2BCallbackPayloadBase;\n\n/** Payload posted to your ConfirmationURL after successful payment */\nexport type C2BConfirmationPayload = C2BCallbackPayloadBase;\n\n/**\n * Union of both C2B callback types.\n * Use the discriminated `TransactionType` field if you need to distinguish them,\n * or register separate Express routes for each URL.\n */\nexport type C2BCallbackPayload = C2BValidationPayload | C2BConfirmationPayload;\n\n// ─── Validation response codes ────────────────────────────────────────────────\n\n/**\n * Codes your ValidationURL must return to Safaricom to accept or reject\n * a payment before it is processed.\n */\nexport const C2B_REJECTION_CODES = {\n /** Accept the transaction — M-PESA proceeds to process and confirm */\n ACCEPT: \"0\",\n /** Reject the transaction — M-PESA cancels and notifies the customer */\n REJECT: \"1\",\n} as const;\n\nexport type C2BRejectionCode =\n (typeof C2B_REJECTION_CODES)[keyof typeof C2B_REJECTION_CODES];\n","/**\n * Dynamic QR Code - Generate LIPA NA M-PESA QR codes\n * API: POST /mpesa/qrcode/v1/generate\n */\n\nimport { httpRequest } from \"../../utils/http\";\nimport type { DynamicQRRequest } from \"./types\";\n\nexport interface DynamicQRResponse {\n ResponseCode: string;\n RequestID: string;\n ResponseDescription: string;\n QRCode: string;\n}\n\nexport async function generateDynamicQR(\n baseUrl: string,\n accessToken: string,\n request: DynamicQRRequest\n): Promise<DynamicQRResponse> {\n const body = {\n MerchantName: request.merchantName,\n RefNo: request.refNo,\n Amount: Math.round(request.amount),\n TrxCode: request.trxCode,\n CPI: request.cpi,\n Size: request.size ?? \"300\",\n };\n\n const { data } = await httpRequest<DynamicQRResponse>(\n `${baseUrl}/mpesa/qrcode/v1/generate`,\n {\n method: \"POST\",\n headers: { Authorization: `Bearer ${accessToken}` },\n body,\n }\n );\n\n return data;\n}\n","/**\n * Transaction Reversal\n * API: POST /mpesa/reversal/v1/request\n */\n\nimport { httpRequest } from \"../../utils/http\";\nimport type { ReversalRequest } from \"./types\";\n\nexport interface ReversalResponse {\n OriginatorConversationID: string;\n ConversationID: string;\n ResponseCode: string;\n ResponseDescription: string;\n}\n\nexport async function processReversal(\n baseUrl: string,\n accessToken: string,\n securityCredential: string,\n initiatorName: string,\n request: ReversalRequest\n): Promise<ReversalResponse> {\n const body = {\n Initiator: initiatorName,\n SecurityCredential: securityCredential,\n CommandID: \"TransactionReversal\",\n TransactionID: request.transactionId,\n Amount: Math.round(request.amount),\n ReceiverParty: request.shortCode,\n RecieverIdentifierType: 4,\n ResultURL: request.resultUrl,\n QueueTimeOutURL: request.timeoutUrl,\n Remarks: request.remarks ?? \"Reversal\",\n Occasion: request.occasion ?? \"Reversal\",\n };\n\n const { data } = await httpRequest<ReversalResponse>(\n `${baseUrl}/mpesa/reversal/v1/request`,\n {\n method: \"POST\",\n headers: { Authorization: `Bearer ${accessToken}` },\n body,\n }\n );\n\n return data;\n}\n","/**\n * M-Pesa Express (STK Push) - Initiates payment prompt on customer's phone\n * API: POST /mpesa/stkpush/v1/processrequest\n */\n\nimport { httpRequest } from \"../../utils/http\";\nimport type { StkPushRequest, StkPushResponse } from \"./types\";\nimport { formatPhoneNumber, getStkPushPassword, getTimestamp } from \"./utils\";\n\nexport async function processStkPush(\n baseUrl: string,\n accessToken: string,\n request: StkPushRequest\n): Promise<StkPushResponse> {\n // Validate amount: Daraja requires a whole number ≥ 1 KES.\n // Math.round(0.3) = 0, which Daraja rejects — catch it here with a clear error.\n const amount = Math.round(request.amount);\n if (amount < 1) {\n throw new Error(\n `Amount must be at least KES 1 (got ${request.amount} which rounds to ${amount}).`\n );\n }\n\n // Generate timestamp ONCE — must be identical in both Password and Timestamp fields.\n // Safaricom validates that Base64(Shortcode+Passkey+Timestamp) matches the\n // Timestamp field; generating two separate timestamps causes auth failures.\n const timestamp = getTimestamp();\n\n // For CustomerBuyGoodsOnline (Till Number), PartyB is the till number.\n // For CustomerPayBillOnline (Paybill), PartyB is the shortcode.\n // Docs: https://developer.safaricom.co.ke/APIs/MpesaExpressSimulate\n const partyB = request.partyB ?? request.shortCode;\n\n const body = {\n BusinessShortCode: request.shortCode,\n Password: getStkPushPassword(request.shortCode, request.passKey, timestamp),\n Timestamp: timestamp,\n TransactionType: request.transactionType ?? \"CustomerPayBillOnline\",\n Amount: amount,\n PartyA: formatPhoneNumber(request.phoneNumber),\n PartyB: partyB,\n PhoneNumber: formatPhoneNumber(request.phoneNumber),\n CallBackURL: request.callbackUrl,\n AccountReference: request.accountReference.slice(0, 12),\n TransactionDesc: request.transactionDesc.slice(0, 13),\n };\n\n const { data } = await httpRequest<StkPushResponse>(\n `${baseUrl}/mpesa/stkpush/v1/processrequest`,\n {\n method: \"POST\",\n headers: { Authorization: `Bearer ${accessToken}` },\n body,\n }\n );\n\n return data;\n}\n","/**\n * STK Push Query - Check status of STK Push transaction\n * API: POST /mpesa/stkpushquery/v1/query\n */\n\nimport { httpRequest } from \"../../utils/http\";\nimport type { StkQueryRequest, StkQueryResponse } from \"./types\";\nimport { getStkPushPassword, getTimestamp } from \"./utils\";\n\nexport async function queryStkPush(\n baseUrl: string,\n accessToken: string,\n request: StkQueryRequest\n): Promise<StkQueryResponse> {\n // Generate timestamp ONCE — Password and Timestamp field must match exactly.\n const timestamp = getTimestamp();\n\n const body = {\n BusinessShortCode: request.shortCode,\n Password: getStkPushPassword(request.shortCode, request.passKey, timestamp),\n Timestamp: timestamp,\n CheckoutRequestID: request.checkoutRequestId,\n };\n\n const { data } = await httpRequest<StkQueryResponse>(\n `${baseUrl}/mpesa/stkpushquery/v1/query`,\n {\n method: \"POST\",\n headers: { Authorization: `Bearer ${accessToken}` },\n body,\n }\n );\n\n return data;\n}\n","/** STK Push (M-Pesa Express) types */\n\nexport type TransactionType =\n | \"CustomerPayBillOnline\"\n | \"CustomerBuyGoodsOnline\";\n\nexport interface StkPushRequest {\n amount: number;\n phoneNumber: string;\n callbackUrl: string;\n accountReference: string;\n transactionDesc: string;\n /** Business shortcode - Paybill or HO/store number for Till */\n shortCode: string;\n passKey: string;\n transactionType?: TransactionType;\n /**\n * The credit party receiving funds.\n * - CustomerPayBillOnline: omit (defaults to shortCode)\n * - CustomerBuyGoodsOnline: provide the Till Number\n * Docs: PartyB must be the Till Number when using Buy Goods.\n */\n partyB?: string;\n}\n\nexport interface StkPushResponse {\n MerchantRequestID: string;\n CheckoutRequestID: string;\n ResponseCode: string;\n ResponseDescription: string;\n CustomerMessage: string;\n}\n\nexport interface StkQueryRequest {\n checkoutRequestId: string;\n shortCode: string;\n passKey: string;\n}\n\nexport interface StkQueryResponse {\n ResponseCode: string;\n ResponseDescription: string;\n MerchantRequestID: string;\n CheckoutRequestID: string;\n /**\n * Daraja returns ResultCode as a NUMBER, not a string.\n * 0 = success\n * 1032 = cancelled by user\n * 1037 = timeout (customer did not respond)\n * 2001 = wrong PIN\n * etc.\n * Always compare with === 0, not === \"0\".\n */\n ResultCode: number;\n ResultDesc: string;\n}\n\n// ---------------------------------------------------------------------------\n// Callback payload types — Safaricom POSTs these to your CallBackURL\n// ---------------------------------------------------------------------------\n\n/** A single metadata item in a successful STK callback */\nexport interface StkCallbackMetadataItem {\n Name: \"Amount\" | \"MpesaReceiptNumber\" | \"TransactionDate\" | \"PhoneNumber\";\n /** Present on successful transactions; absent on failure */\n Value?: number | string;\n}\n\n/** Inner callback object for a SUCCESSFUL STK Push */\nexport interface StkCallbackSuccess {\n MerchantRequestID: string;\n CheckoutRequestID: string;\n /** 0 = success */\n ResultCode: 0;\n ResultDesc: string;\n CallbackMetadata: {\n Item: StkCallbackMetadataItem[];\n };\n}\n\n/** Inner callback object for a FAILED/CANCELLED STK Push */\nexport interface StkCallbackFailure {\n MerchantRequestID: string;\n CheckoutRequestID: string;\n /** Non-zero result codes: e.g. 1032 = cancelled by user */\n ResultCode: number;\n ResultDesc: string;\n CallbackMetadata?: never;\n}\n\nexport type StkCallbackInner = StkCallbackSuccess | StkCallbackFailure;\n\n/** Full wrapper that Safaricom POSTs to your CallBackURL */\nexport interface StkPushCallback {\n Body: {\n stkCallback: StkCallbackInner;\n };\n}\n\n/**\n * Type guard — narrows an StkCallbackInner to the success shape.\n * Usage:\n * if (isStkCallbackSuccess(callback.Body.stkCallback)) {\n * const receipt = getCallbackValue(callback, \"MpesaReceiptNumber\");\n * }\n */\nexport function isStkCallbackSuccess(\n cb: StkCallbackInner\n): cb is StkCallbackSuccess {\n return cb.ResultCode === 0;\n}\n\n/**\n * Extracts a named value from a successful callback's metadata items.\n * Returns undefined if the key is absent or the callback failed.\n */\nexport function getCallbackValue(\n callback: StkPushCallback,\n name: StkCallbackMetadataItem[\"Name\"]\n): string | number | undefined {\n const inner = callback.Body.stkCallback;\n if (!isStkCallbackSuccess(inner)) return undefined;\n return inner.CallbackMetadata.Item.find((i) => i.Name === name)?.Value;\n}\n","/**\n * Transaction Status Query\n * API: POST /mpesa/transactionstatus/v1/query\n */\n\nimport { httpRequest } from \"../../utils/http\";\nimport type { TransactionStatusRequest } from \"./types\";\n\nexport interface TransactionStatusResponse {\n OriginatorConversationID: string;\n ConversationID: string;\n ResponseCode: string;\n ResponseDescription: string;\n}\n\nexport async function queryTransactionStatus(\n baseUrl: string,\n accessToken: string,\n securityCredential: string,\n initiatorName: string,\n request: TransactionStatusRequest\n): Promise<TransactionStatusResponse> {\n const body = {\n Initiator: initiatorName,\n SecurityCredential: securityCredential,\n CommandID: \"TransactionStatusQuery\",\n TransactionID: request.transactionId,\n PartyA: request.shortCode,\n IdentifierType: request.identifierType ?? 4,\n ResultURL: request.resultUrl,\n QueueTimeOutURL: request.timeoutUrl,\n Remarks: \"Status\",\n Occasion: \"Query\",\n };\n\n const { data } = await httpRequest<TransactionStatusResponse>(\n `${baseUrl}/mpesa/transactionstatus/v1/query`,\n {\n method: \"POST\",\n headers: { Authorization: `Bearer ${accessToken}` },\n body,\n }\n );\n\n return data;\n}\n","export type Environment = \"sandbox\" | \"production\";\n\nexport const DARAJA_BASE_URLS = {\n sandbox: \"https://sandbox.safaricom.co.ke\",\n production: \"https://api.safaricom.co.ke\",\n} as const;\n\nexport interface MpesaConfig {\n consumerKey: string;\n consumerSecret: string;\n environment: Environment;\n /** Required for STK Push - Lipa Na M-Pesa passkey from Daraja portal */\n lipaNaMpesaShortCode?: string;\n lipaNaMpesaPassKey?: string;\n /** Required for B2C, B2B, Reversal - initiator name and password */\n initiatorName?: string;\n initiatorPassword?: string;\n /** PEM certificate for encrypting initiator password. Download from Daraja portal */\n certificatePath?: string;\n /** PEM certificate string (alternative to certificatePath) */\n certificatePem?: string;\n /** Pre-encrypted security credential (alternative to initiatorPassword + certificate) */\n securityCredential?: string;\n}\n","/**\n * M-Pesa Daraja API client\n */\n\nimport { TokenManager } from \"../core/auth\";\nimport { encryptSecurityCredential } from \"../core/encryption\";\nimport { type B2BRequest, processB2B } from \"./b2b\";\nimport { type B2CRequest, processB2C } from \"./b2c\";\nimport {\n type C2BRegisterUrlRequest,\n type C2BSimulateRequest,\n registerC2BUrls,\n simulateC2B,\n} from \"./c2b\";\nimport { type DynamicQRRequest, generateDynamicQR } from \"./qr-code\";\nimport { processReversal, type ReversalRequest } from \"./reversal\";\nimport {\n processStkPush,\n queryStkPush,\n type StkPushRequest,\n type StkQueryRequest,\n} from \"./stk-push\";\nimport {\n queryTransactionStatus,\n type TransactionStatusRequest,\n} from \"./transaction-status\";\nimport { DARAJA_BASE_URLS, type MpesaConfig } from \"./types\";\n\nexport class Mpesa {\n private config: MpesaConfig;\n private tokenManager: TokenManager;\n private baseUrl: string;\n\n constructor(config: MpesaConfig) {\n this.config = config;\n this.baseUrl = DARAJA_BASE_URLS[config.environment];\n this.tokenManager = new TokenManager(\n config.consumerKey,\n config.consumerSecret,\n this.baseUrl\n );\n }\n\n private async getToken(): Promise<string> {\n return this.tokenManager.getAccessToken();\n }\n\n private async getSecurityCredential(): Promise<string> {\n if (this.config.securityCredential) return this.config.securityCredential;\n if (!this.config.initiatorPassword) {\n throw new Error(\n \"Security credential required: provide securityCredential or (initiatorPassword + certificatePath/certificatePem)\"\n );\n }\n let cert: string;\n if (this.config.certificatePem) {\n cert = this.config.certificatePem;\n } else if (this.config.certificatePath) {\n cert = await Bun.file(this.config.certificatePath).text();\n } else {\n throw new Error(\n \"certificatePath or certificatePem required for B2C/B2B/Reversal\"\n );\n }\n return encryptSecurityCredential(this.config.initiatorPassword, cert);\n }\n\n /** STK Push (M-Pesa Express) - Initiate payment on customer phone */\n async stkPush(request: Omit<StkPushRequest, \"shortCode\" | \"passKey\">) {\n const shortCode = this.config.lipaNaMpesaShortCode ?? \"\";\n const passKey = this.config.lipaNaMpesaPassKey ?? \"\";\n if (!shortCode || !passKey) {\n throw new Error(\n \"lipaNaMpesaShortCode and lipaNaMpesaPassKey required for STK Push\"\n );\n }\n const token = await this.getToken();\n return processStkPush(this.baseUrl, token, {\n ...request,\n shortCode,\n passKey,\n });\n }\n\n /** STK Query - Check STK Push transaction status */\n async stkQuery(request: Omit<StkQueryRequest, \"shortCode\" | \"passKey\">) {\n const shortCode = this.config.lipaNaMpesaShortCode ?? \"\";\n const passKey = this.config.lipaNaMpesaPassKey ?? \"\";\n if (!shortCode || !passKey) {\n throw new Error(\n \"lipaNaMpesaShortCode and lipaNaMpesaPassKey required for STK Query\"\n );\n }\n const token = await this.getToken();\n return queryStkPush(this.baseUrl, token, {\n ...request,\n shortCode,\n passKey,\n });\n }\n\n /** B2C - Send money to customer */\n async b2c(request: B2CRequest) {\n const initiator = this.config.initiatorName ?? \"\";\n const securityCred = await this.getSecurityCredential();\n if (!initiator) throw new Error(\"initiatorName required for B2C\");\n const token = await this.getToken();\n return processB2C(this.baseUrl, token, securityCred, initiator, request);\n }\n\n /** B2B - Send money to business */\n async b2b(request: B2BRequest) {\n const initiator = this.config.initiatorName ?? \"\";\n const securityCred = await this.getSecurityCredential();\n if (!initiator) throw new Error(\"initiatorName required for B2B\");\n const token = await this.getToken();\n return processB2B(this.baseUrl, token, securityCred, initiator, request);\n }\n\n /** C2B - Register validation/confirmation URLs */\n async c2bRegisterUrls(request: C2BRegisterUrlRequest) {\n const token = await this.getToken();\n return registerC2BUrls(this.baseUrl, token, request);\n }\n\n /** C2B - Simulate payment (sandbox only) */\n async c2bSimulate(request: C2BSimulateRequest) {\n const token = await this.getToken();\n return simulateC2B(this.baseUrl, token, request);\n }\n\n /** Dynamic QR - Generate LIPA NA M-PESA QR code */\n async qrCode(request: DynamicQRRequest) {\n const token = await this.getToken();\n return generateDynamicQR(this.baseUrl, token, request);\n }\n\n /** Transaction Status - Query transaction status */\n async transactionStatus(request: TransactionStatusRequest) {\n const initiator = this.config.initiatorName ?? \"\";\n const securityCred = await this.getSecurityCredential();\n if (!initiator)\n throw new Error(\"initiatorName required for Transaction Status\");\n const token = await this.getToken();\n return queryTransactionStatus(\n this.baseUrl,\n token,\n securityCred,\n initiator,\n request\n );\n }\n\n /** Reversal - Reverse a transaction */\n async reversal(request: ReversalRequest) {\n const initiator = this.config.initiatorName ?? \"\";\n const securityCred = await this.getSecurityCredential();\n if (!initiator) throw new Error(\"initiatorName required for Reversal\");\n const token = await this.getToken();\n return processReversal(\n this.baseUrl,\n token,\n securityCred,\n initiator,\n request\n );\n }\n}\n","/**\n * Express-friendly helpers for M-Pesa Express (STK Push) and C2B simulate.\n *\n * NOTE: This module does NOT depend on Express at runtime. You pass in an\n * existing Express Router instance, and we attach handlers to it.\n */\n\nimport type { NextFunction, Request, Response, Router } from \"express\";\n\nimport { Mpesa } from \"../mpesa\";\nimport type { MpesaConfig } from \"../mpesa/types\";\nimport { PesafyError } from \"../utils/errors\";\n\nexport interface MpesaExpressConfig extends MpesaConfig {\n /**\n * Full callback URL that Safaricom will call after STK Push completes.\n * Example (sandbox):\n * https://your-domain.ngrok.io/api/mpesa/express/callback\n */\n callbackUrl: string;\n}\n\nexport function createMpesaExpressClient(config: MpesaExpressConfig): {\n mpesa: Mpesa;\n} {\n if (!config.consumerKey || !config.consumerSecret) {\n throw new PesafyError({\n code: \"INVALID_CREDENTIALS\",\n message: \"consumerKey and consumerSecret are required\",\n });\n }\n\n if (!config.lipaNaMpesaShortCode || !config.lipaNaMpesaPassKey) {\n throw new PesafyError({\n code: \"VALIDATION_ERROR\",\n message:\n \"lipaNaMpesaShortCode and lipaNaMpesaPassKey are required for STK Push\",\n });\n }\n\n if (!config.callbackUrl) {\n throw new PesafyError({\n code: \"VALIDATION_ERROR\",\n message: \"callbackUrl is required for STK Push callbacks\",\n });\n }\n\n const mpesa = new Mpesa(config);\n return { mpesa };\n}\n\ninterface StkPushBody {\n amount: number;\n phoneNumber: string;\n accountReference?: string;\n transactionDesc?: string;\n}\n\ninterface StkQueryBody {\n checkoutRequestId: string;\n}\n\nfunction normalizePhone(phone: string): string {\n const digits = phone.replace(/\\D/g, \"\");\n if (digits.startsWith(\"254\")) return digits;\n if (digits.startsWith(\"0\")) return `254${digits.slice(1)}`;\n return digits;\n}\n\nfunction sendError(res: Response, error: unknown): void {\n if (error instanceof PesafyError) {\n const status = error.statusCode ?? 400;\n res.status(status).json({\n error: error.code,\n message: error.message,\n statusCode: status,\n });\n return;\n }\n\n res.status(500).json({\n error: \"REQUEST_FAILED\",\n message: \"Unexpected error while processing M-Pesa request\",\n });\n}\n\nexport function createMpesaExpressRouter(\n router: Router,\n config: MpesaExpressConfig\n): Router {\n const { mpesa } = createMpesaExpressClient(config);\n\n // STK Push – initiate payment on customer phone\n router.post(\n \"/mpesa/express/stk-push\",\n async (req: Request, res: Response, next: NextFunction) => {\n try {\n const body = req.body as StkPushBody;\n\n if (!body || typeof body.amount !== \"number\" || body.amount <= 0) {\n throw new PesafyError({\n code: \"VALIDATION_ERROR\",\n message: \"amount must be a positive number\",\n });\n }\n\n if (!body.phoneNumber) {\n throw new PesafyError({\n code: \"VALIDATION_ERROR\",\n message: \"phoneNumber is required\",\n });\n }\n\n const phoneNumber = normalizePhone(body.phoneNumber);\n\n const result = await mpesa.stkPush({\n amount: body.amount,\n phoneNumber,\n callbackUrl: config.callbackUrl,\n accountReference:\n body.accountReference ??\n `PESAFY-${Date.now().toString(36).toUpperCase()}`,\n transactionDesc: body.transactionDesc ?? \"Payment\",\n });\n\n res.status(200).json(result);\n } catch (error) {\n if (res.headersSent) return next(error);\n sendError(res, error);\n }\n }\n );\n\n // STK Query – check status of an STK Push\n router.post(\n \"/mpesa/express/stk-query\",\n async (req: Request, res: Response, next: NextFunction) => {\n try {\n const body = req.body as StkQueryBody;\n\n if (!body || !body.checkoutRequestId) {\n throw new PesafyError({\n code: \"VALIDATION_ERROR\",\n message: \"checkoutRequestId is required\",\n });\n }\n\n const result = await mpesa.stkQuery({\n checkoutRequestId: body.checkoutRequestId,\n });\n\n res.status(200).json(result);\n } catch (error) {\n if (res.headersSent) return next(error);\n sendError(res, error);\n }\n }\n );\n\n // C2B simulate – sandbox only\n router.post(\n \"/mpesa/c2b/simulate\",\n async (req: Request, res: Response, next: NextFunction) => {\n try {\n const result = await mpesa.c2bSimulate(req.body);\n res.status(200).json(result);\n } catch (error) {\n if (res.headersSent) return next(error);\n sendError(res, error);\n }\n }\n );\n\n // C2B register URLs – sandbox only\n router.post(\n \"/mpesa/c2b/register-url\",\n async (req: Request, res: Response, next: NextFunction) => {\n try {\n const result = await mpesa.c2bRegisterUrls(req.body);\n res.status(200).json(result);\n } catch (error) {\n if (res.headersSent) return next(error);\n sendError(res, error);\n }\n }\n );\n\n return router;\n}\n","/**\n * Webhook retry mechanism with exponential backoff\n * For at-least-once delivery guarantee\n */\n\nexport interface RetryOptions {\n maxRetries?: number;\n initialDelay?: number;\n maxDelay?: number;\n backoffMultiplier?: number;\n maxRetryDuration?: number; // milliseconds (30 days default)\n}\n\nconst DEFAULT_OPTIONS: Required<RetryOptions> = {\n maxRetries: Infinity,\n initialDelay: 1000, // 1 second\n maxDelay: 3600000, // 1 hour\n backoffMultiplier: 2,\n maxRetryDuration: 30 * 24 * 60 * 60 * 1000, // 30 days\n};\n\nexport interface RetryResult<T> {\n success: boolean;\n data?: T;\n attempts: number;\n error?: Error;\n}\n\nexport async function retryWithBackoff<T>(\n fn: () => Promise<T>,\n options: RetryOptions = {}\n): Promise<RetryResult<T>> {\n const opts = { ...DEFAULT_OPTIONS, ...options };\n let delay = opts.initialDelay;\n let attempts = 0;\n const startTime = Date.now();\n\n while (attempts < opts.maxRetries) {\n attempts++;\n\n // Check if we've exceeded max retry duration\n if (Date.now() - startTime > opts.maxRetryDuration) {\n return {\n success: false,\n attempts,\n error: new Error(\"Max retry duration exceeded\"),\n };\n }\n\n try {\n const data = await fn();\n return { success: true, data, attempts };\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n\n // Don't retry on 4xx errors (client errors)\n if (err.message.includes(\"4\")) {\n return { success: false, attempts, error: err };\n }\n\n // Wait before retrying\n if (attempts < opts.maxRetries) {\n await new Promise((resolve) => setTimeout(resolve, delay));\n delay = Math.min(delay * opts.backoffMultiplier, opts.maxDelay);\n }\n }\n }\n\n return {\n success: false,\n attempts,\n error: new Error(\"Max retries exceeded\"),\n };\n}\n","/**\n * Webhook signature verification\n * Note: Daraja API doesn't provide webhook signatures in the same way as Stripe\n * Instead, verify by whitelisting IPs: 196.201.214.200, 196.201.214.206, etc.\n * This utility helps parse and validate webhook payloads\n */\n\nimport type { B2CWebhook, C2BWebhook, StkPushWebhook } from \"./types\";\n\nexport interface WebhookVerificationOptions {\n /** IP whitelist - verify request comes from Safaricom */\n allowedIPs?: string[];\n /** Optional: verify request headers match expected pattern */\n verifyHeaders?: boolean;\n}\n\nconst SAFARICOM_IPS = [\n \"196.201.214.200\",\n \"196.201.214.206\",\n \"196.201.213.114\",\n \"196.201.214.207\",\n \"196.201.214.208\",\n \"196.201.213.44\",\n \"196.201.212.127\",\n \"196.201.212.138\",\n \"196.201.212.129\",\n \"196.201.212.136\",\n \"196.201.212.74\",\n \"196.201.212.69\",\n];\n\nexport function verifyWebhookIP(\n requestIP: string,\n allowedIPs: string[] = SAFARICOM_IPS\n): boolean {\n return allowedIPs.includes(requestIP);\n}\n\nexport function parseStkPushWebhook(body: unknown): StkPushWebhook | null {\n try {\n const parsed = body as StkPushWebhook;\n if (parsed.Body?.stkCallback) return parsed;\n return null;\n } catch {\n return null;\n }\n}\n\nexport function parseB2CWebhook(body: unknown): B2CWebhook | null {\n try {\n const parsed = body as B2CWebhook;\n if (parsed.Result?.ResultCode !== undefined) return parsed;\n return null;\n } catch {\n return null;\n }\n}\n\nexport function parseC2BWebhook(body: unknown): C2BWebhook | null {\n try {\n const parsed = body as C2BWebhook;\n if (parsed.TransID && parsed.TransAmount) return parsed;\n return null;\n } catch {\n return null;\n }\n}\n","/**\n * Webhook event handler utilities\n */\n\nimport {\n parseB2CWebhook,\n parseC2BWebhook,\n parseStkPushWebhook,\n verifyWebhookIP,\n} from \"./signature-verifier\";\nimport type {\n B2CWebhook,\n C2BWebhook,\n StkPushWebhook,\n WebhookEventType,\n} from \"./types\";\n\nexport interface WebhookHandlerOptions {\n /** IP address of incoming request */\n requestIP?: string;\n /** Custom IP whitelist */\n allowedIPs?: string[];\n /** Skip IP verification (for testing) */\n skipIPCheck?: boolean;\n}\n\nexport interface WebhookHandlerResult<T = unknown> {\n success: boolean;\n eventType: WebhookEventType | null;\n data: T | null;\n error?: string;\n}\n\nexport function handleWebhook(\n body: unknown,\n options: WebhookHandlerOptions = {}\n): WebhookHandlerResult {\n // Verify IP if provided\n if (!options.skipIPCheck && options.requestIP) {\n if (!verifyWebhookIP(options.requestIP, options.allowedIPs)) {\n return {\n success: false,\n eventType: null,\n data: null,\n error: \"IP address not whitelisted\",\n };\n }\n }\n\n // Try to parse as STK Push\n const stkPush = parseStkPushWebhook(body);\n if (stkPush) {\n return {\n success: true,\n eventType: \"stk_push\",\n data: stkPush,\n };\n }\n\n // Try to parse as B2C\n const b2c = parseB2CWebhook(body);\n if (b2c) {\n return {\n success: true,\n eventType: \"b2c\",\n data: b2c,\n };\n }\n\n // Try to parse as C2B\n const c2b = parseC2BWebhook(body);\n if (c2b) {\n return {\n success: true,\n eventType: \"c2b\",\n data: c2b,\n };\n }\n\n return {\n success: false,\n eventType: null,\n data: null,\n error: \"Unknown webhook format\",\n };\n}\n\nexport function extractTransactionId(\n webhook: StkPushWebhook | B2CWebhook | C2BWebhook\n): string | null {\n if (\"Body\" in webhook && webhook.Body?.stkCallback) {\n const items = webhook.Body.stkCallback.CallbackMetadata?.Item;\n const mpesaReceipt = items?.find(\n (item) => item.Name === \"MpesaReceiptNumber\"\n );\n return mpesaReceipt ? String(mpesaReceipt.Value) : null;\n }\n if (\"Result\" in webhook && webhook.Result?.TransactionID) {\n return webhook.Result.TransactionID;\n }\n if (\"TransID\" in webhook) {\n return webhook.TransID;\n }\n return null;\n}\n\nexport function extractAmount(\n webhook: StkPushWebhook | B2CWebhook | C2BWebhook\n): number | null {\n if (\"Body\" in webhook && webhook.Body?.stkCallback) {\n const items = webhook.Body.stkCallback.CallbackMetadata?.Item;\n const amount = items?.find((item) => item.Name === \"Amount\");\n return amount ? Number(amount.Value) : null;\n }\n if (\"Result\" in webhook && webhook.Result?.ResultParameters) {\n const params = webhook.Result.ResultParameters.ResultParameter;\n const amount = params?.find((p) => p.Key === \"Amount\");\n return amount ? Number(amount.Value) : null;\n }\n if (\"TransAmount\" in webhook) {\n return Number.parseFloat(webhook.TransAmount);\n }\n return null;\n}\n"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -197,15 +197,6 @@ interface C2BRegisterUrlResponse {
|
|
|
197
197
|
ResponseDescription: string;
|
|
198
198
|
}
|
|
199
199
|
|
|
200
|
-
/**
|
|
201
|
-
* C2B Simulate (sandbox only)
|
|
202
|
-
* API: POST /mpesa/c2b/v2/simulate
|
|
203
|
-
*
|
|
204
|
-
* Simulates a customer paying a Paybill or Till number.
|
|
205
|
-
* This endpoint is NOT available in production — use STK Push or Dynamic QR
|
|
206
|
-
* for production payment initiation.
|
|
207
|
-
*/
|
|
208
|
-
|
|
209
200
|
/**
|
|
210
201
|
* Actual response shape from Daraja.
|
|
211
202
|
* Note: Daraja's field name has a typo ("CoversationID" — missing the 'n').
|
package/dist/index.d.ts
CHANGED
|
@@ -197,15 +197,6 @@ interface C2BRegisterUrlResponse {
|
|
|
197
197
|
ResponseDescription: string;
|
|
198
198
|
}
|
|
199
199
|
|
|
200
|
-
/**
|
|
201
|
-
* C2B Simulate (sandbox only)
|
|
202
|
-
* API: POST /mpesa/c2b/v2/simulate
|
|
203
|
-
*
|
|
204
|
-
* Simulates a customer paying a Paybill or Till number.
|
|
205
|
-
* This endpoint is NOT available in production — use STK Push or Dynamic QR
|
|
206
|
-
* for production payment initiation.
|
|
207
|
-
*/
|
|
208
|
-
|
|
209
200
|
/**
|
|
210
201
|
* Actual response shape from Daraja.
|
|
211
202
|
* Note: Daraja's field name has a typo ("CoversationID" — missing the 'n').
|
package/dist/index.js
CHANGED
|
@@ -286,7 +286,7 @@ async function registerC2BUrls(baseUrl, accessToken, request) {
|
|
|
286
286
|
ValidationURL: request.validationUrl
|
|
287
287
|
};
|
|
288
288
|
const { data } = await httpRequest(
|
|
289
|
-
`${baseUrl}/mpesa/c2b/
|
|
289
|
+
`${baseUrl}/mpesa/c2b/v1/registerurl`,
|
|
290
290
|
{
|
|
291
291
|
method: "POST",
|
|
292
292
|
headers: { Authorization: `Bearer ${accessToken}` },
|
|
@@ -300,7 +300,6 @@ async function registerC2BUrls(baseUrl, accessToken, request) {
|
|
|
300
300
|
async function simulateC2B(baseUrl, accessToken, request) {
|
|
301
301
|
const commandId = request.commandId ?? "CustomerPayBillOnline";
|
|
302
302
|
const isBuyGoods = commandId === "CustomerBuyGoodsOnline";
|
|
303
|
-
const billRef = isBuyGoods ? void 0 : request.billRefNumber ?? "";
|
|
304
303
|
const body = {
|
|
305
304
|
ShortCode: Number(request.shortCode),
|
|
306
305
|
CommandID: commandId,
|
|
@@ -308,8 +307,8 @@ async function simulateC2B(baseUrl, accessToken, request) {
|
|
|
308
307
|
// Daraja expects Msisdn as a number (not a quoted string)
|
|
309
308
|
Msisdn: msisdnToNumber(request.phoneNumber)
|
|
310
309
|
};
|
|
311
|
-
if (
|
|
312
|
-
body.BillRefNumber =
|
|
310
|
+
if (!isBuyGoods) {
|
|
311
|
+
body.BillRefNumber = request.billRefNumber ?? "";
|
|
313
312
|
}
|
|
314
313
|
const { data } = await httpRequest(
|
|
315
314
|
`${baseUrl}/mpesa/c2b/v2/simulate`,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/utils/errors/error-factory.ts","../src/utils/http/client.ts","../src/core/auth/token-manager.ts","../src/core/encryption/security-credentials.ts","../src/mpesa/b2b/b2b.ts","../src/utils/phone.ts","../src/mpesa/stk-push/utils.ts","../src/mpesa/b2c/b2c.ts","../src/mpesa/c2b/register-url.ts","../src/mpesa/c2b/simulate.ts","../src/mpesa/c2b/types.ts","../src/mpesa/qr-code/dynamic-qr.ts","../src/mpesa/reversal/reversal.ts","../src/mpesa/stk-push/stk-push.ts","../src/mpesa/stk-push/stk-query.ts","../src/mpesa/stk-push/types.ts","../src/mpesa/transaction-status/query.ts","../src/mpesa/types.ts","../src/mpesa/index.ts","../src/express/index.ts","../src/mpesa/webhooks/retry.ts","../src/mpesa/webhooks/signature-verifier.ts","../src/mpesa/webhooks/webhook-handler.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Error creation utilities for Pesafy\n */\n\nimport type { ErrorCode, PesafyErrorOptions } from \"./types\";\n\nexport class PesafyError extends Error {\n readonly code: ErrorCode;\n readonly statusCode?: number;\n readonly response?: unknown;\n readonly requestId?: string;\n override readonly cause?: unknown;\n\n constructor(options: PesafyErrorOptions) {\n super(options.message);\n Object.defineProperty(this, \"name\", { value: \"PesafyError\" });\n this.code = options.code;\n this.statusCode = options.statusCode;\n this.response = options.response;\n this.requestId = options.requestId;\n this.cause = options.cause;\n\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, PesafyError);\n }\n }\n\n toJSON() {\n return {\n name: this.name,\n code: this.code,\n message: this.message,\n statusCode: this.statusCode,\n requestId: this.requestId,\n };\n }\n}\n\nexport function createError(options: PesafyErrorOptions): PesafyError {\n return new PesafyError(options);\n}\n","/**\n * HTTP client for Daraja API requests\n */\n\nimport { PesafyError } from \"../errors\";\nimport type { HttpRequestOptions, HttpResponse } from \"./types\";\n\nconst DEFAULT_TIMEOUT = 30000;\n\nexport async function httpRequest<T = unknown>(\n url: string,\n options: HttpRequestOptions = {}\n): Promise<HttpResponse<T>> {\n const {\n method = \"GET\",\n headers = {},\n body,\n timeout = DEFAULT_TIMEOUT,\n } = options;\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeout);\n\n try {\n const response = await fetch(url, {\n method,\n headers: {\n \"Content-Type\": \"application/json\",\n ...headers,\n },\n body: body ? JSON.stringify(body) : undefined,\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n let data: T;\n const text = await response.text();\n try {\n data = (text ? JSON.parse(text) : {}) as T;\n } catch {\n data = { raw: text } as T;\n }\n\n if (!response.ok) {\n // Include the full Daraja response body so callers can see exactly what\n // Safaricom returned: \"Invalid Access Token\", \"Bad Request - Invalid\n // Shortcode\", \"Wrong credentials\", etc. Previously this was swallowed.\n const bodyStr = text.length > 0 ? ` — ${text}` : \"\";\n throw new PesafyError({\n code: \"API_ERROR\",\n message: `Request failed with status ${response.status}${bodyStr}`,\n statusCode: response.status,\n response: data,\n });\n }\n\n return {\n data,\n status: response.status,\n headers: response.headers,\n };\n } catch (error) {\n clearTimeout(timeoutId);\n\n if (error instanceof PesafyError) throw error;\n if (error instanceof Error) {\n if (error.name === \"AbortError\") {\n throw new PesafyError({\n code: \"TIMEOUT\",\n message: `Request timed out after ${timeout}ms`,\n cause: error,\n });\n }\n throw new PesafyError({\n code: \"NETWORK_ERROR\",\n message: error.message,\n cause: error,\n });\n }\n throw new PesafyError({\n code: \"REQUEST_FAILED\",\n message: \"An unknown error occurred\",\n cause: error,\n });\n }\n}\n","/**\n * OAuth token manager for Daraja API\n * Token validity: 3600 seconds (1 hour)\n */\n\nimport { PesafyError } from \"../../utils/errors\";\nimport { httpRequest } from \"../../utils/http\";\nimport type { TokenResponse } from \"./types\";\n\nconst TOKEN_BUFFER_SECONDS = 60; // Refresh token 60s before expiry\n\nexport class TokenManager {\n private consumerKey: string;\n private consumerSecret: string;\n private baseUrl: string;\n private cachedToken: string | null = null;\n private tokenExpiresAt = 0;\n\n constructor(consumerKey: string, consumerSecret: string, baseUrl: string) {\n this.consumerKey = consumerKey;\n this.consumerSecret = consumerSecret;\n this.baseUrl = baseUrl;\n }\n\n private getAuthHeader(): string {\n const credentials = `${this.consumerKey}:${this.consumerSecret}`;\n const encoded = Buffer.from(credentials, \"utf-8\").toString(\"base64\");\n return `Basic ${encoded}`;\n }\n\n async getAccessToken(): Promise<string> {\n const now = Date.now() / 1000;\n if (this.cachedToken && this.tokenExpiresAt > now + TOKEN_BUFFER_SECONDS) {\n return this.cachedToken;\n }\n\n const url = `${this.baseUrl}/oauth/v1/generate?grant_type=client_credentials`;\n const response = await httpRequest<TokenResponse>(url, {\n method: \"GET\",\n headers: {\n Authorization: this.getAuthHeader(),\n },\n });\n\n const data = response.data;\n if (!data.access_token) {\n throw new PesafyError({\n code: \"AUTH_FAILED\",\n message: \"Failed to obtain access token\",\n response: data,\n });\n }\n\n this.cachedToken = data.access_token;\n this.tokenExpiresAt = now + (data.expires_in ?? 3600);\n return this.cachedToken;\n }\n\n clearCache(): void {\n this.cachedToken = null;\n this.tokenExpiresAt = 0;\n }\n}\n","/**\n * Security credential encryption for B2C, B2B, Reversal APIs\n * Uses RSA with PKCS#1.5 padding per Daraja API spec\n * Download certificates from: https://developer.safaricom.co.ke/APIs\n */\n\nimport { publicEncrypt } from \"node:crypto\";\nimport { PesafyError } from \"../../utils/errors\";\n\n/** Encrypt initiator password with M-Pesa public certificate (PEM format) */\nexport function encryptSecurityCredential(\n initiatorPassword: string,\n certificatePem: string\n): string {\n try {\n const passwordBuffer = Buffer.from(initiatorPassword, \"utf-8\");\n const encrypted = publicEncrypt(\n {\n key: certificatePem,\n padding: 1, // RSA_PKCS1_PADDING\n },\n passwordBuffer\n );\n return encrypted.toString(\"base64\");\n } catch (error) {\n throw new PesafyError({\n code: \"ENCRYPTION_FAILED\",\n message: \"Failed to encrypt security credential\",\n cause: error,\n });\n }\n}\n","/**\n * B2B - Business to Business payments\n * API: POST /mpesa/b2b/v1/paymentrequest\n */\n\nimport { httpRequest } from \"../../utils/http\";\nimport type { B2BRequest } from \"./types\";\n\nexport interface B2BResponse {\n OriginatorConversationID: string;\n ConversationID: string;\n ResponseCode: string;\n ResponseDescription: string;\n}\n\nexport async function processB2B(\n baseUrl: string,\n accessToken: string,\n securityCredential: string,\n initiatorName: string,\n request: B2BRequest\n): Promise<B2BResponse> {\n const body = {\n Initiator: initiatorName,\n SecurityCredential: securityCredential,\n CommandID: request.commandId ?? \"BusinessPayBill\",\n SenderIdentifierType: request.senderIdentifierType ?? 4,\n RecieverIdentifierType: request.receiverIdentifierType ?? 4,\n Amount: Math.round(request.amount),\n PartyA: request.shortCode,\n PartyB: request.receiverShortCode,\n AccountReference: request.accountReference ?? \"\",\n Remarks: request.remarks ?? \"B2B Payment\",\n QueueTimeOutURL: request.timeoutUrl,\n ResultURL: request.resultUrl,\n };\n\n const { data } = await httpRequest<B2BResponse>(\n `${baseUrl}/mpesa/b2b/v1/paymentrequest`,\n {\n method: \"POST\",\n headers: { Authorization: `Bearer ${accessToken}` },\n body,\n }\n );\n\n return data;\n}\n","/**\n * Shared phone number utilities for M-Pesa API calls.\n *\n * Two formatters are intentionally separate:\n * - formatSafaricomPhone → strict, for STK Push (only Safaricom/Airtel)\n * - formatKenyanMsisdn → permissive, for C2B simulate and B2C PartyB\n *\n * Daraja consistently expects 12-digit MSISDN without the '+' prefix.\n */\n\n/** Strips non-digit characters and normalises leading zeros / country code. */\nfunction toE164Kenya(phone: string): string {\n const digits = phone.replace(/\\D/g, \"\");\n\n if (digits.startsWith(\"254\")) return digits;\n if (digits.startsWith(\"0\")) return `254${digits.slice(1)}`;\n // Bare 9-digit local number (e.g. 712345678)\n if (digits.length === 9) return `254${digits}`;\n\n // Fallback — prepend country code and let the validator catch it\n return `254${digits}`;\n}\n\n/**\n * Strict formatter for STK Push.\n * Accepts Safaricom (2547xx) and Airtel Kenya (2541x) numbers only.\n * Throws a clear error on invalid input so Daraja never receives a bad MSISDN.\n */\nexport function formatSafaricomPhone(phone: string): string {\n const formatted = toE164Kenya(phone);\n\n // 2547xxxxxxxx (Safaricom) | 2541xxxxxxxx (Airtel Kenya)\n if (!/^254[71]\\d{8}$/.test(formatted)) {\n throw new Error(\n `Invalid Kenyan phone number: \"${phone}\". ` +\n \"Expected format: 07XXXXXXXX, 2547XXXXXXXX, or +2547XXXXXXXX.\"\n );\n }\n\n return formatted;\n}\n\n/**\n * Permissive formatter for C2B simulate and B2C PartyB.\n * Accepts any valid 12-digit Kenyan MSISDN (Safaricom, Airtel, Telkom, …).\n * Still throws if the result is structurally impossible.\n */\nexport function formatKenyanMsisdn(phone: string): string {\n const formatted = toE164Kenya(phone);\n\n // Must be exactly 12 digits and start with 254\n if (!/^254\\d{9}$/.test(formatted)) {\n throw new Error(\n `Invalid MSISDN: \"${phone}\". ` +\n \"Expected a 12-digit Kenyan number starting with 254.\"\n );\n }\n\n return formatted;\n}\n\n/**\n * Converts a formatted MSISDN string to the numeric form Daraja expects in\n * C2B simulate requests (\"Msisdn\": 254708374149 — no quotes in the JSON).\n */\nexport function msisdnToNumber(phone: string): number {\n return parseInt(formatKenyanMsisdn(phone), 10);\n}\n","/**\n * STK Push utilities\n *\n * Phone formatting delegates to the shared `formatSafaricomPhone` util which\n * validates Safaricom/Airtel numbers and throws a clear error on bad input.\n */\nexport { formatSafaricomPhone as formatPhoneNumber } from \"../../utils/phone\";\n\n/**\n * Generates the STK Push password.\n * Formula: Base64(Shortcode + Passkey + Timestamp)\n *\n * Uses btoa() instead of Buffer — works in Node.js, Bun, browsers, and\n * edge runtimes (Cloudflare Workers, Deno, etc.).\n *\n * IMPORTANT: The timestamp passed here MUST be the exact same value sent in\n * the request body's `Timestamp` field. Safaricom validates they match.\n * Always call getTimestamp() once and pass the result to both this function\n * and the request body — never call getTimestamp() twice.\n */\nexport function getStkPushPassword(\n shortCode: string,\n passKey: string,\n timestamp: string\n): string {\n const raw = `${shortCode}${passKey}${timestamp}`;\n return btoa(raw);\n}\n\n/**\n * Generates a Daraja-compatible timestamp: YYYYMMDDHHmmss\n * Call this once per request and reuse the value.\n */\nexport function getTimestamp(): string {\n const now = new Date();\n const pad = (n: number) => n.toString().padStart(2, \"0\");\n return [\n now.getFullYear(),\n pad(now.getMonth() + 1),\n pad(now.getDate()),\n pad(now.getHours()),\n pad(now.getMinutes()),\n pad(now.getSeconds()),\n ].join(\"\");\n}\n","/**\n * B2C - Business to Customer payments\n * API: POST /mpesa/b2c/v3/paymentrequest\n */\n\nimport { httpRequest } from \"../../utils/http\";\nimport { formatPhoneNumber } from \"../stk-push/utils\";\nimport type { B2CRequest } from \"./types\";\n\nexport interface B2CResponse {\n OriginatorConversationID: string;\n ConversationID: string;\n ResponseCode: string;\n ResponseDescription: string;\n}\n\nexport async function processB2C(\n baseUrl: string,\n accessToken: string,\n securityCredential: string,\n initiatorName: string,\n request: B2CRequest\n): Promise<B2CResponse> {\n const body = {\n OriginatorConversationID: `AG_${Date.now()}_${Math.random().toString(36).slice(2)}`,\n InitiatorName: initiatorName,\n SecurityCredential: securityCredential,\n CommandID: request.commandId ?? \"BusinessPayment\",\n Amount: Math.round(request.amount),\n PartyA: request.shortCode,\n PartyB: formatPhoneNumber(request.phoneNumber),\n Remarks: request.remarks ?? \"Payment\",\n QueueTimeOutURL: request.timeoutUrl,\n ResultURL: request.resultUrl,\n Occasion: request.occasion ?? \"\",\n };\n\n const { data } = await httpRequest<B2CResponse>(\n `${baseUrl}/mpesa/b2c/v3/paymentrequest`,\n {\n method: \"POST\",\n headers: { Authorization: `Bearer ${accessToken}` },\n body,\n }\n );\n\n return data;\n}\n","/**\n * C2B Register URLs\n * API: POST /mpesa/c2b/v2/registerurl\n *\n * Registers Validation and Confirmation callback URLs for a short code.\n * Sandbox: can be called multiple times.\n * Production: one-time registration; contact Safaricom to change.\n */\nimport { httpRequest } from \"../../utils/http\";\nimport type { C2BRegisterUrlRequest, C2BResponseType } from \"./types\";\n\n/**\n * Actual response shape from Daraja.\n * Note: Daraja's field name has a typo (\"CoversationID\" — missing the 'n').\n * We match it exactly so JSON parsing is lossless.\n */\nexport interface C2BRegisterUrlResponse {\n /** Global unique identifier for this registration request. Daraja typo: \"Coversation\" */\n OriginatorCoversationID: string;\n /** \"0\" = accepted */\n ResponseCode: string;\n ResponseDescription: string;\n}\n\nexport async function registerC2BUrls(\n baseUrl: string,\n accessToken: string,\n request: C2BRegisterUrlRequest\n): Promise<C2BRegisterUrlResponse> {\n const responseType: C2BResponseType = request.responseType ?? \"Completed\";\n\n const body = {\n ShortCode: request.shortCode,\n ResponseType: responseType,\n ConfirmationURL: request.confirmationUrl,\n ValidationURL: request.validationUrl,\n };\n\n const { data } = await httpRequest<C2BRegisterUrlResponse>(\n `${baseUrl}/mpesa/c2b/v2/registerurl`,\n {\n method: \"POST\",\n headers: { Authorization: `Bearer ${accessToken}` },\n body,\n }\n );\n\n return data;\n}\n","/**\n * C2B Simulate (sandbox only)\n * API: POST /mpesa/c2b/v2/simulate\n *\n * Simulates a customer paying a Paybill or Till number.\n * This endpoint is NOT available in production — use STK Push or Dynamic QR\n * for production payment initiation.\n */\n\nimport { httpRequest } from \"../../utils/http\";\nimport { msisdnToNumber } from \"../../utils/phone\";\nimport type { C2BCommandId, C2BSimulateRequest } from \"./types\";\n\n/**\n * Actual response shape from Daraja.\n * Note: Daraja's field name has a typo (\"CoversationID\" — missing the 'n').\n * We match it exactly so JSON parsing is lossless.\n * There is NO ConversationID field in this response (unlike B2C/B2B).\n */\nexport interface C2BSimulateResponse {\n /** Global unique identifier for this simulate request. Daraja typo: \"Coversation\" */\n OriginatorCoversationID: string;\n /** \"0\" = accepted */\n ResponseCode: string;\n ResponseDescription: string;\n}\n\nexport async function simulateC2B(\n baseUrl: string,\n accessToken: string,\n request: C2BSimulateRequest\n): Promise<C2BSimulateResponse> {\n const commandId: C2BCommandId = request.commandId ?? \"CustomerPayBillOnline\";\n const isBuyGoods = commandId === \"CustomerBuyGoodsOnline\";\n\n /**\n * BillRefNumber rules per Daraja docs:\n * - CustomerPayBillOnline: account reference (e.g. \"Test Ref\") — use passed value or empty string\n * - CustomerBuyGoodsOnline: must be null / omitted\n *\n * We omit the key entirely for BuyGoods rather than sending null, which can\n * cause a 400 \"Invalid Request Payload\" on some Daraja versions.\n */\n const billRef = isBuyGoods ? undefined : (request.billRefNumber ?? \"\");\n\n const body: Record<string, unknown> = {\n ShortCode: Number(request.shortCode),\n CommandID: commandId,\n Amount: Math.round(request.amount),\n // Daraja expects Msisdn as a number (not a quoted string)\n Msisdn: msisdnToNumber(request.phoneNumber),\n };\n\n if (billRef !== undefined) {\n body.BillRefNumber = billRef;\n }\n\n const { data } = await httpRequest<C2BSimulateResponse>(\n `${baseUrl}/mpesa/c2b/v2/simulate`,\n {\n method: \"POST\",\n headers: { Authorization: `Bearer ${accessToken}` },\n body,\n }\n );\n\n return data;\n}\n","/**\n * C2B (Customer to Business) types\n * API Reference: https://developer.safaricom.co.ke/APIs/CustomerToBusinessV2\n *\n * C2B v2 uses masked MSISDN (e.g. \"2547 ***** 126\").\n * C2B v1 used SHA-256 hashed MSISDN — do NOT use v1.\n */\n\n// ─── Command IDs ─────────────────────────────────────────────────────────────\n\n/**\n * The two valid C2B CommandID values:\n * - CustomerPayBillOnline → payment to a Paybill number\n * - CustomerBuyGoodsOnline → payment to a Till number\n */\nexport type C2BCommandId = \"CustomerPayBillOnline\" | \"CustomerBuyGoodsOnline\";\n\n/**\n * Default action when the ValidationURL is unreachable.\n * \"Completed\" → M-PESA auto-completes the transaction.\n * \"Cancelled\" → M-PESA auto-cancels the transaction.\n * Note: values are case-sensitive per Daraja docs.\n */\nexport type C2BResponseType = \"Completed\" | \"Cancelled\";\n\n// ─── Register URL ─────────────────────────────────────────────────────────────\n\nexport interface C2BRegisterUrlRequest {\n /** Paybill or Till short code */\n shortCode: string;\n /** URL that receives payment confirmation after a successful transaction */\n confirmationUrl: string;\n /** URL that receives validation requests (only when external validation is enabled) */\n validationUrl: string;\n responseType?: C2BResponseType;\n}\n\n// ─── C2B Simulate (sandbox only) ─────────────────────────────────────────────\n\nexport interface C2BSimulateRequest {\n /** Paybill or Till short code */\n shortCode: string;\n /**\n * Transaction type.\n * \"CustomerPayBillOnline\" → payment to a Paybill number (default)\n * \"CustomerBuyGoodsOnline\" → payment to a Till number\n */\n commandId?: C2BCommandId;\n /** Transaction amount (whole number, min 1 KES) */\n amount: number;\n /** Customer's phone number — any valid Kenyan MSISDN */\n phoneNumber: string;\n /**\n * Account reference for Paybill payments.\n * Must be null / omitted for CustomerBuyGoodsOnline.\n */\n billRefNumber?: string;\n}\n\n// ─── C2B Callback payload types ───────────────────────────────────────────────\n// Safaricom POSTs these to your ConfirmationURL / ValidationURL after payment.\n// v2 returns a masked MSISDN (e.g. \"2547 ***** 126\").\n\n/**\n * Shared fields present in both Validation and Confirmation callback payloads.\n */\nexport interface C2BCallbackPayloadBase {\n TransactionType: string;\n /** Unique M-PESA transaction ID (e.g. \"RKL51ZDR4F\") */\n TransID: string;\n /** 14-digit timestamp: YYYYMMDDHHmmss */\n TransTime: string;\n /** Transaction amount as a string (e.g. \"5.00\") */\n TransAmount: string;\n BusinessShortCode: string;\n /** Account reference; empty for Till transactions */\n BillRefNumber: string;\n InvoiceNumber: string;\n OrgAccountBalance: string;\n ThirdPartyTransID: string;\n /** v2: masked MSISDN e.g. \"2547 ***** 126\" */\n MSISDN: string;\n FirstName: string;\n MiddleName: string;\n LastName: string;\n}\n\n/** Payload posted to your ValidationURL (if external validation is enabled) */\nexport type C2BValidationPayload = C2BCallbackPayloadBase;\n\n/** Payload posted to your ConfirmationURL after successful payment */\nexport type C2BConfirmationPayload = C2BCallbackPayloadBase;\n\n/**\n * Union of both C2B callback types.\n * Use the discriminated `TransactionType` field if you need to distinguish them,\n * or register separate Express routes for each URL.\n */\nexport type C2BCallbackPayload = C2BValidationPayload | C2BConfirmationPayload;\n\n// ─── Validation response codes ────────────────────────────────────────────────\n\n/**\n * Codes your ValidationURL must return to Safaricom to accept or reject\n * a payment before it is processed.\n */\nexport const C2B_REJECTION_CODES = {\n /** Accept the transaction — M-PESA proceeds to process and confirm */\n ACCEPT: \"0\",\n /** Reject the transaction — M-PESA cancels and notifies the customer */\n REJECT: \"1\",\n} as const;\n\nexport type C2BRejectionCode =\n (typeof C2B_REJECTION_CODES)[keyof typeof C2B_REJECTION_CODES];\n","/**\n * Dynamic QR Code - Generate LIPA NA M-PESA QR codes\n * API: POST /mpesa/qrcode/v1/generate\n */\n\nimport { httpRequest } from \"../../utils/http\";\nimport type { DynamicQRRequest } from \"./types\";\n\nexport interface DynamicQRResponse {\n ResponseCode: string;\n RequestID: string;\n ResponseDescription: string;\n QRCode: string;\n}\n\nexport async function generateDynamicQR(\n baseUrl: string,\n accessToken: string,\n request: DynamicQRRequest\n): Promise<DynamicQRResponse> {\n const body = {\n MerchantName: request.merchantName,\n RefNo: request.refNo,\n Amount: Math.round(request.amount),\n TrxCode: request.trxCode,\n CPI: request.cpi,\n Size: request.size ?? \"300\",\n };\n\n const { data } = await httpRequest<DynamicQRResponse>(\n `${baseUrl}/mpesa/qrcode/v1/generate`,\n {\n method: \"POST\",\n headers: { Authorization: `Bearer ${accessToken}` },\n body,\n }\n );\n\n return data;\n}\n","/**\n * Transaction Reversal\n * API: POST /mpesa/reversal/v1/request\n */\n\nimport { httpRequest } from \"../../utils/http\";\nimport type { ReversalRequest } from \"./types\";\n\nexport interface ReversalResponse {\n OriginatorConversationID: string;\n ConversationID: string;\n ResponseCode: string;\n ResponseDescription: string;\n}\n\nexport async function processReversal(\n baseUrl: string,\n accessToken: string,\n securityCredential: string,\n initiatorName: string,\n request: ReversalRequest\n): Promise<ReversalResponse> {\n const body = {\n Initiator: initiatorName,\n SecurityCredential: securityCredential,\n CommandID: \"TransactionReversal\",\n TransactionID: request.transactionId,\n Amount: Math.round(request.amount),\n ReceiverParty: request.shortCode,\n RecieverIdentifierType: 4,\n ResultURL: request.resultUrl,\n QueueTimeOutURL: request.timeoutUrl,\n Remarks: request.remarks ?? \"Reversal\",\n Occasion: request.occasion ?? \"Reversal\",\n };\n\n const { data } = await httpRequest<ReversalResponse>(\n `${baseUrl}/mpesa/reversal/v1/request`,\n {\n method: \"POST\",\n headers: { Authorization: `Bearer ${accessToken}` },\n body,\n }\n );\n\n return data;\n}\n","/**\n * M-Pesa Express (STK Push) - Initiates payment prompt on customer's phone\n * API: POST /mpesa/stkpush/v1/processrequest\n */\n\nimport { httpRequest } from \"../../utils/http\";\nimport type { StkPushRequest, StkPushResponse } from \"./types\";\nimport { formatPhoneNumber, getStkPushPassword, getTimestamp } from \"./utils\";\n\nexport async function processStkPush(\n baseUrl: string,\n accessToken: string,\n request: StkPushRequest\n): Promise<StkPushResponse> {\n // Validate amount: Daraja requires a whole number ≥ 1 KES.\n // Math.round(0.3) = 0, which Daraja rejects — catch it here with a clear error.\n const amount = Math.round(request.amount);\n if (amount < 1) {\n throw new Error(\n `Amount must be at least KES 1 (got ${request.amount} which rounds to ${amount}).`\n );\n }\n\n // Generate timestamp ONCE — must be identical in both Password and Timestamp fields.\n // Safaricom validates that Base64(Shortcode+Passkey+Timestamp) matches the\n // Timestamp field; generating two separate timestamps causes auth failures.\n const timestamp = getTimestamp();\n\n // For CustomerBuyGoodsOnline (Till Number), PartyB is the till number.\n // For CustomerPayBillOnline (Paybill), PartyB is the shortcode.\n // Docs: https://developer.safaricom.co.ke/APIs/MpesaExpressSimulate\n const partyB = request.partyB ?? request.shortCode;\n\n const body = {\n BusinessShortCode: request.shortCode,\n Password: getStkPushPassword(request.shortCode, request.passKey, timestamp),\n Timestamp: timestamp,\n TransactionType: request.transactionType ?? \"CustomerPayBillOnline\",\n Amount: amount,\n PartyA: formatPhoneNumber(request.phoneNumber),\n PartyB: partyB,\n PhoneNumber: formatPhoneNumber(request.phoneNumber),\n CallBackURL: request.callbackUrl,\n AccountReference: request.accountReference.slice(0, 12),\n TransactionDesc: request.transactionDesc.slice(0, 13),\n };\n\n const { data } = await httpRequest<StkPushResponse>(\n `${baseUrl}/mpesa/stkpush/v1/processrequest`,\n {\n method: \"POST\",\n headers: { Authorization: `Bearer ${accessToken}` },\n body,\n }\n );\n\n return data;\n}\n","/**\n * STK Push Query - Check status of STK Push transaction\n * API: POST /mpesa/stkpushquery/v1/query\n */\n\nimport { httpRequest } from \"../../utils/http\";\nimport type { StkQueryRequest, StkQueryResponse } from \"./types\";\nimport { getStkPushPassword, getTimestamp } from \"./utils\";\n\nexport async function queryStkPush(\n baseUrl: string,\n accessToken: string,\n request: StkQueryRequest\n): Promise<StkQueryResponse> {\n // Generate timestamp ONCE — Password and Timestamp field must match exactly.\n const timestamp = getTimestamp();\n\n const body = {\n BusinessShortCode: request.shortCode,\n Password: getStkPushPassword(request.shortCode, request.passKey, timestamp),\n Timestamp: timestamp,\n CheckoutRequestID: request.checkoutRequestId,\n };\n\n const { data } = await httpRequest<StkQueryResponse>(\n `${baseUrl}/mpesa/stkpushquery/v1/query`,\n {\n method: \"POST\",\n headers: { Authorization: `Bearer ${accessToken}` },\n body,\n }\n );\n\n return data;\n}\n","/** STK Push (M-Pesa Express) types */\n\nexport type TransactionType =\n | \"CustomerPayBillOnline\"\n | \"CustomerBuyGoodsOnline\";\n\nexport interface StkPushRequest {\n amount: number;\n phoneNumber: string;\n callbackUrl: string;\n accountReference: string;\n transactionDesc: string;\n /** Business shortcode - Paybill or HO/store number for Till */\n shortCode: string;\n passKey: string;\n transactionType?: TransactionType;\n /**\n * The credit party receiving funds.\n * - CustomerPayBillOnline: omit (defaults to shortCode)\n * - CustomerBuyGoodsOnline: provide the Till Number\n * Docs: PartyB must be the Till Number when using Buy Goods.\n */\n partyB?: string;\n}\n\nexport interface StkPushResponse {\n MerchantRequestID: string;\n CheckoutRequestID: string;\n ResponseCode: string;\n ResponseDescription: string;\n CustomerMessage: string;\n}\n\nexport interface StkQueryRequest {\n checkoutRequestId: string;\n shortCode: string;\n passKey: string;\n}\n\nexport interface StkQueryResponse {\n ResponseCode: string;\n ResponseDescription: string;\n MerchantRequestID: string;\n CheckoutRequestID: string;\n /**\n * Daraja returns ResultCode as a NUMBER, not a string.\n * 0 = success\n * 1032 = cancelled by user\n * 1037 = timeout (customer did not respond)\n * 2001 = wrong PIN\n * etc.\n * Always compare with === 0, not === \"0\".\n */\n ResultCode: number;\n ResultDesc: string;\n}\n\n// ---------------------------------------------------------------------------\n// Callback payload types — Safaricom POSTs these to your CallBackURL\n// ---------------------------------------------------------------------------\n\n/** A single metadata item in a successful STK callback */\nexport interface StkCallbackMetadataItem {\n Name: \"Amount\" | \"MpesaReceiptNumber\" | \"TransactionDate\" | \"PhoneNumber\";\n /** Present on successful transactions; absent on failure */\n Value?: number | string;\n}\n\n/** Inner callback object for a SUCCESSFUL STK Push */\nexport interface StkCallbackSuccess {\n MerchantRequestID: string;\n CheckoutRequestID: string;\n /** 0 = success */\n ResultCode: 0;\n ResultDesc: string;\n CallbackMetadata: {\n Item: StkCallbackMetadataItem[];\n };\n}\n\n/** Inner callback object for a FAILED/CANCELLED STK Push */\nexport interface StkCallbackFailure {\n MerchantRequestID: string;\n CheckoutRequestID: string;\n /** Non-zero result codes: e.g. 1032 = cancelled by user */\n ResultCode: number;\n ResultDesc: string;\n CallbackMetadata?: never;\n}\n\nexport type StkCallbackInner = StkCallbackSuccess | StkCallbackFailure;\n\n/** Full wrapper that Safaricom POSTs to your CallBackURL */\nexport interface StkPushCallback {\n Body: {\n stkCallback: StkCallbackInner;\n };\n}\n\n/**\n * Type guard — narrows an StkCallbackInner to the success shape.\n * Usage:\n * if (isStkCallbackSuccess(callback.Body.stkCallback)) {\n * const receipt = getCallbackValue(callback, \"MpesaReceiptNumber\");\n * }\n */\nexport function isStkCallbackSuccess(\n cb: StkCallbackInner\n): cb is StkCallbackSuccess {\n return cb.ResultCode === 0;\n}\n\n/**\n * Extracts a named value from a successful callback's metadata items.\n * Returns undefined if the key is absent or the callback failed.\n */\nexport function getCallbackValue(\n callback: StkPushCallback,\n name: StkCallbackMetadataItem[\"Name\"]\n): string | number | undefined {\n const inner = callback.Body.stkCallback;\n if (!isStkCallbackSuccess(inner)) return undefined;\n return inner.CallbackMetadata.Item.find((i) => i.Name === name)?.Value;\n}\n","/**\n * Transaction Status Query\n * API: POST /mpesa/transactionstatus/v1/query\n */\n\nimport { httpRequest } from \"../../utils/http\";\nimport type { TransactionStatusRequest } from \"./types\";\n\nexport interface TransactionStatusResponse {\n OriginatorConversationID: string;\n ConversationID: string;\n ResponseCode: string;\n ResponseDescription: string;\n}\n\nexport async function queryTransactionStatus(\n baseUrl: string,\n accessToken: string,\n securityCredential: string,\n initiatorName: string,\n request: TransactionStatusRequest\n): Promise<TransactionStatusResponse> {\n const body = {\n Initiator: initiatorName,\n SecurityCredential: securityCredential,\n CommandID: \"TransactionStatusQuery\",\n TransactionID: request.transactionId,\n PartyA: request.shortCode,\n IdentifierType: request.identifierType ?? 4,\n ResultURL: request.resultUrl,\n QueueTimeOutURL: request.timeoutUrl,\n Remarks: \"Status\",\n Occasion: \"Query\",\n };\n\n const { data } = await httpRequest<TransactionStatusResponse>(\n `${baseUrl}/mpesa/transactionstatus/v1/query`,\n {\n method: \"POST\",\n headers: { Authorization: `Bearer ${accessToken}` },\n body,\n }\n );\n\n return data;\n}\n","export type Environment = \"sandbox\" | \"production\";\n\nexport const DARAJA_BASE_URLS = {\n sandbox: \"https://sandbox.safaricom.co.ke\",\n production: \"https://api.safaricom.co.ke\",\n} as const;\n\nexport interface MpesaConfig {\n consumerKey: string;\n consumerSecret: string;\n environment: Environment;\n /** Required for STK Push - Lipa Na M-Pesa passkey from Daraja portal */\n lipaNaMpesaShortCode?: string;\n lipaNaMpesaPassKey?: string;\n /** Required for B2C, B2B, Reversal - initiator name and password */\n initiatorName?: string;\n initiatorPassword?: string;\n /** PEM certificate for encrypting initiator password. Download from Daraja portal */\n certificatePath?: string;\n /** PEM certificate string (alternative to certificatePath) */\n certificatePem?: string;\n /** Pre-encrypted security credential (alternative to initiatorPassword + certificate) */\n securityCredential?: string;\n}\n","/**\n * M-Pesa Daraja API client\n */\n\nimport { TokenManager } from \"../core/auth\";\nimport { encryptSecurityCredential } from \"../core/encryption\";\nimport { type B2BRequest, processB2B } from \"./b2b\";\nimport { type B2CRequest, processB2C } from \"./b2c\";\nimport {\n type C2BRegisterUrlRequest,\n type C2BSimulateRequest,\n registerC2BUrls,\n simulateC2B,\n} from \"./c2b\";\nimport { type DynamicQRRequest, generateDynamicQR } from \"./qr-code\";\nimport { processReversal, type ReversalRequest } from \"./reversal\";\nimport {\n processStkPush,\n queryStkPush,\n type StkPushRequest,\n type StkQueryRequest,\n} from \"./stk-push\";\nimport {\n queryTransactionStatus,\n type TransactionStatusRequest,\n} from \"./transaction-status\";\nimport { DARAJA_BASE_URLS, type MpesaConfig } from \"./types\";\n\nexport class Mpesa {\n private config: MpesaConfig;\n private tokenManager: TokenManager;\n private baseUrl: string;\n\n constructor(config: MpesaConfig) {\n this.config = config;\n this.baseUrl = DARAJA_BASE_URLS[config.environment];\n this.tokenManager = new TokenManager(\n config.consumerKey,\n config.consumerSecret,\n this.baseUrl\n );\n }\n\n private async getToken(): Promise<string> {\n return this.tokenManager.getAccessToken();\n }\n\n private async getSecurityCredential(): Promise<string> {\n if (this.config.securityCredential) return this.config.securityCredential;\n if (!this.config.initiatorPassword) {\n throw new Error(\n \"Security credential required: provide securityCredential or (initiatorPassword + certificatePath/certificatePem)\"\n );\n }\n let cert: string;\n if (this.config.certificatePem) {\n cert = this.config.certificatePem;\n } else if (this.config.certificatePath) {\n cert = await Bun.file(this.config.certificatePath).text();\n } else {\n throw new Error(\n \"certificatePath or certificatePem required for B2C/B2B/Reversal\"\n );\n }\n return encryptSecurityCredential(this.config.initiatorPassword, cert);\n }\n\n /** STK Push (M-Pesa Express) - Initiate payment on customer phone */\n async stkPush(request: Omit<StkPushRequest, \"shortCode\" | \"passKey\">) {\n const shortCode = this.config.lipaNaMpesaShortCode ?? \"\";\n const passKey = this.config.lipaNaMpesaPassKey ?? \"\";\n if (!shortCode || !passKey) {\n throw new Error(\n \"lipaNaMpesaShortCode and lipaNaMpesaPassKey required for STK Push\"\n );\n }\n const token = await this.getToken();\n return processStkPush(this.baseUrl, token, {\n ...request,\n shortCode,\n passKey,\n });\n }\n\n /** STK Query - Check STK Push transaction status */\n async stkQuery(request: Omit<StkQueryRequest, \"shortCode\" | \"passKey\">) {\n const shortCode = this.config.lipaNaMpesaShortCode ?? \"\";\n const passKey = this.config.lipaNaMpesaPassKey ?? \"\";\n if (!shortCode || !passKey) {\n throw new Error(\n \"lipaNaMpesaShortCode and lipaNaMpesaPassKey required for STK Query\"\n );\n }\n const token = await this.getToken();\n return queryStkPush(this.baseUrl, token, {\n ...request,\n shortCode,\n passKey,\n });\n }\n\n /** B2C - Send money to customer */\n async b2c(request: B2CRequest) {\n const initiator = this.config.initiatorName ?? \"\";\n const securityCred = await this.getSecurityCredential();\n if (!initiator) throw new Error(\"initiatorName required for B2C\");\n const token = await this.getToken();\n return processB2C(this.baseUrl, token, securityCred, initiator, request);\n }\n\n /** B2B - Send money to business */\n async b2b(request: B2BRequest) {\n const initiator = this.config.initiatorName ?? \"\";\n const securityCred = await this.getSecurityCredential();\n if (!initiator) throw new Error(\"initiatorName required for B2B\");\n const token = await this.getToken();\n return processB2B(this.baseUrl, token, securityCred, initiator, request);\n }\n\n /** C2B - Register validation/confirmation URLs */\n async c2bRegisterUrls(request: C2BRegisterUrlRequest) {\n const token = await this.getToken();\n return registerC2BUrls(this.baseUrl, token, request);\n }\n\n /** C2B - Simulate payment (sandbox only) */\n async c2bSimulate(request: C2BSimulateRequest) {\n const token = await this.getToken();\n return simulateC2B(this.baseUrl, token, request);\n }\n\n /** Dynamic QR - Generate LIPA NA M-PESA QR code */\n async qrCode(request: DynamicQRRequest) {\n const token = await this.getToken();\n return generateDynamicQR(this.baseUrl, token, request);\n }\n\n /** Transaction Status - Query transaction status */\n async transactionStatus(request: TransactionStatusRequest) {\n const initiator = this.config.initiatorName ?? \"\";\n const securityCred = await this.getSecurityCredential();\n if (!initiator)\n throw new Error(\"initiatorName required for Transaction Status\");\n const token = await this.getToken();\n return queryTransactionStatus(\n this.baseUrl,\n token,\n securityCred,\n initiator,\n request\n );\n }\n\n /** Reversal - Reverse a transaction */\n async reversal(request: ReversalRequest) {\n const initiator = this.config.initiatorName ?? \"\";\n const securityCred = await this.getSecurityCredential();\n if (!initiator) throw new Error(\"initiatorName required for Reversal\");\n const token = await this.getToken();\n return processReversal(\n this.baseUrl,\n token,\n securityCred,\n initiator,\n request\n );\n }\n}\n","/**\n * Express-friendly helpers for M-Pesa Express (STK Push) and C2B simulate.\n *\n * NOTE: This module does NOT depend on Express at runtime. You pass in an\n * existing Express Router instance, and we attach handlers to it.\n */\n\nimport type { NextFunction, Request, Response, Router } from \"express\";\n\nimport { Mpesa } from \"../mpesa\";\nimport type { MpesaConfig } from \"../mpesa/types\";\nimport { PesafyError } from \"../utils/errors\";\n\nexport interface MpesaExpressConfig extends MpesaConfig {\n /**\n * Full callback URL that Safaricom will call after STK Push completes.\n * Example (sandbox):\n * https://your-domain.ngrok.io/api/mpesa/express/callback\n */\n callbackUrl: string;\n}\n\nexport function createMpesaExpressClient(config: MpesaExpressConfig): {\n mpesa: Mpesa;\n} {\n if (!config.consumerKey || !config.consumerSecret) {\n throw new PesafyError({\n code: \"INVALID_CREDENTIALS\",\n message: \"consumerKey and consumerSecret are required\",\n });\n }\n\n if (!config.lipaNaMpesaShortCode || !config.lipaNaMpesaPassKey) {\n throw new PesafyError({\n code: \"VALIDATION_ERROR\",\n message:\n \"lipaNaMpesaShortCode and lipaNaMpesaPassKey are required for STK Push\",\n });\n }\n\n if (!config.callbackUrl) {\n throw new PesafyError({\n code: \"VALIDATION_ERROR\",\n message: \"callbackUrl is required for STK Push callbacks\",\n });\n }\n\n const mpesa = new Mpesa(config);\n return { mpesa };\n}\n\ninterface StkPushBody {\n amount: number;\n phoneNumber: string;\n accountReference?: string;\n transactionDesc?: string;\n}\n\ninterface StkQueryBody {\n checkoutRequestId: string;\n}\n\nfunction normalizePhone(phone: string): string {\n const digits = phone.replace(/\\D/g, \"\");\n if (digits.startsWith(\"254\")) return digits;\n if (digits.startsWith(\"0\")) return `254${digits.slice(1)}`;\n return digits;\n}\n\nfunction sendError(res: Response, error: unknown): void {\n if (error instanceof PesafyError) {\n const status = error.statusCode ?? 400;\n res.status(status).json({\n error: error.code,\n message: error.message,\n statusCode: status,\n });\n return;\n }\n\n res.status(500).json({\n error: \"REQUEST_FAILED\",\n message: \"Unexpected error while processing M-Pesa request\",\n });\n}\n\nexport function createMpesaExpressRouter(\n router: Router,\n config: MpesaExpressConfig\n): Router {\n const { mpesa } = createMpesaExpressClient(config);\n\n // STK Push – initiate payment on customer phone\n router.post(\n \"/mpesa/express/stk-push\",\n async (req: Request, res: Response, next: NextFunction) => {\n try {\n const body = req.body as StkPushBody;\n\n if (!body || typeof body.amount !== \"number\" || body.amount <= 0) {\n throw new PesafyError({\n code: \"VALIDATION_ERROR\",\n message: \"amount must be a positive number\",\n });\n }\n\n if (!body.phoneNumber) {\n throw new PesafyError({\n code: \"VALIDATION_ERROR\",\n message: \"phoneNumber is required\",\n });\n }\n\n const phoneNumber = normalizePhone(body.phoneNumber);\n\n const result = await mpesa.stkPush({\n amount: body.amount,\n phoneNumber,\n callbackUrl: config.callbackUrl,\n accountReference:\n body.accountReference ??\n `PESAFY-${Date.now().toString(36).toUpperCase()}`,\n transactionDesc: body.transactionDesc ?? \"Payment\",\n });\n\n res.status(200).json(result);\n } catch (error) {\n if (res.headersSent) return next(error);\n sendError(res, error);\n }\n }\n );\n\n // STK Query – check status of an STK Push\n router.post(\n \"/mpesa/express/stk-query\",\n async (req: Request, res: Response, next: NextFunction) => {\n try {\n const body = req.body as StkQueryBody;\n\n if (!body || !body.checkoutRequestId) {\n throw new PesafyError({\n code: \"VALIDATION_ERROR\",\n message: \"checkoutRequestId is required\",\n });\n }\n\n const result = await mpesa.stkQuery({\n checkoutRequestId: body.checkoutRequestId,\n });\n\n res.status(200).json(result);\n } catch (error) {\n if (res.headersSent) return next(error);\n sendError(res, error);\n }\n }\n );\n\n // C2B simulate – sandbox only\n router.post(\n \"/mpesa/c2b/simulate\",\n async (req: Request, res: Response, next: NextFunction) => {\n try {\n const result = await mpesa.c2bSimulate(req.body);\n res.status(200).json(result);\n } catch (error) {\n if (res.headersSent) return next(error);\n sendError(res, error);\n }\n }\n );\n\n // C2B register URLs – sandbox only\n router.post(\n \"/mpesa/c2b/register-url\",\n async (req: Request, res: Response, next: NextFunction) => {\n try {\n const result = await mpesa.c2bRegisterUrls(req.body);\n res.status(200).json(result);\n } catch (error) {\n if (res.headersSent) return next(error);\n sendError(res, error);\n }\n }\n );\n\n return router;\n}\n","/**\n * Webhook retry mechanism with exponential backoff\n * For at-least-once delivery guarantee\n */\n\nexport interface RetryOptions {\n maxRetries?: number;\n initialDelay?: number;\n maxDelay?: number;\n backoffMultiplier?: number;\n maxRetryDuration?: number; // milliseconds (30 days default)\n}\n\nconst DEFAULT_OPTIONS: Required<RetryOptions> = {\n maxRetries: Infinity,\n initialDelay: 1000, // 1 second\n maxDelay: 3600000, // 1 hour\n backoffMultiplier: 2,\n maxRetryDuration: 30 * 24 * 60 * 60 * 1000, // 30 days\n};\n\nexport interface RetryResult<T> {\n success: boolean;\n data?: T;\n attempts: number;\n error?: Error;\n}\n\nexport async function retryWithBackoff<T>(\n fn: () => Promise<T>,\n options: RetryOptions = {}\n): Promise<RetryResult<T>> {\n const opts = { ...DEFAULT_OPTIONS, ...options };\n let delay = opts.initialDelay;\n let attempts = 0;\n const startTime = Date.now();\n\n while (attempts < opts.maxRetries) {\n attempts++;\n\n // Check if we've exceeded max retry duration\n if (Date.now() - startTime > opts.maxRetryDuration) {\n return {\n success: false,\n attempts,\n error: new Error(\"Max retry duration exceeded\"),\n };\n }\n\n try {\n const data = await fn();\n return { success: true, data, attempts };\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n\n // Don't retry on 4xx errors (client errors)\n if (err.message.includes(\"4\")) {\n return { success: false, attempts, error: err };\n }\n\n // Wait before retrying\n if (attempts < opts.maxRetries) {\n await new Promise((resolve) => setTimeout(resolve, delay));\n delay = Math.min(delay * opts.backoffMultiplier, opts.maxDelay);\n }\n }\n }\n\n return {\n success: false,\n attempts,\n error: new Error(\"Max retries exceeded\"),\n };\n}\n","/**\n * Webhook signature verification\n * Note: Daraja API doesn't provide webhook signatures in the same way as Stripe\n * Instead, verify by whitelisting IPs: 196.201.214.200, 196.201.214.206, etc.\n * This utility helps parse and validate webhook payloads\n */\n\nimport type { B2CWebhook, C2BWebhook, StkPushWebhook } from \"./types\";\n\nexport interface WebhookVerificationOptions {\n /** IP whitelist - verify request comes from Safaricom */\n allowedIPs?: string[];\n /** Optional: verify request headers match expected pattern */\n verifyHeaders?: boolean;\n}\n\nconst SAFARICOM_IPS = [\n \"196.201.214.200\",\n \"196.201.214.206\",\n \"196.201.213.114\",\n \"196.201.214.207\",\n \"196.201.214.208\",\n \"196.201.213.44\",\n \"196.201.212.127\",\n \"196.201.212.138\",\n \"196.201.212.129\",\n \"196.201.212.136\",\n \"196.201.212.74\",\n \"196.201.212.69\",\n];\n\nexport function verifyWebhookIP(\n requestIP: string,\n allowedIPs: string[] = SAFARICOM_IPS\n): boolean {\n return allowedIPs.includes(requestIP);\n}\n\nexport function parseStkPushWebhook(body: unknown): StkPushWebhook | null {\n try {\n const parsed = body as StkPushWebhook;\n if (parsed.Body?.stkCallback) return parsed;\n return null;\n } catch {\n return null;\n }\n}\n\nexport function parseB2CWebhook(body: unknown): B2CWebhook | null {\n try {\n const parsed = body as B2CWebhook;\n if (parsed.Result?.ResultCode !== undefined) return parsed;\n return null;\n } catch {\n return null;\n }\n}\n\nexport function parseC2BWebhook(body: unknown): C2BWebhook | null {\n try {\n const parsed = body as C2BWebhook;\n if (parsed.TransID && parsed.TransAmount) return parsed;\n return null;\n } catch {\n return null;\n }\n}\n","/**\n * Webhook event handler utilities\n */\n\nimport {\n parseB2CWebhook,\n parseC2BWebhook,\n parseStkPushWebhook,\n verifyWebhookIP,\n} from \"./signature-verifier\";\nimport type {\n B2CWebhook,\n C2BWebhook,\n StkPushWebhook,\n WebhookEventType,\n} from \"./types\";\n\nexport interface WebhookHandlerOptions {\n /** IP address of incoming request */\n requestIP?: string;\n /** Custom IP whitelist */\n allowedIPs?: string[];\n /** Skip IP verification (for testing) */\n skipIPCheck?: boolean;\n}\n\nexport interface WebhookHandlerResult<T = unknown> {\n success: boolean;\n eventType: WebhookEventType | null;\n data: T | null;\n error?: string;\n}\n\nexport function handleWebhook(\n body: unknown,\n options: WebhookHandlerOptions = {}\n): WebhookHandlerResult {\n // Verify IP if provided\n if (!options.skipIPCheck && options.requestIP) {\n if (!verifyWebhookIP(options.requestIP, options.allowedIPs)) {\n return {\n success: false,\n eventType: null,\n data: null,\n error: \"IP address not whitelisted\",\n };\n }\n }\n\n // Try to parse as STK Push\n const stkPush = parseStkPushWebhook(body);\n if (stkPush) {\n return {\n success: true,\n eventType: \"stk_push\",\n data: stkPush,\n };\n }\n\n // Try to parse as B2C\n const b2c = parseB2CWebhook(body);\n if (b2c) {\n return {\n success: true,\n eventType: \"b2c\",\n data: b2c,\n };\n }\n\n // Try to parse as C2B\n const c2b = parseC2BWebhook(body);\n if (c2b) {\n return {\n success: true,\n eventType: \"c2b\",\n data: c2b,\n };\n }\n\n return {\n success: false,\n eventType: null,\n data: null,\n error: \"Unknown webhook format\",\n };\n}\n\nexport function extractTransactionId(\n webhook: StkPushWebhook | B2CWebhook | C2BWebhook\n): string | null {\n if (\"Body\" in webhook && webhook.Body?.stkCallback) {\n const items = webhook.Body.stkCallback.CallbackMetadata?.Item;\n const mpesaReceipt = items?.find(\n (item) => item.Name === \"MpesaReceiptNumber\"\n );\n return mpesaReceipt ? String(mpesaReceipt.Value) : null;\n }\n if (\"Result\" in webhook && webhook.Result?.TransactionID) {\n return webhook.Result.TransactionID;\n }\n if (\"TransID\" in webhook) {\n return webhook.TransID;\n }\n return null;\n}\n\nexport function extractAmount(\n webhook: StkPushWebhook | B2CWebhook | C2BWebhook\n): number | null {\n if (\"Body\" in webhook && webhook.Body?.stkCallback) {\n const items = webhook.Body.stkCallback.CallbackMetadata?.Item;\n const amount = items?.find((item) => item.Name === \"Amount\");\n return amount ? Number(amount.Value) : null;\n }\n if (\"Result\" in webhook && webhook.Result?.ResultParameters) {\n const params = webhook.Result.ResultParameters.ResultParameter;\n const amount = params?.find((p) => p.Key === \"Amount\");\n return amount ? Number(amount.Value) : null;\n }\n if (\"TransAmount\" in webhook) {\n return Number.parseFloat(webhook.TransAmount);\n }\n return null;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/utils/errors/error-factory.ts","../src/utils/http/client.ts","../src/core/auth/token-manager.ts","../src/core/encryption/security-credentials.ts","../src/mpesa/b2b/b2b.ts","../src/utils/phone.ts","../src/mpesa/stk-push/utils.ts","../src/mpesa/b2c/b2c.ts","../src/mpesa/c2b/register-url.ts","../src/mpesa/c2b/simulate.ts","../src/mpesa/c2b/types.ts","../src/mpesa/qr-code/dynamic-qr.ts","../src/mpesa/reversal/reversal.ts","../src/mpesa/stk-push/stk-push.ts","../src/mpesa/stk-push/stk-query.ts","../src/mpesa/stk-push/types.ts","../src/mpesa/transaction-status/query.ts","../src/mpesa/types.ts","../src/mpesa/index.ts","../src/express/index.ts","../src/mpesa/webhooks/retry.ts","../src/mpesa/webhooks/signature-verifier.ts","../src/mpesa/webhooks/webhook-handler.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Error creation utilities for Pesafy\n */\n\nimport type { ErrorCode, PesafyErrorOptions } from \"./types\";\n\nexport class PesafyError extends Error {\n readonly code: ErrorCode;\n readonly statusCode?: number;\n readonly response?: unknown;\n readonly requestId?: string;\n override readonly cause?: unknown;\n\n constructor(options: PesafyErrorOptions) {\n super(options.message);\n Object.defineProperty(this, \"name\", { value: \"PesafyError\" });\n this.code = options.code;\n this.statusCode = options.statusCode;\n this.response = options.response;\n this.requestId = options.requestId;\n this.cause = options.cause;\n\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, PesafyError);\n }\n }\n\n toJSON() {\n return {\n name: this.name,\n code: this.code,\n message: this.message,\n statusCode: this.statusCode,\n requestId: this.requestId,\n };\n }\n}\n\nexport function createError(options: PesafyErrorOptions): PesafyError {\n return new PesafyError(options);\n}\n","/**\n * HTTP client for Daraja API requests\n */\n\nimport { PesafyError } from \"../errors\";\nimport type { HttpRequestOptions, HttpResponse } from \"./types\";\n\nconst DEFAULT_TIMEOUT = 30000;\n\nexport async function httpRequest<T = unknown>(\n url: string,\n options: HttpRequestOptions = {}\n): Promise<HttpResponse<T>> {\n const {\n method = \"GET\",\n headers = {},\n body,\n timeout = DEFAULT_TIMEOUT,\n } = options;\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeout);\n\n try {\n const response = await fetch(url, {\n method,\n headers: {\n \"Content-Type\": \"application/json\",\n ...headers,\n },\n body: body ? JSON.stringify(body) : undefined,\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n let data: T;\n const text = await response.text();\n try {\n data = (text ? JSON.parse(text) : {}) as T;\n } catch {\n data = { raw: text } as T;\n }\n\n if (!response.ok) {\n // Include the full Daraja response body so callers can see exactly what\n // Safaricom returned: \"Invalid Access Token\", \"Bad Request - Invalid\n // Shortcode\", \"Wrong credentials\", etc. Previously this was swallowed.\n const bodyStr = text.length > 0 ? ` — ${text}` : \"\";\n throw new PesafyError({\n code: \"API_ERROR\",\n message: `Request failed with status ${response.status}${bodyStr}`,\n statusCode: response.status,\n response: data,\n });\n }\n\n return {\n data,\n status: response.status,\n headers: response.headers,\n };\n } catch (error) {\n clearTimeout(timeoutId);\n\n if (error instanceof PesafyError) throw error;\n if (error instanceof Error) {\n if (error.name === \"AbortError\") {\n throw new PesafyError({\n code: \"TIMEOUT\",\n message: `Request timed out after ${timeout}ms`,\n cause: error,\n });\n }\n throw new PesafyError({\n code: \"NETWORK_ERROR\",\n message: error.message,\n cause: error,\n });\n }\n throw new PesafyError({\n code: \"REQUEST_FAILED\",\n message: \"An unknown error occurred\",\n cause: error,\n });\n }\n}\n","/**\n * OAuth token manager for Daraja API\n * Token validity: 3600 seconds (1 hour)\n */\n\nimport { PesafyError } from \"../../utils/errors\";\nimport { httpRequest } from \"../../utils/http\";\nimport type { TokenResponse } from \"./types\";\n\nconst TOKEN_BUFFER_SECONDS = 60; // Refresh token 60s before expiry\n\nexport class TokenManager {\n private consumerKey: string;\n private consumerSecret: string;\n private baseUrl: string;\n private cachedToken: string | null = null;\n private tokenExpiresAt = 0;\n\n constructor(consumerKey: string, consumerSecret: string, baseUrl: string) {\n this.consumerKey = consumerKey;\n this.consumerSecret = consumerSecret;\n this.baseUrl = baseUrl;\n }\n\n private getAuthHeader(): string {\n const credentials = `${this.consumerKey}:${this.consumerSecret}`;\n const encoded = Buffer.from(credentials, \"utf-8\").toString(\"base64\");\n return `Basic ${encoded}`;\n }\n\n async getAccessToken(): Promise<string> {\n const now = Date.now() / 1000;\n if (this.cachedToken && this.tokenExpiresAt > now + TOKEN_BUFFER_SECONDS) {\n return this.cachedToken;\n }\n\n const url = `${this.baseUrl}/oauth/v1/generate?grant_type=client_credentials`;\n const response = await httpRequest<TokenResponse>(url, {\n method: \"GET\",\n headers: {\n Authorization: this.getAuthHeader(),\n },\n });\n\n const data = response.data;\n if (!data.access_token) {\n throw new PesafyError({\n code: \"AUTH_FAILED\",\n message: \"Failed to obtain access token\",\n response: data,\n });\n }\n\n this.cachedToken = data.access_token;\n this.tokenExpiresAt = now + (data.expires_in ?? 3600);\n return this.cachedToken;\n }\n\n clearCache(): void {\n this.cachedToken = null;\n this.tokenExpiresAt = 0;\n }\n}\n","/**\n * Security credential encryption for B2C, B2B, Reversal APIs\n * Uses RSA with PKCS#1.5 padding per Daraja API spec\n * Download certificates from: https://developer.safaricom.co.ke/APIs\n */\n\nimport { publicEncrypt } from \"node:crypto\";\nimport { PesafyError } from \"../../utils/errors\";\n\n/** Encrypt initiator password with M-Pesa public certificate (PEM format) */\nexport function encryptSecurityCredential(\n initiatorPassword: string,\n certificatePem: string\n): string {\n try {\n const passwordBuffer = Buffer.from(initiatorPassword, \"utf-8\");\n const encrypted = publicEncrypt(\n {\n key: certificatePem,\n padding: 1, // RSA_PKCS1_PADDING\n },\n passwordBuffer\n );\n return encrypted.toString(\"base64\");\n } catch (error) {\n throw new PesafyError({\n code: \"ENCRYPTION_FAILED\",\n message: \"Failed to encrypt security credential\",\n cause: error,\n });\n }\n}\n","/**\n * B2B - Business to Business payments\n * API: POST /mpesa/b2b/v1/paymentrequest\n */\n\nimport { httpRequest } from \"../../utils/http\";\nimport type { B2BRequest } from \"./types\";\n\nexport interface B2BResponse {\n OriginatorConversationID: string;\n ConversationID: string;\n ResponseCode: string;\n ResponseDescription: string;\n}\n\nexport async function processB2B(\n baseUrl: string,\n accessToken: string,\n securityCredential: string,\n initiatorName: string,\n request: B2BRequest\n): Promise<B2BResponse> {\n const body = {\n Initiator: initiatorName,\n SecurityCredential: securityCredential,\n CommandID: request.commandId ?? \"BusinessPayBill\",\n SenderIdentifierType: request.senderIdentifierType ?? 4,\n RecieverIdentifierType: request.receiverIdentifierType ?? 4,\n Amount: Math.round(request.amount),\n PartyA: request.shortCode,\n PartyB: request.receiverShortCode,\n AccountReference: request.accountReference ?? \"\",\n Remarks: request.remarks ?? \"B2B Payment\",\n QueueTimeOutURL: request.timeoutUrl,\n ResultURL: request.resultUrl,\n };\n\n const { data } = await httpRequest<B2BResponse>(\n `${baseUrl}/mpesa/b2b/v1/paymentrequest`,\n {\n method: \"POST\",\n headers: { Authorization: `Bearer ${accessToken}` },\n body,\n }\n );\n\n return data;\n}\n","/**\n * Shared phone number utilities for M-Pesa API calls.\n *\n * Two formatters are intentionally separate:\n * - formatSafaricomPhone → strict, for STK Push (only Safaricom/Airtel)\n * - formatKenyanMsisdn → permissive, for C2B simulate and B2C PartyB\n *\n * Daraja consistently expects 12-digit MSISDN without the '+' prefix.\n */\n\n/** Strips non-digit characters and normalises leading zeros / country code. */\nfunction toE164Kenya(phone: string): string {\n const digits = phone.replace(/\\D/g, \"\");\n\n if (digits.startsWith(\"254\")) return digits;\n if (digits.startsWith(\"0\")) return `254${digits.slice(1)}`;\n // Bare 9-digit local number (e.g. 712345678)\n if (digits.length === 9) return `254${digits}`;\n\n // Fallback — prepend country code and let the validator catch it\n return `254${digits}`;\n}\n\n/**\n * Strict formatter for STK Push.\n * Accepts Safaricom (2547xx) and Airtel Kenya (2541x) numbers only.\n * Throws a clear error on invalid input so Daraja never receives a bad MSISDN.\n */\nexport function formatSafaricomPhone(phone: string): string {\n const formatted = toE164Kenya(phone);\n\n // 2547xxxxxxxx (Safaricom) | 2541xxxxxxxx (Airtel Kenya)\n if (!/^254[71]\\d{8}$/.test(formatted)) {\n throw new Error(\n `Invalid Kenyan phone number: \"${phone}\". ` +\n \"Expected format: 07XXXXXXXX, 2547XXXXXXXX, or +2547XXXXXXXX.\"\n );\n }\n\n return formatted;\n}\n\n/**\n * Permissive formatter for C2B simulate and B2C PartyB.\n * Accepts any valid 12-digit Kenyan MSISDN (Safaricom, Airtel, Telkom, …).\n * Still throws if the result is structurally impossible.\n */\nexport function formatKenyanMsisdn(phone: string): string {\n const formatted = toE164Kenya(phone);\n\n // Must be exactly 12 digits and start with 254\n if (!/^254\\d{9}$/.test(formatted)) {\n throw new Error(\n `Invalid MSISDN: \"${phone}\". ` +\n \"Expected a 12-digit Kenyan number starting with 254.\"\n );\n }\n\n return formatted;\n}\n\n/**\n * Converts a formatted MSISDN string to the numeric form Daraja expects in\n * C2B simulate requests (\"Msisdn\": 254708374149 — no quotes in the JSON).\n */\nexport function msisdnToNumber(phone: string): number {\n return parseInt(formatKenyanMsisdn(phone), 10);\n}\n","/**\n * STK Push utilities\n *\n * Phone formatting delegates to the shared `formatSafaricomPhone` util which\n * validates Safaricom/Airtel numbers and throws a clear error on bad input.\n */\nexport { formatSafaricomPhone as formatPhoneNumber } from \"../../utils/phone\";\n\n/**\n * Generates the STK Push password.\n * Formula: Base64(Shortcode + Passkey + Timestamp)\n *\n * Uses btoa() instead of Buffer — works in Node.js, Bun, browsers, and\n * edge runtimes (Cloudflare Workers, Deno, etc.).\n *\n * IMPORTANT: The timestamp passed here MUST be the exact same value sent in\n * the request body's `Timestamp` field. Safaricom validates they match.\n * Always call getTimestamp() once and pass the result to both this function\n * and the request body — never call getTimestamp() twice.\n */\nexport function getStkPushPassword(\n shortCode: string,\n passKey: string,\n timestamp: string\n): string {\n const raw = `${shortCode}${passKey}${timestamp}`;\n return btoa(raw);\n}\n\n/**\n * Generates a Daraja-compatible timestamp: YYYYMMDDHHmmss\n * Call this once per request and reuse the value.\n */\nexport function getTimestamp(): string {\n const now = new Date();\n const pad = (n: number) => n.toString().padStart(2, \"0\");\n return [\n now.getFullYear(),\n pad(now.getMonth() + 1),\n pad(now.getDate()),\n pad(now.getHours()),\n pad(now.getMinutes()),\n pad(now.getSeconds()),\n ].join(\"\");\n}\n","/**\n * B2C - Business to Customer payments\n * API: POST /mpesa/b2c/v3/paymentrequest\n */\n\nimport { httpRequest } from \"../../utils/http\";\nimport { formatPhoneNumber } from \"../stk-push/utils\";\nimport type { B2CRequest } from \"./types\";\n\nexport interface B2CResponse {\n OriginatorConversationID: string;\n ConversationID: string;\n ResponseCode: string;\n ResponseDescription: string;\n}\n\nexport async function processB2C(\n baseUrl: string,\n accessToken: string,\n securityCredential: string,\n initiatorName: string,\n request: B2CRequest\n): Promise<B2CResponse> {\n const body = {\n OriginatorConversationID: `AG_${Date.now()}_${Math.random().toString(36).slice(2)}`,\n InitiatorName: initiatorName,\n SecurityCredential: securityCredential,\n CommandID: request.commandId ?? \"BusinessPayment\",\n Amount: Math.round(request.amount),\n PartyA: request.shortCode,\n PartyB: formatPhoneNumber(request.phoneNumber),\n Remarks: request.remarks ?? \"Payment\",\n QueueTimeOutURL: request.timeoutUrl,\n ResultURL: request.resultUrl,\n Occasion: request.occasion ?? \"\",\n };\n\n const { data } = await httpRequest<B2CResponse>(\n `${baseUrl}/mpesa/b2c/v3/paymentrequest`,\n {\n method: \"POST\",\n headers: { Authorization: `Bearer ${accessToken}` },\n body,\n }\n );\n\n return data;\n}\n","/**\n * C2B Register URLs\n * API: POST /mpesa/c2b/v1/registerurl ← v1 (NOT v2 — v2 does not exist for this endpoint)\n *\n * Registers Validation and Confirmation callback URLs for a short code.\n * Sandbox: can be called multiple times (though Daraja sandbox is flaky — retry on 500).\n * Production: one-time registration; contact Safaricom to change.\n *\n * ⚠️ Common mistake: using /v2/registerurl causes persistent 500.003.1001 errors.\n * The Daraja Postman collection and official docs both confirm the path is /v1/registerurl.\n */\nimport { httpRequest } from \"../../utils/http\";\nimport type { C2BRegisterUrlRequest, C2BResponseType } from \"./types\";\n\n/**\n * Actual response shape from Daraja.\n * Note: Daraja's field name has a typo (\"CoversationID\" — missing the 'n').\n * We match it exactly so JSON parsing is lossless.\n */\nexport interface C2BRegisterUrlResponse {\n /** Global unique identifier for this registration request. Daraja typo: \"Coversation\" */\n OriginatorCoversationID: string;\n /** \"0\" = accepted */\n ResponseCode: string;\n ResponseDescription: string;\n}\n\nexport async function registerC2BUrls(\n baseUrl: string,\n accessToken: string,\n request: C2BRegisterUrlRequest\n): Promise<C2BRegisterUrlResponse> {\n const responseType: C2BResponseType = request.responseType ?? \"Completed\";\n\n const body = {\n ShortCode: request.shortCode,\n ResponseType: responseType,\n ConfirmationURL: request.confirmationUrl,\n ValidationURL: request.validationUrl,\n };\n\n // ✅ FIXED: /v1/registerurl (was incorrectly /v2/registerurl)\n // The v2 path does not exist and will always return 500.003.1001.\n const { data } = await httpRequest<C2BRegisterUrlResponse>(\n `${baseUrl}/mpesa/c2b/v1/registerurl`,\n {\n method: \"POST\",\n headers: { Authorization: `Bearer ${accessToken}` },\n body,\n }\n );\n\n return data;\n}\n","/**\n * C2B Simulate (sandbox only)\n * API: POST /mpesa/c2b/v2/simulate\n *\n * Simulates a customer paying a Paybill or Till number.\n * This endpoint is NOT available in production — use STK Push or Dynamic QR\n * for production payment initiation.\n */\nimport { httpRequest } from \"../../utils/http\";\nimport { msisdnToNumber } from \"../../utils/phone\";\nimport type { C2BCommandId, C2BSimulateRequest } from \"./types\";\n\n/**\n * Actual response shape from Daraja.\n * Note: Daraja's field name has a typo (\"CoversationID\" — missing the 'n').\n * We match it exactly so JSON parsing is lossless.\n * There is NO ConversationID field in this response (unlike B2C/B2B).\n */\nexport interface C2BSimulateResponse {\n /** Global unique identifier for this simulate request. Daraja typo: \"Coversation\" */\n OriginatorCoversationID: string;\n /** \"0\" = accepted */\n ResponseCode: string;\n ResponseDescription: string;\n}\n\nexport async function simulateC2B(\n baseUrl: string,\n accessToken: string,\n request: C2BSimulateRequest\n): Promise<C2BSimulateResponse> {\n const commandId: C2BCommandId = request.commandId ?? \"CustomerPayBillOnline\";\n const isBuyGoods = commandId === \"CustomerBuyGoodsOnline\";\n\n /**\n * BillRefNumber (AccountReference) rules — Daraja v2 sandbox behaviour:\n *\n * CustomerPayBillOnline:\n * - Required. Use the passed billRefNumber or fall back to empty string \"\".\n * - Empty string is accepted; it just means no account reference.\n *\n * CustomerBuyGoodsOnline:\n * - The field MUST be omitted entirely from the request body.\n * - Sending any value (even \"0\", \"\", or null) triggers:\n * 500.003.1001 \"The element AccountReference is invalid.\"\n * - ✅ FIX: conditionally include the field only for Paybill.\n */\n const body: Record<string, unknown> = {\n ShortCode: Number(request.shortCode),\n CommandID: commandId,\n Amount: Math.round(request.amount),\n // Daraja expects Msisdn as a number (not a quoted string)\n Msisdn: msisdnToNumber(request.phoneNumber),\n };\n\n // ✅ FIXED: Only include BillRefNumber for Paybill — omit entirely for Buy Goods\n if (!isBuyGoods) {\n body.BillRefNumber = request.billRefNumber ?? \"\";\n }\n\n const { data } = await httpRequest<C2BSimulateResponse>(\n `${baseUrl}/mpesa/c2b/v2/simulate`,\n {\n method: \"POST\",\n headers: { Authorization: `Bearer ${accessToken}` },\n body,\n }\n );\n\n return data;\n}\n","/**\n * C2B (Customer to Business) types\n * API Reference: https://developer.safaricom.co.ke/APIs/CustomerToBusinessV2\n *\n * C2B v2 uses masked MSISDN (e.g. \"2547 ***** 126\").\n * C2B v1 used SHA-256 hashed MSISDN — do NOT use v1.\n */\n\n// ─── Command IDs ─────────────────────────────────────────────────────────────\n\n/**\n * The two valid C2B CommandID values:\n * - CustomerPayBillOnline → payment to a Paybill number\n * - CustomerBuyGoodsOnline → payment to a Till number\n */\nexport type C2BCommandId = \"CustomerPayBillOnline\" | \"CustomerBuyGoodsOnline\";\n\n/**\n * Default action when the ValidationURL is unreachable.\n * \"Completed\" → M-PESA auto-completes the transaction.\n * \"Cancelled\" → M-PESA auto-cancels the transaction.\n * Note: values are case-sensitive per Daraja docs.\n */\nexport type C2BResponseType = \"Completed\" | \"Cancelled\";\n\n// ─── Register URL ─────────────────────────────────────────────────────────────\n\nexport interface C2BRegisterUrlRequest {\n /** Paybill or Till short code */\n shortCode: string;\n /** URL that receives payment confirmation after a successful transaction */\n confirmationUrl: string;\n /** URL that receives validation requests (only when external validation is enabled) */\n validationUrl: string;\n responseType?: C2BResponseType;\n}\n\n// ─── C2B Simulate (sandbox only) ─────────────────────────────────────────────\n\nexport interface C2BSimulateRequest {\n /** Paybill or Till short code */\n shortCode: string;\n /**\n * Transaction type.\n * \"CustomerPayBillOnline\" → payment to a Paybill number (default)\n * \"CustomerBuyGoodsOnline\" → payment to a Till number\n */\n commandId?: C2BCommandId;\n /** Transaction amount (whole number, min 1 KES) */\n amount: number;\n /** Customer's phone number — any valid Kenyan MSISDN */\n phoneNumber: string;\n /**\n * Account reference for Paybill payments.\n * Must be null / omitted for CustomerBuyGoodsOnline.\n */\n billRefNumber?: string;\n}\n\n// ─── C2B Callback payload types ───────────────────────────────────────────────\n// Safaricom POSTs these to your ConfirmationURL / ValidationURL after payment.\n// v2 returns a masked MSISDN (e.g. \"2547 ***** 126\").\n\n/**\n * Shared fields present in both Validation and Confirmation callback payloads.\n */\nexport interface C2BCallbackPayloadBase {\n TransactionType: string;\n /** Unique M-PESA transaction ID (e.g. \"RKL51ZDR4F\") */\n TransID: string;\n /** 14-digit timestamp: YYYYMMDDHHmmss */\n TransTime: string;\n /** Transaction amount as a string (e.g. \"5.00\") */\n TransAmount: string;\n BusinessShortCode: string;\n /** Account reference; empty for Till transactions */\n BillRefNumber: string;\n InvoiceNumber: string;\n OrgAccountBalance: string;\n ThirdPartyTransID: string;\n /** v2: masked MSISDN e.g. \"2547 ***** 126\" */\n MSISDN: string;\n FirstName: string;\n MiddleName: string;\n LastName: string;\n}\n\n/** Payload posted to your ValidationURL (if external validation is enabled) */\nexport type C2BValidationPayload = C2BCallbackPayloadBase;\n\n/** Payload posted to your ConfirmationURL after successful payment */\nexport type C2BConfirmationPayload = C2BCallbackPayloadBase;\n\n/**\n * Union of both C2B callback types.\n * Use the discriminated `TransactionType` field if you need to distinguish them,\n * or register separate Express routes for each URL.\n */\nexport type C2BCallbackPayload = C2BValidationPayload | C2BConfirmationPayload;\n\n// ─── Validation response codes ────────────────────────────────────────────────\n\n/**\n * Codes your ValidationURL must return to Safaricom to accept or reject\n * a payment before it is processed.\n */\nexport const C2B_REJECTION_CODES = {\n /** Accept the transaction — M-PESA proceeds to process and confirm */\n ACCEPT: \"0\",\n /** Reject the transaction — M-PESA cancels and notifies the customer */\n REJECT: \"1\",\n} as const;\n\nexport type C2BRejectionCode =\n (typeof C2B_REJECTION_CODES)[keyof typeof C2B_REJECTION_CODES];\n","/**\n * Dynamic QR Code - Generate LIPA NA M-PESA QR codes\n * API: POST /mpesa/qrcode/v1/generate\n */\n\nimport { httpRequest } from \"../../utils/http\";\nimport type { DynamicQRRequest } from \"./types\";\n\nexport interface DynamicQRResponse {\n ResponseCode: string;\n RequestID: string;\n ResponseDescription: string;\n QRCode: string;\n}\n\nexport async function generateDynamicQR(\n baseUrl: string,\n accessToken: string,\n request: DynamicQRRequest\n): Promise<DynamicQRResponse> {\n const body = {\n MerchantName: request.merchantName,\n RefNo: request.refNo,\n Amount: Math.round(request.amount),\n TrxCode: request.trxCode,\n CPI: request.cpi,\n Size: request.size ?? \"300\",\n };\n\n const { data } = await httpRequest<DynamicQRResponse>(\n `${baseUrl}/mpesa/qrcode/v1/generate`,\n {\n method: \"POST\",\n headers: { Authorization: `Bearer ${accessToken}` },\n body,\n }\n );\n\n return data;\n}\n","/**\n * Transaction Reversal\n * API: POST /mpesa/reversal/v1/request\n */\n\nimport { httpRequest } from \"../../utils/http\";\nimport type { ReversalRequest } from \"./types\";\n\nexport interface ReversalResponse {\n OriginatorConversationID: string;\n ConversationID: string;\n ResponseCode: string;\n ResponseDescription: string;\n}\n\nexport async function processReversal(\n baseUrl: string,\n accessToken: string,\n securityCredential: string,\n initiatorName: string,\n request: ReversalRequest\n): Promise<ReversalResponse> {\n const body = {\n Initiator: initiatorName,\n SecurityCredential: securityCredential,\n CommandID: \"TransactionReversal\",\n TransactionID: request.transactionId,\n Amount: Math.round(request.amount),\n ReceiverParty: request.shortCode,\n RecieverIdentifierType: 4,\n ResultURL: request.resultUrl,\n QueueTimeOutURL: request.timeoutUrl,\n Remarks: request.remarks ?? \"Reversal\",\n Occasion: request.occasion ?? \"Reversal\",\n };\n\n const { data } = await httpRequest<ReversalResponse>(\n `${baseUrl}/mpesa/reversal/v1/request`,\n {\n method: \"POST\",\n headers: { Authorization: `Bearer ${accessToken}` },\n body,\n }\n );\n\n return data;\n}\n","/**\n * M-Pesa Express (STK Push) - Initiates payment prompt on customer's phone\n * API: POST /mpesa/stkpush/v1/processrequest\n */\n\nimport { httpRequest } from \"../../utils/http\";\nimport type { StkPushRequest, StkPushResponse } from \"./types\";\nimport { formatPhoneNumber, getStkPushPassword, getTimestamp } from \"./utils\";\n\nexport async function processStkPush(\n baseUrl: string,\n accessToken: string,\n request: StkPushRequest\n): Promise<StkPushResponse> {\n // Validate amount: Daraja requires a whole number ≥ 1 KES.\n // Math.round(0.3) = 0, which Daraja rejects — catch it here with a clear error.\n const amount = Math.round(request.amount);\n if (amount < 1) {\n throw new Error(\n `Amount must be at least KES 1 (got ${request.amount} which rounds to ${amount}).`\n );\n }\n\n // Generate timestamp ONCE — must be identical in both Password and Timestamp fields.\n // Safaricom validates that Base64(Shortcode+Passkey+Timestamp) matches the\n // Timestamp field; generating two separate timestamps causes auth failures.\n const timestamp = getTimestamp();\n\n // For CustomerBuyGoodsOnline (Till Number), PartyB is the till number.\n // For CustomerPayBillOnline (Paybill), PartyB is the shortcode.\n // Docs: https://developer.safaricom.co.ke/APIs/MpesaExpressSimulate\n const partyB = request.partyB ?? request.shortCode;\n\n const body = {\n BusinessShortCode: request.shortCode,\n Password: getStkPushPassword(request.shortCode, request.passKey, timestamp),\n Timestamp: timestamp,\n TransactionType: request.transactionType ?? \"CustomerPayBillOnline\",\n Amount: amount,\n PartyA: formatPhoneNumber(request.phoneNumber),\n PartyB: partyB,\n PhoneNumber: formatPhoneNumber(request.phoneNumber),\n CallBackURL: request.callbackUrl,\n AccountReference: request.accountReference.slice(0, 12),\n TransactionDesc: request.transactionDesc.slice(0, 13),\n };\n\n const { data } = await httpRequest<StkPushResponse>(\n `${baseUrl}/mpesa/stkpush/v1/processrequest`,\n {\n method: \"POST\",\n headers: { Authorization: `Bearer ${accessToken}` },\n body,\n }\n );\n\n return data;\n}\n","/**\n * STK Push Query - Check status of STK Push transaction\n * API: POST /mpesa/stkpushquery/v1/query\n */\n\nimport { httpRequest } from \"../../utils/http\";\nimport type { StkQueryRequest, StkQueryResponse } from \"./types\";\nimport { getStkPushPassword, getTimestamp } from \"./utils\";\n\nexport async function queryStkPush(\n baseUrl: string,\n accessToken: string,\n request: StkQueryRequest\n): Promise<StkQueryResponse> {\n // Generate timestamp ONCE — Password and Timestamp field must match exactly.\n const timestamp = getTimestamp();\n\n const body = {\n BusinessShortCode: request.shortCode,\n Password: getStkPushPassword(request.shortCode, request.passKey, timestamp),\n Timestamp: timestamp,\n CheckoutRequestID: request.checkoutRequestId,\n };\n\n const { data } = await httpRequest<StkQueryResponse>(\n `${baseUrl}/mpesa/stkpushquery/v1/query`,\n {\n method: \"POST\",\n headers: { Authorization: `Bearer ${accessToken}` },\n body,\n }\n );\n\n return data;\n}\n","/** STK Push (M-Pesa Express) types */\n\nexport type TransactionType =\n | \"CustomerPayBillOnline\"\n | \"CustomerBuyGoodsOnline\";\n\nexport interface StkPushRequest {\n amount: number;\n phoneNumber: string;\n callbackUrl: string;\n accountReference: string;\n transactionDesc: string;\n /** Business shortcode - Paybill or HO/store number for Till */\n shortCode: string;\n passKey: string;\n transactionType?: TransactionType;\n /**\n * The credit party receiving funds.\n * - CustomerPayBillOnline: omit (defaults to shortCode)\n * - CustomerBuyGoodsOnline: provide the Till Number\n * Docs: PartyB must be the Till Number when using Buy Goods.\n */\n partyB?: string;\n}\n\nexport interface StkPushResponse {\n MerchantRequestID: string;\n CheckoutRequestID: string;\n ResponseCode: string;\n ResponseDescription: string;\n CustomerMessage: string;\n}\n\nexport interface StkQueryRequest {\n checkoutRequestId: string;\n shortCode: string;\n passKey: string;\n}\n\nexport interface StkQueryResponse {\n ResponseCode: string;\n ResponseDescription: string;\n MerchantRequestID: string;\n CheckoutRequestID: string;\n /**\n * Daraja returns ResultCode as a NUMBER, not a string.\n * 0 = success\n * 1032 = cancelled by user\n * 1037 = timeout (customer did not respond)\n * 2001 = wrong PIN\n * etc.\n * Always compare with === 0, not === \"0\".\n */\n ResultCode: number;\n ResultDesc: string;\n}\n\n// ---------------------------------------------------------------------------\n// Callback payload types — Safaricom POSTs these to your CallBackURL\n// ---------------------------------------------------------------------------\n\n/** A single metadata item in a successful STK callback */\nexport interface StkCallbackMetadataItem {\n Name: \"Amount\" | \"MpesaReceiptNumber\" | \"TransactionDate\" | \"PhoneNumber\";\n /** Present on successful transactions; absent on failure */\n Value?: number | string;\n}\n\n/** Inner callback object for a SUCCESSFUL STK Push */\nexport interface StkCallbackSuccess {\n MerchantRequestID: string;\n CheckoutRequestID: string;\n /** 0 = success */\n ResultCode: 0;\n ResultDesc: string;\n CallbackMetadata: {\n Item: StkCallbackMetadataItem[];\n };\n}\n\n/** Inner callback object for a FAILED/CANCELLED STK Push */\nexport interface StkCallbackFailure {\n MerchantRequestID: string;\n CheckoutRequestID: string;\n /** Non-zero result codes: e.g. 1032 = cancelled by user */\n ResultCode: number;\n ResultDesc: string;\n CallbackMetadata?: never;\n}\n\nexport type StkCallbackInner = StkCallbackSuccess | StkCallbackFailure;\n\n/** Full wrapper that Safaricom POSTs to your CallBackURL */\nexport interface StkPushCallback {\n Body: {\n stkCallback: StkCallbackInner;\n };\n}\n\n/**\n * Type guard — narrows an StkCallbackInner to the success shape.\n * Usage:\n * if (isStkCallbackSuccess(callback.Body.stkCallback)) {\n * const receipt = getCallbackValue(callback, \"MpesaReceiptNumber\");\n * }\n */\nexport function isStkCallbackSuccess(\n cb: StkCallbackInner\n): cb is StkCallbackSuccess {\n return cb.ResultCode === 0;\n}\n\n/**\n * Extracts a named value from a successful callback's metadata items.\n * Returns undefined if the key is absent or the callback failed.\n */\nexport function getCallbackValue(\n callback: StkPushCallback,\n name: StkCallbackMetadataItem[\"Name\"]\n): string | number | undefined {\n const inner = callback.Body.stkCallback;\n if (!isStkCallbackSuccess(inner)) return undefined;\n return inner.CallbackMetadata.Item.find((i) => i.Name === name)?.Value;\n}\n","/**\n * Transaction Status Query\n * API: POST /mpesa/transactionstatus/v1/query\n */\n\nimport { httpRequest } from \"../../utils/http\";\nimport type { TransactionStatusRequest } from \"./types\";\n\nexport interface TransactionStatusResponse {\n OriginatorConversationID: string;\n ConversationID: string;\n ResponseCode: string;\n ResponseDescription: string;\n}\n\nexport async function queryTransactionStatus(\n baseUrl: string,\n accessToken: string,\n securityCredential: string,\n initiatorName: string,\n request: TransactionStatusRequest\n): Promise<TransactionStatusResponse> {\n const body = {\n Initiator: initiatorName,\n SecurityCredential: securityCredential,\n CommandID: \"TransactionStatusQuery\",\n TransactionID: request.transactionId,\n PartyA: request.shortCode,\n IdentifierType: request.identifierType ?? 4,\n ResultURL: request.resultUrl,\n QueueTimeOutURL: request.timeoutUrl,\n Remarks: \"Status\",\n Occasion: \"Query\",\n };\n\n const { data } = await httpRequest<TransactionStatusResponse>(\n `${baseUrl}/mpesa/transactionstatus/v1/query`,\n {\n method: \"POST\",\n headers: { Authorization: `Bearer ${accessToken}` },\n body,\n }\n );\n\n return data;\n}\n","export type Environment = \"sandbox\" | \"production\";\n\nexport const DARAJA_BASE_URLS = {\n sandbox: \"https://sandbox.safaricom.co.ke\",\n production: \"https://api.safaricom.co.ke\",\n} as const;\n\nexport interface MpesaConfig {\n consumerKey: string;\n consumerSecret: string;\n environment: Environment;\n /** Required for STK Push - Lipa Na M-Pesa passkey from Daraja portal */\n lipaNaMpesaShortCode?: string;\n lipaNaMpesaPassKey?: string;\n /** Required for B2C, B2B, Reversal - initiator name and password */\n initiatorName?: string;\n initiatorPassword?: string;\n /** PEM certificate for encrypting initiator password. Download from Daraja portal */\n certificatePath?: string;\n /** PEM certificate string (alternative to certificatePath) */\n certificatePem?: string;\n /** Pre-encrypted security credential (alternative to initiatorPassword + certificate) */\n securityCredential?: string;\n}\n","/**\n * M-Pesa Daraja API client\n */\n\nimport { TokenManager } from \"../core/auth\";\nimport { encryptSecurityCredential } from \"../core/encryption\";\nimport { type B2BRequest, processB2B } from \"./b2b\";\nimport { type B2CRequest, processB2C } from \"./b2c\";\nimport {\n type C2BRegisterUrlRequest,\n type C2BSimulateRequest,\n registerC2BUrls,\n simulateC2B,\n} from \"./c2b\";\nimport { type DynamicQRRequest, generateDynamicQR } from \"./qr-code\";\nimport { processReversal, type ReversalRequest } from \"./reversal\";\nimport {\n processStkPush,\n queryStkPush,\n type StkPushRequest,\n type StkQueryRequest,\n} from \"./stk-push\";\nimport {\n queryTransactionStatus,\n type TransactionStatusRequest,\n} from \"./transaction-status\";\nimport { DARAJA_BASE_URLS, type MpesaConfig } from \"./types\";\n\nexport class Mpesa {\n private config: MpesaConfig;\n private tokenManager: TokenManager;\n private baseUrl: string;\n\n constructor(config: MpesaConfig) {\n this.config = config;\n this.baseUrl = DARAJA_BASE_URLS[config.environment];\n this.tokenManager = new TokenManager(\n config.consumerKey,\n config.consumerSecret,\n this.baseUrl\n );\n }\n\n private async getToken(): Promise<string> {\n return this.tokenManager.getAccessToken();\n }\n\n private async getSecurityCredential(): Promise<string> {\n if (this.config.securityCredential) return this.config.securityCredential;\n if (!this.config.initiatorPassword) {\n throw new Error(\n \"Security credential required: provide securityCredential or (initiatorPassword + certificatePath/certificatePem)\"\n );\n }\n let cert: string;\n if (this.config.certificatePem) {\n cert = this.config.certificatePem;\n } else if (this.config.certificatePath) {\n cert = await Bun.file(this.config.certificatePath).text();\n } else {\n throw new Error(\n \"certificatePath or certificatePem required for B2C/B2B/Reversal\"\n );\n }\n return encryptSecurityCredential(this.config.initiatorPassword, cert);\n }\n\n /** STK Push (M-Pesa Express) - Initiate payment on customer phone */\n async stkPush(request: Omit<StkPushRequest, \"shortCode\" | \"passKey\">) {\n const shortCode = this.config.lipaNaMpesaShortCode ?? \"\";\n const passKey = this.config.lipaNaMpesaPassKey ?? \"\";\n if (!shortCode || !passKey) {\n throw new Error(\n \"lipaNaMpesaShortCode and lipaNaMpesaPassKey required for STK Push\"\n );\n }\n const token = await this.getToken();\n return processStkPush(this.baseUrl, token, {\n ...request,\n shortCode,\n passKey,\n });\n }\n\n /** STK Query - Check STK Push transaction status */\n async stkQuery(request: Omit<StkQueryRequest, \"shortCode\" | \"passKey\">) {\n const shortCode = this.config.lipaNaMpesaShortCode ?? \"\";\n const passKey = this.config.lipaNaMpesaPassKey ?? \"\";\n if (!shortCode || !passKey) {\n throw new Error(\n \"lipaNaMpesaShortCode and lipaNaMpesaPassKey required for STK Query\"\n );\n }\n const token = await this.getToken();\n return queryStkPush(this.baseUrl, token, {\n ...request,\n shortCode,\n passKey,\n });\n }\n\n /** B2C - Send money to customer */\n async b2c(request: B2CRequest) {\n const initiator = this.config.initiatorName ?? \"\";\n const securityCred = await this.getSecurityCredential();\n if (!initiator) throw new Error(\"initiatorName required for B2C\");\n const token = await this.getToken();\n return processB2C(this.baseUrl, token, securityCred, initiator, request);\n }\n\n /** B2B - Send money to business */\n async b2b(request: B2BRequest) {\n const initiator = this.config.initiatorName ?? \"\";\n const securityCred = await this.getSecurityCredential();\n if (!initiator) throw new Error(\"initiatorName required for B2B\");\n const token = await this.getToken();\n return processB2B(this.baseUrl, token, securityCred, initiator, request);\n }\n\n /** C2B - Register validation/confirmation URLs */\n async c2bRegisterUrls(request: C2BRegisterUrlRequest) {\n const token = await this.getToken();\n return registerC2BUrls(this.baseUrl, token, request);\n }\n\n /** C2B - Simulate payment (sandbox only) */\n async c2bSimulate(request: C2BSimulateRequest) {\n const token = await this.getToken();\n return simulateC2B(this.baseUrl, token, request);\n }\n\n /** Dynamic QR - Generate LIPA NA M-PESA QR code */\n async qrCode(request: DynamicQRRequest) {\n const token = await this.getToken();\n return generateDynamicQR(this.baseUrl, token, request);\n }\n\n /** Transaction Status - Query transaction status */\n async transactionStatus(request: TransactionStatusRequest) {\n const initiator = this.config.initiatorName ?? \"\";\n const securityCred = await this.getSecurityCredential();\n if (!initiator)\n throw new Error(\"initiatorName required for Transaction Status\");\n const token = await this.getToken();\n return queryTransactionStatus(\n this.baseUrl,\n token,\n securityCred,\n initiator,\n request\n );\n }\n\n /** Reversal - Reverse a transaction */\n async reversal(request: ReversalRequest) {\n const initiator = this.config.initiatorName ?? \"\";\n const securityCred = await this.getSecurityCredential();\n if (!initiator) throw new Error(\"initiatorName required for Reversal\");\n const token = await this.getToken();\n return processReversal(\n this.baseUrl,\n token,\n securityCred,\n initiator,\n request\n );\n }\n}\n","/**\n * Express-friendly helpers for M-Pesa Express (STK Push) and C2B simulate.\n *\n * NOTE: This module does NOT depend on Express at runtime. You pass in an\n * existing Express Router instance, and we attach handlers to it.\n */\n\nimport type { NextFunction, Request, Response, Router } from \"express\";\n\nimport { Mpesa } from \"../mpesa\";\nimport type { MpesaConfig } from \"../mpesa/types\";\nimport { PesafyError } from \"../utils/errors\";\n\nexport interface MpesaExpressConfig extends MpesaConfig {\n /**\n * Full callback URL that Safaricom will call after STK Push completes.\n * Example (sandbox):\n * https://your-domain.ngrok.io/api/mpesa/express/callback\n */\n callbackUrl: string;\n}\n\nexport function createMpesaExpressClient(config: MpesaExpressConfig): {\n mpesa: Mpesa;\n} {\n if (!config.consumerKey || !config.consumerSecret) {\n throw new PesafyError({\n code: \"INVALID_CREDENTIALS\",\n message: \"consumerKey and consumerSecret are required\",\n });\n }\n\n if (!config.lipaNaMpesaShortCode || !config.lipaNaMpesaPassKey) {\n throw new PesafyError({\n code: \"VALIDATION_ERROR\",\n message:\n \"lipaNaMpesaShortCode and lipaNaMpesaPassKey are required for STK Push\",\n });\n }\n\n if (!config.callbackUrl) {\n throw new PesafyError({\n code: \"VALIDATION_ERROR\",\n message: \"callbackUrl is required for STK Push callbacks\",\n });\n }\n\n const mpesa = new Mpesa(config);\n return { mpesa };\n}\n\ninterface StkPushBody {\n amount: number;\n phoneNumber: string;\n accountReference?: string;\n transactionDesc?: string;\n}\n\ninterface StkQueryBody {\n checkoutRequestId: string;\n}\n\nfunction normalizePhone(phone: string): string {\n const digits = phone.replace(/\\D/g, \"\");\n if (digits.startsWith(\"254\")) return digits;\n if (digits.startsWith(\"0\")) return `254${digits.slice(1)}`;\n return digits;\n}\n\nfunction sendError(res: Response, error: unknown): void {\n if (error instanceof PesafyError) {\n const status = error.statusCode ?? 400;\n res.status(status).json({\n error: error.code,\n message: error.message,\n statusCode: status,\n });\n return;\n }\n\n res.status(500).json({\n error: \"REQUEST_FAILED\",\n message: \"Unexpected error while processing M-Pesa request\",\n });\n}\n\nexport function createMpesaExpressRouter(\n router: Router,\n config: MpesaExpressConfig\n): Router {\n const { mpesa } = createMpesaExpressClient(config);\n\n // STK Push – initiate payment on customer phone\n router.post(\n \"/mpesa/express/stk-push\",\n async (req: Request, res: Response, next: NextFunction) => {\n try {\n const body = req.body as StkPushBody;\n\n if (!body || typeof body.amount !== \"number\" || body.amount <= 0) {\n throw new PesafyError({\n code: \"VALIDATION_ERROR\",\n message: \"amount must be a positive number\",\n });\n }\n\n if (!body.phoneNumber) {\n throw new PesafyError({\n code: \"VALIDATION_ERROR\",\n message: \"phoneNumber is required\",\n });\n }\n\n const phoneNumber = normalizePhone(body.phoneNumber);\n\n const result = await mpesa.stkPush({\n amount: body.amount,\n phoneNumber,\n callbackUrl: config.callbackUrl,\n accountReference:\n body.accountReference ??\n `PESAFY-${Date.now().toString(36).toUpperCase()}`,\n transactionDesc: body.transactionDesc ?? \"Payment\",\n });\n\n res.status(200).json(result);\n } catch (error) {\n if (res.headersSent) return next(error);\n sendError(res, error);\n }\n }\n );\n\n // STK Query – check status of an STK Push\n router.post(\n \"/mpesa/express/stk-query\",\n async (req: Request, res: Response, next: NextFunction) => {\n try {\n const body = req.body as StkQueryBody;\n\n if (!body || !body.checkoutRequestId) {\n throw new PesafyError({\n code: \"VALIDATION_ERROR\",\n message: \"checkoutRequestId is required\",\n });\n }\n\n const result = await mpesa.stkQuery({\n checkoutRequestId: body.checkoutRequestId,\n });\n\n res.status(200).json(result);\n } catch (error) {\n if (res.headersSent) return next(error);\n sendError(res, error);\n }\n }\n );\n\n // C2B simulate – sandbox only\n router.post(\n \"/mpesa/c2b/simulate\",\n async (req: Request, res: Response, next: NextFunction) => {\n try {\n const result = await mpesa.c2bSimulate(req.body);\n res.status(200).json(result);\n } catch (error) {\n if (res.headersSent) return next(error);\n sendError(res, error);\n }\n }\n );\n\n // C2B register URLs – sandbox only\n router.post(\n \"/mpesa/c2b/register-url\",\n async (req: Request, res: Response, next: NextFunction) => {\n try {\n const result = await mpesa.c2bRegisterUrls(req.body);\n res.status(200).json(result);\n } catch (error) {\n if (res.headersSent) return next(error);\n sendError(res, error);\n }\n }\n );\n\n return router;\n}\n","/**\n * Webhook retry mechanism with exponential backoff\n * For at-least-once delivery guarantee\n */\n\nexport interface RetryOptions {\n maxRetries?: number;\n initialDelay?: number;\n maxDelay?: number;\n backoffMultiplier?: number;\n maxRetryDuration?: number; // milliseconds (30 days default)\n}\n\nconst DEFAULT_OPTIONS: Required<RetryOptions> = {\n maxRetries: Infinity,\n initialDelay: 1000, // 1 second\n maxDelay: 3600000, // 1 hour\n backoffMultiplier: 2,\n maxRetryDuration: 30 * 24 * 60 * 60 * 1000, // 30 days\n};\n\nexport interface RetryResult<T> {\n success: boolean;\n data?: T;\n attempts: number;\n error?: Error;\n}\n\nexport async function retryWithBackoff<T>(\n fn: () => Promise<T>,\n options: RetryOptions = {}\n): Promise<RetryResult<T>> {\n const opts = { ...DEFAULT_OPTIONS, ...options };\n let delay = opts.initialDelay;\n let attempts = 0;\n const startTime = Date.now();\n\n while (attempts < opts.maxRetries) {\n attempts++;\n\n // Check if we've exceeded max retry duration\n if (Date.now() - startTime > opts.maxRetryDuration) {\n return {\n success: false,\n attempts,\n error: new Error(\"Max retry duration exceeded\"),\n };\n }\n\n try {\n const data = await fn();\n return { success: true, data, attempts };\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n\n // Don't retry on 4xx errors (client errors)\n if (err.message.includes(\"4\")) {\n return { success: false, attempts, error: err };\n }\n\n // Wait before retrying\n if (attempts < opts.maxRetries) {\n await new Promise((resolve) => setTimeout(resolve, delay));\n delay = Math.min(delay * opts.backoffMultiplier, opts.maxDelay);\n }\n }\n }\n\n return {\n success: false,\n attempts,\n error: new Error(\"Max retries exceeded\"),\n };\n}\n","/**\n * Webhook signature verification\n * Note: Daraja API doesn't provide webhook signatures in the same way as Stripe\n * Instead, verify by whitelisting IPs: 196.201.214.200, 196.201.214.206, etc.\n * This utility helps parse and validate webhook payloads\n */\n\nimport type { B2CWebhook, C2BWebhook, StkPushWebhook } from \"./types\";\n\nexport interface WebhookVerificationOptions {\n /** IP whitelist - verify request comes from Safaricom */\n allowedIPs?: string[];\n /** Optional: verify request headers match expected pattern */\n verifyHeaders?: boolean;\n}\n\nconst SAFARICOM_IPS = [\n \"196.201.214.200\",\n \"196.201.214.206\",\n \"196.201.213.114\",\n \"196.201.214.207\",\n \"196.201.214.208\",\n \"196.201.213.44\",\n \"196.201.212.127\",\n \"196.201.212.138\",\n \"196.201.212.129\",\n \"196.201.212.136\",\n \"196.201.212.74\",\n \"196.201.212.69\",\n];\n\nexport function verifyWebhookIP(\n requestIP: string,\n allowedIPs: string[] = SAFARICOM_IPS\n): boolean {\n return allowedIPs.includes(requestIP);\n}\n\nexport function parseStkPushWebhook(body: unknown): StkPushWebhook | null {\n try {\n const parsed = body as StkPushWebhook;\n if (parsed.Body?.stkCallback) return parsed;\n return null;\n } catch {\n return null;\n }\n}\n\nexport function parseB2CWebhook(body: unknown): B2CWebhook | null {\n try {\n const parsed = body as B2CWebhook;\n if (parsed.Result?.ResultCode !== undefined) return parsed;\n return null;\n } catch {\n return null;\n }\n}\n\nexport function parseC2BWebhook(body: unknown): C2BWebhook | null {\n try {\n const parsed = body as C2BWebhook;\n if (parsed.TransID && parsed.TransAmount) return parsed;\n return null;\n } catch {\n return null;\n }\n}\n","/**\n * Webhook event handler utilities\n */\n\nimport {\n parseB2CWebhook,\n parseC2BWebhook,\n parseStkPushWebhook,\n verifyWebhookIP,\n} from \"./signature-verifier\";\nimport type {\n B2CWebhook,\n C2BWebhook,\n StkPushWebhook,\n WebhookEventType,\n} from \"./types\";\n\nexport interface WebhookHandlerOptions {\n /** IP address of incoming request */\n requestIP?: string;\n /** Custom IP whitelist */\n allowedIPs?: string[];\n /** Skip IP verification (for testing) */\n skipIPCheck?: boolean;\n}\n\nexport interface WebhookHandlerResult<T = unknown> {\n success: boolean;\n eventType: WebhookEventType | null;\n data: T | null;\n error?: string;\n}\n\nexport function handleWebhook(\n body: unknown,\n options: WebhookHandlerOptions = {}\n): WebhookHandlerResult {\n // Verify IP if provided\n if (!options.skipIPCheck && options.requestIP) {\n if (!verifyWebhookIP(options.requestIP, options.allowedIPs)) {\n return {\n success: false,\n eventType: null,\n data: null,\n error: \"IP address not whitelisted\",\n };\n }\n }\n\n // Try to parse as STK Push\n const stkPush = parseStkPushWebhook(body);\n if (stkPush) {\n return {\n success: true,\n eventType: \"stk_push\",\n data: stkPush,\n };\n }\n\n // Try to parse as B2C\n const b2c = parseB2CWebhook(body);\n if (b2c) {\n return {\n success: true,\n eventType: \"b2c\",\n data: b2c,\n };\n }\n\n // Try to parse as C2B\n const c2b = parseC2BWebhook(body);\n if (c2b) {\n return {\n success: true,\n eventType: \"c2b\",\n data: c2b,\n };\n }\n\n return {\n success: false,\n eventType: null,\n data: null,\n error: \"Unknown webhook format\",\n };\n}\n\nexport function extractTransactionId(\n webhook: StkPushWebhook | B2CWebhook | C2BWebhook\n): string | null {\n if (\"Body\" in webhook && webhook.Body?.stkCallback) {\n const items = webhook.Body.stkCallback.CallbackMetadata?.Item;\n const mpesaReceipt = items?.find(\n (item) => item.Name === \"MpesaReceiptNumber\"\n );\n return mpesaReceipt ? String(mpesaReceipt.Value) : null;\n }\n if (\"Result\" in webhook && webhook.Result?.TransactionID) {\n return webhook.Result.TransactionID;\n }\n if (\"TransID\" in webhook) {\n return webhook.TransID;\n }\n return null;\n}\n\nexport function extractAmount(\n webhook: StkPushWebhook | B2CWebhook | C2BWebhook\n): number | null {\n if (\"Body\" in webhook && webhook.Body?.stkCallback) {\n const items = webhook.Body.stkCallback.CallbackMetadata?.Item;\n const amount = items?.find((item) => item.Name === \"Amount\");\n return amount ? Number(amount.Value) : null;\n }\n if (\"Result\" in webhook && webhook.Result?.ResultParameters) {\n const params = webhook.Result.ResultParameters.ResultParameter;\n const amount = params?.find((p) => p.Key === \"Amount\");\n return amount ? Number(amount.Value) : null;\n }\n if (\"TransAmount\" in webhook) {\n return Number.parseFloat(webhook.TransAmount);\n }\n return null;\n}\n"]}
|
package/package.json
CHANGED