pesafy 0.2.2 → 0.2.4

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
  });
@@ -206,13 +207,26 @@ async function processB2B(baseUrl, accessToken, securityCredential, initiatorNam
206
207
  // src/mpesa/stk-push/utils.ts
207
208
  function formatPhoneNumber(phone) {
208
209
  const cleaned = phone.replace(/\D/g, "");
209
- if (cleaned.startsWith("0")) return "254" + cleaned.slice(1);
210
- if (cleaned.startsWith("254")) return cleaned;
211
- return "254" + cleaned;
210
+ let formatted;
211
+ if (cleaned.startsWith("254")) {
212
+ formatted = cleaned;
213
+ } else if (cleaned.startsWith("0")) {
214
+ formatted = "254" + cleaned.slice(1);
215
+ } else if (cleaned.length === 9) {
216
+ formatted = "254" + cleaned;
217
+ } else {
218
+ formatted = "254" + cleaned;
219
+ }
220
+ if (!/^254[71]\d{8}$/.test(formatted)) {
221
+ throw new Error(
222
+ `Invalid Kenyan phone number: "${phone}". Expected format: 07XXXXXXXX, 2547XXXXXXXX, or +2547XXXXXXXX.`
223
+ );
224
+ }
225
+ return formatted;
212
226
  }
213
227
  function getStkPushPassword(shortCode, passKey, timestamp) {
214
228
  const raw = `${shortCode}${passKey}${timestamp}`;
215
- return Buffer.from(raw, "utf-8").toString("base64");
229
+ return btoa(raw);
216
230
  }
217
231
  function getTimestamp() {
218
232
  const now = /* @__PURE__ */ new Date();
@@ -341,6 +355,12 @@ async function processReversal(baseUrl, accessToken, securityCredential, initiat
341
355
 
342
356
  // src/mpesa/stk-push/stk-push.ts
343
357
  async function processStkPush(baseUrl, accessToken, request) {
358
+ const amount = Math.round(request.amount);
359
+ if (amount < 1) {
360
+ throw new Error(
361
+ `Amount must be at least KES 1 (got ${request.amount} which rounds to ${amount}).`
362
+ );
363
+ }
344
364
  const timestamp = getTimestamp();
345
365
  const partyB = request.partyB ?? request.shortCode;
346
366
  const body = {
@@ -348,7 +368,7 @@ async function processStkPush(baseUrl, accessToken, request) {
348
368
  Password: getStkPushPassword(request.shortCode, request.passKey, timestamp),
349
369
  Timestamp: timestamp,
350
370
  TransactionType: request.transactionType ?? "CustomerPayBillOnline",
351
- Amount: Math.round(request.amount),
371
+ Amount: amount,
352
372
  PartyA: formatPhoneNumber(request.phoneNumber),
353
373
  PartyB: partyB,
354
374
  PhoneNumber: formatPhoneNumber(request.phoneNumber),
@@ -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 */\nexport function formatPhoneNumber(phone: string): string {\n const cleaned = phone.replace(/\\D/g, \"\");\n if (cleaned.startsWith(\"0\")) return \"254\" + cleaned.slice(1);\n if (cleaned.startsWith(\"254\")) return cleaned;\n return \"254\" + cleaned;\n}\n\n/**\n * Generates the STK Push password.\n * Formula: Base64(Shortcode + Passkey + Timestamp)\n *\n * IMPORTANT: The timestamp passed here MUST be the exact same value sent in\n * the request body's `Timestamp` field. Safaricom validates they match.\n * Always call getTimestamp() once and pass the result to both this function\n * and the request body — never call getTimestamp() twice.\n */\nexport function getStkPushPassword(\n shortCode: string,\n passKey: string,\n timestamp: string\n): string {\n const raw = `${shortCode}${passKey}${timestamp}`;\n return Buffer.from(raw, \"utf-8\").toString(\"base64\");\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 // 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: Math.round(request.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/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":";;;;;;;;;;;AAMO,IAAM,WAAA,GAAN,MAAM,YAAA,SAAoB,KAAA,CAAM;AAAA,EAOrC,YAAY,OAAA,EAA6B;AACvC,IAAA,KAAA,CAAM,QAAQ,OAAO,CAAA;AAPvB,IAAA,aAAA,CAAA,IAAA,EAAS,MAAA,CAAA;AACT,IAAA,aAAA,CAAA,IAAA,EAAS,YAAA,CAAA;AACT,IAAA,aAAA,CAAA,IAAA,EAAS,UAAA,CAAA;AACT,IAAA,aAAA,CAAA,IAAA,EAAS,WAAA,CAAA;AACT,IAAA,aAAA,CAAA,IAAA,EAAkB,OAAA,CAAA;AAIhB,IAAA,MAAA,CAAO,eAAe,IAAA,EAAM,MAAA,EAAQ,EAAE,KAAA,EAAO,eAAe,CAAA;AAC5D,IAAA,IAAA,CAAK,OAAO,OAAA,CAAQ,IAAA;AACpB,IAAA,IAAA,CAAK,aAAa,OAAA,CAAQ,UAAA;AAC1B,IAAA,IAAA,CAAK,WAAW,OAAA,CAAQ,QAAA;AACxB,IAAA,IAAA,CAAK,YAAY,OAAA,CAAQ,SAAA;AACzB,IAAA,IAAA,CAAK,QAAQ,OAAA,CAAQ,KAAA;AAErB,IAAA,IAAI,MAAM,iBAAA,EAAmB;AAC3B,MAAA,KAAA,CAAM,iBAAA,CAAkB,MAAM,YAAW,CAAA;AAAA,IAC3C;AAAA,EACF;AAAA,EAEA,MAAA,GAAS;AACP,IAAA,OAAO;AAAA,MACL,MAAM,IAAA,CAAK,IAAA;AAAA,MACX,MAAM,IAAA,CAAK,IAAA;AAAA,MACX,SAAS,IAAA,CAAK,OAAA;AAAA,MACd,YAAY,IAAA,CAAK,UAAA;AAAA,MACjB,WAAW,IAAA,CAAK;AAAA,KAClB;AAAA,EACF;AACF;AAEO,SAAS,YAAY,OAAA,EAA0C;AACpE,EAAA,OAAO,IAAI,YAAY,OAAO,CAAA;AAChC;;;ACjCA,IAAM,eAAA,GAAkB,GAAA;AAExB,eAAsB,WAAA,CACpB,GAAA,EACA,OAAA,GAA8B,EAAC,EACL;AAC1B,EAAA,MAAM;AAAA,IACJ,MAAA,GAAS,KAAA;AAAA,IACT,UAAU,EAAC;AAAA,IACX,IAAA;AAAA,IACA,OAAA,GAAU;AAAA,GACZ,GAAI,OAAA;AAEJ,EAAA,MAAM,UAAA,GAAa,IAAI,eAAA,EAAgB;AACvC,EAAA,MAAM,YAAY,UAAA,CAAW,MAAM,UAAA,CAAW,KAAA,IAAS,OAAO,CAAA;AAE9D,EAAA,IAAI;AACF,IAAA,MAAM,QAAA,GAAW,MAAM,KAAA,CAAM,GAAA,EAAK;AAAA,MAChC,MAAA;AAAA,MACA,OAAA,EAAS;AAAA,QACP,cAAA,EAAgB,kBAAA;AAAA,QAChB,GAAG;AAAA,OACL;AAAA,MACA,IAAA,EAAM,IAAA,GAAO,IAAA,CAAK,SAAA,CAAU,IAAI,CAAA,GAAI,KAAA,CAAA;AAAA,MACpC,QAAQ,UAAA,CAAW;AAAA,KACpB,CAAA;AAED,IAAA,YAAA,CAAa,SAAS,CAAA;AAEtB,IAAA,IAAI,IAAA;AACJ,IAAA,MAAM,IAAA,GAAO,MAAM,QAAA,CAAS,IAAA,EAAK;AACjC,IAAA,IAAI;AACF,MAAA,IAAA,GAAQ,IAAA,GAAO,IAAA,CAAK,KAAA,CAAM,IAAI,IAAI,EAAC;AAAA,IACrC,CAAA,CAAA,MAAQ;AACN,MAAA,IAAA,GAAO,EAAE,KAAK,IAAA,EAAK;AAAA,IACrB;AAEA,IAAA,IAAI,CAAC,SAAS,EAAA,EAAI;AAIhB,MAAA,MAAM,UAAU,IAAA,CAAK,MAAA,GAAS,CAAA,GAAI,CAAA,QAAA,EAAM,IAAI,CAAA,CAAA,GAAK,EAAA;AACjD,MAAA,MAAM,IAAI,WAAA,CAAY;AAAA,QACpB,IAAA,EAAM,WAAA;AAAA,QACN,OAAA,EAAS,CAAA,2BAAA,EAA8B,QAAA,CAAS,MAAM,GAAG,OAAO,CAAA,CAAA;AAAA,QAChE,YAAY,QAAA,CAAS,MAAA;AAAA,QACrB,QAAA,EAAU;AAAA,OACX,CAAA;AAAA,IACH;AAEA,IAAA,OAAO;AAAA,MACL,IAAA;AAAA,MACA,QAAQ,QAAA,CAAS,MAAA;AAAA,MACjB,SAAS,QAAA,CAAS;AAAA,KACpB;AAAA,EACF,SAAS,KAAA,EAAO;AACd,IAAA,YAAA,CAAa,SAAS,CAAA;AAEtB,IAAA,IAAI,KAAA,YAAiB,aAAa,MAAM,KAAA;AACxC,IAAA,IAAI,iBAAiB,KAAA,EAAO;AAC1B,MAAA,IAAI,KAAA,CAAM,SAAS,YAAA,EAAc;AAC/B,QAAA,MAAM,IAAI,WAAA,CAAY;AAAA,UACpB,IAAA,EAAM,SAAA;AAAA,UACN,OAAA,EAAS,2BAA2B,OAAO,CAAA,EAAA,CAAA;AAAA,UAC3C,KAAA,EAAO;AAAA,SACR,CAAA;AAAA,MACH;AACA,MAAA,MAAM,IAAI,WAAA,CAAY;AAAA,QACpB,IAAA,EAAM,eAAA;AAAA,QACN,SAAS,KAAA,CAAM,OAAA;AAAA,QACf,KAAA,EAAO;AAAA,OACR,CAAA;AAAA,IACH;AACA,IAAA,MAAM,IAAI,WAAA,CAAY;AAAA,MACpB,IAAA,EAAM,gBAAA;AAAA,MACN,OAAA,EAAS,2BAAA;AAAA,MACT,KAAA,EAAO;AAAA,KACR,CAAA;AAAA,EACH;AACF;;;AC7EA,IAAM,oBAAA,GAAuB,EAAA;AAEtB,IAAM,eAAN,MAAmB;AAAA,EAOxB,WAAA,CAAY,WAAA,EAAqB,cAAA,EAAwB,OAAA,EAAiB;AAN1E,IAAA,aAAA,CAAA,IAAA,EAAQ,aAAA,CAAA;AACR,IAAA,aAAA,CAAA,IAAA,EAAQ,gBAAA,CAAA;AACR,IAAA,aAAA,CAAA,IAAA,EAAQ,SAAA,CAAA;AACR,IAAA,aAAA,CAAA,IAAA,EAAQ,aAAA,EAA6B,IAAA,CAAA;AACrC,IAAA,aAAA,CAAA,IAAA,EAAQ,gBAAA,EAAiB,CAAA,CAAA;AAGvB,IAAA,IAAA,CAAK,WAAA,GAAc,WAAA;AACnB,IAAA,IAAA,CAAK,cAAA,GAAiB,cAAA;AACtB,IAAA,IAAA,CAAK,OAAA,GAAU,OAAA;AAAA,EACjB;AAAA,EAEQ,aAAA,GAAwB;AAC9B,IAAA,MAAM,cAAc,CAAA,EAAG,IAAA,CAAK,WAAW,CAAA,CAAA,EAAI,KAAK,cAAc,CAAA,CAAA;AAC9D,IAAA,MAAM,UAAU,MAAA,CAAO,IAAA,CAAK,aAAa,OAAO,CAAA,CAAE,SAAS,QAAQ,CAAA;AACnE,IAAA,OAAO,SAAS,OAAO,CAAA,CAAA;AAAA,EACzB;AAAA,EAEA,MAAM,cAAA,GAAkC;AACtC,IAAA,MAAM,GAAA,GAAM,IAAA,CAAK,GAAA,EAAI,GAAI,GAAA;AACzB,IAAA,IAAI,IAAA,CAAK,WAAA,IAAe,IAAA,CAAK,cAAA,GAAiB,MAAM,oBAAA,EAAsB;AACxE,MAAA,OAAO,IAAA,CAAK,WAAA;AAAA,IACd;AAEA,IAAA,MAAM,GAAA,GAAM,CAAA,EAAG,IAAA,CAAK,OAAO,CAAA,gDAAA,CAAA;AAC3B,IAAA,MAAM,QAAA,GAAW,MAAM,WAAA,CAA2B,GAAA,EAAK;AAAA,MACrD,MAAA,EAAQ,KAAA;AAAA,MACR,OAAA,EAAS;AAAA,QACP,aAAA,EAAe,KAAK,aAAA;AAAc;AACpC,KACD,CAAA;AAED,IAAA,MAAM,OAAO,QAAA,CAAS,IAAA;AACtB,IAAA,IAAI,CAAC,KAAK,YAAA,EAAc;AACtB,MAAA,MAAM,IAAI,WAAA,CAAY;AAAA,QACpB,IAAA,EAAM,aAAA;AAAA,QACN,OAAA,EAAS,+BAAA;AAAA,QACT,QAAA,EAAU;AAAA,OACX,CAAA;AAAA,IACH;AAEA,IAAA,IAAA,CAAK,cAAc,IAAA,CAAK,YAAA;AACxB,IAAA,IAAA,CAAK,cAAA,GAAiB,GAAA,IAAO,IAAA,CAAK,UAAA,IAAc,IAAA,CAAA;AAChD,IAAA,OAAO,IAAA,CAAK,WAAA;AAAA,EACd;AAAA,EAEA,UAAA,GAAmB;AACjB,IAAA,IAAA,CAAK,WAAA,GAAc,IAAA;AACnB,IAAA,IAAA,CAAK,cAAA,GAAiB,CAAA;AAAA,EACxB;AACF;ACpDO,SAAS,yBAAA,CACd,mBACA,cAAA,EACQ;AACR,EAAA,IAAI;AACF,IAAA,MAAM,cAAA,GAAiB,MAAA,CAAO,IAAA,CAAK,iBAAA,EAAmB,OAAO,CAAA;AAC7D,IAAA,MAAM,SAAA,GAAYA,oBAAA;AAAA,MAChB;AAAA,QACE,GAAA,EAAK,cAAA;AAAA,QACL,OAAA,EAAS;AAAA;AAAA,OACX;AAAA,MACA;AAAA,KACF;AACA,IAAA,OAAO,SAAA,CAAU,SAAS,QAAQ,CAAA;AAAA,EACpC,SAAS,KAAA,EAAO;AACd,IAAA,MAAM,IAAI,WAAA,CAAY;AAAA,MACpB,IAAA,EAAM,mBAAA;AAAA,MACN,OAAA,EAAS,uCAAA;AAAA,MACT,KAAA,EAAO;AAAA,KACR,CAAA;AAAA,EACH;AACF;;;AChBA,eAAsB,UAAA,CACpB,OAAA,EACA,WAAA,EACA,kBAAA,EACA,eACA,OAAA,EACsB;AACtB,EAAA,MAAM,IAAA,GAAO;AAAA,IACX,SAAA,EAAW,aAAA;AAAA,IACX,kBAAA,EAAoB,kBAAA;AAAA,IACpB,SAAA,EAAW,QAAQ,SAAA,IAAa,iBAAA;AAAA,IAChC,oBAAA,EAAsB,QAAQ,oBAAA,IAAwB,CAAA;AAAA,IACtD,sBAAA,EAAwB,QAAQ,sBAAA,IAA0B,CAAA;AAAA,IAC1D,MAAA,EAAQ,IAAA,CAAK,KAAA,CAAM,OAAA,CAAQ,MAAM,CAAA;AAAA,IACjC,QAAQ,OAAA,CAAQ,SAAA;AAAA,IAChB,QAAQ,OAAA,CAAQ,iBAAA;AAAA,IAChB,gBAAA,EAAkB,QAAQ,gBAAA,IAAoB,EAAA;AAAA,IAC9C,OAAA,EAAS,QAAQ,OAAA,IAAW,aAAA;AAAA,IAC5B,iBAAiB,OAAA,CAAQ,UAAA;AAAA,IACzB,WAAW,OAAA,CAAQ;AAAA,GACrB;AAEA,EAAA,MAAM,EAAE,IAAA,EAAK,GAAI,MAAM,WAAA;AAAA,IACrB,GAAG,OAAO,CAAA,4BAAA,CAAA;AAAA,IACV;AAAA,MACE,MAAA,EAAQ,MAAA;AAAA,MACR,OAAA,EAAS,EAAE,aAAA,EAAe,CAAA,OAAA,EAAU,WAAW,CAAA,CAAA,EAAG;AAAA,MAClD;AAAA;AACF,GACF;AAEA,EAAA,OAAO,IAAA;AACT;;;ACtCO,SAAS,kBAAkB,KAAA,EAAuB;AACvD,EAAA,MAAM,OAAA,GAAU,KAAA,CAAM,OAAA,CAAQ,KAAA,EAAO,EAAE,CAAA;AAEvC,EAAA,IAAI,SAAA;AACJ,EAAA,IAAI,OAAA,CAAQ,UAAA,CAAW,KAAK,CAAA,EAAG;AAC7B,IAAA,SAAA,GAAY,OAAA;AAAA,EACd,CAAA,MAAA,IAAW,OAAA,CAAQ,UAAA,CAAW,GAAG,CAAA,EAAG;AAClC,IAAA,SAAA,GAAY,KAAA,GAAQ,OAAA,CAAQ,KAAA,CAAM,CAAC,CAAA;AAAA,EACrC,CAAA,MAAA,IAAW,OAAA,CAAQ,MAAA,KAAW,CAAA,EAAG;AAE/B,IAAA,SAAA,GAAY,KAAA,GAAQ,OAAA;AAAA,EACtB,CAAA,MAAO;AACL,IAAA,SAAA,GAAY,KAAA,GAAQ,OAAA;AAAA,EACtB;AAGA,EAAA,IAAI,CAAC,gBAAA,CAAiB,IAAA,CAAK,SAAS,CAAA,EAAG;AACrC,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,iCAAiC,KAAK,CAAA,+DAAA;AAAA,KAExC;AAAA,EACF;AAEA,EAAA,OAAO,SAAA;AACT;AAcO,SAAS,kBAAA,CACd,SAAA,EACA,OAAA,EACA,SAAA,EACQ;AACR,EAAA,MAAM,MAAM,CAAA,EAAG,SAAS,CAAA,EAAG,OAAO,GAAG,SAAS,CAAA,CAAA;AAE9C,EAAA,OAAO,KAAK,GAAG,CAAA;AACjB;AAMO,SAAS,YAAA,GAAuB;AACrC,EAAA,MAAM,GAAA,uBAAU,IAAA,EAAK;AACrB,EAAA,MAAM,GAAA,GAAM,CAAC,CAAA,KAAc,CAAA,CAAE,UAAS,CAAE,QAAA,CAAS,GAAG,GAAG,CAAA;AACvD,EAAA,OAAO;AAAA,IACL,IAAI,WAAA,EAAY;AAAA,IAChB,GAAA,CAAI,GAAA,CAAI,QAAA,EAAS,GAAI,CAAC,CAAA;AAAA,IACtB,GAAA,CAAI,GAAA,CAAI,OAAA,EAAS,CAAA;AAAA,IACjB,GAAA,CAAI,GAAA,CAAI,QAAA,EAAU,CAAA;AAAA,IAClB,GAAA,CAAI,GAAA,CAAI,UAAA,EAAY,CAAA;AAAA,IACpB,GAAA,CAAI,GAAA,CAAI,UAAA,EAAY;AAAA,GACtB,CAAE,KAAK,EAAE,CAAA;AACX;;;ACxDA,eAAsB,UAAA,CACpB,OAAA,EACA,WAAA,EACA,kBAAA,EACA,eACA,OAAA,EACsB;AACtB,EAAA,MAAM,IAAA,GAAO;AAAA,IACX,wBAAA,EAA0B,CAAA,GAAA,EAAM,IAAA,CAAK,GAAA,EAAK,CAAA,CAAA,EAAI,IAAA,CAAK,MAAA,EAAO,CAAE,QAAA,CAAS,EAAE,CAAA,CAAE,KAAA,CAAM,CAAC,CAAC,CAAA,CAAA;AAAA,IACjF,aAAA,EAAe,aAAA;AAAA,IACf,kBAAA,EAAoB,kBAAA;AAAA,IACpB,SAAA,EAAW,QAAQ,SAAA,IAAa,iBAAA;AAAA,IAChC,MAAA,EAAQ,IAAA,CAAK,KAAA,CAAM,OAAA,CAAQ,MAAM,CAAA;AAAA,IACjC,QAAQ,OAAA,CAAQ,SAAA;AAAA,IAChB,MAAA,EAAQ,iBAAA,CAAkB,OAAA,CAAQ,WAAW,CAAA;AAAA,IAC7C,OAAA,EAAS,QAAQ,OAAA,IAAW,SAAA;AAAA,IAC5B,iBAAiB,OAAA,CAAQ,UAAA;AAAA,IACzB,WAAW,OAAA,CAAQ,SAAA;AAAA,IACnB,QAAA,EAAU,QAAQ,QAAA,IAAY;AAAA,GAChC;AAEA,EAAA,MAAM,EAAE,IAAA,EAAK,GAAI,MAAM,WAAA;AAAA,IACrB,GAAG,OAAO,CAAA,4BAAA,CAAA;AAAA,IACV;AAAA,MACE,MAAA,EAAQ,MAAA;AAAA,MACR,OAAA,EAAS,EAAE,aAAA,EAAe,CAAA,OAAA,EAAU,WAAW,CAAA,CAAA,EAAG;AAAA,MAClD;AAAA;AACF,GACF;AAEA,EAAA,OAAO,IAAA;AACT;;;AChCA,eAAsB,eAAA,CACpB,OAAA,EACA,WAAA,EACA,OAAA,EACiC;AACjC,EAAA,MAAM,IAAA,GAAO;AAAA,IACX,WAAW,OAAA,CAAQ,SAAA;AAAA,IACnB,YAAA,EAAc,QAAQ,YAAA,IAAgB,WAAA;AAAA,IACtC,iBAAiB,OAAA,CAAQ,eAAA;AAAA,IACzB,eAAe,OAAA,CAAQ;AAAA,GACzB;AAEA,EAAA,MAAM,EAAE,IAAA,EAAK,GAAI,MAAM,WAAA;AAAA,IACrB,GAAG,OAAO,CAAA,yBAAA,CAAA;AAAA,IACV;AAAA,MACE,MAAA,EAAQ,MAAA;AAAA,MACR,OAAA,EAAS,EAAE,aAAA,EAAe,CAAA,OAAA,EAAU,WAAW,CAAA,CAAA,EAAG;AAAA,MAClD;AAAA;AACF,GACF;AAEA,EAAA,OAAO,IAAA;AACT;;;ACrBA,eAAsB,WAAA,CACpB,OAAA,EACA,WAAA,EACA,OAAA,EAC8B;AAC9B,EAAA,MAAM,IAAA,GAAO;AAAA,IACX,WAAW,OAAA,CAAQ,SAAA;AAAA,IACnB,SAAA,EAAW,uBAAA;AAAA,IACX,MAAA,EAAQ,IAAA,CAAK,KAAA,CAAM,OAAA,CAAQ,MAAM,CAAA;AAAA,IACjC,MAAA,EAAQ,iBAAA,CAAkB,OAAA,CAAQ,WAAW,CAAA;AAAA,IAC7C,aAAA,EAAe,QAAQ,aAAA,IAAiB;AAAA,GAC1C;AAEA,EAAA,MAAM,EAAE,IAAA,EAAK,GAAI,MAAM,WAAA;AAAA,IACrB,GAAG,OAAO,CAAA,sBAAA,CAAA;AAAA,IACV;AAAA,MACE,MAAA,EAAQ,MAAA;AAAA,MACR,OAAA,EAAS,EAAE,aAAA,EAAe,CAAA,OAAA,EAAU,WAAW,CAAA,CAAA,EAAG;AAAA,MAClD;AAAA;AACF,GACF;AAEA,EAAA,OAAO,IAAA;AACT;;;ACxBA,eAAsB,iBAAA,CACpB,OAAA,EACA,WAAA,EACA,OAAA,EAC4B;AAC5B,EAAA,MAAM,IAAA,GAAO;AAAA,IACX,cAAc,OAAA,CAAQ,YAAA;AAAA,IACtB,OAAO,OAAA,CAAQ,KAAA;AAAA,IACf,MAAA,EAAQ,IAAA,CAAK,KAAA,CAAM,OAAA,CAAQ,MAAM,CAAA;AAAA,IACjC,SAAS,OAAA,CAAQ,OAAA;AAAA,IACjB,KAAK,OAAA,CAAQ,GAAA;AAAA,IACb,IAAA,EAAM,QAAQ,IAAA,IAAQ;AAAA,GACxB;AAEA,EAAA,MAAM,EAAE,IAAA,EAAK,GAAI,MAAM,WAAA;AAAA,IACrB,GAAG,OAAO,CAAA,yBAAA,CAAA;AAAA,IACV;AAAA,MACE,MAAA,EAAQ,MAAA;AAAA,MACR,OAAA,EAAS,EAAE,aAAA,EAAe,CAAA,OAAA,EAAU,WAAW,CAAA,CAAA,EAAG;AAAA,MAClD;AAAA;AACF,GACF;AAEA,EAAA,OAAO,IAAA;AACT;;;ACxBA,eAAsB,eAAA,CACpB,OAAA,EACA,WAAA,EACA,kBAAA,EACA,eACA,OAAA,EAC2B;AAC3B,EAAA,MAAM,IAAA,GAAO;AAAA,IACX,SAAA,EAAW,aAAA;AAAA,IACX,kBAAA,EAAoB,kBAAA;AAAA,IACpB,SAAA,EAAW,qBAAA;AAAA,IACX,eAAe,OAAA,CAAQ,aAAA;AAAA,IACvB,MAAA,EAAQ,IAAA,CAAK,KAAA,CAAM,OAAA,CAAQ,MAAM,CAAA;AAAA,IACjC,eAAe,OAAA,CAAQ,SAAA;AAAA,IACvB,sBAAA,EAAwB,CAAA;AAAA,IACxB,WAAW,OAAA,CAAQ,SAAA;AAAA,IACnB,iBAAiB,OAAA,CAAQ,UAAA;AAAA,IACzB,OAAA,EAAS,QAAQ,OAAA,IAAW,UAAA;AAAA,IAC5B,QAAA,EAAU,QAAQ,QAAA,IAAY;AAAA,GAChC;AAEA,EAAA,MAAM,EAAE,IAAA,EAAK,GAAI,MAAM,WAAA;AAAA,IACrB,GAAG,OAAO,CAAA,0BAAA,CAAA;AAAA,IACV;AAAA,MACE,MAAA,EAAQ,MAAA;AAAA,MACR,OAAA,EAAS,EAAE,aAAA,EAAe,CAAA,OAAA,EAAU,WAAW,CAAA,CAAA,EAAG;AAAA,MAClD;AAAA;AACF,GACF;AAEA,EAAA,OAAO,IAAA;AACT;;;ACrCA,eAAsB,cAAA,CACpB,OAAA,EACA,WAAA,EACA,OAAA,EAC0B;AAG1B,EAAA,MAAM,MAAA,GAAS,IAAA,CAAK,KAAA,CAAM,OAAA,CAAQ,MAAM,CAAA;AACxC,EAAA,IAAI,SAAS,CAAA,EAAG;AACd,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,CAAA,mCAAA,EAAsC,OAAA,CAAQ,MAAM,CAAA,iBAAA,EAAoB,MAAM,CAAA,EAAA;AAAA,KAChF;AAAA,EACF;AAKA,EAAA,MAAM,YAAY,YAAA,EAAa;AAK/B,EAAA,MAAM,MAAA,GAAS,OAAA,CAAQ,MAAA,IAAU,OAAA,CAAQ,SAAA;AAEzC,EAAA,MAAM,IAAA,GAAO;AAAA,IACX,mBAAmB,OAAA,CAAQ,SAAA;AAAA,IAC3B,UAAU,kBAAA,CAAmB,OAAA,CAAQ,SAAA,EAAW,OAAA,CAAQ,SAAS,SAAS,CAAA;AAAA,IAC1E,SAAA,EAAW,SAAA;AAAA,IACX,eAAA,EAAiB,QAAQ,eAAA,IAAmB,uBAAA;AAAA,IAC5C,MAAA,EAAQ,MAAA;AAAA,IACR,MAAA,EAAQ,iBAAA,CAAkB,OAAA,CAAQ,WAAW,CAAA;AAAA,IAC7C,MAAA,EAAQ,MAAA;AAAA,IACR,WAAA,EAAa,iBAAA,CAAkB,OAAA,CAAQ,WAAW,CAAA;AAAA,IAClD,aAAa,OAAA,CAAQ,WAAA;AAAA,IACrB,gBAAA,EAAkB,OAAA,CAAQ,gBAAA,CAAiB,KAAA,CAAM,GAAG,EAAE,CAAA;AAAA,IACtD,eAAA,EAAiB,OAAA,CAAQ,eAAA,CAAgB,KAAA,CAAM,GAAG,EAAE;AAAA,GACtD;AAEA,EAAA,MAAM,EAAE,IAAA,EAAK,GAAI,MAAM,WAAA;AAAA,IACrB,GAAG,OAAO,CAAA,gCAAA,CAAA;AAAA,IACV;AAAA,MACE,MAAA,EAAQ,MAAA;AAAA,MACR,OAAA,EAAS,EAAE,aAAA,EAAe,CAAA,OAAA,EAAU,WAAW,CAAA,CAAA,EAAG;AAAA,MAClD;AAAA;AACF,GACF;AAEA,EAAA,OAAO,IAAA;AACT;;;AChDA,eAAsB,YAAA,CACpB,OAAA,EACA,WAAA,EACA,OAAA,EAC2B;AAE3B,EAAA,MAAM,YAAY,YAAA,EAAa;AAE/B,EAAA,MAAM,IAAA,GAAO;AAAA,IACX,mBAAmB,OAAA,CAAQ,SAAA;AAAA,IAC3B,UAAU,kBAAA,CAAmB,OAAA,CAAQ,SAAA,EAAW,OAAA,CAAQ,SAAS,SAAS,CAAA;AAAA,IAC1E,SAAA,EAAW,SAAA;AAAA,IACX,mBAAmB,OAAA,CAAQ;AAAA,GAC7B;AAEA,EAAA,MAAM,EAAE,IAAA,EAAK,GAAI,MAAM,WAAA;AAAA,IACrB,GAAG,OAAO,CAAA,4BAAA,CAAA;AAAA,IACV;AAAA,MACE,MAAA,EAAQ,MAAA;AAAA,MACR,OAAA,EAAS,EAAE,aAAA,EAAe,CAAA,OAAA,EAAU,WAAW,CAAA,CAAA,EAAG;AAAA,MAClD;AAAA;AACF,GACF;AAEA,EAAA,OAAO,IAAA;AACT;;;ACnBA,eAAsB,sBAAA,CACpB,OAAA,EACA,WAAA,EACA,kBAAA,EACA,eACA,OAAA,EACoC;AACpC,EAAA,MAAM,IAAA,GAAO;AAAA,IACX,SAAA,EAAW,aAAA;AAAA,IACX,kBAAA,EAAoB,kBAAA;AAAA,IACpB,SAAA,EAAW,wBAAA;AAAA,IACX,eAAe,OAAA,CAAQ,aAAA;AAAA,IACvB,QAAQ,OAAA,CAAQ,SAAA;AAAA,IAChB,cAAA,EAAgB,QAAQ,cAAA,IAAkB,CAAA;AAAA,IAC1C,WAAW,OAAA,CAAQ,SAAA;AAAA,IACnB,iBAAiB,OAAA,CAAQ,UAAA;AAAA,IACzB,OAAA,EAAS,QAAA;AAAA,IACT,QAAA,EAAU;AAAA,GACZ;AAEA,EAAA,MAAM,EAAE,IAAA,EAAK,GAAI,MAAM,WAAA;AAAA,IACrB,GAAG,OAAO,CAAA,iCAAA,CAAA;AAAA,IACV;AAAA,MACE,MAAA,EAAQ,MAAA;AAAA,MACR,OAAA,EAAS,EAAE,aAAA,EAAe,CAAA,OAAA,EAAU,WAAW,CAAA,CAAA,EAAG;AAAA,MAClD;AAAA;AACF,GACF;AAEA,EAAA,OAAO,IAAA;AACT;;;AC3CO,IAAM,gBAAA,GAAmB;AAAA,EAC9B,OAAA,EAAS,iCAAA;AAAA,EACT,UAAA,EAAY;AACd;;;ACuBO,IAAM,QAAN,MAAY;AAAA,EAKjB,YAAY,MAAA,EAAqB;AAJjC,IAAA,aAAA,CAAA,IAAA,EAAQ,QAAA,CAAA;AACR,IAAA,aAAA,CAAA,IAAA,EAAQ,cAAA,CAAA;AACR,IAAA,aAAA,CAAA,IAAA,EAAQ,SAAA,CAAA;AAGN,IAAA,IAAA,CAAK,MAAA,GAAS,MAAA;AACd,IAAA,IAAA,CAAK,OAAA,GAAU,gBAAA,CAAiB,MAAA,CAAO,WAAW,CAAA;AAClD,IAAA,IAAA,CAAK,eAAe,IAAI,YAAA;AAAA,MACtB,MAAA,CAAO,WAAA;AAAA,MACP,MAAA,CAAO,cAAA;AAAA,MACP,IAAA,CAAK;AAAA,KACP;AAAA,EACF;AAAA,EAEA,MAAc,QAAA,GAA4B;AACxC,IAAA,OAAO,IAAA,CAAK,aAAa,cAAA,EAAe;AAAA,EAC1C;AAAA,EAEA,MAAc,qBAAA,GAAyC;AACrD,IAAA,IAAI,IAAA,CAAK,MAAA,CAAO,kBAAA,EAAoB,OAAO,KAAK,MAAA,CAAO,kBAAA;AACvD,IAAA,IAAI,CAAC,IAAA,CAAK,MAAA,CAAO,iBAAA,EAAmB;AAClC,MAAA,MAAM,IAAI,KAAA;AAAA,QACR;AAAA,OACF;AAAA,IACF;AACA,IAAA,IAAI,IAAA;AACJ,IAAA,IAAI,IAAA,CAAK,OAAO,cAAA,EAAgB;AAC9B,MAAA,IAAA,GAAO,KAAK,MAAA,CAAO,cAAA;AAAA,IACrB,CAAA,MAAA,IAAW,IAAA,CAAK,MAAA,CAAO,eAAA,EAAiB;AACtC,MAAA,IAAA,GAAO,MAAM,GAAA,CAAI,IAAA,CAAK,KAAK,MAAA,CAAO,eAAe,EAAE,IAAA,EAAK;AAAA,IAC1D,CAAA,MAAO;AACL,MAAA,MAAM,IAAI,KAAA;AAAA,QACR;AAAA,OACF;AAAA,IACF;AACA,IAAA,OAAO,yBAAA,CAA0B,IAAA,CAAK,MAAA,CAAO,iBAAA,EAAmB,IAAI,CAAA;AAAA,EACtE;AAAA;AAAA,EAGA,MAAM,QAAQ,OAAA,EAAwD;AACpE,IAAA,MAAM,SAAA,GAAY,IAAA,CAAK,MAAA,CAAO,oBAAA,IAAwB,EAAA;AACtD,IAAA,MAAM,OAAA,GAAU,IAAA,CAAK,MAAA,CAAO,kBAAA,IAAsB,EAAA;AAClD,IAAA,IAAI,CAAC,SAAA,IAAa,CAAC,OAAA,EAAS;AAC1B,MAAA,MAAM,IAAI,KAAA;AAAA,QACR;AAAA,OACF;AAAA,IACF;AACA,IAAA,MAAM,KAAA,GAAQ,MAAM,IAAA,CAAK,QAAA,EAAS;AAClC,IAAA,OAAO,cAAA,CAAe,IAAA,CAAK,OAAA,EAAS,KAAA,EAAO;AAAA,MACzC,GAAG,OAAA;AAAA,MACH,SAAA;AAAA,MACA;AAAA,KACD,CAAA;AAAA,EACH;AAAA;AAAA,EAGA,MAAM,SAAS,OAAA,EAAyD;AACtE,IAAA,MAAM,SAAA,GAAY,IAAA,CAAK,MAAA,CAAO,oBAAA,IAAwB,EAAA;AACtD,IAAA,MAAM,OAAA,GAAU,IAAA,CAAK,MAAA,CAAO,kBAAA,IAAsB,EAAA;AAClD,IAAA,IAAI,CAAC,SAAA,IAAa,CAAC,OAAA,EAAS;AAC1B,MAAA,MAAM,IAAI,KAAA;AAAA,QACR;AAAA,OACF;AAAA,IACF;AACA,IAAA,MAAM,KAAA,GAAQ,MAAM,IAAA,CAAK,QAAA,EAAS;AAClC,IAAA,OAAO,YAAA,CAAa,IAAA,CAAK,OAAA,EAAS,KAAA,EAAO;AAAA,MACvC,GAAG,OAAA;AAAA,MACH,SAAA;AAAA,MACA;AAAA,KACD,CAAA;AAAA,EACH;AAAA;AAAA,EAGA,MAAM,IAAI,OAAA,EAAqB;AAC7B,IAAA,MAAM,SAAA,GAAY,IAAA,CAAK,MAAA,CAAO,aAAA,IAAiB,EAAA;AAC/C,IAAA,MAAM,YAAA,GAAe,MAAM,IAAA,CAAK,qBAAA,EAAsB;AACtD,IAAA,IAAI,CAAC,SAAA,EAAW,MAAM,IAAI,MAAM,gCAAgC,CAAA;AAChE,IAAA,MAAM,KAAA,GAAQ,MAAM,IAAA,CAAK,QAAA,EAAS;AAClC,IAAA,OAAO,WAAW,IAAA,CAAK,OAAA,EAAS,KAAA,EAAO,YAAA,EAAc,WAAW,OAAO,CAAA;AAAA,EACzE;AAAA;AAAA,EAGA,MAAM,IAAI,OAAA,EAAqB;AAC7B,IAAA,MAAM,SAAA,GAAY,IAAA,CAAK,MAAA,CAAO,aAAA,IAAiB,EAAA;AAC/C,IAAA,MAAM,YAAA,GAAe,MAAM,IAAA,CAAK,qBAAA,EAAsB;AACtD,IAAA,IAAI,CAAC,SAAA,EAAW,MAAM,IAAI,MAAM,gCAAgC,CAAA;AAChE,IAAA,MAAM,KAAA,GAAQ,MAAM,IAAA,CAAK,QAAA,EAAS;AAClC,IAAA,OAAO,WAAW,IAAA,CAAK,OAAA,EAAS,KAAA,EAAO,YAAA,EAAc,WAAW,OAAO,CAAA;AAAA,EACzE;AAAA;AAAA,EAGA,MAAM,gBAAgB,OAAA,EAAgC;AACpD,IAAA,MAAM,KAAA,GAAQ,MAAM,IAAA,CAAK,QAAA,EAAS;AAClC,IAAA,OAAO,eAAA,CAAgB,IAAA,CAAK,OAAA,EAAS,KAAA,EAAO,OAAO,CAAA;AAAA,EACrD;AAAA;AAAA,EAGA,MAAM,YAAY,OAAA,EAA6B;AAC7C,IAAA,MAAM,KAAA,GAAQ,MAAM,IAAA,CAAK,QAAA,EAAS;AAClC,IAAA,OAAO,WAAA,CAAY,IAAA,CAAK,OAAA,EAAS,KAAA,EAAO,OAAO,CAAA;AAAA,EACjD;AAAA;AAAA,EAGA,MAAM,OAAO,OAAA,EAA2B;AACtC,IAAA,MAAM,KAAA,GAAQ,MAAM,IAAA,CAAK,QAAA,EAAS;AAClC,IAAA,OAAO,iBAAA,CAAkB,IAAA,CAAK,OAAA,EAAS,KAAA,EAAO,OAAO,CAAA;AAAA,EACvD;AAAA;AAAA,EAGA,MAAM,kBAAkB,OAAA,EAAmC;AACzD,IAAA,MAAM,SAAA,GAAY,IAAA,CAAK,MAAA,CAAO,aAAA,IAAiB,EAAA;AAC/C,IAAA,MAAM,YAAA,GAAe,MAAM,IAAA,CAAK,qBAAA,EAAsB;AACtD,IAAA,IAAI,CAAC,SAAA;AACH,MAAA,MAAM,IAAI,MAAM,+CAA+C,CAAA;AACjE,IAAA,MAAM,KAAA,GAAQ,MAAM,IAAA,CAAK,QAAA,EAAS;AAClC,IAAA,OAAO,sBAAA;AAAA,MACL,IAAA,CAAK,OAAA;AAAA,MACL,KAAA;AAAA,MACA,YAAA;AAAA,MACA,SAAA;AAAA,MACA;AAAA,KACF;AAAA,EACF;AAAA;AAAA,EAGA,MAAM,SAAS,OAAA,EAA0B;AACvC,IAAA,MAAM,SAAA,GAAY,IAAA,CAAK,MAAA,CAAO,aAAA,IAAiB,EAAA;AAC/C,IAAA,MAAM,YAAA,GAAe,MAAM,IAAA,CAAK,qBAAA,EAAsB;AACtD,IAAA,IAAI,CAAC,SAAA,EAAW,MAAM,IAAI,MAAM,qCAAqC,CAAA;AACrE,IAAA,MAAM,KAAA,GAAQ,MAAM,IAAA,CAAK,QAAA,EAAS;AAClC,IAAA,OAAO,eAAA;AAAA,MACL,IAAA,CAAK,OAAA;AAAA,MACL,KAAA;AAAA,MACA,YAAA;AAAA,MACA,SAAA;AAAA,MACA;AAAA,KACF;AAAA,EACF;AACF;;;ACjJO,SAAS,yBAAyB,MAAA,EAEvC;AACA,EAAA,IAAI,CAAC,MAAA,CAAO,WAAA,IAAe,CAAC,OAAO,cAAA,EAAgB;AACjD,IAAA,MAAM,IAAI,WAAA,CAAY;AAAA,MACpB,IAAA,EAAM,qBAAA;AAAA,MACN,OAAA,EAAS;AAAA,KACV,CAAA;AAAA,EACH;AAEA,EAAA,IAAI,CAAC,MAAA,CAAO,oBAAA,IAAwB,CAAC,OAAO,kBAAA,EAAoB;AAC9D,IAAA,MAAM,IAAI,WAAA,CAAY;AAAA,MACpB,IAAA,EAAM,kBAAA;AAAA,MACN,OAAA,EACE;AAAA,KACH,CAAA;AAAA,EACH;AAEA,EAAA,IAAI,CAAC,OAAO,WAAA,EAAa;AACvB,IAAA,MAAM,IAAI,WAAA,CAAY;AAAA,MACpB,IAAA,EAAM,kBAAA;AAAA,MACN,OAAA,EAAS;AAAA,KACV,CAAA;AAAA,EACH;AAEA,EAAA,MAAM,KAAA,GAAQ,IAAI,KAAA,CAAM,MAAM,CAAA;AAC9B,EAAA,OAAO,EAAE,KAAA,EAAM;AACjB;AAaA,SAAS,eAAe,KAAA,EAAuB;AAC7C,EAAA,MAAM,MAAA,GAAS,KAAA,CAAM,OAAA,CAAQ,KAAA,EAAO,EAAE,CAAA;AACtC,EAAA,IAAI,MAAA,CAAO,UAAA,CAAW,KAAK,CAAA,EAAG,OAAO,MAAA;AACrC,EAAA,IAAI,MAAA,CAAO,WAAW,GAAG,CAAA,SAAU,CAAA,GAAA,EAAM,MAAA,CAAO,KAAA,CAAM,CAAC,CAAC,CAAA,CAAA;AACxD,EAAA,OAAO,MAAA;AACT;AAEA,SAAS,SAAA,CAAU,KAAe,KAAA,EAAsB;AACtD,EAAA,IAAI,iBAAiB,WAAA,EAAa;AAChC,IAAA,MAAM,MAAA,GAAS,MAAM,UAAA,IAAc,GAAA;AACnC,IAAA,GAAA,CAAI,MAAA,CAAO,MAAM,CAAA,CAAE,IAAA,CAAK;AAAA,MACtB,OAAO,KAAA,CAAM,IAAA;AAAA,MACb,SAAS,KAAA,CAAM,OAAA;AAAA,MACf,UAAA,EAAY;AAAA,KACb,CAAA;AACD,IAAA;AAAA,EACF;AAEA,EAAA,GAAA,CAAI,MAAA,CAAO,GAAG,CAAA,CAAE,IAAA,CAAK;AAAA,IACnB,KAAA,EAAO,gBAAA;AAAA,IACP,OAAA,EAAS;AAAA,GACV,CAAA;AACH;AAEO,SAAS,wBAAA,CACd,QACA,MAAA,EACQ;AACR,EAAA,MAAM,EAAE,KAAA,EAAM,GAAI,wBAAA,CAAyB,MAAM,CAAA;AAGjD,EAAA,MAAA,CAAO,IAAA;AAAA,IACL,yBAAA;AAAA,IACA,OAAO,GAAA,EAAc,GAAA,EAAe,IAAA,KAAuB;AACzD,MAAA,IAAI;AACF,QAAA,MAAM,OAAO,GAAA,CAAI,IAAA;AAEjB,QAAA,IAAI,CAAC,QAAQ,OAAO,IAAA,CAAK,WAAW,QAAA,IAAY,IAAA,CAAK,UAAU,CAAA,EAAG;AAChE,UAAA,MAAM,IAAI,WAAA,CAAY;AAAA,YACpB,IAAA,EAAM,kBAAA;AAAA,YACN,OAAA,EAAS;AAAA,WACV,CAAA;AAAA,QACH;AAEA,QAAA,IAAI,CAAC,KAAK,WAAA,EAAa;AACrB,UAAA,MAAM,IAAI,WAAA,CAAY;AAAA,YACpB,IAAA,EAAM,kBAAA;AAAA,YACN,OAAA,EAAS;AAAA,WACV,CAAA;AAAA,QACH;AAEA,QAAA,MAAM,WAAA,GAAc,cAAA,CAAe,IAAA,CAAK,WAAW,CAAA;AAEnD,QAAA,MAAM,MAAA,GAAS,MAAM,KAAA,CAAM,OAAA,CAAQ;AAAA,UACjC,QAAQ,IAAA,CAAK,MAAA;AAAA,UACb,WAAA;AAAA,UACA,aAAa,MAAA,CAAO,WAAA;AAAA,UACpB,gBAAA,EACE,IAAA,CAAK,gBAAA,IACL,CAAA,OAAA,EAAU,IAAA,CAAK,GAAA,EAAI,CAAE,QAAA,CAAS,EAAE,CAAA,CAAE,WAAA,EAAa,CAAA,CAAA;AAAA,UACjD,eAAA,EAAiB,KAAK,eAAA,IAAmB;AAAA,SAC1C,CAAA;AAED,QAAA,GAAA,CAAI,MAAA,CAAO,GAAG,CAAA,CAAE,IAAA,CAAK,MAAM,CAAA;AAAA,MAC7B,SAAS,KAAA,EAAO;AACd,QAAA,IAAI,GAAA,CAAI,WAAA,EAAa,OAAO,IAAA,CAAK,KAAK,CAAA;AACtC,QAAA,SAAA,CAAU,KAAK,KAAK,CAAA;AAAA,MACtB;AAAA,IACF;AAAA,GACF;AAGA,EAAA,MAAA,CAAO,IAAA;AAAA,IACL,0BAAA;AAAA,IACA,OAAO,GAAA,EAAc,GAAA,EAAe,IAAA,KAAuB;AACzD,MAAA,IAAI;AACF,QAAA,MAAM,OAAO,GAAA,CAAI,IAAA;AAEjB,QAAA,IAAI,CAAC,IAAA,IAAQ,CAAC,IAAA,CAAK,iBAAA,EAAmB;AACpC,UAAA,MAAM,IAAI,WAAA,CAAY;AAAA,YACpB,IAAA,EAAM,kBAAA;AAAA,YACN,OAAA,EAAS;AAAA,WACV,CAAA;AAAA,QACH;AAEA,QAAA,MAAM,MAAA,GAAS,MAAM,KAAA,CAAM,QAAA,CAAS;AAAA,UAClC,mBAAmB,IAAA,CAAK;AAAA,SACzB,CAAA;AAED,QAAA,GAAA,CAAI,MAAA,CAAO,GAAG,CAAA,CAAE,IAAA,CAAK,MAAM,CAAA;AAAA,MAC7B,SAAS,KAAA,EAAO;AACd,QAAA,IAAI,GAAA,CAAI,WAAA,EAAa,OAAO,IAAA,CAAK,KAAK,CAAA;AACtC,QAAA,SAAA,CAAU,KAAK,KAAK,CAAA;AAAA,MACtB;AAAA,IACF;AAAA,GACF;AAGA,EAAA,MAAA,CAAO,IAAA;AAAA,IACL,qBAAA;AAAA,IACA,OAAO,GAAA,EAAc,GAAA,EAAe,IAAA,KAAuB;AACzD,MAAA,IAAI;AACF,QAAA,MAAM,MAAA,GAAS,MAAM,KAAA,CAAM,WAAA,CAAY,IAAI,IAAI,CAAA;AAC/C,QAAA,GAAA,CAAI,MAAA,CAAO,GAAG,CAAA,CAAE,IAAA,CAAK,MAAM,CAAA;AAAA,MAC7B,SAAS,KAAA,EAAO;AACd,QAAA,IAAI,GAAA,CAAI,WAAA,EAAa,OAAO,IAAA,CAAK,KAAK,CAAA;AACtC,QAAA,SAAA,CAAU,KAAK,KAAK,CAAA;AAAA,MACtB;AAAA,IACF;AAAA,GACF;AAGA,EAAA,MAAA,CAAO,IAAA;AAAA,IACL,yBAAA;AAAA,IACA,OAAO,GAAA,EAAc,GAAA,EAAe,IAAA,KAAuB;AACzD,MAAA,IAAI;AACF,QAAA,MAAM,MAAA,GAAS,MAAM,KAAA,CAAM,eAAA,CAAgB,IAAI,IAAI,CAAA;AACnD,QAAA,GAAA,CAAI,MAAA,CAAO,GAAG,CAAA,CAAE,IAAA,CAAK,MAAM,CAAA;AAAA,MAC7B,SAAS,KAAA,EAAO;AACd,QAAA,IAAI,GAAA,CAAI,WAAA,EAAa,OAAO,IAAA,CAAK,KAAK,CAAA;AACtC,QAAA,SAAA,CAAU,KAAK,KAAK,CAAA;AAAA,MACtB;AAAA,IACF;AAAA,GACF;AAEA,EAAA,OAAO,MAAA;AACT;;;AC/KA,IAAM,eAAA,GAA0C;AAAA,EAC9C,UAAA,EAAY,QAAA;AAAA,EACZ,YAAA,EAAc,GAAA;AAAA;AAAA,EACd,QAAA,EAAU,IAAA;AAAA;AAAA,EACV,iBAAA,EAAmB,CAAA;AAAA,EACnB,gBAAA,EAAkB,EAAA,GAAK,EAAA,GAAK,EAAA,GAAK,EAAA,GAAK;AAAA;AACxC,CAAA;AASA,eAAsB,gBAAA,CACpB,EAAA,EACA,OAAA,GAAwB,EAAC,EACA;AACzB,EAAA,MAAM,IAAA,GAAO,EAAE,GAAG,eAAA,EAAiB,GAAG,OAAA,EAAQ;AAC9C,EAAA,IAAI,QAAQ,IAAA,CAAK,YAAA;AACjB,EAAA,IAAI,QAAA,GAAW,CAAA;AACf,EAAA,MAAM,SAAA,GAAY,KAAK,GAAA,EAAI;AAE3B,EAAA,OAAO,QAAA,GAAW,KAAK,UAAA,EAAY;AACjC,IAAA,QAAA,EAAA;AAGA,IAAA,IAAI,IAAA,CAAK,GAAA,EAAI,GAAI,SAAA,GAAY,KAAK,gBAAA,EAAkB;AAClD,MAAA,OAAO;AAAA,QACL,OAAA,EAAS,KAAA;AAAA,QACT,QAAA;AAAA,QACA,KAAA,EAAO,IAAI,KAAA,CAAM,6BAA6B;AAAA,OAChD;AAAA,IACF;AAEA,IAAA,IAAI;AACF,MAAA,MAAM,IAAA,GAAO,MAAM,EAAA,EAAG;AACtB,MAAA,OAAO,EAAE,OAAA,EAAS,IAAA,EAAM,IAAA,EAAM,QAAA,EAAS;AAAA,IACzC,SAAS,KAAA,EAAO;AACd,MAAA,MAAM,GAAA,GAAM,iBAAiB,KAAA,GAAQ,KAAA,GAAQ,IAAI,KAAA,CAAM,MAAA,CAAO,KAAK,CAAC,CAAA;AAGpE,MAAA,IAAI,GAAA,CAAI,OAAA,CAAQ,QAAA,CAAS,GAAG,CAAA,EAAG;AAC7B,QAAA,OAAO,EAAE,OAAA,EAAS,KAAA,EAAO,QAAA,EAAU,OAAO,GAAA,EAAI;AAAA,MAChD;AAGA,MAAA,IAAI,QAAA,GAAW,KAAK,UAAA,EAAY;AAC9B,QAAA,MAAM,IAAI,OAAA,CAAQ,CAAC,YAAY,UAAA,CAAW,OAAA,EAAS,KAAK,CAAC,CAAA;AACzD,QAAA,KAAA,GAAQ,KAAK,GAAA,CAAI,KAAA,GAAQ,IAAA,CAAK,iBAAA,EAAmB,KAAK,QAAQ,CAAA;AAAA,MAChE;AAAA,IACF;AAAA,EACF;AAEA,EAAA,OAAO;AAAA,IACL,OAAA,EAAS,KAAA;AAAA,IACT,QAAA;AAAA,IACA,KAAA,EAAO,IAAI,KAAA,CAAM,sBAAsB;AAAA,GACzC;AACF;;;ACzDA,IAAM,aAAA,GAAgB;AAAA,EACpB,iBAAA;AAAA,EACA,iBAAA;AAAA,EACA,iBAAA;AAAA,EACA,iBAAA;AAAA,EACA,iBAAA;AAAA,EACA,gBAAA;AAAA,EACA,iBAAA;AAAA,EACA,iBAAA;AAAA,EACA,iBAAA;AAAA,EACA,iBAAA;AAAA,EACA,gBAAA;AAAA,EACA;AACF,CAAA;AAEO,SAAS,eAAA,CACd,SAAA,EACA,UAAA,GAAuB,aAAA,EACd;AACT,EAAA,OAAO,UAAA,CAAW,SAAS,SAAS,CAAA;AACtC;AAEO,SAAS,oBAAoB,IAAA,EAAsC;AACxE,EAAA,IAAI;AACF,IAAA,MAAM,MAAA,GAAS,IAAA;AACf,IAAA,IAAI,MAAA,CAAO,IAAA,EAAM,WAAA,EAAa,OAAO,MAAA;AACrC,IAAA,OAAO,IAAA;AAAA,EACT,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,IAAA;AAAA,EACT;AACF;AAEO,SAAS,gBAAgB,IAAA,EAAkC;AAChE,EAAA,IAAI;AACF,IAAA,MAAM,MAAA,GAAS,IAAA;AACf,IAAA,IAAI,MAAA,CAAO,MAAA,EAAQ,UAAA,KAAe,KAAA,CAAA,EAAW,OAAO,MAAA;AACpD,IAAA,OAAO,IAAA;AAAA,EACT,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,IAAA;AAAA,EACT;AACF;AAEO,SAAS,gBAAgB,IAAA,EAAkC;AAChE,EAAA,IAAI;AACF,IAAA,MAAM,MAAA,GAAS,IAAA;AACf,IAAA,IAAI,MAAA,CAAO,OAAA,IAAW,MAAA,CAAO,WAAA,EAAa,OAAO,MAAA;AACjD,IAAA,OAAO,IAAA;AAAA,EACT,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,IAAA;AAAA,EACT;AACF;;;ACjCO,SAAS,aAAA,CACd,IAAA,EACA,OAAA,GAAiC,EAAC,EACZ;AAEtB,EAAA,IAAI,CAAC,OAAA,CAAQ,WAAA,IAAe,OAAA,CAAQ,SAAA,EAAW;AAC7C,IAAA,IAAI,CAAC,eAAA,CAAgB,OAAA,CAAQ,SAAA,EAAW,OAAA,CAAQ,UAAU,CAAA,EAAG;AAC3D,MAAA,OAAO;AAAA,QACL,OAAA,EAAS,KAAA;AAAA,QACT,SAAA,EAAW,IAAA;AAAA,QACX,IAAA,EAAM,IAAA;AAAA,QACN,KAAA,EAAO;AAAA,OACT;AAAA,IACF;AAAA,EACF;AAGA,EAAA,MAAM,OAAA,GAAU,oBAAoB,IAAI,CAAA;AACxC,EAAA,IAAI,OAAA,EAAS;AACX,IAAA,OAAO;AAAA,MACL,OAAA,EAAS,IAAA;AAAA,MACT,SAAA,EAAW,UAAA;AAAA,MACX,IAAA,EAAM;AAAA,KACR;AAAA,EACF;AAGA,EAAA,MAAM,GAAA,GAAM,gBAAgB,IAAI,CAAA;AAChC,EAAA,IAAI,GAAA,EAAK;AACP,IAAA,OAAO;AAAA,MACL,OAAA,EAAS,IAAA;AAAA,MACT,SAAA,EAAW,KAAA;AAAA,MACX,IAAA,EAAM;AAAA,KACR;AAAA,EACF;AAGA,EAAA,MAAM,GAAA,GAAM,gBAAgB,IAAI,CAAA;AAChC,EAAA,IAAI,GAAA,EAAK;AACP,IAAA,OAAO;AAAA,MACL,OAAA,EAAS,IAAA;AAAA,MACT,SAAA,EAAW,KAAA;AAAA,MACX,IAAA,EAAM;AAAA,KACR;AAAA,EACF;AAEA,EAAA,OAAO;AAAA,IACL,OAAA,EAAS,KAAA;AAAA,IACT,SAAA,EAAW,IAAA;AAAA,IACX,IAAA,EAAM,IAAA;AAAA,IACN,KAAA,EAAO;AAAA,GACT;AACF;AAEO,SAAS,qBACd,OAAA,EACe;AACf,EAAA,IAAI,MAAA,IAAU,OAAA,IAAW,OAAA,CAAQ,IAAA,EAAM,WAAA,EAAa;AAClD,IAAA,MAAM,KAAA,GAAQ,OAAA,CAAQ,IAAA,CAAK,WAAA,CAAY,gBAAA,EAAkB,IAAA;AACzD,IAAA,MAAM,eAAe,KAAA,EAAO,IAAA;AAAA,MAC1B,CAAC,IAAA,KAAS,IAAA,CAAK,IAAA,KAAS;AAAA,KAC1B;AACA,IAAA,OAAO,YAAA,GAAe,MAAA,CAAO,YAAA,CAAa,KAAK,CAAA,GAAI,IAAA;AAAA,EACrD;AACA,EAAA,IAAI,QAAA,IAAY,OAAA,IAAW,OAAA,CAAQ,MAAA,EAAQ,aAAA,EAAe;AACxD,IAAA,OAAO,QAAQ,MAAA,CAAO,aAAA;AAAA,EACxB;AACA,EAAA,IAAI,aAAa,OAAA,EAAS;AACxB,IAAA,OAAO,OAAA,CAAQ,OAAA;AAAA,EACjB;AACA,EAAA,OAAO,IAAA;AACT;AAEO,SAAS,cACd,OAAA,EACe;AACf,EAAA,IAAI,MAAA,IAAU,OAAA,IAAW,OAAA,CAAQ,IAAA,EAAM,WAAA,EAAa;AAClD,IAAA,MAAM,KAAA,GAAQ,OAAA,CAAQ,IAAA,CAAK,WAAA,CAAY,gBAAA,EAAkB,IAAA;AACzD,IAAA,MAAM,SAAS,KAAA,EAAO,IAAA,CAAK,CAAC,IAAA,KAAS,IAAA,CAAK,SAAS,QAAQ,CAAA;AAC3D,IAAA,OAAO,MAAA,GAAS,MAAA,CAAO,MAAA,CAAO,KAAK,CAAA,GAAI,IAAA;AAAA,EACzC;AACA,EAAA,IAAI,QAAA,IAAY,OAAA,IAAW,OAAA,CAAQ,MAAA,EAAQ,gBAAA,EAAkB;AAC3D,IAAA,MAAM,MAAA,GAAS,OAAA,CAAQ,MAAA,CAAO,gBAAA,CAAiB,eAAA;AAC/C,IAAA,MAAM,SAAS,MAAA,EAAQ,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,QAAQ,QAAQ,CAAA;AACrD,IAAA,OAAO,MAAA,GAAS,MAAA,CAAO,MAAA,CAAO,KAAK,CAAA,GAAI,IAAA;AAAA,EACzC;AACA,EAAA,IAAI,iBAAiB,OAAA,EAAS;AAC5B,IAAA,OAAO,MAAA,CAAO,UAAA,CAAW,OAAA,CAAQ,WAAW,CAAA;AAAA,EAC9C;AACA,EAAA,OAAO,IAAA;AACT","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 - 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"]}
package/dist/index.d.cts CHANGED
@@ -218,7 +218,16 @@ interface StkQueryResponse {
218
218
  ResponseDescription: string;
219
219
  MerchantRequestID: string;
220
220
  CheckoutRequestID: string;
221
- ResultCode: string;
221
+ /**
222
+ * Daraja returns ResultCode as a NUMBER, not a string.
223
+ * 0 = success
224
+ * 1032 = cancelled by user
225
+ * 1037 = timeout (customer did not respond)
226
+ * 2001 = wrong PIN
227
+ * etc.
228
+ * Always compare with === 0, not === "0".
229
+ */
230
+ ResultCode: number;
222
231
  ResultDesc: string;
223
232
  }
224
233
 
package/dist/index.d.ts CHANGED
@@ -218,7 +218,16 @@ interface StkQueryResponse {
218
218
  ResponseDescription: string;
219
219
  MerchantRequestID: string;
220
220
  CheckoutRequestID: string;
221
- ResultCode: string;
221
+ /**
222
+ * Daraja returns ResultCode as a NUMBER, not a string.
223
+ * 0 = success
224
+ * 1032 = cancelled by user
225
+ * 1037 = timeout (customer did not respond)
226
+ * 2001 = wrong PIN
227
+ * etc.
228
+ * Always compare with === 0, not === "0".
229
+ */
230
+ ResultCode: number;
222
231
  ResultDesc: string;
223
232
  }
224
233
 
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
  });
@@ -204,13 +205,26 @@ async function processB2B(baseUrl, accessToken, securityCredential, initiatorNam
204
205
  // src/mpesa/stk-push/utils.ts
205
206
  function formatPhoneNumber(phone) {
206
207
  const cleaned = phone.replace(/\D/g, "");
207
- if (cleaned.startsWith("0")) return "254" + cleaned.slice(1);
208
- if (cleaned.startsWith("254")) return cleaned;
209
- return "254" + cleaned;
208
+ let formatted;
209
+ if (cleaned.startsWith("254")) {
210
+ formatted = cleaned;
211
+ } else if (cleaned.startsWith("0")) {
212
+ formatted = "254" + cleaned.slice(1);
213
+ } else if (cleaned.length === 9) {
214
+ formatted = "254" + cleaned;
215
+ } else {
216
+ formatted = "254" + cleaned;
217
+ }
218
+ if (!/^254[71]\d{8}$/.test(formatted)) {
219
+ throw new Error(
220
+ `Invalid Kenyan phone number: "${phone}". Expected format: 07XXXXXXXX, 2547XXXXXXXX, or +2547XXXXXXXX.`
221
+ );
222
+ }
223
+ return formatted;
210
224
  }
211
225
  function getStkPushPassword(shortCode, passKey, timestamp) {
212
226
  const raw = `${shortCode}${passKey}${timestamp}`;
213
- return Buffer.from(raw, "utf-8").toString("base64");
227
+ return btoa(raw);
214
228
  }
215
229
  function getTimestamp() {
216
230
  const now = /* @__PURE__ */ new Date();
@@ -339,6 +353,12 @@ async function processReversal(baseUrl, accessToken, securityCredential, initiat
339
353
 
340
354
  // src/mpesa/stk-push/stk-push.ts
341
355
  async function processStkPush(baseUrl, accessToken, request) {
356
+ const amount = Math.round(request.amount);
357
+ if (amount < 1) {
358
+ throw new Error(
359
+ `Amount must be at least KES 1 (got ${request.amount} which rounds to ${amount}).`
360
+ );
361
+ }
342
362
  const timestamp = getTimestamp();
343
363
  const partyB = request.partyB ?? request.shortCode;
344
364
  const body = {
@@ -346,7 +366,7 @@ async function processStkPush(baseUrl, accessToken, request) {
346
366
  Password: getStkPushPassword(request.shortCode, request.passKey, timestamp),
347
367
  Timestamp: timestamp,
348
368
  TransactionType: request.transactionType ?? "CustomerPayBillOnline",
349
- Amount: Math.round(request.amount),
369
+ Amount: amount,
350
370
  PartyA: formatPhoneNumber(request.phoneNumber),
351
371
  PartyB: partyB,
352
372
  PhoneNumber: formatPhoneNumber(request.phoneNumber),
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 */\nexport function formatPhoneNumber(phone: string): string {\n const cleaned = phone.replace(/\\D/g, \"\");\n if (cleaned.startsWith(\"0\")) return \"254\" + cleaned.slice(1);\n if (cleaned.startsWith(\"254\")) return cleaned;\n return \"254\" + cleaned;\n}\n\n/**\n * Generates the STK Push password.\n * Formula: Base64(Shortcode + Passkey + Timestamp)\n *\n * IMPORTANT: The timestamp passed here MUST be the exact same value sent in\n * the request body's `Timestamp` field. Safaricom validates they match.\n * Always call getTimestamp() once and pass the result to both this function\n * and the request body — never call getTimestamp() twice.\n */\nexport function getStkPushPassword(\n shortCode: string,\n passKey: string,\n timestamp: string\n): string {\n const raw = `${shortCode}${passKey}${timestamp}`;\n return Buffer.from(raw, \"utf-8\").toString(\"base64\");\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 // 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: Math.round(request.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/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 - 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"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pesafy",
3
- "version": "0.2.2",
3
+ "version": "0.2.4",
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",