pesafy 0.2.3 → 0.3.0

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 CHANGED
@@ -71,9 +71,10 @@ async function httpRequest(url, options = {}) {
71
71
  data = { raw: text };
72
72
  }
73
73
  if (!response.ok) {
74
+ const bodyStr = text.length > 0 ? ` \u2014 ${text}` : "";
74
75
  throw new PesafyError({
75
76
  code: "API_ERROR",
76
- message: `Request failed with status ${response.status}`,
77
+ message: `Request failed with status ${response.status}${bodyStr}`,
77
78
  statusCode: response.status,
78
79
  response: data
79
80
  });
@@ -268,11 +269,14 @@ async function processB2C(baseUrl, accessToken, securityCredential, initiatorNam
268
269
 
269
270
  // src/mpesa/c2b/register-url.ts
270
271
  async function registerC2BUrls(baseUrl, accessToken, request) {
272
+ const responseType = request.responseType ?? "Completed";
271
273
  const body = {
272
274
  ShortCode: request.shortCode,
273
- ResponseType: request.responseType ?? "Completed",
275
+ ResponseType: responseType,
274
276
  ConfirmationURL: request.confirmationUrl,
275
- ValidationURL: request.validationUrl
277
+ // Daraja requires ValidationURL even when external validation is disabled.
278
+ // Use the same URL as confirmation if you don't need custom validation.
279
+ ValidationURL: request.validationUrl ?? request.confirmationUrl
276
280
  };
277
281
  const { data } = await httpRequest(
278
282
  `${baseUrl}/mpesa/c2b/v2/registerurl`,
@@ -287,12 +291,20 @@ async function registerC2BUrls(baseUrl, accessToken, request) {
287
291
 
288
292
  // src/mpesa/c2b/simulate.ts
289
293
  async function simulateC2B(baseUrl, accessToken, request) {
294
+ const amount = Math.round(request.amount);
295
+ if (amount < 1) {
296
+ throw new Error(
297
+ `C2B simulate amount must be at least 1 KES (got ${request.amount}).`
298
+ );
299
+ }
290
300
  const body = {
291
301
  ShortCode: request.shortCode,
292
- CommandID: "CustomerPayBillOnline",
293
- Amount: Math.round(request.amount),
294
- Msisdn: formatPhoneNumber(request.phoneNumber),
295
- BillRefNumber: request.billRefNumber ?? "default"
302
+ CommandID: request.commandId,
303
+ Amount: amount,
304
+ // Daraja expects numeric MSISDN — strip formatting
305
+ Msisdn: Number(formatPhoneNumber(request.phoneNumber)),
306
+ // Paybill: pass account reference. Buy Goods: pass null (Daraja accepts it).
307
+ BillRefNumber: request.billRefNumber ?? null
296
308
  };
297
309
  const { data } = await httpRequest(
298
310
  `${baseUrl}/mpesa/c2b/v2/simulate`,
@@ -305,6 +317,16 @@ async function simulateC2B(baseUrl, accessToken, request) {
305
317
  return data;
306
318
  }
307
319
 
320
+ // src/mpesa/c2b/types.ts
321
+ var C2B_REJECTION_CODES = {
322
+ INVALID_MSISDN: "C2B00011",
323
+ INVALID_ACCOUNT_NUMBER: "C2B00012",
324
+ INVALID_AMOUNT: "C2B00013",
325
+ INVALID_KYC_DETAILS: "C2B00014",
326
+ INVALID_SHORT_CODE: "C2B00015",
327
+ OTHER_ERROR: "C2B00016"
328
+ };
329
+
308
330
  // src/mpesa/qr-code/dynamic-qr.ts
309
331
  async function generateDynamicQR(baseUrl, accessToken, request) {
310
332
  const body = {
@@ -861,6 +883,7 @@ function extractAmount(webhook) {
861
883
  return null;
862
884
  }
863
885
 
886
+ exports.C2B_REJECTION_CODES = C2B_REJECTION_CODES;
864
887
  exports.DARAJA_BASE_URLS = DARAJA_BASE_URLS;
865
888
  exports.Mpesa = Mpesa;
866
889
  exports.PesafyError = PesafyError;
@@ -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/mpesa/stk-push/utils.ts","../src/mpesa/b2c/b2c.ts","../src/mpesa/c2b/register-url.ts","../src/mpesa/c2b/simulate.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/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 throw new PesafyError({\n code: \"API_ERROR\",\n message: `Request failed with status ${response.status}`,\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","/** STK Push utilities */\n\n/**\n * Formats a phone number to the required 2547XXXXXXXX format.\n * Handles 07XXXXXXXX, 2547XXXXXXXX, and +2547XXXXXXXX inputs.\n *\n * Throws if the result is not a valid Kenyan number (12 digits, 2547… or 2541…).\n * This surfaces a clear error instead of letting Daraja return a cryptic one.\n */\nexport function formatPhoneNumber(phone: string): string {\n const cleaned = phone.replace(/\\D/g, \"\");\n\n let formatted: string;\n if (cleaned.startsWith(\"254\")) {\n formatted = cleaned;\n } else if (cleaned.startsWith(\"0\")) {\n formatted = \"254\" + cleaned.slice(1);\n } else if (cleaned.length === 9) {\n // bare 9-digit number without leading 0 or country code\n formatted = \"254\" + cleaned;\n } else {\n formatted = \"254\" + cleaned;\n }\n\n // Validate: must be 12 digits starting with 2547 (Safaricom) or 2541 (Airtel)\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 * 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 // btoa is available globally in Node 16+, Bun, browsers, and edge runtimes.\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 - Register validation and confirmation URLs\n * API: POST /mpesa/c2b/v2/registerurl\n */\n\nimport { httpRequest } from \"../../utils/http\";\nimport type { C2BRegisterUrlRequest } from \"./types\";\n\nexport interface C2BRegisterUrlResponse {\n ConversationID: string;\n OriginatorCoversationID: string;\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 body = {\n ShortCode: request.shortCode,\n ResponseType: request.responseType ?? \"Completed\",\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 - Simulate C2B payment (sandbox only)\n * API: POST /mpesa/c2b/v2/simulate\n */\n\nimport { httpRequest } from \"../../utils/http\";\nimport { formatPhoneNumber } from \"../stk-push/utils\";\nimport type { C2BSimulateRequest } from \"./types\";\n\nexport interface C2BSimulateResponse {\n ConversationID: string;\n OriginatorCoversationID: string;\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 body = {\n ShortCode: request.shortCode,\n CommandID: \"CustomerPayBillOnline\",\n Amount: Math.round(request.amount),\n Msisdn: formatPhoneNumber(request.phoneNumber),\n BillRefNumber: request.billRefNumber ?? \"default\",\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 * 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","/**\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/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/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","/** STK Push utilities */\n\n/**\n * Formats a phone number to the required 2547XXXXXXXX format.\n * Handles 07XXXXXXXX, 2547XXXXXXXX, and +2547XXXXXXXX inputs.\n *\n * Throws if the result is not a valid Kenyan number (12 digits, 2547… or 2541…).\n * This surfaces a clear error instead of letting Daraja return a cryptic one.\n */\nexport function formatPhoneNumber(phone: string): string {\n const cleaned = phone.replace(/\\D/g, \"\");\n\n let formatted: string;\n if (cleaned.startsWith(\"254\")) {\n formatted = cleaned;\n } else if (cleaned.startsWith(\"0\")) {\n formatted = \"254\" + cleaned.slice(1);\n } else if (cleaned.length === 9) {\n // bare 9-digit number without leading 0 or country code\n formatted = \"254\" + cleaned;\n } else {\n formatted = \"254\" + cleaned;\n }\n\n // Validate: must be 12 digits starting with 2547 (Safaricom) or 2541 (Airtel)\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 * 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 // btoa is available globally in Node 16+, Bun, browsers, and edge runtimes.\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 your Confirmation and Validation URLs with Safaricom.\n *\n * Sandbox: register before EVERY simulation.\n * Production: one-time registration. To change, delete via Daraja portal\n * self-service then re-register.\n *\n * URL requirements:\n * - Must be publicly accessible (no localhost, ngrok, requestbin in prod)\n * - Production must be HTTPS; sandbox allows HTTP\n * - Must NOT contain: mpesa, safaricom, exe, exec, cmd, sql, query\n */\n\nimport { httpRequest } from \"../../utils/http\";\nimport type {\n C2BRegisterUrlRequest,\n C2BRegisterUrlResponse,\n C2BResponseType,\n} from \"./types\";\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: Record<string, string> = {\n ShortCode: request.shortCode,\n ResponseType: responseType,\n ConfirmationURL: request.confirmationUrl,\n // Daraja requires ValidationURL even when external validation is disabled.\n // Use the same URL as confirmation if you don't need custom validation.\n ValidationURL: request.validationUrl ?? request.confirmationUrl,\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 payment to your Paybill or Till number.\n * NOT available in production — real payments come from the M-Pesa app / USSD.\n *\n * CommandID options:\n * \"CustomerPayBillOnline\" → Paybill payment (use BillRefNumber)\n * \"CustomerBuyGoodsOnline\" → Buy Goods / Till payment (BillRefNumber = null)\n */\n\nimport { httpRequest } from \"../../utils/http\";\nimport { formatPhoneNumber } from \"../stk-push/utils\";\nimport type { C2BSimulateRequest, C2BSimulateResponse } from \"./types\";\n\nexport async function simulateC2B(\n baseUrl: string,\n accessToken: string,\n request: C2BSimulateRequest\n): Promise<C2BSimulateResponse> {\n const amount = Math.round(request.amount);\n if (amount < 1) {\n throw new Error(\n `C2B simulate amount must be at least 1 KES (got ${request.amount}).`\n );\n }\n\n const body: Record<string, string | number | null> = {\n ShortCode: request.shortCode,\n CommandID: request.commandId,\n Amount: amount,\n // Daraja expects numeric MSISDN — strip formatting\n Msisdn: Number(formatPhoneNumber(request.phoneNumber)),\n // Paybill: pass account reference. Buy Goods: pass null (Daraja accepts it).\n BillRefNumber: request.billRefNumber ?? null,\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// ─── Register URL ─────────────────────────────────────────────────────────────\n\n/**\n * ResponseType controls what M-Pesa does if your Validation URL is unreachable.\n * \"Completed\" = auto-complete. \"Cancelled\" = auto-cancel.\n * Note: Must be sentence case exactly as shown.\n */\nexport type C2BResponseType = \"Completed\" | \"Cancelled\";\n\nexport interface C2BRegisterUrlRequest {\n /** Your Paybill or Till shortcode (5–6 digits). */\n shortCode: string;\n /**\n * What M-Pesa does if your Validation URL is unreachable.\n * Only relevant if External Validation is enabled on your shortcode.\n * Defaults to \"Completed\".\n */\n responseType?: C2BResponseType;\n /**\n * URL that receives payment confirmation after transaction completes.\n * Must be HTTPS in production. HTTP is allowed in sandbox.\n */\n confirmationUrl: string;\n /**\n * URL for payment validation before M-Pesa completes the transaction.\n * Only called if External Validation is enabled on your shortcode.\n * Optional — omit if validation is not required.\n */\n validationUrl?: string;\n}\n\nexport interface C2BRegisterUrlResponse {\n /**\n * Global unique identifier for the request.\n * NOTE: Daraja has a typo in their field name (\"Coversa\" not \"Conversa\").\n * We mirror it exactly so JSON parsing works.\n */\n OriginatorCoversationID: string;\n ResponseCode: string;\n ResponseDescription: string;\n}\n\n// ─── Simulate (Sandbox Only) ─────────────────────────────────────────────────\n\n/**\n * CommandID controls whether this is a Paybill or Buy Goods (Till) payment.\n * - \"CustomerPayBillOnline\" → Paybill (requires BillRefNumber / account number)\n * - \"CustomerBuyGoodsOnline\" → Till/Buy Goods (BillRefNumber is null)\n */\nexport type C2BCommandId = \"CustomerPayBillOnline\" | \"CustomerBuyGoodsOnline\";\n\nexport interface C2BSimulateRequest {\n /** Your Paybill or Till shortcode. */\n shortCode: string;\n /** Payment type. Paybill = \"CustomerPayBillOnline\", Till = \"CustomerBuyGoodsOnline\". */\n commandId: C2BCommandId;\n /** Amount to simulate (whole numbers only — Daraja rejects decimals). */\n amount: number;\n /** Phone number to debit in simulation. Use sandbox test numbers. */\n phoneNumber: string;\n /**\n * Account number / reference for Paybill payments.\n * Pass null or omit for Buy Goods (Till) transactions.\n */\n billRefNumber?: string | null;\n}\n\nexport interface C2BSimulateResponse {\n /** @see C2BRegisterUrlResponse.OriginatorCoversationID (Daraja typo) */\n OriginatorCoversationID: string;\n ResponseCode: string;\n ResponseDescription: string;\n}\n\n// ─── Webhook Callback Payloads ───────────────────────────────────────────────\n\n/**\n * C2B Confirmation/Validation callback body posted by Safaricom to your URLs.\n * C2B v2 returns a masked MSISDN: \"2547 ***** 126\"\n *\n * This type covers BOTH the confirmation and validation callbacks —\n * they share the same shape. The difference is:\n * - Validation: OrgAccountBalance is blank; you must respond Accept/Reject.\n * - Confirmation: OrgAccountBalance has the post-payment balance.\n */\nexport interface C2BCallbackPayload {\n /** \"Pay Bill\" or \"Buy Goods\" */\n TransactionType: string;\n /** Unique M-Pesa transaction ID (e.g. \"RKL51ZDR4F\") */\n TransID: string;\n /** Timestamp: \"YYYYMMDDHHmmss\" (e.g. \"20231121121325\") */\n TransTime: string;\n /** Amount as string decimal (e.g. \"5.00\") */\n TransAmount: string;\n /** Your Paybill or Till shortcode */\n BusinessShortCode: string;\n /**\n * Account reference / bill number entered by customer.\n * Applies to Paybill transactions. Empty string for Buy Goods.\n */\n BillRefNumber: string;\n /** Invoice number if applicable (usually empty string) */\n InvoiceNumber: string;\n /**\n * Post-payment balance of your Utility Account (confirmation only).\n * Empty string in validation requests.\n */\n OrgAccountBalance: string;\n /**\n * Opaque ID the partner can echo back in the validation response.\n * Safaricom sends it back in the confirmation if you returned it.\n */\n ThirdPartyTransID: string;\n /**\n * Masked phone number: \"2547 ***** 126\"\n * C2B v2 masks for privacy. C2B v1 used SHA-256 hash.\n */\n MSISDN: string;\n /** Customer first name (may be empty) */\n FirstName: string;\n /** Customer middle name (may be empty) */\n MiddleName: string;\n /** Customer last name (may be empty) */\n LastName: string;\n}\n\n/**\n * Your response to M-Pesa's Validation request.\n * You must reply within ~8 seconds or M-Pesa uses ResponseType default.\n */\nexport interface C2BValidationResponse {\n /**\n * \"0\" to accept the payment.\n * Any of the C2B rejection codes to reject (e.g. \"C2B00011\").\n */\n ResultCode: string;\n /** \"Accepted\" or \"Rejected\" */\n ResultDesc: string;\n}\n\n/**\n * Rejection codes for the Validation response.\n * Use these ResultCode values when rejecting a payment.\n */\nexport const C2B_REJECTION_CODES = {\n INVALID_MSISDN: \"C2B00011\",\n INVALID_ACCOUNT_NUMBER: \"C2B00012\",\n INVALID_AMOUNT: \"C2B00013\",\n INVALID_KYC_DETAILS: \"C2B00014\",\n INVALID_SHORT_CODE: \"C2B00015\",\n OTHER_ERROR: \"C2B00016\",\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","/**\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
@@ -91,43 +91,152 @@ interface DynamicQRResponse {
91
91
  QRCode: string;
92
92
  }
93
93
 
94
- /** C2B (Customer to Business) types */
94
+ /**
95
+ * C2B (Customer to Business) types
96
+ * API Reference: https://developer.safaricom.co.ke/APIs/CustomerToBusinessV2
97
+ *
98
+ * C2B v2 uses masked MSISDN (e.g. "2547 ***** 126").
99
+ * C2B v1 used SHA-256 hashed MSISDN — do NOT use v1.
100
+ */
101
+ /**
102
+ * ResponseType controls what M-Pesa does if your Validation URL is unreachable.
103
+ * "Completed" = auto-complete. "Cancelled" = auto-cancel.
104
+ * Note: Must be sentence case exactly as shown.
105
+ */
106
+ type C2BResponseType = "Completed" | "Cancelled";
95
107
  interface C2BRegisterUrlRequest {
108
+ /** Your Paybill or Till shortcode (5–6 digits). */
96
109
  shortCode: string;
110
+ /**
111
+ * What M-Pesa does if your Validation URL is unreachable.
112
+ * Only relevant if External Validation is enabled on your shortcode.
113
+ * Defaults to "Completed".
114
+ */
115
+ responseType?: C2BResponseType;
116
+ /**
117
+ * URL that receives payment confirmation after transaction completes.
118
+ * Must be HTTPS in production. HTTP is allowed in sandbox.
119
+ */
97
120
  confirmationUrl: string;
98
- validationUrl: string;
99
- responseType?: "Completed" | "Cancelled";
100
- }
101
- interface C2BSimulateRequest {
102
- shortCode: string;
103
- amount: number;
104
- phoneNumber: string;
105
- billRefNumber?: string;
121
+ /**
122
+ * URL for payment validation before M-Pesa completes the transaction.
123
+ * Only called if External Validation is enabled on your shortcode.
124
+ * Optional — omit if validation is not required.
125
+ */
126
+ validationUrl?: string;
106
127
  }
107
-
108
- /**
109
- * C2B Register URLs - Register validation and confirmation URLs
110
- * API: POST /mpesa/c2b/v2/registerurl
111
- */
112
-
113
128
  interface C2BRegisterUrlResponse {
114
- ConversationID: string;
129
+ /**
130
+ * Global unique identifier for the request.
131
+ * NOTE: Daraja has a typo in their field name ("Coversa" not "Conversa").
132
+ * We mirror it exactly so JSON parsing works.
133
+ */
115
134
  OriginatorCoversationID: string;
116
135
  ResponseCode: string;
117
136
  ResponseDescription: string;
118
137
  }
119
-
120
138
  /**
121
- * C2B Simulate - Simulate C2B payment (sandbox only)
122
- * API: POST /mpesa/c2b/v2/simulate
139
+ * CommandID controls whether this is a Paybill or Buy Goods (Till) payment.
140
+ * - "CustomerPayBillOnline" → Paybill (requires BillRefNumber / account number)
141
+ * - "CustomerBuyGoodsOnline" → Till/Buy Goods (BillRefNumber is null)
123
142
  */
124
-
143
+ type C2BCommandId = "CustomerPayBillOnline" | "CustomerBuyGoodsOnline";
144
+ interface C2BSimulateRequest {
145
+ /** Your Paybill or Till shortcode. */
146
+ shortCode: string;
147
+ /** Payment type. Paybill = "CustomerPayBillOnline", Till = "CustomerBuyGoodsOnline". */
148
+ commandId: C2BCommandId;
149
+ /** Amount to simulate (whole numbers only — Daraja rejects decimals). */
150
+ amount: number;
151
+ /** Phone number to debit in simulation. Use sandbox test numbers. */
152
+ phoneNumber: string;
153
+ /**
154
+ * Account number / reference for Paybill payments.
155
+ * Pass null or omit for Buy Goods (Till) transactions.
156
+ */
157
+ billRefNumber?: string | null;
158
+ }
125
159
  interface C2BSimulateResponse {
126
- ConversationID: string;
160
+ /** @see C2BRegisterUrlResponse.OriginatorCoversationID (Daraja typo) */
127
161
  OriginatorCoversationID: string;
128
162
  ResponseCode: string;
129
163
  ResponseDescription: string;
130
164
  }
165
+ /**
166
+ * C2B Confirmation/Validation callback body posted by Safaricom to your URLs.
167
+ * C2B v2 returns a masked MSISDN: "2547 ***** 126"
168
+ *
169
+ * This type covers BOTH the confirmation and validation callbacks —
170
+ * they share the same shape. The difference is:
171
+ * - Validation: OrgAccountBalance is blank; you must respond Accept/Reject.
172
+ * - Confirmation: OrgAccountBalance has the post-payment balance.
173
+ */
174
+ interface C2BCallbackPayload {
175
+ /** "Pay Bill" or "Buy Goods" */
176
+ TransactionType: string;
177
+ /** Unique M-Pesa transaction ID (e.g. "RKL51ZDR4F") */
178
+ TransID: string;
179
+ /** Timestamp: "YYYYMMDDHHmmss" (e.g. "20231121121325") */
180
+ TransTime: string;
181
+ /** Amount as string decimal (e.g. "5.00") */
182
+ TransAmount: string;
183
+ /** Your Paybill or Till shortcode */
184
+ BusinessShortCode: string;
185
+ /**
186
+ * Account reference / bill number entered by customer.
187
+ * Applies to Paybill transactions. Empty string for Buy Goods.
188
+ */
189
+ BillRefNumber: string;
190
+ /** Invoice number if applicable (usually empty string) */
191
+ InvoiceNumber: string;
192
+ /**
193
+ * Post-payment balance of your Utility Account (confirmation only).
194
+ * Empty string in validation requests.
195
+ */
196
+ OrgAccountBalance: string;
197
+ /**
198
+ * Opaque ID the partner can echo back in the validation response.
199
+ * Safaricom sends it back in the confirmation if you returned it.
200
+ */
201
+ ThirdPartyTransID: string;
202
+ /**
203
+ * Masked phone number: "2547 ***** 126"
204
+ * C2B v2 masks for privacy. C2B v1 used SHA-256 hash.
205
+ */
206
+ MSISDN: string;
207
+ /** Customer first name (may be empty) */
208
+ FirstName: string;
209
+ /** Customer middle name (may be empty) */
210
+ MiddleName: string;
211
+ /** Customer last name (may be empty) */
212
+ LastName: string;
213
+ }
214
+ /**
215
+ * Your response to M-Pesa's Validation request.
216
+ * You must reply within ~8 seconds or M-Pesa uses ResponseType default.
217
+ */
218
+ interface C2BValidationResponse {
219
+ /**
220
+ * "0" to accept the payment.
221
+ * Any of the C2B rejection codes to reject (e.g. "C2B00011").
222
+ */
223
+ ResultCode: string;
224
+ /** "Accepted" or "Rejected" */
225
+ ResultDesc: string;
226
+ }
227
+ /**
228
+ * Rejection codes for the Validation response.
229
+ * Use these ResultCode values when rejecting a payment.
230
+ */
231
+ declare const C2B_REJECTION_CODES: {
232
+ readonly INVALID_MSISDN: "C2B00011";
233
+ readonly INVALID_ACCOUNT_NUMBER: "C2B00012";
234
+ readonly INVALID_AMOUNT: "C2B00013";
235
+ readonly INVALID_KYC_DETAILS: "C2B00014";
236
+ readonly INVALID_SHORT_CODE: "C2B00015";
237
+ readonly OTHER_ERROR: "C2B00016";
238
+ };
239
+ type C2BRejectionCode = (typeof C2B_REJECTION_CODES)[keyof typeof C2B_REJECTION_CODES];
131
240
 
132
241
  /** B2B (Business to Business) types */
133
242
  type B2BCommandId = "BusinessPayBill" | "BusinessBuyGoods" | "DisburseFundsToBusiness" | "BusinessToBusinessTransfer";
@@ -451,4 +560,4 @@ declare class PesafyError extends Error {
451
560
  }
452
561
  declare function createError(options: PesafyErrorOptions): PesafyError;
453
562
 
454
- export { type B2BRequest, type B2BResponse, type B2CRequest, type B2CResponse, type B2CWebhook, type C2BRegisterUrlRequest, type C2BRegisterUrlResponse, type C2BSimulateRequest, type C2BSimulateResponse, type C2BWebhook, DARAJA_BASE_URLS, type DynamicQRRequest, type DynamicQRResponse, type ErrorCode, Mpesa, type MpesaConfig, type MpesaExpressConfig, PesafyError, type RetryOptions, type RetryResult, type ReversalRequest, type ReversalResponse, type StkPushRequest, type StkPushResponse, type StkPushWebhook, type StkQueryRequest, type StkQueryResponse, TokenManager, type TransactionStatusRequest, type TransactionStatusResponse, type WebhookEvent, type WebhookEventType, type WebhookHandlerOptions, type WebhookHandlerResult, createError, createMpesaExpressClient, createMpesaExpressRouter, encryptSecurityCredential, extractAmount, extractTransactionId, handleWebhook, retryWithBackoff, verifyWebhookIP };
563
+ export { type B2BRequest, type B2BResponse, type B2CRequest, type B2CResponse, type B2CWebhook, type C2BCallbackPayload, type C2BCommandId, type C2BRegisterUrlRequest, type C2BRegisterUrlResponse, type C2BRejectionCode, type C2BResponseType, type C2BSimulateRequest, type C2BSimulateResponse, type C2BValidationResponse, type C2BWebhook, C2B_REJECTION_CODES, DARAJA_BASE_URLS, type DynamicQRRequest, type DynamicQRResponse, type ErrorCode, Mpesa, type MpesaConfig, type MpesaExpressConfig, PesafyError, type RetryOptions, type RetryResult, type ReversalRequest, type ReversalResponse, type StkPushRequest, type StkPushResponse, type StkPushWebhook, type StkQueryRequest, type StkQueryResponse, TokenManager, type TransactionStatusRequest, type TransactionStatusResponse, type WebhookEvent, type WebhookEventType, type WebhookHandlerOptions, type WebhookHandlerResult, createError, createMpesaExpressClient, createMpesaExpressRouter, encryptSecurityCredential, extractAmount, extractTransactionId, handleWebhook, retryWithBackoff, verifyWebhookIP };
package/dist/index.d.ts CHANGED
@@ -91,43 +91,152 @@ interface DynamicQRResponse {
91
91
  QRCode: string;
92
92
  }
93
93
 
94
- /** C2B (Customer to Business) types */
94
+ /**
95
+ * C2B (Customer to Business) types
96
+ * API Reference: https://developer.safaricom.co.ke/APIs/CustomerToBusinessV2
97
+ *
98
+ * C2B v2 uses masked MSISDN (e.g. "2547 ***** 126").
99
+ * C2B v1 used SHA-256 hashed MSISDN — do NOT use v1.
100
+ */
101
+ /**
102
+ * ResponseType controls what M-Pesa does if your Validation URL is unreachable.
103
+ * "Completed" = auto-complete. "Cancelled" = auto-cancel.
104
+ * Note: Must be sentence case exactly as shown.
105
+ */
106
+ type C2BResponseType = "Completed" | "Cancelled";
95
107
  interface C2BRegisterUrlRequest {
108
+ /** Your Paybill or Till shortcode (5–6 digits). */
96
109
  shortCode: string;
110
+ /**
111
+ * What M-Pesa does if your Validation URL is unreachable.
112
+ * Only relevant if External Validation is enabled on your shortcode.
113
+ * Defaults to "Completed".
114
+ */
115
+ responseType?: C2BResponseType;
116
+ /**
117
+ * URL that receives payment confirmation after transaction completes.
118
+ * Must be HTTPS in production. HTTP is allowed in sandbox.
119
+ */
97
120
  confirmationUrl: string;
98
- validationUrl: string;
99
- responseType?: "Completed" | "Cancelled";
100
- }
101
- interface C2BSimulateRequest {
102
- shortCode: string;
103
- amount: number;
104
- phoneNumber: string;
105
- billRefNumber?: string;
121
+ /**
122
+ * URL for payment validation before M-Pesa completes the transaction.
123
+ * Only called if External Validation is enabled on your shortcode.
124
+ * Optional — omit if validation is not required.
125
+ */
126
+ validationUrl?: string;
106
127
  }
107
-
108
- /**
109
- * C2B Register URLs - Register validation and confirmation URLs
110
- * API: POST /mpesa/c2b/v2/registerurl
111
- */
112
-
113
128
  interface C2BRegisterUrlResponse {
114
- ConversationID: string;
129
+ /**
130
+ * Global unique identifier for the request.
131
+ * NOTE: Daraja has a typo in their field name ("Coversa" not "Conversa").
132
+ * We mirror it exactly so JSON parsing works.
133
+ */
115
134
  OriginatorCoversationID: string;
116
135
  ResponseCode: string;
117
136
  ResponseDescription: string;
118
137
  }
119
-
120
138
  /**
121
- * C2B Simulate - Simulate C2B payment (sandbox only)
122
- * API: POST /mpesa/c2b/v2/simulate
139
+ * CommandID controls whether this is a Paybill or Buy Goods (Till) payment.
140
+ * - "CustomerPayBillOnline" → Paybill (requires BillRefNumber / account number)
141
+ * - "CustomerBuyGoodsOnline" → Till/Buy Goods (BillRefNumber is null)
123
142
  */
124
-
143
+ type C2BCommandId = "CustomerPayBillOnline" | "CustomerBuyGoodsOnline";
144
+ interface C2BSimulateRequest {
145
+ /** Your Paybill or Till shortcode. */
146
+ shortCode: string;
147
+ /** Payment type. Paybill = "CustomerPayBillOnline", Till = "CustomerBuyGoodsOnline". */
148
+ commandId: C2BCommandId;
149
+ /** Amount to simulate (whole numbers only — Daraja rejects decimals). */
150
+ amount: number;
151
+ /** Phone number to debit in simulation. Use sandbox test numbers. */
152
+ phoneNumber: string;
153
+ /**
154
+ * Account number / reference for Paybill payments.
155
+ * Pass null or omit for Buy Goods (Till) transactions.
156
+ */
157
+ billRefNumber?: string | null;
158
+ }
125
159
  interface C2BSimulateResponse {
126
- ConversationID: string;
160
+ /** @see C2BRegisterUrlResponse.OriginatorCoversationID (Daraja typo) */
127
161
  OriginatorCoversationID: string;
128
162
  ResponseCode: string;
129
163
  ResponseDescription: string;
130
164
  }
165
+ /**
166
+ * C2B Confirmation/Validation callback body posted by Safaricom to your URLs.
167
+ * C2B v2 returns a masked MSISDN: "2547 ***** 126"
168
+ *
169
+ * This type covers BOTH the confirmation and validation callbacks —
170
+ * they share the same shape. The difference is:
171
+ * - Validation: OrgAccountBalance is blank; you must respond Accept/Reject.
172
+ * - Confirmation: OrgAccountBalance has the post-payment balance.
173
+ */
174
+ interface C2BCallbackPayload {
175
+ /** "Pay Bill" or "Buy Goods" */
176
+ TransactionType: string;
177
+ /** Unique M-Pesa transaction ID (e.g. "RKL51ZDR4F") */
178
+ TransID: string;
179
+ /** Timestamp: "YYYYMMDDHHmmss" (e.g. "20231121121325") */
180
+ TransTime: string;
181
+ /** Amount as string decimal (e.g. "5.00") */
182
+ TransAmount: string;
183
+ /** Your Paybill or Till shortcode */
184
+ BusinessShortCode: string;
185
+ /**
186
+ * Account reference / bill number entered by customer.
187
+ * Applies to Paybill transactions. Empty string for Buy Goods.
188
+ */
189
+ BillRefNumber: string;
190
+ /** Invoice number if applicable (usually empty string) */
191
+ InvoiceNumber: string;
192
+ /**
193
+ * Post-payment balance of your Utility Account (confirmation only).
194
+ * Empty string in validation requests.
195
+ */
196
+ OrgAccountBalance: string;
197
+ /**
198
+ * Opaque ID the partner can echo back in the validation response.
199
+ * Safaricom sends it back in the confirmation if you returned it.
200
+ */
201
+ ThirdPartyTransID: string;
202
+ /**
203
+ * Masked phone number: "2547 ***** 126"
204
+ * C2B v2 masks for privacy. C2B v1 used SHA-256 hash.
205
+ */
206
+ MSISDN: string;
207
+ /** Customer first name (may be empty) */
208
+ FirstName: string;
209
+ /** Customer middle name (may be empty) */
210
+ MiddleName: string;
211
+ /** Customer last name (may be empty) */
212
+ LastName: string;
213
+ }
214
+ /**
215
+ * Your response to M-Pesa's Validation request.
216
+ * You must reply within ~8 seconds or M-Pesa uses ResponseType default.
217
+ */
218
+ interface C2BValidationResponse {
219
+ /**
220
+ * "0" to accept the payment.
221
+ * Any of the C2B rejection codes to reject (e.g. "C2B00011").
222
+ */
223
+ ResultCode: string;
224
+ /** "Accepted" or "Rejected" */
225
+ ResultDesc: string;
226
+ }
227
+ /**
228
+ * Rejection codes for the Validation response.
229
+ * Use these ResultCode values when rejecting a payment.
230
+ */
231
+ declare const C2B_REJECTION_CODES: {
232
+ readonly INVALID_MSISDN: "C2B00011";
233
+ readonly INVALID_ACCOUNT_NUMBER: "C2B00012";
234
+ readonly INVALID_AMOUNT: "C2B00013";
235
+ readonly INVALID_KYC_DETAILS: "C2B00014";
236
+ readonly INVALID_SHORT_CODE: "C2B00015";
237
+ readonly OTHER_ERROR: "C2B00016";
238
+ };
239
+ type C2BRejectionCode = (typeof C2B_REJECTION_CODES)[keyof typeof C2B_REJECTION_CODES];
131
240
 
132
241
  /** B2B (Business to Business) types */
133
242
  type B2BCommandId = "BusinessPayBill" | "BusinessBuyGoods" | "DisburseFundsToBusiness" | "BusinessToBusinessTransfer";
@@ -451,4 +560,4 @@ declare class PesafyError extends Error {
451
560
  }
452
561
  declare function createError(options: PesafyErrorOptions): PesafyError;
453
562
 
454
- export { type B2BRequest, type B2BResponse, type B2CRequest, type B2CResponse, type B2CWebhook, type C2BRegisterUrlRequest, type C2BRegisterUrlResponse, type C2BSimulateRequest, type C2BSimulateResponse, type C2BWebhook, DARAJA_BASE_URLS, type DynamicQRRequest, type DynamicQRResponse, type ErrorCode, Mpesa, type MpesaConfig, type MpesaExpressConfig, PesafyError, type RetryOptions, type RetryResult, type ReversalRequest, type ReversalResponse, type StkPushRequest, type StkPushResponse, type StkPushWebhook, type StkQueryRequest, type StkQueryResponse, TokenManager, type TransactionStatusRequest, type TransactionStatusResponse, type WebhookEvent, type WebhookEventType, type WebhookHandlerOptions, type WebhookHandlerResult, createError, createMpesaExpressClient, createMpesaExpressRouter, encryptSecurityCredential, extractAmount, extractTransactionId, handleWebhook, retryWithBackoff, verifyWebhookIP };
563
+ export { type B2BRequest, type B2BResponse, type B2CRequest, type B2CResponse, type B2CWebhook, type C2BCallbackPayload, type C2BCommandId, type C2BRegisterUrlRequest, type C2BRegisterUrlResponse, type C2BRejectionCode, type C2BResponseType, type C2BSimulateRequest, type C2BSimulateResponse, type C2BValidationResponse, type C2BWebhook, C2B_REJECTION_CODES, DARAJA_BASE_URLS, type DynamicQRRequest, type DynamicQRResponse, type ErrorCode, Mpesa, type MpesaConfig, type MpesaExpressConfig, PesafyError, type RetryOptions, type RetryResult, type ReversalRequest, type ReversalResponse, type StkPushRequest, type StkPushResponse, type StkPushWebhook, type StkQueryRequest, type StkQueryResponse, TokenManager, type TransactionStatusRequest, type TransactionStatusResponse, type WebhookEvent, type WebhookEventType, type WebhookHandlerOptions, type WebhookHandlerResult, createError, createMpesaExpressClient, createMpesaExpressRouter, encryptSecurityCredential, extractAmount, extractTransactionId, handleWebhook, retryWithBackoff, verifyWebhookIP };
package/dist/index.js CHANGED
@@ -69,9 +69,10 @@ async function httpRequest(url, options = {}) {
69
69
  data = { raw: text };
70
70
  }
71
71
  if (!response.ok) {
72
+ const bodyStr = text.length > 0 ? ` \u2014 ${text}` : "";
72
73
  throw new PesafyError({
73
74
  code: "API_ERROR",
74
- message: `Request failed with status ${response.status}`,
75
+ message: `Request failed with status ${response.status}${bodyStr}`,
75
76
  statusCode: response.status,
76
77
  response: data
77
78
  });
@@ -266,11 +267,14 @@ async function processB2C(baseUrl, accessToken, securityCredential, initiatorNam
266
267
 
267
268
  // src/mpesa/c2b/register-url.ts
268
269
  async function registerC2BUrls(baseUrl, accessToken, request) {
270
+ const responseType = request.responseType ?? "Completed";
269
271
  const body = {
270
272
  ShortCode: request.shortCode,
271
- ResponseType: request.responseType ?? "Completed",
273
+ ResponseType: responseType,
272
274
  ConfirmationURL: request.confirmationUrl,
273
- ValidationURL: request.validationUrl
275
+ // Daraja requires ValidationURL even when external validation is disabled.
276
+ // Use the same URL as confirmation if you don't need custom validation.
277
+ ValidationURL: request.validationUrl ?? request.confirmationUrl
274
278
  };
275
279
  const { data } = await httpRequest(
276
280
  `${baseUrl}/mpesa/c2b/v2/registerurl`,
@@ -285,12 +289,20 @@ async function registerC2BUrls(baseUrl, accessToken, request) {
285
289
 
286
290
  // src/mpesa/c2b/simulate.ts
287
291
  async function simulateC2B(baseUrl, accessToken, request) {
292
+ const amount = Math.round(request.amount);
293
+ if (amount < 1) {
294
+ throw new Error(
295
+ `C2B simulate amount must be at least 1 KES (got ${request.amount}).`
296
+ );
297
+ }
288
298
  const body = {
289
299
  ShortCode: request.shortCode,
290
- CommandID: "CustomerPayBillOnline",
291
- Amount: Math.round(request.amount),
292
- Msisdn: formatPhoneNumber(request.phoneNumber),
293
- BillRefNumber: request.billRefNumber ?? "default"
300
+ CommandID: request.commandId,
301
+ Amount: amount,
302
+ // Daraja expects numeric MSISDN — strip formatting
303
+ Msisdn: Number(formatPhoneNumber(request.phoneNumber)),
304
+ // Paybill: pass account reference. Buy Goods: pass null (Daraja accepts it).
305
+ BillRefNumber: request.billRefNumber ?? null
294
306
  };
295
307
  const { data } = await httpRequest(
296
308
  `${baseUrl}/mpesa/c2b/v2/simulate`,
@@ -303,6 +315,16 @@ async function simulateC2B(baseUrl, accessToken, request) {
303
315
  return data;
304
316
  }
305
317
 
318
+ // src/mpesa/c2b/types.ts
319
+ var C2B_REJECTION_CODES = {
320
+ INVALID_MSISDN: "C2B00011",
321
+ INVALID_ACCOUNT_NUMBER: "C2B00012",
322
+ INVALID_AMOUNT: "C2B00013",
323
+ INVALID_KYC_DETAILS: "C2B00014",
324
+ INVALID_SHORT_CODE: "C2B00015",
325
+ OTHER_ERROR: "C2B00016"
326
+ };
327
+
306
328
  // src/mpesa/qr-code/dynamic-qr.ts
307
329
  async function generateDynamicQR(baseUrl, accessToken, request) {
308
330
  const body = {
@@ -859,6 +881,6 @@ function extractAmount(webhook) {
859
881
  return null;
860
882
  }
861
883
 
862
- export { DARAJA_BASE_URLS, Mpesa, PesafyError, TokenManager, createError, createMpesaExpressClient, createMpesaExpressRouter, encryptSecurityCredential, extractAmount, extractTransactionId, handleWebhook, retryWithBackoff, verifyWebhookIP };
884
+ export { C2B_REJECTION_CODES, DARAJA_BASE_URLS, Mpesa, PesafyError, TokenManager, createError, createMpesaExpressClient, createMpesaExpressRouter, encryptSecurityCredential, extractAmount, extractTransactionId, handleWebhook, retryWithBackoff, verifyWebhookIP };
863
885
  //# sourceMappingURL=index.js.map
864
886
  //# sourceMappingURL=index.js.map
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/mpesa/stk-push/utils.ts","../src/mpesa/b2c/b2c.ts","../src/mpesa/c2b/register-url.ts","../src/mpesa/c2b/simulate.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/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 throw new PesafyError({\n code: \"API_ERROR\",\n message: `Request failed with status ${response.status}`,\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","/** STK Push utilities */\n\n/**\n * Formats a phone number to the required 2547XXXXXXXX format.\n * Handles 07XXXXXXXX, 2547XXXXXXXX, and +2547XXXXXXXX inputs.\n *\n * Throws if the result is not a valid Kenyan number (12 digits, 2547… or 2541…).\n * This surfaces a clear error instead of letting Daraja return a cryptic one.\n */\nexport function formatPhoneNumber(phone: string): string {\n const cleaned = phone.replace(/\\D/g, \"\");\n\n let formatted: string;\n if (cleaned.startsWith(\"254\")) {\n formatted = cleaned;\n } else if (cleaned.startsWith(\"0\")) {\n formatted = \"254\" + cleaned.slice(1);\n } else if (cleaned.length === 9) {\n // bare 9-digit number without leading 0 or country code\n formatted = \"254\" + cleaned;\n } else {\n formatted = \"254\" + cleaned;\n }\n\n // Validate: must be 12 digits starting with 2547 (Safaricom) or 2541 (Airtel)\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 * 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 // btoa is available globally in Node 16+, Bun, browsers, and edge runtimes.\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 - Register validation and confirmation URLs\n * API: POST /mpesa/c2b/v2/registerurl\n */\n\nimport { httpRequest } from \"../../utils/http\";\nimport type { C2BRegisterUrlRequest } from \"./types\";\n\nexport interface C2BRegisterUrlResponse {\n ConversationID: string;\n OriginatorCoversationID: string;\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 body = {\n ShortCode: request.shortCode,\n ResponseType: request.responseType ?? \"Completed\",\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 - Simulate C2B payment (sandbox only)\n * API: POST /mpesa/c2b/v2/simulate\n */\n\nimport { httpRequest } from \"../../utils/http\";\nimport { formatPhoneNumber } from \"../stk-push/utils\";\nimport type { C2BSimulateRequest } from \"./types\";\n\nexport interface C2BSimulateResponse {\n ConversationID: string;\n OriginatorCoversationID: string;\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 body = {\n ShortCode: request.shortCode,\n CommandID: \"CustomerPayBillOnline\",\n Amount: Math.round(request.amount),\n Msisdn: formatPhoneNumber(request.phoneNumber),\n BillRefNumber: request.billRefNumber ?? \"default\",\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 * 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","/**\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/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/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","/** STK Push utilities */\n\n/**\n * Formats a phone number to the required 2547XXXXXXXX format.\n * Handles 07XXXXXXXX, 2547XXXXXXXX, and +2547XXXXXXXX inputs.\n *\n * Throws if the result is not a valid Kenyan number (12 digits, 2547… or 2541…).\n * This surfaces a clear error instead of letting Daraja return a cryptic one.\n */\nexport function formatPhoneNumber(phone: string): string {\n const cleaned = phone.replace(/\\D/g, \"\");\n\n let formatted: string;\n if (cleaned.startsWith(\"254\")) {\n formatted = cleaned;\n } else if (cleaned.startsWith(\"0\")) {\n formatted = \"254\" + cleaned.slice(1);\n } else if (cleaned.length === 9) {\n // bare 9-digit number without leading 0 or country code\n formatted = \"254\" + cleaned;\n } else {\n formatted = \"254\" + cleaned;\n }\n\n // Validate: must be 12 digits starting with 2547 (Safaricom) or 2541 (Airtel)\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 * 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 // btoa is available globally in Node 16+, Bun, browsers, and edge runtimes.\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 your Confirmation and Validation URLs with Safaricom.\n *\n * Sandbox: register before EVERY simulation.\n * Production: one-time registration. To change, delete via Daraja portal\n * self-service then re-register.\n *\n * URL requirements:\n * - Must be publicly accessible (no localhost, ngrok, requestbin in prod)\n * - Production must be HTTPS; sandbox allows HTTP\n * - Must NOT contain: mpesa, safaricom, exe, exec, cmd, sql, query\n */\n\nimport { httpRequest } from \"../../utils/http\";\nimport type {\n C2BRegisterUrlRequest,\n C2BRegisterUrlResponse,\n C2BResponseType,\n} from \"./types\";\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: Record<string, string> = {\n ShortCode: request.shortCode,\n ResponseType: responseType,\n ConfirmationURL: request.confirmationUrl,\n // Daraja requires ValidationURL even when external validation is disabled.\n // Use the same URL as confirmation if you don't need custom validation.\n ValidationURL: request.validationUrl ?? request.confirmationUrl,\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 payment to your Paybill or Till number.\n * NOT available in production — real payments come from the M-Pesa app / USSD.\n *\n * CommandID options:\n * \"CustomerPayBillOnline\" → Paybill payment (use BillRefNumber)\n * \"CustomerBuyGoodsOnline\" → Buy Goods / Till payment (BillRefNumber = null)\n */\n\nimport { httpRequest } from \"../../utils/http\";\nimport { formatPhoneNumber } from \"../stk-push/utils\";\nimport type { C2BSimulateRequest, C2BSimulateResponse } from \"./types\";\n\nexport async function simulateC2B(\n baseUrl: string,\n accessToken: string,\n request: C2BSimulateRequest\n): Promise<C2BSimulateResponse> {\n const amount = Math.round(request.amount);\n if (amount < 1) {\n throw new Error(\n `C2B simulate amount must be at least 1 KES (got ${request.amount}).`\n );\n }\n\n const body: Record<string, string | number | null> = {\n ShortCode: request.shortCode,\n CommandID: request.commandId,\n Amount: amount,\n // Daraja expects numeric MSISDN — strip formatting\n Msisdn: Number(formatPhoneNumber(request.phoneNumber)),\n // Paybill: pass account reference. Buy Goods: pass null (Daraja accepts it).\n BillRefNumber: request.billRefNumber ?? null,\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// ─── Register URL ─────────────────────────────────────────────────────────────\n\n/**\n * ResponseType controls what M-Pesa does if your Validation URL is unreachable.\n * \"Completed\" = auto-complete. \"Cancelled\" = auto-cancel.\n * Note: Must be sentence case exactly as shown.\n */\nexport type C2BResponseType = \"Completed\" | \"Cancelled\";\n\nexport interface C2BRegisterUrlRequest {\n /** Your Paybill or Till shortcode (5–6 digits). */\n shortCode: string;\n /**\n * What M-Pesa does if your Validation URL is unreachable.\n * Only relevant if External Validation is enabled on your shortcode.\n * Defaults to \"Completed\".\n */\n responseType?: C2BResponseType;\n /**\n * URL that receives payment confirmation after transaction completes.\n * Must be HTTPS in production. HTTP is allowed in sandbox.\n */\n confirmationUrl: string;\n /**\n * URL for payment validation before M-Pesa completes the transaction.\n * Only called if External Validation is enabled on your shortcode.\n * Optional — omit if validation is not required.\n */\n validationUrl?: string;\n}\n\nexport interface C2BRegisterUrlResponse {\n /**\n * Global unique identifier for the request.\n * NOTE: Daraja has a typo in their field name (\"Coversa\" not \"Conversa\").\n * We mirror it exactly so JSON parsing works.\n */\n OriginatorCoversationID: string;\n ResponseCode: string;\n ResponseDescription: string;\n}\n\n// ─── Simulate (Sandbox Only) ─────────────────────────────────────────────────\n\n/**\n * CommandID controls whether this is a Paybill or Buy Goods (Till) payment.\n * - \"CustomerPayBillOnline\" → Paybill (requires BillRefNumber / account number)\n * - \"CustomerBuyGoodsOnline\" → Till/Buy Goods (BillRefNumber is null)\n */\nexport type C2BCommandId = \"CustomerPayBillOnline\" | \"CustomerBuyGoodsOnline\";\n\nexport interface C2BSimulateRequest {\n /** Your Paybill or Till shortcode. */\n shortCode: string;\n /** Payment type. Paybill = \"CustomerPayBillOnline\", Till = \"CustomerBuyGoodsOnline\". */\n commandId: C2BCommandId;\n /** Amount to simulate (whole numbers only — Daraja rejects decimals). */\n amount: number;\n /** Phone number to debit in simulation. Use sandbox test numbers. */\n phoneNumber: string;\n /**\n * Account number / reference for Paybill payments.\n * Pass null or omit for Buy Goods (Till) transactions.\n */\n billRefNumber?: string | null;\n}\n\nexport interface C2BSimulateResponse {\n /** @see C2BRegisterUrlResponse.OriginatorCoversationID (Daraja typo) */\n OriginatorCoversationID: string;\n ResponseCode: string;\n ResponseDescription: string;\n}\n\n// ─── Webhook Callback Payloads ───────────────────────────────────────────────\n\n/**\n * C2B Confirmation/Validation callback body posted by Safaricom to your URLs.\n * C2B v2 returns a masked MSISDN: \"2547 ***** 126\"\n *\n * This type covers BOTH the confirmation and validation callbacks —\n * they share the same shape. The difference is:\n * - Validation: OrgAccountBalance is blank; you must respond Accept/Reject.\n * - Confirmation: OrgAccountBalance has the post-payment balance.\n */\nexport interface C2BCallbackPayload {\n /** \"Pay Bill\" or \"Buy Goods\" */\n TransactionType: string;\n /** Unique M-Pesa transaction ID (e.g. \"RKL51ZDR4F\") */\n TransID: string;\n /** Timestamp: \"YYYYMMDDHHmmss\" (e.g. \"20231121121325\") */\n TransTime: string;\n /** Amount as string decimal (e.g. \"5.00\") */\n TransAmount: string;\n /** Your Paybill or Till shortcode */\n BusinessShortCode: string;\n /**\n * Account reference / bill number entered by customer.\n * Applies to Paybill transactions. Empty string for Buy Goods.\n */\n BillRefNumber: string;\n /** Invoice number if applicable (usually empty string) */\n InvoiceNumber: string;\n /**\n * Post-payment balance of your Utility Account (confirmation only).\n * Empty string in validation requests.\n */\n OrgAccountBalance: string;\n /**\n * Opaque ID the partner can echo back in the validation response.\n * Safaricom sends it back in the confirmation if you returned it.\n */\n ThirdPartyTransID: string;\n /**\n * Masked phone number: \"2547 ***** 126\"\n * C2B v2 masks for privacy. C2B v1 used SHA-256 hash.\n */\n MSISDN: string;\n /** Customer first name (may be empty) */\n FirstName: string;\n /** Customer middle name (may be empty) */\n MiddleName: string;\n /** Customer last name (may be empty) */\n LastName: string;\n}\n\n/**\n * Your response to M-Pesa's Validation request.\n * You must reply within ~8 seconds or M-Pesa uses ResponseType default.\n */\nexport interface C2BValidationResponse {\n /**\n * \"0\" to accept the payment.\n * Any of the C2B rejection codes to reject (e.g. \"C2B00011\").\n */\n ResultCode: string;\n /** \"Accepted\" or \"Rejected\" */\n ResultDesc: string;\n}\n\n/**\n * Rejection codes for the Validation response.\n * Use these ResultCode values when rejecting a payment.\n */\nexport const C2B_REJECTION_CODES = {\n INVALID_MSISDN: \"C2B00011\",\n INVALID_ACCOUNT_NUMBER: \"C2B00012\",\n INVALID_AMOUNT: \"C2B00013\",\n INVALID_KYC_DETAILS: \"C2B00014\",\n INVALID_SHORT_CODE: \"C2B00015\",\n OTHER_ERROR: \"C2B00016\",\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","/**\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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pesafy",
3
- "version": "0.2.3",
3
+ "version": "0.3.0",
4
4
  "private": false,
5
5
  "description": "A powerful payment gateway library for African payment systems, starting with M-Pesa Daraja API",
6
6
  "author": "lewisodero27@gmail.com",