pesafy 0.0.2 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.mjs","names":["IDENTIFIER_TYPE","IDENTIFIER_TYPE","formatPhoneNumber","_generateDynamicQR","_registerC2BUrls","_simulateC2B","_remitTax","_initiateB2BExpressCheckout","_initiateB2BBuyGoods","_initiateB2BPayBill","_initiateB2CPayment","_initiateB2CDisbursement","_billManagerOptIn","_updateOptIn","_sendSingleInvoice","_sendBulkInvoices","_cancelInvoice","_cancelBulkInvoices","_reconcilePayment"],"sources":["../src/cli/lib/output.ts","../src/cli/lib/env.ts","../src/cli/lib/context.ts","../src/cli/lib/daraja.ts","../src/core/encryption/security-credentials.ts","../src/core/encryption/index.ts","../src/utils/http/index.ts","../src/core/auth/types.ts","../src/core/auth/token-manager.ts","../src/core/idempotency/generate-key.ts","../src/core/idempotency/store.ts","../src/core/idempotency/manager.ts","../src/types/branded.ts","../src/core/validation/zod-error.ts","../src/schemas/common.ts","../src/schemas/async-apis.ts","../src/mpesa/account-balance/query.ts","../src/schemas/b2b.ts","../src/mpesa/b2b-express-checkout/initiate.ts","../src/mpesa/b2b-buy-goods/payment.ts","../src/mpesa/b2b-pay-bill/payment.ts","../src/schemas/b2c.ts","../src/mpesa/b2c/payment.ts","../src/mpesa/b2c-disbursement/payment.ts","../src/schemas/bill-manager.ts","../src/mpesa/bill-manager/invoice.ts","../src/schemas/c2b.ts","../src/mpesa/c2b/register-url.ts","../src/mpesa/c2b/simulate.ts","../src/mpesa/dynamic-qr/generate.ts","../src/mpesa/reversal/types.ts","../src/mpesa/reversal/request.ts","../src/schemas/stk-push.ts","../src/mpesa/stk-push/types.ts","../src/mpesa/stk-push/utils.ts","../src/mpesa/stk-push/stk-push.ts","../src/mpesa/stk-push/stk-query.ts","../src/mpesa/tax-remittance/remit-tax.ts","../src/mpesa/transaction-status/query.ts","../src/mpesa/types.ts","../src/mpesa/index.ts","../src/cli/lib/mpesa-from-env.ts","../src/cli/commands/version.ts","../src/cli/commands/help.ts","../src/cli/commands/init.ts","../src/cli/commands/doctor.ts","../src/cli/commands/token.ts","../src/cli/commands/encrypt.ts","../src/cli/commands/validate-phone.ts","../src/cli/commands/stk-push.ts","../src/cli/commands/stk-query.ts","../src/cli/commands/balance.ts","../src/cli/commands/register-c2b-urls.ts","../src/cli/commands/simulate-c2b.ts","../src/cli/commands/reversal.ts","../src/cli/commands/mpesa-api.ts","../src/cli/index.ts"],"sourcesContent":["export const C = {\n reset: '\\x1b[0m',\n bold: '\\x1b[1m',\n dim: '\\x1b[2m',\n green: '\\x1b[32m',\n yellow: '\\x1b[33m',\n red: '\\x1b[31m',\n cyan: '\\x1b[36m',\n blue: '\\x1b[34m',\n magenta: '\\x1b[35m',\n white: '\\x1b[37m',\n}\n\nexport const g = (s: string) => `${C.green}${s}${C.reset}`\nexport const y = (s: string) => `${C.yellow}${s}${C.reset}`\nexport const r = (s: string) => `${C.red}${s}${C.reset}`\nexport const b = (s: string) => `${C.bold}${s}${C.reset}`\nexport const c = (s: string) => `${C.cyan}${s}${C.reset}`\nexport const dim = (s: string) => `${C.dim}${s}${C.reset}`\n\nexport const EXIT_OK = 0\nexport const EXIT_ERROR = 1\nexport const EXIT_CONFIG = 2\n\nexport function printJson(data: unknown): void {\n console.log(JSON.stringify(data, null, 2))\n}\n","import { existsSync, readFileSync } from 'node:fs'\nimport { resolve } from 'node:path'\nimport { EXIT_CONFIG } from './output'\n\nexport function loadEnv(envFile?: string): Record<string, string> {\n const envPath = resolve(process.cwd(), envFile ?? '.env')\n if (!existsSync(envPath)) return {}\n const lines = readFileSync(envPath, 'utf-8').split('\\n')\n const env: Record<string, string> = {}\n for (const line of lines) {\n const trimmed = line.trim()\n if (!trimmed || trimmed.startsWith('#')) continue\n const eq = trimmed.indexOf('=')\n if (eq === -1) continue\n const key = trimmed.slice(0, eq).trim()\n const val = trimmed\n .slice(eq + 1)\n .trim()\n .replace(/^[\"']|[\"']$/g, '')\n env[key] = val\n }\n return env\n}\n\nexport function applyEnvOverride(\n env: Record<string, string>,\n override?: 'sandbox' | 'production',\n): Record<string, string> {\n if (!override) return env\n return { ...env, MPESA_ENVIRONMENT: override }\n}\n\nexport function requireEnv(env: Record<string, string>, ...keys: string[]): void {\n const missing = keys.filter((k) => !env[k])\n if (missing.length) {\n console.error(`✖ Missing env vars: ${missing.join(', ')}`)\n console.error(' Run: npx pesafy init')\n process.exit(EXIT_CONFIG)\n }\n}\n\nexport function getBaseUrl(env: Record<string, string>): string {\n return env['MPESA_ENVIRONMENT'] === 'production'\n ? 'https://api.safaricom.co.ke'\n : 'https://sandbox.safaricom.co.ke'\n}\n","import { createInterface } from 'node:readline'\nimport { readFileSync } from 'node:fs'\nimport { resolve } from 'node:path'\nimport { loadEnv, applyEnvOverride } from './env'\nimport { dim } from './output'\n\nexport interface CliContext {\n args: string[]\n json: boolean\n env: Record<string, string>\n}\n\nexport function createCliContext(argv: string[]): CliContext {\n const args: string[] = []\n let json = false\n let envFile: string | undefined\n let envOverride: 'sandbox' | 'production' | undefined\n\n for (let i = 0; i < argv.length; i++) {\n const a = argv[i]\n if (a === '--json') json = true\n else if (a === '--env-file' && argv[i + 1]) envFile = argv[++i]\n else if (a === '--env' && argv[i + 1]) {\n const v = argv[++i]\n if (v === 'sandbox' || v === 'production') envOverride = v\n } else args.push(a ?? '')\n }\n\n const env = applyEnvOverride(loadEnv(envFile), envOverride)\n return { args, json, env }\n}\n\nexport function getPkgVersion(): string {\n try {\n const pkgPath = resolve(new URL('../../package.json', import.meta.url).pathname)\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8')) as { version: string }\n return pkg.version\n } catch {\n return 'unknown'\n }\n}\n\nexport function prompt(question: string, defaultVal = ''): Promise<string> {\n const rl = createInterface({ input: process.stdin, output: process.stdout })\n const displayDefault = defaultVal ? dim(` [${defaultVal}]`) : ''\n return new Promise((resolve) => {\n rl.question(`${question}${displayDefault}: `, (answer) => {\n rl.close()\n resolve(answer.trim() || defaultVal)\n })\n })\n}\n","export async function fetchJson<T>(\n url: string,\n method: 'GET' | 'POST',\n headers: Record<string, string>,\n body?: unknown,\n): Promise<T> {\n const res = await fetch(url, {\n method,\n headers: {\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n ...headers,\n },\n ...(body ? { body: JSON.stringify(body) } : {}),\n })\n const text = await res.text()\n try {\n return JSON.parse(text) as T\n } catch {\n throw new Error(`Non-JSON response (${res.status}): ${text.slice(0, 200)}`)\n }\n}\n\nexport async function getToken(\n consumerKey: string,\n consumerSecret: string,\n baseUrl: string,\n): Promise<string> {\n const creds = Buffer.from(`${consumerKey}:${consumerSecret}`).toString('base64')\n const data = await fetchJson<{ access_token: string }>(\n `${baseUrl}/oauth/v1/generate?grant_type=client_credentials`,\n 'GET',\n { Authorization: `Basic ${creds}` },\n )\n if (!data.access_token) throw new Error('No access_token in response')\n return data.access_token\n}\n","// src/core/encryption/security-credentials.ts\n\nimport { constants, publicEncrypt } from 'node:crypto'\nimport { PesafyError } from '../../utils/errors'\n\nexport function encryptSecurityCredential(\n initiatorPassword: string,\n certificatePem: string,\n): string {\n try {\n const passwordBuffer = Buffer.from(initiatorPassword, 'utf-8')\n\n const encrypted = publicEncrypt(\n {\n key: certificatePem,\n // RSA_PKCS1_PADDING = 1 (NOT RSA_PKCS1_OAEP_PADDING = 4)\n padding: constants.RSA_PKCS1_PADDING,\n },\n passwordBuffer,\n )\n\n return encrypted.toString('base64')\n } catch (error) {\n throw new PesafyError({\n code: 'ENCRYPTION_FAILED',\n message:\n 'Failed to encrypt security credential. ' +\n 'Ensure the certificate PEM is valid and matches the environment (sandbox/production).',\n cause: error,\n })\n }\n}\n","// src/core/encryption/index.ts\nexport { encryptSecurityCredential } from './security-credentials'\nexport type { CertificateSource } from './types'\n","// 📁 PATH: src/utils/http/index.ts\n\n/**\n * HTTP client for Daraja API calls.\n *\n * Single, canonical implementation — retries transient errors with\n * exponential back-off + ±25 % jitter. Never retries 4xx errors.\n */\n\nimport type { IdempotencyManager } from '../../core/idempotency'\nimport { PesafyError } from '../errors'\n\nexport type { DarajaHttpOptions } from './types'\nimport type { DarajaHttpOptions } from './types'\n\n/** Merge explicit Daraja HTTP options into an httpRequest options object. */\nexport function withDarajaHttp(\n options: HttpRequestOptions,\n http?: DarajaHttpOptions,\n): HttpRequestOptions {\n if (!http?.idempotency) return options\n return { ...options, idempotency: http.idempotency }\n}\n\n// ── Types ─────────────────────────────────────────────────────────────────────\n\nexport interface HttpRequestOptions {\n method: 'GET' | 'POST'\n headers?: Record<string, string>\n /**\n * Request body — JSON-serialised and sent as application/json.\n * Omit or pass `undefined` to send no body.\n */\n body?: unknown\n /**\n * Retry attempts on transient errors (503, 429, 502, 504, network).\n * Default: 4. Set 0 to disable.\n */\n retries?: number\n /**\n * Base delay in ms before first retry. Doubles each attempt + ±25 % jitter.\n * Default: 2000 ms.\n */\n retryDelay?: number\n /**\n * Per-attempt timeout in ms. Does NOT cover total retry duration.\n * Default: 30 000 ms.\n */\n timeout?: number\n /**\n * Optional idempotency key — passed as Idempotency-Key header.\n * Daraja ignores it but useful for your own gateway layer.\n */\n idempotencyKey?: string\n /**\n * Idempotency manager — auto-injects keys and detects duplicates on POST.\n * Pass from `Mpesa` via `DarajaHttpOptions`; do not use module-global state.\n */\n idempotency?: IdempotencyManager\n}\n\nexport interface HttpResponse<T> {\n data: T\n status: number\n headers: Record<string, string>\n}\n\n// ── Helpers ───────────────────────────────────────────────────────────────────\n\nconst RETRYABLE_STATUSES = new Set([429, 500, 502, 503, 504])\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((r) => setTimeout(r, ms))\n}\n\nfunction withJitter(base: number): number {\n const spread = base * 0.25\n return base + (Math.random() * spread * 2 - spread)\n}\n\n/** Origin + path only (no query) for safe retry logs. */\nfunction logSafeUrl(url: string): string {\n try {\n const u = new URL(url)\n return `${u.origin}${u.pathname}`\n } catch {\n const noQuery = url.split('?')[0]\n return noQuery ?? url\n }\n}\n\n// ── httpRequest ───────────────────────────────────────────────────────────────\n\n/**\n * Sends an HTTP request to Daraja and returns parsed JSON.\n * Automatically retries transient failures with exponential back-off.\n *\n * @throws {PesafyError} on non-retryable errors or exhausted retries.\n */\nexport async function httpRequest<T = unknown>(\n url: string,\n options: HttpRequestOptions,\n): Promise<HttpResponse<T>> {\n const maxRetries = options.retries ?? 4\n const baseDelay = options.retryDelay ?? 2_000\n const timeout = options.timeout ?? 30_000\n\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n ...options.headers,\n }\n\n const manager = options.idempotency\n let idempotencyKey = options.idempotencyKey\n\n if (options.method === 'POST' && manager?.enabled) {\n idempotencyKey = manager.reserve(idempotencyKey)\n const headerName = manager.headerName\n headers[headerName] = idempotencyKey\n } else if (idempotencyKey) {\n headers['Idempotency-Key'] = idempotencyKey\n }\n\n const init: RequestInit = {\n method: options.method,\n headers,\n ...(options.body !== undefined ? { body: JSON.stringify(options.body) } : {}),\n }\n\n let lastError: PesafyError | null = null\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n if (attempt > 0) {\n const delay = withJitter(baseDelay * Math.pow(2, attempt - 1))\n console.warn(\n `[pesafy] Retry ${attempt}/${maxRetries} → ${options.method} ${logSafeUrl(url)} in ${Math.round(delay)} ms`,\n )\n await sleep(delay)\n }\n\n const controller = new AbortController()\n const tid = setTimeout(() => controller.abort(), timeout)\n let response: Response\n\n try {\n response = await fetch(url, { ...init, signal: controller.signal })\n } catch (err) {\n clearTimeout(tid)\n if (err instanceof Error && err.name === 'AbortError') {\n lastError = new PesafyError({\n code: 'TIMEOUT',\n message: `Request to ${url} timed out after ${timeout} ms`,\n cause: err,\n retryable: true,\n })\n } else {\n lastError = new PesafyError({\n code: 'NETWORK_ERROR',\n message: `Network error: ${err instanceof Error ? err.message : String(err)}`,\n cause: err,\n retryable: true,\n })\n }\n if (attempt < maxRetries) continue\n if (idempotencyKey && manager?.enabled) manager.release(idempotencyKey)\n throw lastError\n } finally {\n clearTimeout(tid)\n }\n\n // ── Parse body ───────────────────────────────────────────────────────────\n let rawText = ''\n let data: unknown = null\n const ct = response.headers.get('content-type') ?? ''\n\n try {\n rawText = await response.text()\n if (rawText) {\n data = ct.includes('application/json') ? JSON.parse(rawText) : rawText\n }\n } catch {\n data = rawText || null\n }\n\n const responseHeaders: Record<string, string> = {}\n response.headers.forEach((v, k) => {\n responseHeaders[k] = v\n })\n\n if (response.ok) {\n if (idempotencyKey && manager?.enabled) {\n manager.complete(idempotencyKey)\n }\n return { data: data as T, status: response.status, headers: responseHeaders }\n }\n\n // ── Error path ───────────────────────────────────────────────────────────\n const isTransient = RETRYABLE_STATUSES.has(response.status)\n const daraja =\n typeof data === 'object' && data !== null ? (data as Record<string, unknown>) : {}\n\n const message =\n (daraja['errorMessage'] as string | undefined) ??\n (daraja['ResponseDescription'] as string | undefined) ??\n (daraja['resultDesc'] as string | undefined) ??\n rawText ??\n `HTTP ${response.status}`\n\n // exactOptionalPropertyTypes: only include requestId when it is actually\n // a string — never pass `undefined` for an optional property at a call-site.\n lastError = new PesafyError({\n code: isTransient ? 'REQUEST_FAILED' : 'API_ERROR',\n message,\n statusCode: response.status,\n response: data,\n retryable: isTransient,\n ...(typeof daraja['requestId'] === 'string' ? { requestId: daraja['requestId'] } : {}),\n })\n\n if (isTransient && attempt < maxRetries) continue\n if (idempotencyKey && manager?.enabled) {\n manager.release(idempotencyKey)\n }\n throw lastError\n }\n\n if (idempotencyKey && manager?.enabled) {\n manager.release(idempotencyKey)\n }\n throw lastError!\n}\n","// src/core/auth/types.ts\n\nexport interface TokenResponse {\n /** The OAuth bearer token */\n access_token: string\n /** Seconds until expiry — Daraja returns 3599 */\n expires_in: number\n}\n\nexport interface TokenCacheEntry {\n token: string\n /** Unix timestamp (seconds) after which token is considered expired */\n expiresAt: number\n}\n\n/**\n * Daraja Authorization API error codes\n * Documented at: https://developer.safaricom.co.ke/APIs/Authorization\n *\n * These are returned in the `errorCode` field of a 400 error response body\n * when the OAuth token request is malformed.\n */\nexport const AUTH_ERROR_CODES = {\n /**\n * Invalid authentication type passed.\n * Mitigation: Use Basic authentication (Authorization: Basic <base64>).\n */\n INVALID_AUTH_TYPE: '400.008.01',\n /**\n * Invalid grant type passed.\n * Mitigation: Set grant_type=client_credentials in the request params.\n */\n INVALID_GRANT_TYPE: '400.008.02',\n} as const\n\nexport type AuthErrorCode = (typeof AUTH_ERROR_CODES)[keyof typeof AUTH_ERROR_CODES]\n\n/** Error response body returned by the Daraja Authorization API on failure */\nexport interface AuthErrorResponse {\n /** Daraja-specific error code, e.g. \"400.008.01\" */\n errorCode: string\n /** Human-readable error description */\n errorMessage: string\n}\n","// src/core/auth/token-manager.ts\n\nimport { PesafyError } from '../../utils/errors'\nimport { httpRequest } from '../../utils/http'\nimport { AUTH_ERROR_CODES } from './types'\nimport type { TokenResponse } from './types'\n\n/** Refresh the token this many seconds before it actually expires */\nconst TOKEN_BUFFER_SECONDS = 60\n\nexport class TokenManager {\n private readonly consumerKey: string\n private readonly consumerSecret: string\n private readonly baseUrl: string\n\n private cachedToken: string | null = null\n private tokenExpiresAt = 0 // Unix seconds\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 getBasicAuthHeader(): string {\n // Daraja spec: Base64(consumerKey:consumerSecret)\n const credentials = `${this.consumerKey}:${this.consumerSecret}`\n const encoded = Buffer.from(credentials, 'utf-8').toString('base64')\n return `Basic ${encoded}`\n }\n\n /**\n * Maps Daraja-specific auth error codes (400.008.01 / 400.008.02) to\n * descriptive PesafyError messages so callers get actionable feedback.\n *\n * Always throws — the `never` return type signals this to TypeScript.\n */\n private mapAuthError(error: unknown): never {\n if (error instanceof PesafyError) {\n // If we already enriched this error, re-throw it as-is.\n if (error.code === 'AUTH_FAILED') throw error\n\n const raw = error.response as Record<string, unknown> | null | undefined\n if (raw && typeof raw === 'object') {\n // Daraja sends the code in `errorCode`; guard against both spellings.\n const errorCode = (raw['errorCode'] ?? raw['error_code']) as string | undefined\n\n if (errorCode === AUTH_ERROR_CODES.INVALID_AUTH_TYPE) {\n throw new PesafyError({\n code: 'AUTH_FAILED',\n message:\n 'Invalid authentication type (400.008.01). ' +\n 'Use Basic authentication: Authorization: Basic <Base64(consumerKey:consumerSecret)>.',\n ...(error.statusCode !== undefined && { statusCode: error.statusCode }),\n response: error.response,\n })\n }\n\n if (errorCode === AUTH_ERROR_CODES.INVALID_GRANT_TYPE) {\n throw new PesafyError({\n code: 'AUTH_FAILED',\n message:\n 'Invalid grant type (400.008.02). ' +\n 'Set grant_type=client_credentials in the request query parameters.',\n ...(error.statusCode !== undefined && { statusCode: error.statusCode }),\n response: error.response,\n })\n }\n }\n\n throw error\n }\n\n // Non-PesafyError (e.g. raw network exception) — re-throw unchanged.\n throw error\n }\n\n /**\n * Returns a valid access token, fetching a new one when the cached token\n * is absent or within TOKEN_BUFFER_SECONDS of expiry.\n *\n * Daraja endpoint: GET /oauth/v1/generate?grant_type=client_credentials\n * Auth: Basic Base64(consumerKey:consumerSecret)\n * Token lifetime: 3599 seconds (Daraja docs)\n */\n async getAccessToken(): Promise<string> {\n const now = Date.now() / 1000\n\n if (this.cachedToken && this.tokenExpiresAt > now + TOKEN_BUFFER_SECONDS) {\n return this.cachedToken\n }\n\n // Daraja Authorization API — GET with Basic Auth + grant_type query param\n const url = `${this.baseUrl}/oauth/v1/generate?grant_type=client_credentials`\n\n try {\n const response = await httpRequest<TokenResponse>(url, {\n method: 'GET',\n headers: {\n Authorization: this.getBasicAuthHeader(),\n },\n })\n\n const { access_token, expires_in } = response.data\n\n if (!access_token) {\n throw new PesafyError({\n code: 'AUTH_FAILED',\n message:\n 'Daraja did not return an access token. ' +\n 'Verify your consumer key and consumer secret.',\n response: response.data,\n })\n }\n\n this.cachedToken = access_token\n // expires_in is 3599 per Daraja docs; fall back to 3600 if absent.\n this.tokenExpiresAt = now + (expires_in ?? 3600)\n\n return this.cachedToken\n } catch (error) {\n // mapAuthError always throws — satisfies TypeScript's control-flow analysis.\n return this.mapAuthError(error)\n }\n }\n\n /** Force token refresh on the next call (e.g. after a 401 response) */\n clearCache(): void {\n this.cachedToken = null\n this.tokenExpiresAt = 0\n }\n}\n","/**\n * Generate idempotency keys for Daraja mutating requests.\n */\n\n/** UUID v4 idempotency key, optionally prefixed for debugging. */\nexport function generateIdempotencyKey(prefix?: string): string {\n const id = crypto.randomUUID()\n return prefix ? `${prefix}-${id}` : id\n}\n\n/** Daraja OriginatorConversationID — unique per async API request. */\nexport function generateOriginatorConversationId(): string {\n return generateIdempotencyKey('pesafy')\n}\n\n/** B2B Express RequestRefID — unique per checkout request. */\nexport function generateRequestRefId(): string {\n return crypto.randomUUID()\n}\n","/**\n * In-memory idempotency store (per-process).\n * For multi-instance deployments, supply a custom IdempotencyStore (e.g. Redis).\n */\n\nexport interface IdempotencyEntry {\n key: string\n createdAt: number\n completedAt?: number\n}\n\nexport interface IdempotencyStore {\n get(key: string): IdempotencyEntry | undefined\n set(key: string, entry: IdempotencyEntry): void\n delete(key: string): void\n}\n\nexport class InMemoryIdempotencyStore implements IdempotencyStore {\n private readonly entries = new Map<string, IdempotencyEntry>()\n\n get(key: string): IdempotencyEntry | undefined {\n return this.entries.get(key)\n }\n\n set(key: string, entry: IdempotencyEntry): void {\n this.entries.set(key, entry)\n }\n\n delete(key: string): void {\n this.entries.delete(key)\n }\n\n /** Remove entries older than ttlMs. */\n prune(ttlMs: number): void {\n const cutoff = Date.now() - ttlMs\n for (const [key, entry] of this.entries) {\n if (entry.createdAt < cutoff) this.entries.delete(key)\n }\n }\n}\n","import { PesafyError } from '../../utils/errors'\nimport { generateIdempotencyKey } from './generate-key'\nimport { InMemoryIdempotencyStore, type IdempotencyStore } from './store'\n\nexport interface IdempotencyConfig {\n /** Default true for mutating POSTs */\n enabled?: boolean\n /** HTTP header name. Default: Idempotency-Key */\n headerName?: string\n /** Pluggable store; default in-memory */\n store?: IdempotencyStore\n /** Dedup window in ms. Default: 24h */\n ttlMs?: number\n /** Custom key generator */\n generateKey?: () => string\n}\n\nconst DEFAULT_TTL_MS = 24 * 60 * 60 * 1000\n\nexport class IdempotencyManager {\n readonly enabled: boolean\n readonly headerName: string\n readonly ttlMs: number\n private readonly store: IdempotencyStore\n private readonly generateKey: () => string\n\n constructor(config: IdempotencyConfig = {}) {\n this.enabled = config.enabled !== false\n this.headerName = config.headerName ?? 'Idempotency-Key'\n this.ttlMs = config.ttlMs ?? DEFAULT_TTL_MS\n this.store = config.store ?? new InMemoryIdempotencyStore()\n this.generateKey = config.generateKey ?? generateIdempotencyKey\n }\n\n /**\n * Reserve an idempotency key before the HTTP call.\n * @throws PesafyError with IDEMPOTENCY_ERROR if duplicate in-flight/completed within TTL.\n */\n reserve(key?: string): string {\n if (!this.enabled) return key ?? this.generateKey()\n\n this.pruneExpired()\n\n const resolved = key ?? this.generateKey()\n const existing = this.store.get(resolved)\n\n if (existing) {\n const age = Date.now() - existing.createdAt\n if (age < this.ttlMs) {\n throw new PesafyError({\n code: 'IDEMPOTENCY_ERROR',\n message: `Duplicate request detected for idempotency key \"${resolved}\".`,\n })\n }\n this.store.delete(resolved)\n }\n\n this.store.set(resolved, { key: resolved, createdAt: Date.now() })\n return resolved\n }\n\n /** Mark key as successfully completed. */\n complete(key: string): void {\n if (!this.enabled) return\n const entry = this.store.get(key)\n if (entry) {\n this.store.set(key, { ...entry, completedAt: Date.now() })\n }\n }\n\n /** Release reservation on failure so callers can retry with same key. */\n release(key: string): void {\n if (!this.enabled) return\n this.store.delete(key)\n }\n\n private pruneExpired(): void {\n if (this.store instanceof InMemoryIdempotencyStore) {\n this.store.prune(this.ttlMs)\n }\n }\n}\n","// 📁 PATH: src/types/branded.ts\n\ndeclare const __brand: unique symbol\ntype Brand<T, B extends string> = T & { readonly [__brand]: B }\n\n// ── Money ─────────────────────────────────────────────────────────────────────\n\n/**\n * A whole-number KES amount (Kenyan Shillings).\n * M-PESA only supports whole numbers — fractional shillings are rejected.\n */\nexport type KesAmount = Brand<number, 'KesAmount'>\n\n/**\n * Creates a validated KesAmount.\n * @throws {TypeError} if amount is not a whole number ≥ 1\n */\nexport function toKesAmount(value: number): KesAmount {\n const rounded = Math.round(value)\n if (!Number.isFinite(rounded) || rounded < 1) {\n throw new TypeError(`KesAmount must be a whole number ≥ 1, got ${value}`)\n }\n return rounded as KesAmount\n}\n\n// ── Phone numbers ─────────────────────────────────────────────────────────────\n\n/**\n * A validated Kenyan MSISDN in Daraja format: 254XXXXXXXXX (12 digits).\n */\nexport type MsisdnKE = Brand<string, 'MsisdnKE'>\n\n/**\n * Creates a validated MsisdnKE from any common Kenyan phone format.\n */\nexport function toMsisdn(phone: string): MsisdnKE {\n const digits = phone.replace(/\\D/g, '')\n let normalised: string\n\n if (digits.startsWith('254') && digits.length === 12) {\n normalised = digits\n } else if (digits.startsWith('0') && digits.length === 10) {\n normalised = `254${digits.slice(1)}`\n } else if (digits.length === 9) {\n normalised = `254${digits}`\n } else {\n throw new TypeError(\n `Cannot normalise \"${phone}\" to 254XXXXXXXXX. Use 07XX…, 2547XX…, or +2547XX….`,\n )\n }\n\n if (normalised.length !== 12) {\n throw new TypeError(`Phone \"${phone}\" normalised to \"${normalised}\" — expected 12 digits.`)\n }\n return normalised as MsisdnKE\n}\n\n// ── Shortcodes ────────────────────────────────────────────────────────────────\n\n/** M-PESA Paybill shortcode */\nexport type PaybillCode = Brand<string, 'PaybillCode'>\n\n/** M-PESA Till/Buy-Goods shortcode */\nexport type TillCode = Brand<string, 'TillCode'>\n\n/** Any M-PESA shortcode (paybill, till, or B2C) */\nexport type ShortCode = PaybillCode | TillCode | Brand<string, 'ShortCode'>\n\nexport function toPaybill(code: string | number): PaybillCode {\n return String(code) as PaybillCode\n}\nexport function toTill(code: string | number): TillCode {\n return String(code) as TillCode\n}\nexport function toShortCode(code: string | number): ShortCode {\n return String(code) as ShortCode\n}\n\n// ── Transaction IDs ───────────────────────────────────────────────────────────\n\n/** M-PESA receipt number, e.g. \"OEI2AK4XXXX\" */\nexport type MpesaReceiptNumber = Brand<string, 'MpesaReceiptNumber'>\n\n/** Daraja ConversationID */\nexport type ConversationID = Brand<string, 'ConversationID'>\n\n/** Daraja OriginatorConversationID */\nexport type OriginatorConversationID = Brand<string, 'OriginatorConversationID'>\n\n/** Daraja CheckoutRequestID (STK Push) */\nexport type CheckoutRequestID = Brand<string, 'CheckoutRequestID'>\n\n// ── Result type ───────────────────────────────────────────────────────────────\n\n/**\n * A discriminated union result — either Ok<T> or Err<E>.\n * Prefer this over throwing in application-level code.\n *\n * @example\n * const result = await mpesa.stkPushSafe({ ... });\n * if (result.ok) {\n * console.log(result.data.CheckoutRequestID);\n * } else {\n * console.error(result.error.code, result.error.message);\n * }\n */\nexport type Result<T, E = import('../utils/errors').PesafyError> =\n | { readonly ok: true; readonly data: T }\n | { readonly ok: false; readonly error: E }\n\nexport function ok<T>(data: T): Result<T, never> {\n return { ok: true, data }\n}\n\nexport function err<E>(error: E): Result<never, E> {\n return { ok: false, error }\n}\n\n// ── Utility types ─────────────────────────────────────────────────────────────\n\n/** Makes all properties deeply readonly */\nexport type DeepReadonly<T> = {\n readonly [K in keyof T]: T[K] extends object ? DeepReadonly<T[K]> : T[K]\n}\n\n/** Strict pick — only allows known keys */\nexport type StrictPick<T, K extends keyof T> = Pick<T, K>\n\n/** Non-empty string */\nexport type NonEmptyString = Brand<string, 'NonEmptyString'>\nexport function toNonEmpty(s: string): NonEmptyString {\n if (!s.trim()) throw new TypeError('String must not be empty')\n return s as NonEmptyString\n}\n","import type { ZodError, ZodSchema } from 'zod'\nimport { PesafyError } from '../../utils/errors'\n\n/** Map Zod validation failures to PesafyError. */\nexport function zodToPesafyError(error: ZodError, label = 'Request'): PesafyError {\n const issues = error.issues.map((i) => `${i.path.join('.')}: ${i.message}`).join('; ')\n return new PesafyError({\n code: 'VALIDATION_ERROR',\n message: `${label} validation failed: ${issues}`,\n cause: error,\n })\n}\n\n/** Parse with Zod; throw PesafyError on failure. */\nexport function parseWithSchema<T>(schema: ZodSchema<T>, data: unknown, label?: string): T {\n const result = schema.safeParse(data)\n if (!result.success) throw zodToPesafyError(result.error, label)\n return result.data\n}\n\n/** Safe parse — returns null on failure. */\nexport function safeParseWithSchema<T>(\n schema: ZodSchema<T>,\n data: unknown,\n): { success: true; data: T } | { success: false; error: PesafyError } {\n const result = schema.safeParse(data)\n if (result.success) return { success: true, data: result.data }\n return { success: false, error: zodToPesafyError(result.error) }\n}\n","import { z } from 'zod'\n\nexport const EnvironmentSchema = z.enum(['sandbox', 'production'])\n\nexport const MsisdnSchema = z\n .string()\n .min(10)\n .regex(/^254\\d{9}$/, 'Must be Safaricom format 2547XXXXXXXX')\n\nexport const KesAmountSchema = z\n .number()\n .finite()\n .positive()\n .refine((n) => Math.round(n) >= 1, {\n message: 'amount must round to at least 1 KES',\n })\n\nexport const NonEmptyStringSchema = z.string().trim().min(1)\n\nexport const UrlSchema = z.string().url()\n\nexport const DarajaErrorResponseSchema = z\n .object({\n errorMessage: z.string().optional(),\n ResponseDescription: z.string().optional(),\n resultDesc: z.string().optional(),\n requestId: z.string().optional(),\n })\n .passthrough()\n","import { z } from 'zod'\nimport { KesAmountSchema, NonEmptyStringSchema, UrlSchema } from './common'\n\nconst IdentifierTypeSchema = z.enum(['1', '2', '4'])\n\nexport const AsyncApiResponseSchema = z\n .object({\n ConversationID: z.string().optional(),\n OriginatorConversationID: z.string().optional(),\n ResponseCode: z.string(),\n ResponseDescription: z.string(),\n })\n .passthrough()\n\nexport const TransactionStatusRequestSchema = z\n .object({\n transactionId: z.string().optional(),\n originalConversationId: z.string().optional(),\n partyA: NonEmptyStringSchema,\n identifierType: IdentifierTypeSchema,\n resultUrl: UrlSchema,\n queueTimeOutUrl: UrlSchema,\n commandId: z.literal('TransactionStatusQuery').optional(),\n remarks: z.string().optional(),\n occasion: z.string().optional(),\n })\n .superRefine((data, ctx) => {\n if (!data.transactionId?.trim() && !data.originalConversationId?.trim()) {\n ctx.addIssue({\n code: 'custom',\n message:\n 'Either transactionId (M-Pesa Receipt Number) or originalConversationId is required',\n path: ['transactionId'],\n })\n }\n })\n\nexport const TransactionStatusResponseSchema = AsyncApiResponseSchema\n\nexport const AccountBalanceRequestSchema = z.object({\n partyA: NonEmptyStringSchema,\n identifierType: IdentifierTypeSchema,\n resultUrl: UrlSchema,\n queueTimeOutUrl: UrlSchema,\n remarks: z.string().optional(),\n})\n\nexport const AccountBalanceResponseSchema = AsyncApiResponseSchema\n\nexport const ReversalRequestSchema = z\n .object({\n transactionId: NonEmptyStringSchema,\n receiverParty: NonEmptyStringSchema,\n receiverIdentifierType: z.literal('11').optional(),\n amount: KesAmountSchema,\n resultUrl: UrlSchema,\n queueTimeOutUrl: UrlSchema,\n remarks: z.string().optional(),\n occasion: z.string().optional(),\n })\n .superRefine((data, ctx) => {\n if (data.receiverIdentifierType !== undefined && data.receiverIdentifierType !== '11') {\n ctx.addIssue({\n code: 'custom',\n message: 'receiverIdentifierType must be \"11\" for the Reversals API',\n path: ['receiverIdentifierType'],\n })\n }\n const remarks = data.remarks ?? 'Transaction Reversal'\n if (remarks.length < 2 || remarks.length > 100) {\n ctx.addIssue({\n code: 'custom',\n message: 'remarks must be between 2 and 100 characters',\n path: ['remarks'],\n })\n }\n })\n\nexport const ReversalResponseSchema = AsyncApiResponseSchema\n\nexport const TaxRemittanceRequestSchema = z.object({\n amount: KesAmountSchema,\n partyA: NonEmptyStringSchema,\n partyB: z.string().optional(),\n accountReference: NonEmptyStringSchema,\n resultUrl: UrlSchema,\n queueTimeOutUrl: UrlSchema,\n remarks: z.string().optional(),\n})\n\nexport const TaxRemittanceResponseSchema = AsyncApiResponseSchema\n\nexport const DynamicQRRequestSchema = z.object({\n merchantName: NonEmptyStringSchema,\n refNo: NonEmptyStringSchema,\n amount: KesAmountSchema,\n trxCode: z.enum(['BG', 'WA', 'PB', 'SM', 'SB']),\n cpi: NonEmptyStringSchema,\n size: z.number().int().min(1).max(1000).optional(),\n})\n\nexport const DynamicQRResponseSchema = z\n .object({\n ResponseCode: z.string(),\n RequestID: z.string().optional(),\n ResponseDescription: z.string(),\n QRCode: z.string().optional(),\n })\n .passthrough()\n","// 📁 PATH: src/mpesa/account-balance/query.ts\n\n/**\n * Account Balance Query — checks the balance of an M-PESA shortcode.\n *\n * API: POST /mpesa/accountbalance/v1/query\n *\n * This is ASYNCHRONOUS. The sync response only confirms receipt.\n * Balance data arrives via POST to your ResultURL.\n *\n * Required org portal role: \"Balance Query ORG API\" (Account Balance ORG API initiator)\n *\n * Ref: https://sandbox.safaricom.co.ke/mpesa/accountbalance/v1/query\n */\n\nimport { parseWithSchema } from '../../core/validation/zod-error'\nimport { AccountBalanceRequestSchema, AccountBalanceResponseSchema } from '../../schemas/async-apis'\nimport { httpRequest, type DarajaHttpOptions, withDarajaHttp } from '../../utils/http'\nimport type { AccountBalanceRequest, AccountBalanceResponse } from './types'\n\nexport async function queryAccountBalance(\n baseUrl: string,\n accessToken: string,\n securityCredential: string,\n initiatorName: string,\n request: AccountBalanceRequest,\n http?: DarajaHttpOptions,\n): Promise<AccountBalanceResponse> {\n const validated = parseWithSchema(AccountBalanceRequestSchema, request, 'Account Balance request')\n\n const payload = {\n Initiator: initiatorName,\n SecurityCredential: securityCredential,\n CommandID: 'AccountBalance',\n PartyA: String(validated.partyA.trim()),\n IdentifierType: validated.identifierType,\n ResultURL: validated.resultUrl,\n QueueTimeOutURL: validated.queueTimeOutUrl,\n Remarks: validated.remarks ?? 'Account Balance Query',\n }\n\n const { data } = await httpRequest<AccountBalanceResponse>(\n `${baseUrl}/mpesa/accountbalance/v1/query`,\n withDarajaHttp(\n {\n method: 'POST',\n headers: { Authorization: `Bearer ${accessToken}` },\n body: payload,\n },\n http,\n ),\n )\n\n return parseWithSchema(\n AccountBalanceResponseSchema,\n data,\n 'Account Balance response',\n ) as AccountBalanceResponse\n}\n","import { z } from 'zod'\nimport { KesAmountSchema, NonEmptyStringSchema, UrlSchema } from './common'\n\nconst B2BAsyncResponseSchema = z\n .object({\n ConversationID: z.string(),\n OriginatorConversationID: z.string(),\n ResponseCode: z.string(),\n ResponseDescription: z.string(),\n })\n .passthrough()\n\nconst B2BPaymentBaseSchema = z.object({\n amount: KesAmountSchema,\n partyA: NonEmptyStringSchema,\n partyB: NonEmptyStringSchema,\n accountReference: NonEmptyStringSchema,\n requester: z.string().optional(),\n remarks: z.string().optional(),\n resultUrl: UrlSchema,\n queueTimeOutUrl: UrlSchema,\n occasion: z.string().optional(),\n})\n\nexport const B2BBuyGoodsRequestSchema = B2BPaymentBaseSchema.extend({\n commandId: z.literal('BusinessBuyGoods'),\n})\n\nexport const B2BPayBillRequestSchema = B2BPaymentBaseSchema.extend({\n commandId: z.literal('BusinessPayBill'),\n})\n\nexport const B2BBuyGoodsResponseSchema = B2BAsyncResponseSchema\nexport const B2BPayBillResponseSchema = B2BAsyncResponseSchema\n\nexport const B2BExpressCheckoutRequestSchema = z.object({\n primaryShortCode: NonEmptyStringSchema,\n receiverShortCode: NonEmptyStringSchema,\n amount: KesAmountSchema,\n paymentRef: NonEmptyStringSchema,\n callbackUrl: UrlSchema,\n partnerName: NonEmptyStringSchema,\n requestRefId: NonEmptyStringSchema.optional(),\n})\n\nexport const B2BExpressCheckoutResponseSchema = z\n .object({\n code: z.string(),\n status: z.string(),\n })\n .passthrough()\n\nexport const B2BResponseSchema = z\n .object({\n ConversationID: z.string().optional(),\n OriginatorConversationID: z.string().optional(),\n ResponseCode: z.string(),\n ResponseDescription: z.string(),\n })\n .passthrough()\n","/**\n * src/mpesa/b2b-express-checkout/initiate.ts\n *\n * B2B Express Checkout USSD Push to Till implementation.\n */\n\nimport { generateRequestRefId } from '../../core/idempotency'\nimport { parseWithSchema } from '../../core/validation/zod-error'\nimport {\n B2BExpressCheckoutRequestSchema,\n B2BExpressCheckoutResponseSchema,\n} from '../../schemas/b2b'\nimport { httpRequest, type DarajaHttpOptions, withDarajaHttp } from '../../utils/http'\nimport type { B2BExpressCheckoutRequest, B2BExpressCheckoutResponse } from './types'\n\nexport async function initiateB2BExpressCheckout(\n baseUrl: string,\n accessToken: string,\n request: B2BExpressCheckoutRequest,\n http?: DarajaHttpOptions,\n): Promise<B2BExpressCheckoutResponse> {\n const validated = parseWithSchema(\n B2BExpressCheckoutRequestSchema,\n request,\n 'B2B Express Checkout request',\n )\n\n const amount = Math.round(validated.amount)\n\n const payload = {\n primaryShortCode: String(validated.primaryShortCode),\n receiverShortCode: String(validated.receiverShortCode),\n amount: String(amount),\n paymentRef: validated.paymentRef,\n callbackUrl: validated.callbackUrl,\n partnerName: validated.partnerName,\n RequestRefID: validated.requestRefId ?? generateRequestRefId(),\n }\n\n const { data } = await httpRequest<B2BExpressCheckoutResponse>(\n `${baseUrl}/v1/ussdpush/get-msisdn`,\n withDarajaHttp(\n {\n method: 'POST',\n headers: { Authorization: `Bearer ${accessToken}` },\n body: payload,\n },\n http,\n ),\n )\n\n return parseWithSchema(B2BExpressCheckoutResponseSchema, data, 'B2B Express Checkout response')\n}\n","/**\n * src/mpesa/b2b-buy-goods/payment.ts\n *\n * Initiates a Business Buy Goods payment via Safaricom Daraja.\n * Endpoint: POST /mpesa/b2b/v1/paymentrequest\n *\n * Strictly follows the Safaricom Daraja Business Buy Goods API documentation:\n * - CommandID must be \"BusinessBuyGoods\"\n * - SenderIdentifierType is always \"4\" (hardcoded per docs)\n * - RecieverIdentifierType is always \"4\" (hardcoded per docs)\n * - Amount is sent as a string per the JSON spec\n * - AccountReference is truncated to max 13 characters per docs\n * - Requester and Occassion are optional\n */\n\nimport { parseWithSchema } from '../../core/validation/zod-error'\nimport { B2BBuyGoodsRequestSchema, B2BBuyGoodsResponseSchema } from '../../schemas/b2b'\nimport { httpRequest, type DarajaHttpOptions, withDarajaHttp } from '../../utils/http'\nimport type { B2BBuyGoodsRequest, B2BBuyGoodsResponse } from './types'\n\n/** Daraja Business Buy Goods endpoint — same as Pay Bill per docs */\nconst B2B_BUY_GOODS_ENDPOINT = '/mpesa/b2b/v1/paymentrequest'\n\n/**\n * Per documentation: SenderIdentifierType and RecieverIdentifierType\n * must always be \"4\" (Organisation ShortCode). Not configurable.\n */\nconst IDENTIFIER_TYPE = '4' as const\n\n/**\n * Initiates a Business Buy Goods payment request.\n *\n * Moves money from your MMF/Working account to the recipient's merchant account\n * (till number, merchant store number, or Merchant HO).\n * The sync response is acknowledgement only — the result arrives via resultUrl.\n *\n * @param baseUrl - Daraja base URL (sandbox or production)\n * @param accessToken - Valid OAuth Bearer token\n * @param securityCredential - RSA-encrypted initiator password (base64)\n * @param initiatorName - M-Pesa API operator username with B2B role\n * @param request - Business Buy Goods request parameters\n * @returns Synchronous acknowledgement response from Daraja\n * @throws {PesafyError} VALIDATION_ERROR for invalid input before HTTP call\n * @throws {PesafyError} From httpRequest on network / API errors\n */\nexport async function initiateB2BBuyGoods(\n baseUrl: string,\n accessToken: string,\n securityCredential: string,\n initiatorName: string,\n request: B2BBuyGoodsRequest,\n http?: DarajaHttpOptions,\n): Promise<B2BBuyGoodsResponse> {\n const validated = parseWithSchema(B2BBuyGoodsRequestSchema, request, 'B2B Buy Goods request')\n const amount = Math.round(validated.amount)\n\n // ── Build payload matching Daraja spec exactly ──────────────────────────────\n //\n // Field mapping (camelCase → Daraja PascalCase):\n // commandId → CommandID (\"BusinessBuyGoods\")\n // partyA → PartyA (string)\n // partyB → PartyB (string)\n // accountReference → AccountReference (string, max 13 chars per docs)\n // requester → Requester (optional)\n // remarks → Remarks (string)\n // resultUrl → ResultURL (string)\n // queueTimeOutUrl → QueueTimeOutURL (string)\n // occasion → Occassion (optional, Daraja typo preserved)\n // amount → Amount (string per JSON sample in docs)\n //\n // Hardcoded per docs:\n // SenderIdentifierType → \"4\"\n // RecieverIdentifierType → \"4\"\n //\n const payload: Record<string, unknown> = {\n Initiator: initiatorName,\n SecurityCredential: securityCredential,\n CommandID: validated.commandId,\n SenderIdentifierType: IDENTIFIER_TYPE,\n RecieverIdentifierType: IDENTIFIER_TYPE,\n Amount: String(amount),\n PartyA: String(validated.partyA),\n PartyB: String(validated.partyB),\n AccountReference: validated.accountReference.slice(0, 13),\n Remarks: validated.remarks ?? 'Business Buy Goods',\n QueueTimeOutURL: validated.queueTimeOutUrl,\n ResultURL: validated.resultUrl,\n }\n\n if (validated.requester?.trim()) {\n payload['Requester'] = String(validated.requester)\n }\n\n if (validated.occasion?.trim()) {\n payload['Occassion'] = validated.occasion\n }\n\n const { data } = await httpRequest<B2BBuyGoodsResponse>(\n `${baseUrl}${B2B_BUY_GOODS_ENDPOINT}`,\n withDarajaHttp(\n {\n method: 'POST',\n headers: { Authorization: `Bearer ${accessToken}` },\n body: payload,\n },\n http,\n ),\n )\n\n return parseWithSchema(\n B2BBuyGoodsResponseSchema,\n data,\n 'B2B Buy Goods response',\n ) as B2BBuyGoodsResponse\n}\n","/**\n * src/mpesa/b2b-pay-bill/payment.ts\n *\n * Initiates a Business Pay Bill payment via Safaricom Daraja.\n * Endpoint: POST /mpesa/b2b/v1/paymentrequest\n *\n * Strictly follows the Safaricom Daraja Business Pay Bill API documentation:\n * - CommandID must be \"BusinessPayBill\"\n * - SenderIdentifierType is always \"4\" (hardcoded per docs)\n * - RecieverIdentifierType is always \"4\" (hardcoded per docs)\n * - Amount is sent as a string per the JSON spec\n * - AccountReference is truncated to max 13 characters per docs\n * - Requester and Occassion are optional\n */\n\nimport { parseWithSchema } from '../../core/validation/zod-error'\nimport { B2BPayBillRequestSchema, B2BPayBillResponseSchema } from '../../schemas/b2b'\nimport { httpRequest, type DarajaHttpOptions, withDarajaHttp } from '../../utils/http'\nimport type { B2BPayBillRequest, B2BPayBillResponse } from './types'\n\n/** Daraja Business Pay Bill endpoint */\nconst B2B_PAY_BILL_ENDPOINT = '/mpesa/b2b/v1/paymentrequest'\n\n/**\n * Per documentation: SenderIdentifierType and RecieverIdentifierType\n * must always be \"4\" (Organisation ShortCode). Not configurable.\n */\nconst IDENTIFIER_TYPE = '4' as const\n\n/**\n * Initiates a Business Pay Bill payment request.\n *\n * Moves money from your MMF/Working account to the recipient's utility account.\n * The sync response is acknowledgement only — the result arrives via resultUrl.\n *\n * @param baseUrl - Daraja base URL (sandbox or production)\n * @param accessToken - Valid OAuth Bearer token\n * @param securityCredential - RSA-encrypted initiator password (base64)\n * @param initiatorName - M-Pesa API operator username with B2B role\n * @param request - Business Pay Bill request parameters\n * @returns Synchronous acknowledgement response from Daraja\n * @throws {PesafyError} VALIDATION_ERROR for invalid input before HTTP call\n * @throws {PesafyError} From httpRequest on network / API errors\n */\nexport async function initiateB2BPayBill(\n baseUrl: string,\n accessToken: string,\n securityCredential: string,\n initiatorName: string,\n request: B2BPayBillRequest,\n http?: DarajaHttpOptions,\n): Promise<B2BPayBillResponse> {\n const validated = parseWithSchema(B2BPayBillRequestSchema, request, 'B2B Pay Bill request')\n const amount = Math.round(validated.amount)\n\n // ── Build payload matching Daraja spec exactly ──────────────────────────────\n //\n // Field mapping (camelCase → Daraja PascalCase):\n // commandId → CommandID (\"BusinessPayBill\")\n // partyA → PartyA (string)\n // partyB → PartyB (string)\n // accountReference → AccountReference (string, max 13 chars per docs)\n // requester → Requester (optional)\n // remarks → Remarks (string)\n // resultUrl → ResultURL (string)\n // queueTimeOutUrl → QueueTimeOutURL (string)\n // occasion → Occassion (optional, Daraja typo preserved)\n // amount → Amount (string per JSON sample in docs)\n //\n // Hardcoded per docs:\n // SenderIdentifierType → \"4\"\n // RecieverIdentifierType → \"4\"\n //\n const payload: Record<string, unknown> = {\n Initiator: initiatorName,\n SecurityCredential: securityCredential,\n CommandID: validated.commandId,\n SenderIdentifierType: IDENTIFIER_TYPE,\n RecieverIdentifierType: IDENTIFIER_TYPE,\n Amount: String(amount),\n PartyA: String(validated.partyA),\n PartyB: String(validated.partyB),\n AccountReference: validated.accountReference.slice(0, 13),\n Remarks: validated.remarks ?? 'Business Pay Bill',\n QueueTimeOutURL: validated.queueTimeOutUrl,\n ResultURL: validated.resultUrl,\n }\n\n if (validated.requester?.trim()) {\n payload['Requester'] = String(validated.requester)\n }\n\n if (validated.occasion?.trim()) {\n payload['Occassion'] = validated.occasion\n }\n\n const { data } = await httpRequest<B2BPayBillResponse>(\n `${baseUrl}${B2B_PAY_BILL_ENDPOINT}`,\n withDarajaHttp(\n {\n method: 'POST',\n headers: { Authorization: `Bearer ${accessToken}` },\n body: payload,\n },\n http,\n ),\n )\n\n return parseWithSchema(\n B2BPayBillResponseSchema,\n data,\n 'B2B Pay Bill response',\n ) as B2BPayBillResponse\n}\n","import { z } from 'zod'\nimport { KesAmountSchema, NonEmptyStringSchema, UrlSchema } from './common'\n\nconst B2CAsyncResponseSchema = z\n .object({\n ConversationID: z.string(),\n OriginatorConversationID: z.string(),\n ResponseCode: z.string(),\n ResponseDescription: z.string(),\n })\n .passthrough()\n\n/** B2C Account Top Up (BusinessPayToBulk) */\nexport const B2CRequestSchema = z.object({\n commandId: z.literal('BusinessPayToBulk'),\n amount: KesAmountSchema,\n partyA: NonEmptyStringSchema,\n partyB: NonEmptyStringSchema,\n accountReference: NonEmptyStringSchema,\n requester: z.string().optional(),\n remarks: z.string().optional(),\n resultUrl: UrlSchema,\n queueTimeOutUrl: UrlSchema,\n})\n\nexport const B2CResponseSchema = B2CAsyncResponseSchema\n\nexport const B2CDisbursementRequestSchema = z.object({\n commandId: z.enum(['BusinessPayment', 'SalaryPayment', 'PromotionPayment']),\n amount: z.number().finite().positive(),\n partyA: NonEmptyStringSchema,\n partyB: NonEmptyStringSchema,\n remarks: NonEmptyStringSchema,\n queueTimeOutUrl: UrlSchema,\n resultUrl: UrlSchema,\n originatorConversationId: NonEmptyStringSchema.optional(),\n occasion: z.string().optional(),\n})\n\nexport const B2CDisbursementResponseSchema = B2CAsyncResponseSchema\n","/**\n * src/mpesa/b2c/payment.ts\n *\n * Initiates a B2C Account Top Up via Safaricom Daraja.\n * Endpoint: POST /mpesa/b2b/v1/paymentrequest\n *\n * Strictly follows the Safaricom Daraja B2C Account Top Up API documentation:\n * - CommandID must be \"BusinessPayToBulk\"\n * - SenderIdentifierType is always \"4\" (hardcoded per docs)\n * - RecieverIdentifierType is always \"4\" (hardcoded per docs)\n * - Amount is sent as a string per the JSON spec\n * - Requester is optional\n */\n\nimport { parseWithSchema } from '../../core/validation/zod-error'\nimport { B2CRequestSchema, B2CResponseSchema } from '../../schemas/b2c'\nimport { httpRequest, type DarajaHttpOptions, withDarajaHttp } from '../../utils/http'\nimport type { B2CRequest, B2CResponse } from './types'\n\n/** The only endpoint documented for this API */\nconst B2C_ENDPOINT = '/mpesa/b2b/v1/paymentrequest'\n\n/**\n * Per documentation: SenderIdentifierType and RecieverIdentifierType\n * must always be \"4\" (Organisation ShortCode). Not configurable.\n */\nconst IDENTIFIER_TYPE = '4' as const\n\n/**\n * Initiates a B2C Account Top Up payment request.\n *\n * @param baseUrl - Daraja base URL (sandbox or production)\n * @param accessToken - Valid OAuth Bearer token\n * @param securityCredential - RSA-encrypted initiator password (base64)\n * @param initiatorName - M-Pesa API operator username with B2B role\n * @param request - B2C top-up request parameters\n * @returns Synchronous acknowledgement response from Daraja\n * @throws {PesafyError} VALIDATION_ERROR for invalid input before HTTP call\n * @throws {PesafyError} From httpRequest on network / API errors\n */\nexport async function initiateB2CPayment(\n baseUrl: string,\n accessToken: string,\n securityCredential: string,\n initiatorName: string,\n request: B2CRequest,\n http?: DarajaHttpOptions,\n): Promise<B2CResponse> {\n const validated = parseWithSchema(B2CRequestSchema, request, 'B2C request')\n const amount = Math.round(validated.amount)\n\n // ── Build payload matching Daraja spec exactly ──────────────────────────────\n //\n // Field mapping (camelCase → Daraja PascalCase):\n // commandId → CommandID (\"BusinessPayToBulk\")\n // partyA → PartyA (string)\n // partyB → PartyB (string)\n // accountReference → AccountReference (string)\n // requester → Requester (string, omitted if not provided)\n // remarks → Remarks (string)\n // resultUrl → ResultURL (string)\n // queueTimeOutUrl → QueueTimeOutURL (string)\n // amount → Amount (string per JSON sample in docs)\n //\n // Hardcoded per docs:\n // SenderIdentifierType → \"4\"\n // RecieverIdentifierType → \"4\"\n\n const payload: Record<string, unknown> = {\n Initiator: initiatorName,\n SecurityCredential: securityCredential,\n CommandID: validated.commandId,\n SenderIdentifierType: IDENTIFIER_TYPE,\n RecieverIdentifierType: IDENTIFIER_TYPE,\n Amount: String(amount),\n PartyA: String(validated.partyA),\n PartyB: String(validated.partyB),\n AccountReference: validated.accountReference,\n Remarks: validated.remarks ?? 'B2C Account Top Up',\n QueueTimeOutURL: validated.queueTimeOutUrl,\n ResultURL: validated.resultUrl,\n }\n\n if (validated.requester?.trim()) {\n payload['Requester'] = String(validated.requester)\n }\n\n const { data } = await httpRequest<B2CResponse>(\n `${baseUrl}${B2C_ENDPOINT}`,\n withDarajaHttp(\n {\n method: 'POST',\n headers: { Authorization: `Bearer ${accessToken}` },\n body: payload,\n },\n http,\n ),\n )\n\n return parseWithSchema(B2CResponseSchema, data, 'B2C response') as B2CResponse\n}\n","/**\n * src/mpesa/b2c-disbursement/payment.ts\n *\n * Initiates a B2C Disbursement payment (Salary / Cashback / Promotion).\n * Endpoint: POST /mpesa/b2c/v3/paymentrequest\n */\n\nimport { generateOriginatorConversationId } from '../../core/idempotency'\nimport { parseWithSchema } from '../../core/validation/zod-error'\nimport { B2CDisbursementRequestSchema, B2CDisbursementResponseSchema } from '../../schemas/b2c'\nimport { createError } from '../../utils/errors'\nimport { httpRequest, type DarajaHttpOptions, withDarajaHttp } from '../../utils/http'\nimport type { B2CDisbursementRequest, B2CDisbursementResponse } from './types'\n\nconst B2C_DISBURSEMENT_ENDPOINT = '/mpesa/b2c/v3/paymentrequest'\n\nconst VALID_COMMAND_IDS = new Set(['BusinessPayment', 'SalaryPayment', 'PromotionPayment'])\n\n/**\n * Initiates a B2C disbursement payment request.\n *\n * @param baseUrl - Daraja base URL (sandbox or production)\n * @param accessToken - Valid OAuth Bearer token\n * @param securityCredential - RSA-encrypted initiator password (base64)\n * @param initiatorName - M-Pesa API operator username\n * @param request - B2C disbursement request parameters\n */\nexport async function initiateB2CDisbursement(\n baseUrl: string,\n accessToken: string,\n securityCredential: string,\n initiatorName: string,\n request: B2CDisbursementRequest,\n http?: DarajaHttpOptions,\n): Promise<B2CDisbursementResponse> {\n const originatorConversationId =\n request.originatorConversationId?.trim() || generateOriginatorConversationId()\n const validated = parseWithSchema(\n B2CDisbursementRequestSchema,\n { ...request, originatorConversationId },\n 'B2C Disbursement request',\n )\n\n // ── Validate CommandID ──────────────────────────────────────────────────────\n if (!validated.commandId || !VALID_COMMAND_IDS.has(validated.commandId)) {\n throw createError({\n code: 'VALIDATION_ERROR',\n message:\n `commandId must be one of: BusinessPayment, SalaryPayment, PromotionPayment. ` +\n `Got \"${validated.commandId}\".`,\n })\n }\n\n // ── Validate amount ─────────────────────────────────────────────────────────\n const amount = Math.round(validated.amount)\n if (!Number.isFinite(amount) || amount < 10) {\n throw createError({\n code: 'VALIDATION_ERROR',\n message: `amount must be ≥ 10 KES (got ${validated.amount} which rounds to ${amount}).`,\n })\n }\n\n // ── Validate required string fields ────────────────────────────────────────\n if (!validated.partyA?.trim()) {\n throw createError({\n code: 'VALIDATION_ERROR',\n message: 'partyA is required — the sending organisation shortcode.',\n })\n }\n\n if (!validated.partyB?.trim()) {\n throw createError({\n code: 'VALIDATION_ERROR',\n message: 'partyB is required — the receiving customer MSISDN (2547XXXXXXXX).',\n })\n }\n\n if (!validated.remarks?.trim()) {\n throw createError({\n code: 'VALIDATION_ERROR',\n message: 'remarks is required (2–100 characters).',\n })\n }\n\n if (!validated.resultUrl?.trim()) {\n throw createError({\n code: 'VALIDATION_ERROR',\n message: 'resultUrl is required — Safaricom POSTs the async result here.',\n })\n }\n\n if (!validated.queueTimeOutUrl?.trim()) {\n throw createError({\n code: 'VALIDATION_ERROR',\n message: 'queueTimeOutUrl is required — Safaricom calls this on request timeout.',\n })\n }\n\n // ── Build payload ───────────────────────────────────────────────────────────\n const payload: Record<string, unknown> = {\n OriginatorConversationID: originatorConversationId,\n InitiatorName: initiatorName,\n SecurityCredential: securityCredential,\n CommandID: validated.commandId,\n Amount: amount,\n PartyA: String(validated.partyA),\n PartyB: String(validated.partyB),\n Remarks: validated.remarks,\n QueueTimeOutURL: validated.queueTimeOutUrl,\n ResultURL: validated.resultUrl,\n }\n\n // Occassion is optional (sic — Daraja typo preserved)\n if (validated.occasion?.trim()) {\n payload['Occassion'] = validated.occasion\n }\n\n const { data } = await httpRequest<B2CDisbursementResponse>(\n `${baseUrl}${B2C_DISBURSEMENT_ENDPOINT}`,\n withDarajaHttp(\n {\n method: 'POST',\n headers: { Authorization: `Bearer ${accessToken}` },\n body: payload,\n },\n http,\n ),\n )\n\n return parseWithSchema(B2CDisbursementResponseSchema, data, 'B2C Disbursement response')\n}\n","import { z } from 'zod'\nimport { KesAmountSchema, NonEmptyStringSchema, UrlSchema } from './common'\n\nconst SendRemindersSchema = z.enum(['0', '1'])\n\nexport const BillManagerOptInRequestSchema = z.object({\n shortcode: NonEmptyStringSchema,\n email: NonEmptyStringSchema,\n officialContact: NonEmptyStringSchema,\n sendReminders: SendRemindersSchema,\n logo: z.string().optional(),\n callbackUrl: UrlSchema,\n})\n\nexport const BillManagerOptInResponseSchema = z\n .object({\n app_key: z.string().optional(),\n resmsg: z.string(),\n rescode: z.string(),\n })\n .passthrough()\n\nexport const BillManagerUpdateOptInRequestSchema = BillManagerOptInRequestSchema\nexport const BillManagerUpdateOptInResponseSchema = z\n .object({\n resmsg: z.string(),\n rescode: z.string(),\n })\n .passthrough()\n\nexport const BillManagerInvoiceItemSchema = z.object({\n itemName: NonEmptyStringSchema,\n amount: KesAmountSchema,\n})\n\nexport const BillManagerSingleInvoiceRequestSchema = z.object({\n externalReference: NonEmptyStringSchema,\n billedFullName: NonEmptyStringSchema,\n billedPhoneNumber: NonEmptyStringSchema,\n billedPeriod: NonEmptyStringSchema,\n invoiceName: NonEmptyStringSchema,\n dueDate: NonEmptyStringSchema,\n accountReference: NonEmptyStringSchema,\n amount: KesAmountSchema,\n invoiceItems: z.array(BillManagerInvoiceItemSchema).optional(),\n})\n\nexport const BillManagerSingleInvoiceResponseSchema = z\n .object({\n Status_Message: z.string().optional(),\n resmsg: z.string(),\n rescode: z.string(),\n })\n .passthrough()\n\nexport const BillManagerBulkInvoiceRequestSchema = z.object({\n invoices: z.array(BillManagerSingleInvoiceRequestSchema).min(1).max(1000),\n})\n\nexport const BillManagerBulkInvoiceResponseSchema = z\n .object({\n Status_Message: z.string().optional(),\n resmsg: z.string(),\n rescode: z.string(),\n })\n .passthrough()\n\nexport const BillManagerCancelInvoiceRequestSchema = z.object({\n externalReference: NonEmptyStringSchema,\n})\n\nexport const BillManagerCancelInvoiceResponseSchema = z\n .object({\n Status_Message: z.string().optional(),\n resmsg: z.string(),\n rescode: z.string(),\n errors: z.array(z.unknown()).optional(),\n })\n .passthrough()\n\nexport const BillManagerCancelBulkInvoiceRequestSchema = z.object({\n externalReferences: z.array(NonEmptyStringSchema).min(1),\n})\n\nexport const BillManagerCancelBulkInvoiceResponseSchema = z\n .object({\n Status_Message: z.string().optional(),\n resmsg: z.string(),\n rescode: z.string(),\n errors: z.array(z.unknown()).optional(),\n })\n .passthrough()\n\nexport const BillManagerReconciliationRequestSchema = z.object({\n paymentDate: NonEmptyStringSchema,\n paidAmount: NonEmptyStringSchema,\n accountReference: NonEmptyStringSchema,\n transactionId: NonEmptyStringSchema,\n phoneNumber: NonEmptyStringSchema,\n fullName: NonEmptyStringSchema,\n invoiceName: NonEmptyStringSchema,\n externalReference: NonEmptyStringSchema,\n})\n\nexport const BillManagerReconciliationResponseSchema = z\n .object({\n resmsg: z.string(),\n rescode: z.string(),\n })\n .passthrough()\n","// src/mpesa/bill-manager/invoice.ts\n\n/**\n * src/mpesa/bill-manager/invoice.ts\n *\n * Bill Manager — opt-in, invoice creation/cancellation, and payment reconciliation.\n *\n * Strictly aligned with Safaricom Daraja Bill Manager API documentation.\n *\n * APIs:\n * POST /v1/billmanager-invoice/optin — Opt-in shortcode\n * POST /v1/billmanager-invoice/change-optin-details — Update opt-in details\n * POST /v1/billmanager-invoice/single-invoicing — Send a single invoice\n * POST /v1/billmanager-invoice/bulk-invoicing — Send bulk invoices (up to 1000)\n * POST /v1/billmanager-invoice/cancel-single-invoice — Cancel a single invoice\n * POST /v1/billmanager-invoice/cancel-bulk-invoices — Cancel multiple invoices\n * POST /v1/billmanager-invoice/reconciliation — Acknowledge a payment\n */\n\nimport { parseWithSchema } from '../../core/validation/zod-error'\nimport {\n BillManagerBulkInvoiceRequestSchema,\n BillManagerBulkInvoiceResponseSchema,\n BillManagerCancelBulkInvoiceRequestSchema,\n BillManagerCancelBulkInvoiceResponseSchema,\n BillManagerCancelInvoiceRequestSchema,\n BillManagerCancelInvoiceResponseSchema,\n BillManagerOptInRequestSchema,\n BillManagerOptInResponseSchema,\n BillManagerReconciliationRequestSchema,\n BillManagerReconciliationResponseSchema,\n BillManagerSingleInvoiceRequestSchema,\n BillManagerSingleInvoiceResponseSchema,\n BillManagerUpdateOptInRequestSchema,\n BillManagerUpdateOptInResponseSchema,\n} from '../../schemas/bill-manager'\nimport { httpRequest, type DarajaHttpOptions, withDarajaHttp } from '../../utils/http'\nimport type {\n BillManagerBulkInvoiceRequest,\n BillManagerBulkInvoiceResponse,\n BillManagerCancelBulkInvoiceRequest,\n BillManagerCancelBulkInvoiceResponse,\n BillManagerCancelInvoiceRequest,\n BillManagerCancelInvoiceResponse,\n BillManagerOptInRequest,\n BillManagerOptInResponse,\n BillManagerReconciliationRequest,\n BillManagerReconciliationResponse,\n BillManagerSingleInvoiceRequest,\n BillManagerSingleInvoiceResponse,\n BillManagerUpdateOptInRequest,\n BillManagerUpdateOptInResponse,\n} from './types'\n\nexport async function billManagerOptIn(\n baseUrl: string,\n accessToken: string,\n request: BillManagerOptInRequest,\n http?: DarajaHttpOptions,\n): Promise<BillManagerOptInResponse> {\n const validated = parseWithSchema(\n BillManagerOptInRequestSchema,\n request,\n 'Bill Manager opt-in request',\n )\n\n const payload: Record<string, unknown> = {\n shortcode: validated.shortcode,\n email: validated.email,\n officialContact: validated.officialContact,\n sendReminders: validated.sendReminders,\n logo: validated.logo ?? '',\n callbackurl: validated.callbackUrl,\n }\n\n const { data } = await httpRequest<BillManagerOptInResponse>(\n `${baseUrl}/v1/billmanager-invoice/optin`,\n withDarajaHttp(\n {\n method: 'POST',\n headers: { Authorization: `Bearer ${accessToken}` },\n body: payload,\n },\n http,\n ),\n )\n return parseWithSchema(\n BillManagerOptInResponseSchema,\n data,\n 'Bill Manager opt-in response',\n ) as BillManagerOptInResponse\n}\n\nexport async function updateOptIn(\n baseUrl: string,\n accessToken: string,\n request: BillManagerUpdateOptInRequest,\n http?: DarajaHttpOptions,\n): Promise<BillManagerUpdateOptInResponse> {\n const validated = parseWithSchema(\n BillManagerUpdateOptInRequestSchema,\n request,\n 'Bill Manager update opt-in request',\n )\n\n const payload: Record<string, unknown> = {\n shortcode: validated.shortcode,\n email: validated.email,\n officialContact: validated.officialContact,\n sendReminders: validated.sendReminders,\n logo: validated.logo ?? '',\n callbackurl: validated.callbackUrl,\n }\n\n const { data } = await httpRequest<BillManagerUpdateOptInResponse>(\n `${baseUrl}/v1/billmanager-invoice/change-optin-details`,\n withDarajaHttp(\n {\n method: 'POST',\n headers: { Authorization: `Bearer ${accessToken}` },\n body: payload,\n },\n http,\n ),\n )\n return parseWithSchema(\n BillManagerUpdateOptInResponseSchema,\n data,\n 'Bill Manager update opt-in response',\n ) as BillManagerUpdateOptInResponse\n}\n\nexport async function sendSingleInvoice(\n baseUrl: string,\n accessToken: string,\n request: BillManagerSingleInvoiceRequest,\n http?: DarajaHttpOptions,\n): Promise<BillManagerSingleInvoiceResponse> {\n const validated = parseWithSchema(\n BillManagerSingleInvoiceRequestSchema,\n request,\n 'Bill Manager single invoice request',\n )\n const amount = Math.round(validated.amount)\n\n const payload: Record<string, unknown> = {\n externalReference: validated.externalReference,\n billedFullName: validated.billedFullName,\n billedPhoneNumber: validated.billedPhoneNumber,\n billedPeriod: validated.billedPeriod,\n invoiceName: validated.invoiceName,\n dueDate: validated.dueDate,\n accountReference: validated.accountReference,\n amount: String(amount),\n invoiceItems:\n validated.invoiceItems?.map((i) => ({\n itemName: i.itemName,\n amount: String(Math.round(i.amount)),\n })) ?? [],\n }\n\n const { data } = await httpRequest<BillManagerSingleInvoiceResponse>(\n `${baseUrl}/v1/billmanager-invoice/single-invoicing`,\n withDarajaHttp(\n {\n method: 'POST',\n headers: { Authorization: `Bearer ${accessToken}` },\n body: payload,\n },\n http,\n ),\n )\n return parseWithSchema(\n BillManagerSingleInvoiceResponseSchema,\n data,\n 'Bill Manager single invoice response',\n ) as BillManagerSingleInvoiceResponse\n}\n\nexport async function sendBulkInvoices(\n baseUrl: string,\n accessToken: string,\n request: BillManagerBulkInvoiceRequest,\n http?: DarajaHttpOptions,\n): Promise<BillManagerBulkInvoiceResponse> {\n const validated = parseWithSchema(\n BillManagerBulkInvoiceRequestSchema,\n request,\n 'Bill Manager bulk invoice request',\n )\n\n const payload = validated.invoices.map((inv) => ({\n externalReference: inv.externalReference,\n billedFullName: inv.billedFullName,\n billedPhoneNumber: inv.billedPhoneNumber,\n billedPeriod: inv.billedPeriod,\n invoiceName: inv.invoiceName,\n dueDate: inv.dueDate,\n accountReference: inv.accountReference,\n amount: String(Math.round(inv.amount)),\n invoiceItems:\n inv.invoiceItems?.map((item) => ({\n itemName: item.itemName,\n amount: String(Math.round(item.amount)),\n })) ?? [],\n }))\n\n const { data } = await httpRequest<BillManagerBulkInvoiceResponse>(\n `${baseUrl}/v1/billmanager-invoice/bulk-invoicing`,\n withDarajaHttp(\n {\n method: 'POST',\n headers: { Authorization: `Bearer ${accessToken}` },\n body: payload,\n },\n http,\n ),\n )\n return parseWithSchema(\n BillManagerBulkInvoiceResponseSchema,\n data,\n 'Bill Manager bulk invoice response',\n ) as BillManagerBulkInvoiceResponse\n}\n\nexport async function cancelInvoice(\n baseUrl: string,\n accessToken: string,\n request: BillManagerCancelInvoiceRequest,\n http?: DarajaHttpOptions,\n): Promise<BillManagerCancelInvoiceResponse> {\n const validated = parseWithSchema(\n BillManagerCancelInvoiceRequestSchema,\n request,\n 'Bill Manager cancel invoice request',\n )\n\n const { data } = await httpRequest<BillManagerCancelInvoiceResponse>(\n `${baseUrl}/v1/billmanager-invoice/cancel-single-invoice`,\n withDarajaHttp(\n {\n method: 'POST',\n headers: { Authorization: `Bearer ${accessToken}` },\n body: { externalReference: validated.externalReference },\n },\n http,\n ),\n )\n return parseWithSchema(\n BillManagerCancelInvoiceResponseSchema,\n data,\n 'Bill Manager cancel invoice response',\n ) as BillManagerCancelInvoiceResponse\n}\n\nexport async function cancelBulkInvoices(\n baseUrl: string,\n accessToken: string,\n request: BillManagerCancelBulkInvoiceRequest,\n http?: DarajaHttpOptions,\n): Promise<BillManagerCancelBulkInvoiceResponse> {\n const validated = parseWithSchema(\n BillManagerCancelBulkInvoiceRequestSchema,\n request,\n 'Bill Manager cancel bulk invoices request',\n )\n\n const payload = validated.externalReferences.map((ref) => ({ externalReference: ref }))\n\n const { data } = await httpRequest<BillManagerCancelBulkInvoiceResponse>(\n `${baseUrl}/v1/billmanager-invoice/cancel-bulk-invoices`,\n withDarajaHttp(\n {\n method: 'POST',\n headers: { Authorization: `Bearer ${accessToken}` },\n body: payload,\n },\n http,\n ),\n )\n return parseWithSchema(\n BillManagerCancelBulkInvoiceResponseSchema,\n data,\n 'Bill Manager cancel bulk invoices response',\n ) as BillManagerCancelBulkInvoiceResponse\n}\n\nexport async function reconcilePayment(\n baseUrl: string,\n accessToken: string,\n request: BillManagerReconciliationRequest,\n http?: DarajaHttpOptions,\n): Promise<BillManagerReconciliationResponse> {\n const validated = parseWithSchema(\n BillManagerReconciliationRequestSchema,\n request,\n 'Bill Manager reconciliation request',\n )\n\n const payload: Record<string, unknown> = {\n paymentDate: validated.paymentDate,\n paidAmount: validated.paidAmount,\n accountReference: validated.accountReference,\n transactionId: validated.transactionId,\n phoneNumber: validated.phoneNumber,\n fullName: validated.fullName,\n invoiceName: validated.invoiceName,\n externalReference: validated.externalReference,\n }\n\n const { data } = await httpRequest<BillManagerReconciliationResponse>(\n `${baseUrl}/v1/billmanager-invoice/reconciliation`,\n withDarajaHttp(\n {\n method: 'POST',\n headers: { Authorization: `Bearer ${accessToken}` },\n body: payload,\n },\n http,\n ),\n )\n return parseWithSchema(\n BillManagerReconciliationResponseSchema,\n data,\n 'Bill Manager reconciliation response',\n ) as BillManagerReconciliationResponse\n}\n","import { z } from 'zod'\nimport { KesAmountSchema, NonEmptyStringSchema, UrlSchema } from './common'\n\nexport const C2BRegisterUrlRequestSchema = z.object({\n shortCode: NonEmptyStringSchema,\n responseType: z.enum(['Completed', 'Cancelled']),\n confirmationUrl: UrlSchema,\n validationUrl: UrlSchema,\n apiVersion: z.enum(['v1', 'v2']).optional(),\n})\n\nexport const C2BBaseResponseSchema = z\n .object({\n OriginatorCoversationID: z.string(),\n ResponseCode: z.string(),\n ResponseDescription: z.string(),\n })\n .passthrough()\n\nexport const C2BRegisterUrlResponseSchema = C2BBaseResponseSchema\n\nexport const C2BSimulateResponseSchema = C2BBaseResponseSchema\n\nexport const C2BSimulateRequestSchema = z\n .object({\n shortCode: z.union([NonEmptyStringSchema, z.number()]),\n commandId: z.enum(['CustomerPayBillOnline', 'CustomerBuyGoodsOnline']),\n amount: KesAmountSchema,\n msisdn: z.union([NonEmptyStringSchema, z.number()]),\n billRefNumber: z.union([NonEmptyStringSchema, z.null()]).optional(),\n apiVersion: z.enum(['v1', 'v2']).optional(),\n })\n .superRefine((data, ctx) => {\n if (data.commandId === 'CustomerPayBillOnline' && !data.billRefNumber?.trim()) {\n ctx.addIssue({\n code: 'custom',\n message: 'billRefNumber is required for CustomerPayBillOnline',\n path: ['billRefNumber'],\n })\n }\n })\n\nexport const C2BValidationWebhookSchema = z\n .object({\n TransactionType: z.string(),\n TransID: z.string(),\n TransTime: z.string(),\n TransAmount: z.union([z.string(), z.number()]),\n BusinessShortCode: z.string(),\n BillRefNumber: z.string().optional(),\n MSISDN: z.string(),\n })\n .passthrough()\n","/**\n * src/mpesa/c2b/register-url.ts\n *\n * C2B Register URL implementation.\n * Strictly aligned with Safaricom Daraja C2B Register URL API documentation.\n *\n * Endpoint (v1 — documented primary):\n * Sandbox: POST https://sandbox.safaricom.co.ke/mpesa/c2b/v1/registerurl\n * Production: POST https://api.safaricom.co.ke/mpesa/c2b/v1/registerurl\n *\n * Also supports v2 via the apiVersion option.\n */\n\nimport { parseWithSchema } from '../../core/validation/zod-error'\nimport { C2BRegisterUrlRequestSchema, C2BRegisterUrlResponseSchema } from '../../schemas/c2b'\nimport { createError } from '../../utils/errors'\nimport { httpRequest, type DarajaHttpOptions, withDarajaHttp } from '../../utils/http'\nimport type { C2BApiVersion, C2BRegisterUrlRequest, C2BRegisterUrlResponse } from './types'\n\n/**\n * Forbidden URL keywords per Daraja documentation:\n * \"Avoid keywords such as M-PESA, M-Pesa, Safaricom, exe, exec, cme, or variants in your URLs.\"\n *\n * We lowercase-compare, so \"MPESA\", \"Mpesa\", \"mPeSa\" are all caught.\n *\n * Additional blocked keywords (documented variants): cmd, sql, query\n */\nconst FORBIDDEN_URL_KEYWORDS: readonly string[] = [\n 'mpesa',\n 'safaricom',\n 'exec',\n 'exe',\n 'cme', // explicitly documented by Daraja\n 'cmd', // documented variant of cme\n 'sql',\n 'query',\n] as const\n\n/**\n * Validates a callback URL against Daraja's documented URL requirements.\n * Throws PesafyError if the URL violates any documented rule.\n */\nfunction validateCallbackUrl(url: string, fieldName: string): void {\n if (!url || !url.trim()) {\n throw createError({\n code: 'VALIDATION_ERROR',\n message: `${fieldName} is required`,\n })\n }\n\n const lower = url.toLowerCase()\n\n for (const keyword of FORBIDDEN_URL_KEYWORDS) {\n if (lower.includes(keyword)) {\n throw createError({\n code: 'VALIDATION_ERROR',\n message:\n `${fieldName} must not contain the keyword \"${keyword}\". ` +\n `Daraja rejects URLs containing: mpesa, safaricom, exe, exec, cme ` +\n `(and variants: cmd, sql, query).`,\n })\n }\n }\n}\n\n/**\n * Registers C2B Confirmation and Validation URLs with Safaricom.\n *\n * Per Daraja documentation:\n * - Sandbox: may be called multiple times (URLs can be overwritten).\n * - Production: one-time call. To change URLs, delete existing on the portal\n * or email apisupport@safaricom.co.ke, then re-register.\n * - ResponseType must be sentence-case: \"Completed\" or \"Cancelled\".\n * - Both URLs must be publicly accessible and internet-reachable.\n * - Production requires HTTPS; Sandbox allows HTTP.\n * - Do not use public URL testers (ngrok, mockbin, requestbin) — they are blocked.\n * - The Validation URL is only called when external validation is enabled.\n * To activate, email apisupport@safaricom.co.ke.\n * - If M-PESA cannot reach your Validation URL within ~8 seconds, it defaults\n * to the ResponseType action set during registration.\n *\n * @param baseUrl - Daraja environment base URL\n * @param accessToken - Valid OAuth bearer token from Authorization API\n * @param request - Registration parameters\n * @returns - Daraja registration response (ResponseCode \"0\" = success)\n */\nexport async function registerC2BUrls(\n baseUrl: string,\n accessToken: string,\n request: C2BRegisterUrlRequest,\n http?: DarajaHttpOptions,\n): Promise<C2BRegisterUrlResponse> {\n const validated = parseWithSchema(\n C2BRegisterUrlRequestSchema,\n request,\n 'C2B Register URL request',\n )\n\n validateCallbackUrl(validated.confirmationUrl, 'confirmationUrl')\n validateCallbackUrl(validated.validationUrl, 'validationUrl')\n\n const version: C2BApiVersion = validated.apiVersion ?? 'v2'\n\n const payload = {\n ShortCode: String(validated.shortCode),\n ResponseType: validated.responseType,\n ConfirmationURL: validated.confirmationUrl,\n ValidationURL: validated.validationUrl,\n }\n\n const { data } = await httpRequest<C2BRegisterUrlResponse>(\n `${baseUrl}/mpesa/c2b/${version}/registerurl`,\n withDarajaHttp(\n {\n method: 'POST',\n headers: { Authorization: `Bearer ${accessToken}` },\n body: payload,\n },\n http,\n ),\n )\n\n return parseWithSchema(\n C2BRegisterUrlResponseSchema,\n data,\n 'C2B Register URL response',\n ) as C2BRegisterUrlResponse\n}\n","/**\n * src/mpesa/c2b/simulate.ts\n *\n * C2B Simulate implementation (Sandbox ONLY).\n * Strictly aligned with Safaricom Daraja C2B API documentation.\n *\n * Per docs: \"NB: Simulation is not supported on production.\"\n *\n * Endpoint (v2, sandbox only):\n * POST https://sandbox.safaricom.co.ke/mpesa/c2b/v2/simulate\n */\n\nimport { parseWithSchema } from '../../core/validation/zod-error'\nimport { C2BSimulateRequestSchema, C2BSimulateResponseSchema } from '../../schemas/c2b'\nimport { createError } from '../../utils/errors'\nimport { httpRequest, type DarajaHttpOptions, withDarajaHttp } from '../../utils/http'\nimport type { C2BApiVersion, C2BSimulateRequest, C2BSimulateResponse } from './types'\n\n/**\n * Simulates a C2B customer payment. SANDBOX ONLY.\n *\n * Daraja payload shape:\n * {\n * \"ShortCode\": 600984, ← numeric\n * \"CommandID\": \"CustomerPayBillOnline\",\n * \"Amount\": 1, ← numeric, whole number ≥ 1\n * \"Msisdn\": 254708374149, ← numeric\n * \"BillRefNumber\": \"AccountRef\" ← Paybill only; OMIT for BuyGoods\n * }\n *\n * CRITICAL — BillRefNumber handling (per docs):\n * \"Account reference for Customer paybills and null for customer buy goods\"\n * We omit the key entirely for BuyGoods (not null, not \"\") because Daraja\n * validates field presence and rejects even null/empty values for Buy Goods.\n *\n * @param baseUrl - Must be the sandbox base URL\n * @param accessToken - Valid OAuth bearer token from Authorization API\n * @param request - Simulation parameters\n * @returns - Daraja simulate response (ResponseCode \"0\" = accepted)\n */\nexport async function simulateC2B(\n baseUrl: string,\n accessToken: string,\n request: C2BSimulateRequest,\n http?: DarajaHttpOptions,\n): Promise<C2BSimulateResponse> {\n const validated = parseWithSchema(C2BSimulateRequestSchema, request, 'C2B Simulate request')\n\n if (!baseUrl.includes('sandbox')) {\n throw createError({\n code: 'VALIDATION_ERROR',\n message:\n 'C2B simulate is only available in the Sandbox environment (per Daraja docs). ' +\n 'In production, customers initiate payments directly via M-PESA App, USSD, or SIM Toolkit.',\n })\n }\n\n const amount = Math.round(validated.amount)\n const isBuyGoods = validated.commandId === 'CustomerBuyGoodsOnline'\n const version: C2BApiVersion = validated.apiVersion ?? 'v2'\n\n // ── Build payload ───────────────────────────────────────────────────────────\n //\n // ShortCode and Msisdn are sent as numbers per Daraja docs.\n // BillRefNumber is ONLY included for CustomerPayBillOnline.\n // For CustomerBuyGoodsOnline the key must be absent from the payload.\n //\n const payload: Record<string, unknown> = {\n ShortCode: Number(validated.shortCode),\n CommandID: validated.commandId,\n Amount: amount,\n Msisdn: Number(validated.msisdn),\n }\n\n if (!isBuyGoods) {\n payload['BillRefNumber'] = validated.billRefNumber!.trim()\n }\n\n const { data } = await httpRequest<C2BSimulateResponse>(\n `${baseUrl}/mpesa/c2b/${version}/simulate`,\n withDarajaHttp(\n {\n method: 'POST',\n headers: { Authorization: `Bearer ${accessToken}` },\n body: payload,\n },\n http,\n ),\n )\n\n return parseWithSchema(\n C2BSimulateResponseSchema,\n data,\n 'C2B Simulate response',\n ) as C2BSimulateResponse\n}\n","/**\n * src/mpesa/dynamic-qr/generate.ts\n *\n * Core logic for the Safaricom Daraja Dynamic QR Code API.\n *\n * API: POST /mpesa/qrcode/v1/generate\n *\n * Error codes from Daraja docs:\n * 404.001.04 — Invalid Authentication Header\n * 400.002.05 — Invalid Request Payload\n * 400.003.01 — Invalid Access Token\n */\n\nimport { parseWithSchema } from '../../core/validation/zod-error'\nimport { DynamicQRRequestSchema, DynamicQRResponseSchema } from '../../schemas/async-apis'\nimport { PesafyError } from '../../utils/errors'\nimport { httpRequest, type DarajaHttpOptions, withDarajaHttp } from '../../utils/http'\nimport type {\n DynamicQRDarajaPayload,\n DynamicQRErrorResponse,\n DynamicQRRequest,\n DynamicQRResponse,\n} from './types'\nimport { DEFAULT_QR_SIZE } from './validators'\n\n// ── Daraja error code → PesafyError mapping ───────────────────────────────────\n\n/**\n * Maps Daraja-specific error codes to structured PesafyErrors with\n * actionable developer guidance.\n *\n * @internal\n */\nfunction mapDarajaError(errorCode: string, errorMessage: string): PesafyError {\n switch (errorCode) {\n case '404.001.04':\n return new PesafyError({\n code: 'AUTH_FAILED',\n message:\n 'Daraja rejected the request due to an invalid authentication header. ' +\n 'Ensure the Dynamic QR endpoint is called with POST and that the ' +\n `Authorization: Bearer <token> header is present. Daraja: \"${errorMessage}\"`,\n statusCode: 404,\n })\n\n case '400.003.01':\n return new PesafyError({\n code: 'AUTH_FAILED',\n message:\n 'The M-PESA access token is invalid or has expired. ' +\n 'Call clearTokenCache() on the Mpesa instance to force a token refresh ' +\n `and retry the request. Daraja: \"${errorMessage}\"`,\n statusCode: 401,\n })\n\n case '400.002.05':\n return new PesafyError({\n code: 'VALIDATION_ERROR',\n message:\n 'Daraja rejected the request payload as malformed. ' +\n 'Verify that all required fields (MerchantName, RefNo, Amount, TrxCode, CPI, Size) ' +\n `are present and have correct types. Daraja: \"${errorMessage}\"`,\n statusCode: 400,\n })\n\n default:\n return new PesafyError({\n code: 'REQUEST_FAILED',\n message: `Dynamic QR request failed (${errorCode}): ${errorMessage}`,\n statusCode: 400,\n })\n }\n}\n\n/**\n * Returns `true` when the raw response body looks like a Daraja error object.\n * @internal\n */\nfunction isDarajaError(body: unknown): body is DynamicQRErrorResponse {\n return (\n typeof body === 'object' &&\n body !== null &&\n 'errorCode' in body &&\n typeof (body as DynamicQRErrorResponse).errorCode === 'string'\n )\n}\n\n/**\n * Returns `true` when the response has the required QR success fields.\n * @internal\n */\nfunction isDarajaSuccess(body: unknown): body is DynamicQRResponse {\n return (\n typeof body === 'object' &&\n body !== null &&\n 'ResponseCode' in body &&\n 'QRCode' in body &&\n typeof (body as DynamicQRResponse).QRCode === 'string' &&\n (body as DynamicQRResponse).QRCode.length > 0\n )\n}\n\n// ── Public API ────────────────────────────────────────────────────────────────\n\n/**\n * Generates a Dynamic M-PESA QR Code via the Safaricom Daraja API.\n *\n * The QR code can be rendered directly in a browser as a base64 PNG:\n * ```html\n * <img src=\"data:image/png;base64,{response.QRCode}\" />\n * ```\n * Or written to disk:\n * ```ts\n * import { writeFileSync } from 'node:fs'\n * writeFileSync('qr.png', Buffer.from(response.QRCode, 'base64'))\n * ```\n *\n * @param baseUrl - Daraja base URL (`https://sandbox.safaricom.co.ke` or\n * `https://api.safaricom.co.ke`)\n * @param accessToken - Valid Daraja OAuth2 Bearer token\n * @param request - QR generation parameters (see {@link DynamicQRRequest})\n * @returns - Daraja response including the base64-encoded QR image\n *\n * @throws {PesafyError} `VALIDATION_ERROR` — payload failed pre-flight checks\n * @throws {PesafyError} `AUTH_FAILED` — bad/expired token or wrong headers\n * @throws {PesafyError} `REQUEST_FAILED` — unexpected Daraja error\n *\n * @example\n * ```ts\n * const response = await generateDynamicQR(\n * 'https://sandbox.safaricom.co.ke',\n * accessToken,\n * {\n * merchantName: 'Test Supermarket',\n * refNo: 'INV-001',\n * amount: 500,\n * trxCode: 'BG',\n * cpi: '373132',\n * size: 300,\n * },\n * )\n * console.log(response.QRCode) // base64 PNG\n * ```\n */\nexport async function generateDynamicQR(\n baseUrl: string,\n accessToken: string,\n request: DynamicQRRequest,\n http?: DarajaHttpOptions,\n): Promise<DynamicQRResponse> {\n const validated = parseWithSchema(DynamicQRRequestSchema, request, 'Dynamic QR request')\n\n // ── Guard: access token must be non-empty ───────────────────────────────\n\n if (!accessToken || typeof accessToken !== 'string' || accessToken.trim().length === 0) {\n throw new PesafyError({\n code: 'AUTH_FAILED',\n message:\n 'accessToken is required. Obtain one via the Daraja Authorization API ' +\n '(GET /oauth/v1/generate?grant_type=client_credentials).',\n })\n }\n\n // ── 3. Build Daraja payload (camelCase → PascalCase, size coerced to string) ─\n\n const size = validated.size ?? DEFAULT_QR_SIZE\n const amount = Math.round(validated.amount)\n\n const payload: DynamicQRDarajaPayload = {\n MerchantName: validated.merchantName.trim(),\n RefNo: validated.refNo.trim(),\n Amount: amount,\n TrxCode: validated.trxCode,\n CPI: validated.cpi.trim(),\n Size: String(size),\n }\n\n // ── 4. HTTP request ────────────────────────────────────────────────────────\n\n const url = `${baseUrl}/mpesa/qrcode/v1/generate`\n\n const { data } = await httpRequest<DynamicQRResponse | DynamicQRErrorResponse>(\n url,\n withDarajaHttp(\n {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${accessToken}`,\n },\n body: payload,\n },\n http,\n ),\n )\n\n // ── 5. Handle Daraja error responses ──────────────────────────────────────\n\n if (isDarajaError(data)) {\n throw mapDarajaError(data.errorCode, data.errorMessage)\n }\n\n // ── 6. Guard against malformed success responses ──────────────────────────\n\n if (!isDarajaSuccess(data)) {\n throw new PesafyError({\n code: 'REQUEST_FAILED',\n message:\n 'Daraja returned an unexpected response structure for the Dynamic QR request. ' +\n 'The response was missing required fields (ResponseCode, QRCode). ' +\n `Raw response: ${JSON.stringify(data).slice(0, 300)}`,\n })\n }\n\n return parseWithSchema(DynamicQRResponseSchema, data, 'Dynamic QR response') as DynamicQRResponse\n}\n","/**\n * src/mpesa/reversal/types.ts\n *\n * Transaction Reversal types, constants, and helpers.\n *\n * API: POST /mpesa/reversal/v1/request\n *\n * Reverses a completed M-PESA C2B transaction. The API is ASYNCHRONOUS —\n * the synchronous response is only an acknowledgement. The actual reversal\n * result is POSTed to your ResultURL after processing.\n *\n * Required org portal role: \"Org Reversals Initiator\"\n *\n * Per Daraja docs:\n * RecieverIdentifierType MUST always be \"11\" for reversals.\n * CommandID MUST always be \"TransactionReversal\".\n *\n * Ref: Reversals — Safaricom Daraja Developer Portal\n */\n\n// ── Constants ──────────────────────────────────────────────────────────────────\n\n/**\n * CommandID for the Reversals API.\n * Only \"TransactionReversal\" is allowed per Daraja docs.\n */\nexport const REVERSAL_COMMAND_ID = 'TransactionReversal' as const\n\n/**\n * RecieverIdentifierType for the Reversals API.\n * Per Daraja docs: \"Type of Organization (should be '11')\".\n * This value is fixed for all reversal requests.\n */\nexport const REVERSAL_RECEIVER_IDENTIFIER_TYPE = '11' as const\n\n/**\n * Result codes returned in the async callback POSTed to your ResultURL.\n *\n * Per Daraja Reversals documentation:\n *\n * | ResultCode | Meaning |\n * |------------|----------------------------------------------------|\n * | 0 | Success — transaction reversed |\n * | 1 | Insufficient balance |\n * | 11 | DebitParty in invalid state (shortcode not active) |\n * | 21 | Initiator not allowed to initiate reversals |\n * | 2001 | Initiator information invalid (bad credentials) |\n * | 2006 | Declined due to account rule (shortcode inactive) |\n * | 2028 | Not permitted (shortcode lacks reversal permission)|\n * | 8006 | Security credential locked |\n * | R000001 | Transaction already reversed |\n * | R000002 | OriginalTransactionID is invalid / does not exist |\n */\nexport const REVERSAL_RESULT_CODES = {\n /** Transaction reversed successfully */\n SUCCESS: 0,\n /** Organisation shortcode has insufficient balance */\n INSUFFICIENT_BALANCE: 1,\n /** DebitParty (shortcode) is in an invalid state */\n DEBIT_PARTY_INVALID_STATE: 11,\n /** Initiator does not have the Org Reversals Initiator role */\n INITIATOR_NOT_ALLOWED: 21,\n /** API user credentials are invalid */\n INITIATOR_INFORMATION_INVALID: 2001,\n /** Organisation/shortcode account is not active */\n DECLINED_ACCOUNT_RULE: 2006,\n /** Shortcode has no permission to perform this reversal */\n NOT_PERMITTED: 2028,\n /** API user password is locked — too many failed attempts */\n SECURITY_CREDENTIAL_LOCKED: 8006,\n /** The TransactionID has already been reversed */\n ALREADY_REVERSED: 'R000001',\n /** The TransactionID is invalid or does not exist on M-PESA */\n INVALID_TRANSACTION_ID: 'R000002',\n} as const satisfies Record<string, number | string>\n\nexport type ReversalResultCode = (typeof REVERSAL_RESULT_CODES)[keyof typeof REVERSAL_RESULT_CODES]\n\n/**\n * API-level error codes returned in the synchronous HTTP error response.\n *\n * Per Daraja Reversals error response documentation:\n *\n * | errorCode | Meaning | HTTP |\n * |-----------------|-------------------------------------------------|------|\n * | 404.001.03 | Invalid Access Token | 404 |\n * | 400.002.02 | Bad Request — Invalid payload field | 400 |\n * | 404.001.01 | Resource not found (wrong endpoint) | 404 |\n * | 500.001.1001 | Internal Server Error | 500 |\n * | 500.003.02 | Spike Arrest Violation (TPS limit exceeded) | 500 |\n * | 500.003.03 | Quota Violation (API request limit exceeded) | 500 |\n */\nexport const REVERSAL_ERROR_CODES = {\n /** Access token is invalid or expired. Regenerate and retry. */\n INVALID_ACCESS_TOKEN: '404.001.03',\n /** Request payload is malformed or missing required fields. */\n BAD_REQUEST: '400.002.02',\n /** Wrong endpoint URL — verify you are calling /mpesa/reversal/v1/request */\n RESOURCE_NOT_FOUND: '404.001.01',\n /** Internal server error on Daraja. Verify payload matches documentation. */\n INTERNAL_SERVER_ERROR: '500.001.1001',\n /** Too many requests per second — reduce TPS. */\n SPIKE_ARREST: '500.003.02',\n /** API request quota exceeded. */\n QUOTA_VIOLATION: '500.003.03',\n} as const\n\nexport type ReversalErrorCode = (typeof REVERSAL_ERROR_CODES)[keyof typeof REVERSAL_ERROR_CODES]\n\n// ── ResultParameter keys ──────────────────────────────────────────────────────\n\n/**\n * Keys present in the ResultParameters.ResultParameter array of a successful\n * reversal callback, as documented by Daraja.\n */\nexport type ReversalResultParameterKey =\n | 'DebitAccountBalance'\n | 'Amount'\n | 'TransCompletedTime'\n | 'OriginalTransactionID'\n | 'Charge'\n | 'CreditPartyPublicName'\n | 'DebitPartyPublicName'\n\n// ── Request ───────────────────────────────────────────────────────────────────\n\n/**\n * Parameters for the Reversals API request.\n *\n * Daraja request body shape:\n * ```json\n * {\n * \"Initiator\": \"apiop37\",\n * \"SecurityCredential\": \"RC6E9WDx9X2c6z3gp0oC5Th==\",\n * \"CommandID\": \"TransactionReversal\",\n * \"TransactionID\": \"PDU91HIVIT\",\n * \"Amount\": \"200\",\n * \"ReceiverParty\": \"603021\",\n * \"RecieverIdentifierType\":\"11\",\n * \"ResultURL\": \"https://example.org/reversal/result\",\n * \"QueueTimeOutURL\": \"https://example.org/reversal/queue\",\n * \"Remarks\": \"Payment reversal\"\n * }\n * ```\n *\n * Note: Initiator and SecurityCredential are supplied by the SDK layer;\n * they are not part of this user-facing request interface.\n */\nexport interface ReversalRequest {\n /**\n * The M-PESA receipt number of the transaction to reverse.\n * Example: \"PDU91HIVIT\"\n * Daraja field: TransactionID\n */\n transactionId: string\n\n /**\n * Your organisation shortcode (Paybill or Till number).\n * Daraja field: ReceiverParty\n */\n receiverParty: string\n\n /**\n * Identifier type for ReceiverParty.\n * Per Daraja docs: MUST be \"11\" (Organisation/ShortCode reversal type).\n * This value is validated and always sent as \"11\".\n * Daraja field: RecieverIdentifierType (note Daraja's spelling)\n *\n * @default \"11\"\n */\n receiverIdentifierType?: '11'\n\n /**\n * Amount to reverse. Must equal the original transaction amount.\n * Must be a whole number ≥ 1.\n * Daraja field: Amount (sent as string per Daraja docs sample)\n */\n amount: number\n\n /**\n * URL where Safaricom POSTs the reversal result asynchronously.\n * Must be publicly accessible over the internet.\n * Daraja field: ResultURL\n */\n resultUrl: string\n\n /**\n * URL called when the request times out in the Daraja queue.\n * Must be publicly accessible over the internet.\n * Daraja field: QueueTimeOutURL\n */\n queueTimeOutUrl: string\n\n /**\n * Short description of the reversal reason (2–100 characters).\n * Daraja field: Remarks\n * @default \"Transaction Reversal\"\n */\n remarks?: string\n\n /**\n * Optional occasion/reference string.\n * Not in the Daraja sample payload but accepted by the API.\n * Daraja field: Occasion\n */\n occasion?: string\n}\n\n// ── Synchronous acknowledgement ───────────────────────────────────────────────\n\n/**\n * Synchronous response from the Reversals API.\n *\n * This is only an acknowledgement that the request was received.\n * The actual reversal result arrives asynchronously at your ResultURL.\n *\n * Daraja response shape:\n * ```json\n * {\n * \"OriginatorConversationID\": \"f1e2-4b95-a71d-b30d3cdbb7a7735297\",\n * \"ConversationID\": \"AG_20210706_20106e9209f64bebd05b\",\n * \"ResponseCode\": \"0\",\n * \"ResponseDescription\": \"Accept the service request successfully.\"\n * }\n * ```\n */\nexport interface ReversalResponse {\n /** Unique identifier for this reversal request from M-PESA */\n OriginatorConversationID: string\n /** Unique global identifier for the transaction request */\n ConversationID: string\n /**\n * \"0\" = request accepted successfully.\n * Any other value = API-level error (not reversal failure).\n */\n ResponseCode: string\n /** Human-readable acknowledgement message */\n ResponseDescription: string\n}\n\n// ── Async result callback (POSTed to ResultURL) ───────────────────────────────\n\n/**\n * Result parameter item in a successful reversal callback.\n */\nexport interface ReversalResultParameter {\n Key: ReversalResultParameterKey\n Value: string | number\n}\n\n/**\n * Full async reversal result POSTed to your ResultURL after processing.\n *\n * Successful callback shape (per Daraja docs):\n * ```json\n * {\n * \"Result\": {\n * \"ResultType\": 0,\n * \"ResultCode\": 0,\n * \"ResultDesc\": \"The service request is processed successfully.\",\n * \"OriginatorConversationID\": \"...\",\n * \"ConversationID\": \"AG_...\",\n * \"TransactionID\": \"SKE52PAWR9\",\n * \"ResultParameters\": {\n * \"ResultParameter\": [\n * { \"Key\": \"DebitAccountBalance\", \"Value\": \"Utility Account|KES|...\" },\n * { \"Key\": \"Amount\", \"Value\": 1.00 },\n * { \"Key\": \"TransCompletedTime\", \"Value\": 20211114132711 },\n * { \"Key\": \"OriginalTransactionID\",\"Value\": \"SKC82PACB8\" },\n * { \"Key\": \"Charge\", \"Value\": 0.00 },\n * { \"Key\": \"CreditPartyPublicName\",\"Value\": \"254705912645 - NICHOLAS JOHN SONGOK\" },\n * { \"Key\": \"DebitPartyPublicName\", \"Value\": \"600992 - Safaricom Daraja 992\" }\n * ]\n * },\n * \"ReferenceData\": {\n * \"ReferenceItem\": {\n * \"Key\": \"QueueTimeoutURL\",\n * \"Value\": \"https://...\"\n * }\n * }\n * }\n * }\n * ```\n *\n * Failed callback shape (per Daraja docs):\n * ```json\n * {\n * \"Result\": {\n * \"ResultType\": 0,\n * \"ResultCode\": \"R000002\",\n * \"ResultDesc\": \"The OriginalTransactionID is invalid.\",\n * ...\n * }\n * }\n * ```\n *\n * Note: ResultCode is 0 (number) on success and a string like \"R000002\" on failure.\n */\nexport interface ReversalResult {\n Result: {\n /** Status type (usually 0) */\n ResultType: number\n /**\n * 0 (number) = success.\n * String codes like \"R000002\" = failure.\n * See REVERSAL_RESULT_CODES for all documented values.\n */\n ResultCode: number | string\n /** Human-readable result description */\n ResultDesc: string\n /** Unique identifier for the reversal request */\n OriginatorConversationID: string\n /** Unique identifier from M-PESA */\n ConversationID: string\n /** M-PESA receipt number for the reversal transaction */\n TransactionID: string\n /**\n * Present on success — contains transaction details.\n * Absent on failure.\n */\n ResultParameters?: {\n ResultParameter: ReversalResultParameter[]\n }\n ReferenceData?: {\n ReferenceItem: { Key: string; Value: string } | Array<{ Key: string; Value: string }>\n }\n }\n}\n\n// ── Type guards ───────────────────────────────────────────────────────────────\n\n/**\n * Returns true when the payload matches the shape of a ReversalResult.\n * Works for both success and failure callbacks.\n */\nexport function isReversalResult(body: unknown): body is ReversalResult {\n if (!body || typeof body !== 'object') return false\n const b = body as Record<string, unknown>\n if (!b['Result'] || typeof b['Result'] !== 'object') return false\n const r = b['Result'] as Record<string, unknown>\n return (\n typeof r['ResultCode'] !== 'undefined' &&\n typeof r['ResultDesc'] === 'string' &&\n typeof r['ConversationID'] === 'string'\n )\n}\n\n/**\n * Returns true when the reversal result indicates a successful reversal.\n * A successful reversal has ResultCode === 0 (number).\n */\nexport function isReversalSuccess(result: ReversalResult): boolean {\n return result.Result.ResultCode === REVERSAL_RESULT_CODES.SUCCESS\n}\n\n/**\n * Returns true when the reversal result indicates a failure.\n */\nexport function isReversalFailure(result: ReversalResult): boolean {\n return !isReversalSuccess(result)\n}\n\n/**\n * Returns true when the specified result code is a known documented code.\n */\nexport function isKnownReversalResultCode(code: number | string): code is ReversalResultCode {\n return Object.values(REVERSAL_RESULT_CODES).includes(code as ReversalResultCode)\n}\n\n// ── Result extractors ─────────────────────────────────────────────────────────\n\n/**\n * Extracts the M-PESA receipt number for the reversal transaction.\n * Returns null if the result does not contain a TransactionID.\n */\nexport function getReversalTransactionId(result: ReversalResult): string | null {\n return result.Result.TransactionID ?? null\n}\n\n/**\n * Extracts the ConversationID from the reversal result.\n */\nexport function getReversalConversationId(result: ReversalResult): string {\n return result.Result.ConversationID\n}\n\n/**\n * Extracts the OriginatorConversationID from the reversal result.\n */\nexport function getReversalOriginatorConversationId(result: ReversalResult): string {\n return result.Result.OriginatorConversationID\n}\n\n/**\n * Extracts the ResultCode from the reversal result.\n * Returns 0 for success, or a string/number error code for failure.\n */\nexport function getReversalResultCode(result: ReversalResult): number | string {\n return result.Result.ResultCode\n}\n\n/**\n * Extracts the ResultDesc from the reversal result.\n */\nexport function getReversalResultDesc(result: ReversalResult): string {\n return result.Result.ResultDesc\n}\n\n/**\n * Extracts a named parameter value from the ResultParameters of a successful\n * reversal callback. Returns undefined if absent or if the reversal failed.\n *\n * @example\n * const amount = getReversalResultParam(result, 'Amount')\n * const origTxId = getReversalResultParam(result, 'OriginalTransactionID')\n */\nexport function getReversalResultParam(\n result: ReversalResult,\n key: ReversalResultParameterKey,\n): string | number | undefined {\n const params = result.Result.ResultParameters?.ResultParameter\n if (!params) return undefined\n return params.find((p) => p.Key === key)?.Value\n}\n\n/**\n * Extracts the reversed transaction amount from a successful reversal callback.\n */\nexport function getReversalAmount(result: ReversalResult): number | undefined {\n const val = getReversalResultParam(result, 'Amount')\n return val !== undefined ? Number(val) : undefined\n}\n\n/**\n * Extracts the original TransactionID that was reversed.\n * This is the M-PESA receipt number of the original C2B transaction.\n */\nexport function getReversalOriginalTransactionId(result: ReversalResult): string | undefined {\n const val = getReversalResultParam(result, 'OriginalTransactionID')\n return val !== undefined ? String(val) : undefined\n}\n\n/**\n * Extracts the CreditPartyPublicName from a successful reversal callback.\n * Format: \"254705912645 - NICHOLAS JOHN SONGOK\"\n */\nexport function getReversalCreditPartyPublicName(result: ReversalResult): string | undefined {\n const val = getReversalResultParam(result, 'CreditPartyPublicName')\n return val !== undefined ? String(val) : undefined\n}\n\n/**\n * Extracts the DebitPartyPublicName from a successful reversal callback.\n * Format: \"600992 - Safaricom Daraja 992\"\n */\nexport function getReversalDebitPartyPublicName(result: ReversalResult): string | undefined {\n const val = getReversalResultParam(result, 'DebitPartyPublicName')\n return val !== undefined ? String(val) : undefined\n}\n\n/**\n * Extracts the DebitAccountBalance from a successful reversal callback.\n * Format: \"Utility Account|KES|7722179.62|7722179.62|0.00|0.00\"\n */\nexport function getReversalDebitAccountBalance(result: ReversalResult): string | undefined {\n const val = getReversalResultParam(result, 'DebitAccountBalance')\n return val !== undefined ? String(val) : undefined\n}\n\n/**\n * Extracts the reversal completion timestamp.\n * Format: YYYYMMDDHHmmss (e.g. 20211114132711)\n */\nexport function getReversalCompletedTime(result: ReversalResult): number | undefined {\n const val = getReversalResultParam(result, 'TransCompletedTime')\n return val !== undefined ? Number(val) : undefined\n}\n\n/**\n * Extracts the Charge from a successful reversal callback.\n * Per docs: usually 0.00 for reversals.\n */\nexport function getReversalCharge(result: ReversalResult): number | undefined {\n const val = getReversalResultParam(result, 'Charge')\n return val !== undefined ? Number(val) : undefined\n}\n","/**\n * src/mpesa/reversal/request.ts\n *\n * Transaction Reversal — reverses a completed M-PESA C2B transaction.\n *\n * API: POST /mpesa/reversal/v1/request\n *\n * ASYNCHRONOUS: The synchronous response is acknowledgement only.\n * The actual reversal result is POSTed to your ResultURL after processing.\n *\n * Per Daraja docs:\n * - CommandID is always \"TransactionReversal\"\n * - RecieverIdentifierType is always \"11\" (Organisation ShortCode for reversals)\n * - Amount is sent as a string per the Daraja sample payload\n * - Remarks must be 2–100 characters\n * - Cannot be used for B2C reversals (those are done on the M-PESA portal)\n *\n * Required org portal role: \"Org Reversals Initiator\"\n */\n\nimport { parseWithSchema } from '../../core/validation/zod-error'\nimport { ReversalRequestSchema, ReversalResponseSchema } from '../../schemas/async-apis'\nimport { httpRequest, type DarajaHttpOptions, withDarajaHttp } from '../../utils/http'\nimport {\n REVERSAL_COMMAND_ID,\n REVERSAL_RECEIVER_IDENTIFIER_TYPE,\n type ReversalRequest,\n type ReversalResponse,\n} from './types'\n\nexport async function requestReversal(\n baseUrl: string,\n accessToken: string,\n securityCredential: string,\n initiatorName: string,\n request: ReversalRequest,\n http?: DarajaHttpOptions,\n): Promise<ReversalResponse> {\n const validated = parseWithSchema(ReversalRequestSchema, request, 'Reversal request')\n const amount = Math.round(validated.amount)\n const remarks = validated.remarks ?? 'Transaction Reversal'\n\n const payload: Record<string, unknown> = {\n Initiator: initiatorName,\n SecurityCredential: securityCredential,\n CommandID: REVERSAL_COMMAND_ID,\n TransactionID: validated.transactionId,\n Amount: String(amount),\n ReceiverParty: String(validated.receiverParty),\n RecieverIdentifierType: REVERSAL_RECEIVER_IDENTIFIER_TYPE,\n ResultURL: validated.resultUrl,\n QueueTimeOutURL: validated.queueTimeOutUrl,\n Remarks: remarks,\n }\n\n if (validated.occasion !== undefined && validated.occasion !== null) {\n payload['Occasion'] = validated.occasion\n }\n\n const { data } = await httpRequest<ReversalResponse>(\n `${baseUrl}/mpesa/reversal/v1/request`,\n withDarajaHttp(\n {\n method: 'POST',\n headers: { Authorization: `Bearer ${accessToken}` },\n body: payload,\n },\n http,\n ),\n )\n\n return parseWithSchema(ReversalResponseSchema, data, 'Reversal response') as ReversalResponse\n}\n","import { z } from 'zod'\nimport { NonEmptyStringSchema, UrlSchema } from './common'\n\nexport const TransactionTypeSchema = z.enum(['CustomerPayBillOnline', 'CustomerBuyGoodsOnline'])\n\nexport const StkPushRequestSchema = z.object({\n amount: z.number().finite({ message: 'amount must be a finite number (not NaN or Infinity)' }),\n phoneNumber: NonEmptyStringSchema,\n shortCode: NonEmptyStringSchema,\n passKey: NonEmptyStringSchema,\n callbackUrl: UrlSchema,\n accountReference: NonEmptyStringSchema,\n transactionDesc: NonEmptyStringSchema,\n transactionType: TransactionTypeSchema.optional(),\n partyB: z.string().optional(),\n})\n\nexport const StkPushResponseSchema = z\n .object({\n MerchantRequestID: z.string(),\n CheckoutRequestID: z.string(),\n ResponseCode: z.string(),\n ResponseDescription: z.string(),\n CustomerMessage: z.string().optional(),\n })\n .passthrough()\n\nexport const StkQueryRequestSchema = z.object({\n checkoutRequestId: NonEmptyStringSchema,\n shortCode: NonEmptyStringSchema,\n passKey: NonEmptyStringSchema,\n})\n\nexport const StkQueryResponseSchema = z\n .object({\n ResponseCode: z.string(),\n ResponseDescription: z.string(),\n MerchantRequestID: z.string().optional(),\n CheckoutRequestID: z.string().optional(),\n ResultCode: z.union([z.string(), z.number()]).optional(),\n ResultDesc: z.string().optional(),\n })\n .passthrough()\n\nexport const StkPushWebhookSchema = z.object({\n Body: z.object({\n stkCallback: z\n .object({\n MerchantRequestID: z.string(),\n CheckoutRequestID: z.string(),\n ResultCode: z.number(),\n ResultDesc: z.string(),\n CallbackMetadata: z\n .object({\n Item: z.array(\n z.object({\n Name: z.string(),\n Value: z.union([z.string(), z.number()]),\n }),\n ),\n })\n .optional(),\n })\n .passthrough(),\n }),\n})\n\nexport type StkPushRequestInput = z.infer<typeof StkPushRequestSchema>\nexport type StkPushResponseOutput = z.infer<typeof StkPushResponseSchema>\n","/**\n * src/mpesa/stk-push/types.ts\n *\n * Types, constants, and helpers for the M-PESA STK Push (M-PESA Express) API.\n *\n * Daraja docs:\n * STK Push → POST /mpesa/stkpush/v1/processrequest\n * STK Query → POST /mpesa/stkpushquery/v1/query\n */\n\n// ── Transaction type ──────────────────────────────────────────────────────────\n\nexport type TransactionType = 'CustomerPayBillOnline' | 'CustomerBuyGoodsOnline'\n\n// ── Transaction limits (from Daraja docs) ─────────────────────────────────────\n\n/**\n * M-PESA transaction limits as documented by Safaricom Daraja.\n *\n * | Limit | Value |\n * |------------------|-----------|\n * | Min per tx | KES 1 |\n * | Max per tx | KES 250 000|\n * | Max daily | KES 500 000|\n * | Max balance | KES 500 000|\n */\nexport const STK_PUSH_LIMITS = {\n MIN_AMOUNT: 1,\n MAX_AMOUNT: 250_000,\n} as const\n\n// ── Result codes (from Daraja docs) ──────────────────────────────────────────\n\n/**\n * All documented STK Push / Query result codes.\n *\n * These codes appear in:\n * - STK Query response → `ResultCode` field\n * - STK Callback body → `Body.stkCallback.ResultCode`\n *\n * | Code | Meaning |\n * |------|--------------------------|\n * | 0 | Transaction successful |\n * | 1 | Insufficient balance |\n * | 1032 | Cancelled by user |\n * | 1037 | Phone unreachable |\n * | 2001 | Invalid PIN |\n */\nexport const STK_RESULT_CODES = {\n SUCCESS: 0,\n INSUFFICIENT_BALANCE: 1,\n CANCELLED_BY_USER: 1032,\n PHONE_UNREACHABLE: 1037,\n INVALID_PIN: 2001,\n} as const satisfies Record<string, number>\n\n/** Union of all documented STK result code values */\nexport type StkResultCode = (typeof STK_RESULT_CODES)[keyof typeof STK_RESULT_CODES]\n\n/**\n * Returns true when `code` is one of the documented STK result codes.\n * Useful for narrowing unknown values from the Daraja response.\n */\nexport function isKnownStkResultCode(code: number): code is StkResultCode {\n return Object.values(STK_RESULT_CODES).includes(code as StkResultCode)\n}\n\n// ── STK Push request ─────────────────────────────────────────────────────────\n\nexport interface StkPushRequest {\n /**\n * Transaction amount in KES.\n * Min: {@link STK_PUSH_LIMITS.MIN_AMOUNT} (KES 1)\n * Max: {@link STK_PUSH_LIMITS.MAX_AMOUNT} (KES 250 000)\n * Must round to a whole number ≥ 1.\n * Daraja field: `Amount`\n */\n amount: number\n\n /**\n * Phone number sending the money. Format: 2547XXXXXXXX.\n * Must be a valid Safaricom M-PESA number.\n * Daraja fields: `PartyA`, `PhoneNumber`\n */\n phoneNumber: string\n\n /**\n * URL where Safaricom will POST the callback result.\n * Must be publicly accessible (use ngrok/localtunnel for local dev).\n * Daraja field: `CallBackURL`\n */\n callbackUrl: string\n\n /**\n * Alpha-numeric reference shown to customer in the USSD prompt.\n * Truncated to max 12 characters before sending.\n * Daraja field: `AccountReference`\n */\n accountReference: string\n\n /**\n * Additional description for the transaction.\n * Truncated to max 13 characters before sending.\n * Daraja field: `TransactionDesc`\n */\n transactionDesc: string\n\n /**\n * Business shortcode — Paybill number or HO/Store number for Till.\n * Daraja field: `BusinessShortCode`\n */\n shortCode: string\n\n /**\n * Passkey used to generate the Password.\n * Sandbox: from Daraja simulator test data.\n * Production: emailed after Go Live.\n */\n passKey: string\n\n /**\n * \"CustomerPayBillOnline\" (default) for Paybill.\n * \"CustomerBuyGoodsOnline\" for Till Numbers.\n * Daraja field: `TransactionType`\n */\n transactionType?: TransactionType\n\n /**\n * Credit party receiving funds.\n * - CustomerPayBillOnline → defaults to `shortCode`\n * - CustomerBuyGoodsOnline → set to the Till Number\n * Daraja field: `PartyB`\n */\n partyB?: string\n}\n\n// ── STK Push response ────────────────────────────────────────────────────────\n\n/**\n * Synchronous acknowledgement returned immediately after STK Push submission.\n * This is NOT the payment result — the result arrives via callback.\n *\n * Daraja endpoint: POST /mpesa/stkpush/v1/processrequest\n */\nexport interface StkPushResponse {\n /** Global unique identifier for the submitted payment request */\n MerchantRequestID: string\n /** Global unique identifier for the checkout transaction — use this for STK Query */\n CheckoutRequestID: string\n /**\n * \"0\" = request accepted successfully.\n * Any other value = submission error.\n */\n ResponseCode: string\n /** Human-readable submission status */\n ResponseDescription: string\n /** Message shown to customer on their device */\n CustomerMessage: string\n}\n\n// ── STK Query request / response ─────────────────────────────────────────────\n\n/**\n * Parameters for querying the status of a previously initiated STK Push.\n *\n * Daraja endpoint: POST /mpesa/stkpushquery/v1/query\n */\nexport interface StkQueryRequest {\n /** `CheckoutRequestID` from the STK Push response */\n checkoutRequestId: string\n shortCode: string\n passKey: string\n}\n\n/**\n * Response from the STK Query API.\n *\n * Note: `ResultCode` is documented as \"Numeric\" by Daraja.\n * The Daraja JSON example shows it as a string (`\"ResultCode\": \"0\"`),\n * but actual API responses return a number. Typed as `number` here.\n * Use {@link STK_RESULT_CODES} constants for comparisons.\n */\nexport interface StkQueryResponse {\n /** \"0\" = request accepted. Not the final payment status. */\n ResponseCode: string\n /** Submission status message */\n ResponseDescription: string\n /** Unique identifier for the merchant request */\n MerchantRequestID: string\n /** Unique identifier for the checkout transaction */\n CheckoutRequestID: string\n /**\n * Payment processing status code.\n * See {@link STK_RESULT_CODES} for all documented values:\n * 0 → success\n * 1 → insufficient balance\n * 1032 → cancelled by user\n * 1037 → phone unreachable\n * 2001 → wrong PIN\n */\n ResultCode: number\n /** Human-readable description of the result */\n ResultDesc: string\n}\n\n// ── Callback payload types ────────────────────────────────────────────────────\n\n/**\n * Single metadata item in a successful STK callback.\n * Daraja always sends these 4 items on success; `Balance` may appear on some accounts.\n */\nexport interface StkCallbackMetadataItem {\n Name: 'Amount' | 'MpesaReceiptNumber' | 'TransactionDate' | 'PhoneNumber' | 'Balance'\n /**\n * Present on successful transactions; absent on failure.\n * `TransactionDate` and `PhoneNumber` come back as numbers from Daraja.\n */\n Value?: number | string\n}\n\n/** Inner callback for a SUCCESSFUL STK Push — `ResultCode === 0` */\nexport interface StkCallbackSuccess {\n MerchantRequestID: string\n CheckoutRequestID: string\n ResultCode: typeof STK_RESULT_CODES.SUCCESS\n ResultDesc: string\n CallbackMetadata: {\n Item: StkCallbackMetadataItem[]\n }\n}\n\n/**\n * Inner callback for a FAILED or CANCELLED STK Push.\n * `ResultCode` is one of 1, 1032, 1037, 2001 (or another undocumented code).\n */\nexport interface StkCallbackFailure {\n MerchantRequestID: string\n CheckoutRequestID: string\n /** e.g. 1032 = cancelled by user, 1037 = timeout */\n ResultCode: number\n ResultDesc: string\n CallbackMetadata?: never\n}\n\nexport type StkCallbackInner = StkCallbackSuccess | StkCallbackFailure\n\n/**\n * Full wrapper Safaricom POSTs to your `CallBackURL`.\n *\n * Successful example:\n * ```json\n * {\n * \"Body\": {\n * \"stkCallback\": {\n * \"MerchantRequestID\": \"29115\",\n * \"CheckoutRequestID\": \"ws_CO_191220\",\n * \"ResultCode\": 0,\n * \"ResultDesc\": \"Success\",\n * \"CallbackMetadata\": {\n * \"Item\": [\n * { \"Name\": \"Amount\", \"Value\": 1.00 },\n * { \"Name\": \"MpesaReceiptNumber\", \"Value\": \"NLJ7RT61SV\" },\n * { \"Name\": \"TransactionDate\", \"Value\": 20191219102115 },\n * { \"Name\": \"PhoneNumber\", \"Value\": 254708374149 }\n * ]\n * }\n * }\n * }\n * }\n * ```\n */\nexport interface StkPushCallback {\n Body: {\n stkCallback: StkCallbackInner\n }\n}\n\n// ── Type guards & helpers ─────────────────────────────────────────────────────\n\n/**\n * Narrows `StkCallbackInner` to the success shape.\n * A callback is successful when `ResultCode === 0`.\n *\n * @example\n * if (isStkCallbackSuccess(callback.Body.stkCallback)) {\n * const receipt = getCallbackValue(callback, 'MpesaReceiptNumber')\n * }\n */\nexport function isStkCallbackSuccess(cb: StkCallbackInner): cb is StkCallbackSuccess {\n return cb.ResultCode === STK_RESULT_CODES.SUCCESS\n}\n\n/**\n * Extracts a named value from a successful callback's metadata.\n * Returns `undefined` if the key is absent or the transaction failed.\n *\n * @example\n * const receipt = getCallbackValue(callback, 'MpesaReceiptNumber') // \"NLJ7RT61SV\"\n * const amount = getCallbackValue(callback, 'Amount') // 1\n * const phone = getCallbackValue(callback, 'PhoneNumber') // 254708374149\n * const date = getCallbackValue(callback, 'TransactionDate') // 20191219102115\n */\nexport function getCallbackValue(\n callback: StkPushCallback,\n name: StkCallbackMetadataItem['Name'],\n): string | number | undefined {\n const inner = callback.Body.stkCallback\n if (!isStkCallbackSuccess(inner)) return undefined\n return inner.CallbackMetadata.Item.find((i) => i.Name === name)?.Value\n}\n","// src/mpesa/stk-push/utils.ts\n\nexport { formatSafaricomPhone as formatPhoneNumber } from '../../utils/phone'\n\n/**\n * Generates the STK Push password.\n * Formula: Base64( Shortcode + Passkey + Timestamp )\n *\n * Uses btoa() — works in Node.js ≥18, Bun, browsers, and edge runtimes.\n */\nexport function getStkPushPassword(shortCode: string, passKey: string, timestamp: string): string {\n return btoa(`${shortCode}${passKey}${timestamp}`)\n}\n\n/**\n * Returns a Daraja-compatible timestamp: YYYYMMDDHHmmss\n *\n * Call this ONCE per request and reuse the result.\n */\nexport function getTimestamp(): string {\n const now = new Date()\n const pad = (n: number): string => 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 * src/mpesa/stk-push/stk-push.ts\n *\n * Initiates an STK Push (M-PESA Express) payment.\n *\n * Daraja endpoint: POST /mpesa/stkpush/v1/processrequest\n *\n * Transaction limits (Daraja docs):\n * Min per transaction: KES 1\n * Max per transaction: KES 250,000\n */\n\nimport { parseWithSchema } from '../../core/validation/zod-error'\nimport { StkPushRequestSchema, StkPushResponseSchema } from '../../schemas/stk-push'\nimport { PesafyError } from '../../utils/errors'\nimport { httpRequest, type DarajaHttpOptions, withDarajaHttp } from '../../utils/http'\nimport { STK_PUSH_LIMITS, type StkPushRequest, type StkPushResponse } from './types'\nimport { formatPhoneNumber, getStkPushPassword, getTimestamp } from './utils'\n\nexport async function processStkPush(\n baseUrl: string,\n accessToken: string,\n request: StkPushRequest,\n http?: DarajaHttpOptions,\n): Promise<StkPushResponse> {\n const validated = parseWithSchema(StkPushRequestSchema, request, 'STK Push request')\n\n // ── Amount validation (Daraja transaction limits) ──────────────────────────\n\n if (!Number.isFinite(validated.amount)) {\n throw new PesafyError({\n code: 'VALIDATION_ERROR',\n message: `amount must be a finite number (got ${validated.amount}).`,\n })\n }\n\n const amount = Math.round(validated.amount)\n\n if (amount < STK_PUSH_LIMITS.MIN_AMOUNT) {\n throw new PesafyError({\n code: 'VALIDATION_ERROR',\n message:\n `Amount must be at least KES ${STK_PUSH_LIMITS.MIN_AMOUNT} ` +\n `(got ${validated.amount} which rounds to ${amount}).`,\n })\n }\n\n if (amount > STK_PUSH_LIMITS.MAX_AMOUNT) {\n throw new PesafyError({\n code: 'VALIDATION_ERROR',\n message:\n `Amount must not exceed KES ${STK_PUSH_LIMITS.MAX_AMOUNT.toLocaleString()} per transaction ` +\n `as per Safaricom Daraja limits (got ${validated.amount} which rounds to ${amount}).`,\n })\n }\n\n // ── Generate timestamp ONCE ─────────────────────────────────────────────────\n // The Password and Timestamp fields MUST use the same timestamp value.\n // Daraja format: YYYYMMDDHHmmss\n const timestamp = getTimestamp()\n\n // ── PartyB logic ────────────────────────────────────────────────────────────\n // CustomerPayBillOnline → PartyB = shortCode (Paybill number)\n // CustomerBuyGoodsOnline → PartyB = till number (passed as request.partyB)\n const partyB = validated.partyB ?? validated.shortCode\n\n // ── Build Daraja request body ───────────────────────────────────────────────\n // All 11 fields documented by Safaricom Daraja are present.\n const body = {\n BusinessShortCode: validated.shortCode,\n Password: getStkPushPassword(validated.shortCode, validated.passKey, timestamp),\n Timestamp: timestamp,\n TransactionType: validated.transactionType ?? 'CustomerPayBillOnline',\n Amount: amount,\n PartyA: formatPhoneNumber(validated.phoneNumber),\n PartyB: partyB,\n PhoneNumber: formatPhoneNumber(validated.phoneNumber),\n CallBackURL: validated.callbackUrl,\n // Daraja docs: AccountReference max 12 chars, TransactionDesc max 13 chars\n AccountReference: validated.accountReference.slice(0, 12),\n TransactionDesc: validated.transactionDesc.slice(0, 13),\n }\n\n // ── HTTP request ────────────────────────────────────────────────────────────\n // httpRequest retries 503/429/5xx with exponential backoff + jitter.\n // If all retries are exhausted it throws PesafyError with code \"REQUEST_FAILED\"\n // and statusCode 503. Never mark a transaction \"failed\" on a 503 — the\n // transaction may have been processed even if the acknowledgement was lost.\n const { data } = await httpRequest<StkPushResponse>(\n `${baseUrl}/mpesa/stkpush/v1/processrequest`,\n withDarajaHttp(\n {\n method: 'POST',\n headers: { Authorization: `Bearer ${accessToken}` },\n body,\n retries: 5,\n retryDelay: 3_000,\n },\n http,\n ),\n )\n\n return parseWithSchema(StkPushResponseSchema, data, 'STK Push response') as StkPushResponse\n}\n","// src/mpesa/stk-push/stk-query.ts\n\nimport { parseWithSchema } from '../../core/validation/zod-error'\nimport { StkQueryRequestSchema, StkQueryResponseSchema } from '../../schemas/stk-push'\nimport { httpRequest, type DarajaHttpOptions, withDarajaHttp } 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 http?: DarajaHttpOptions,\n): Promise<StkQueryResponse> {\n const validated = parseWithSchema(StkQueryRequestSchema, request, 'STK Query request')\n\n const timestamp = getTimestamp()\n\n const body = {\n BusinessShortCode: validated.shortCode,\n Password: getStkPushPassword(validated.shortCode, validated.passKey, timestamp),\n Timestamp: timestamp,\n CheckoutRequestID: validated.checkoutRequestId,\n }\n\n const { data } = await httpRequest<StkQueryResponse>(\n `${baseUrl}/mpesa/stkpushquery/v1/query`,\n withDarajaHttp(\n {\n method: 'POST',\n headers: { Authorization: `Bearer ${accessToken}` },\n body,\n },\n http,\n ),\n )\n\n return parseWithSchema(StkQueryResponseSchema, data, 'STK Query response') as StkQueryResponse\n}\n","// src/mpesa/tax-remittance/remit-tax.ts\n\nimport { parseWithSchema } from '../../core/validation/zod-error'\nimport { TaxRemittanceRequestSchema, TaxRemittanceResponseSchema } from '../../schemas/async-apis'\nimport { httpRequest, type DarajaHttpOptions, withDarajaHttp } from '../../utils/http'\nimport type { TaxRemittanceRequest, TaxRemittanceResponse } from './types'\n\n/** KRA's M-PESA shortcode — the only allowed PartyB for tax remittance */\nexport const KRA_SHORTCODE = '572572'\n\n/** The only CommandID accepted by the Tax Remittance API */\nexport const TAX_COMMAND_ID = 'PayTaxToKRA'\n\n/**\n * Remits tax to Kenya Revenue Authority (KRA) via M-PESA.\n *\n * Endpoint: POST /mpesa/b2b/v1/remittax\n *\n * @param baseUrl - Daraja base URL (sandbox or production)\n * @param accessToken - Valid OAuth bearer token\n * @param securityCredential - RSA-encrypted initiator password (base64)\n * @param initiatorName - M-PESA org portal API operator username\n * @param request - Tax remittance parameters\n * @returns - Daraja synchronous acknowledgement response\n *\n * @example\n * const response = await remitTax(\n * 'https://sandbox.safaricom.co.ke',\n * accessToken,\n * securityCredential,\n * 'TaxPayer',\n * {\n * amount: 239,\n * partyA: '888880',\n * accountReference: '353353',\n * resultUrl: 'https://example.org/b2b/remittax/result/',\n * queueTimeOutUrl: 'https://example.org/b2b/remittax/queue/',\n * },\n * )\n */\nexport async function remitTax(\n baseUrl: string,\n accessToken: string,\n securityCredential: string,\n initiatorName: string,\n request: TaxRemittanceRequest,\n http?: DarajaHttpOptions,\n): Promise<TaxRemittanceResponse> {\n const validated = parseWithSchema(TaxRemittanceRequestSchema, request, 'Tax Remittance request')\n const amount = Math.round(validated.amount)\n\n // ── Build payload matching Daraja spec exactly ──────────────────────────────\n //\n // Fixed values per Daraja Tax Remittance docs:\n // CommandID: \"PayTaxToKRA\" — only valid value\n // SenderIdentifierType: \"4\" — Organisation ShortCode (only allowed)\n // RecieverIdentifierType: \"4\" — Organisation ShortCode (only allowed)\n // PartyB: \"572572\" — KRA shortcode (only allowed)\n\n const payload = {\n Initiator: initiatorName,\n SecurityCredential: securityCredential,\n CommandID: TAX_COMMAND_ID,\n SenderIdentifierType: '4',\n RecieverIdentifierType: '4',\n Amount: String(amount),\n PartyA: String(validated.partyA),\n PartyB: validated.partyB ?? KRA_SHORTCODE,\n AccountReference: validated.accountReference,\n Remarks: validated.remarks ?? 'Tax Remittance',\n QueueTimeOutURL: validated.queueTimeOutUrl,\n ResultURL: validated.resultUrl,\n }\n\n const { data } = await httpRequest<TaxRemittanceResponse>(\n `${baseUrl}/mpesa/b2b/v1/remittax`,\n withDarajaHttp(\n {\n method: 'POST',\n headers: { Authorization: `Bearer ${accessToken}` },\n body: payload,\n },\n http,\n ),\n )\n\n return parseWithSchema(\n TaxRemittanceResponseSchema,\n data,\n 'Tax Remittance response',\n ) as TaxRemittanceResponse\n}\n","// src/mpesa/transaction-status/query.ts\n\n/**\n * Transaction Status Query implementation\n *\n * API: POST /mpesa/transactionstatus/v1/query\n *\n * This is ASYNCHRONOUS. The synchronous response only acknowledges receipt.\n * Final results arrive via POST to your ResultURL.\n *\n * Required M-PESA org portal role: \"Transaction Status query ORG API\"\n *\n * Reconciliation options (at least one required):\n * - transactionId — M-Pesa Receipt Number (e.g. \"NEF61H8J60\")\n * - originalConversationId — OriginatorConversationID from the original call\n */\n\nimport { parseWithSchema } from '../../core/validation/zod-error'\nimport {\n TransactionStatusRequestSchema,\n TransactionStatusResponseSchema,\n} from '../../schemas/async-apis'\nimport { httpRequest, type DarajaHttpOptions, withDarajaHttp } from '../../utils/http'\nimport type { TransactionStatusRequest, TransactionStatusResponse } from './types'\n\nexport async function queryTransactionStatus(\n baseUrl: string,\n token: string,\n securityCredential: string,\n initiator: string,\n request: TransactionStatusRequest,\n http?: DarajaHttpOptions,\n): Promise<TransactionStatusResponse> {\n const validated = parseWithSchema(\n TransactionStatusRequestSchema,\n request,\n 'Transaction Status request',\n )\n\n const payload: Record<string, string> = {\n Initiator: initiator,\n SecurityCredential: securityCredential,\n CommandID: validated.commandId ?? 'TransactionStatusQuery',\n TransactionID: validated.transactionId ?? '',\n OriginalConversationID: validated.originalConversationId ?? '',\n PartyA: validated.partyA,\n IdentifierType: validated.identifierType,\n ResultURL: validated.resultUrl,\n QueueTimeOutURL: validated.queueTimeOutUrl,\n Remarks: validated.remarks ?? 'Transaction Status Query',\n Occasion: validated.occasion ?? '',\n }\n\n const { data } = await httpRequest<TransactionStatusResponse>(\n `${baseUrl}/mpesa/transactionstatus/v1/query`,\n withDarajaHttp(\n {\n method: 'POST',\n headers: { Authorization: `Bearer ${token}` },\n body: payload,\n },\n http,\n ),\n )\n\n return parseWithSchema(\n TransactionStatusResponseSchema,\n data,\n 'Transaction Status response',\n ) as TransactionStatusResponse\n}\n","// 📁 PATH: src/mpesa/types.ts\n\nimport type { IdempotencyConfig } from '../core/idempotency'\n\nexport type Environment = 'sandbox' | 'production'\n\nexport const DARAJA_BASE_URLS: Record<Environment, string> = {\n sandbox: 'https://sandbox.safaricom.co.ke',\n production: 'https://api.safaricom.co.ke',\n} as const\n\nexport interface MpesaConfig {\n // ── Required for all APIs ─────────────────────────────────────────────────\n consumerKey: string\n consumerSecret: string\n environment: Environment\n\n // ── STK Push (M-Pesa Express) ─────────────────────────────────────────────\n lipaNaMpesaShortCode?: string\n lipaNaMpesaPassKey?: string\n\n // ── Initiator (B2C / B2B / Reversal / Account Balance / Tax / TxStatus) ──\n initiatorName?: string\n initiatorPassword?: string\n\n // ── Certificate (one of) ──────────────────────────────────────────────────\n certificatePath?: string\n certificatePem?: string\n /**\n * Pre-computed base64 SecurityCredential — skips RSA encryption.\n * Use when you encrypt at startup outside the library.\n */\n securityCredential?: string\n\n // ── HTTP tuning ───────────────────────────────────────────────────────────\n /** Override default retry count (4) for all API calls */\n retries?: number\n /** Override default base retry delay in ms (2000) */\n retryDelay?: number\n /** Override default per-request timeout in ms (30000) */\n timeout?: number\n\n // ── Idempotency ───────────────────────────────────────────────────────────\n /** Automatic Idempotency-Key headers and duplicate detection */\n idempotency?: IdempotencyConfig\n\n // ── Webhooks ──────────────────────────────────────────────────────────────\n webhooks?: {\n allowedIPs?: readonly string[]\n /** HMAC secret when Safaricom ships signature support */\n secret?: string\n /** Signature header name (placeholder until official docs) */\n signatureHeader?: string\n /** Fail closed if HMAC required but missing. Default false */\n requireHMAC?: boolean\n }\n}\n","/**\n * src/mpesa/index.ts\n *\n * Primary M-PESA module entry point.\n *\n * Exports:\n * 1. Mpesa class — the main SDK client\n * 2. All submodule APIs, types, constants, and helpers\n *\n * Submodules re-exported here:\n * - account-balance\n * - b2b-buy-goods\n * - b2b-express-checkout\n * - b2b-pay-bill\n * - b2c (Account Top Up)\n * - b2c-disbursement\n * - bill-manager\n * - c2b\n * - dynamic-qr\n * - reversal\n * - stk-push\n * - tax-remittance\n * - transaction-status\n * - webhooks\n */\n\nimport { readFile } from 'node:fs/promises'\nimport { TokenManager } from '../core/auth'\nimport { encryptSecurityCredential } from '../core/encryption'\nimport { generateOriginatorConversationId, IdempotencyManager } from '../core/idempotency'\nimport { PesafyError } from '../utils/errors'\nimport type { DarajaHttpOptions } from '../utils/http'\nimport { err, ok, type Result } from '../types/branded'\n\nimport {\n queryAccountBalance,\n type AccountBalanceRequest,\n type AccountBalanceResponse,\n} from './account-balance'\nimport {\n initiateB2BExpressCheckout as _initiateB2BExpressCheckout,\n type B2BExpressCheckoutRequest,\n type B2BExpressCheckoutResponse,\n} from './b2b-express-checkout'\nimport {\n initiateB2BBuyGoods as _initiateB2BBuyGoods,\n type B2BBuyGoodsRequest,\n type B2BBuyGoodsResponse,\n} from './b2b-buy-goods'\nimport {\n initiateB2BPayBill as _initiateB2BPayBill,\n type B2BPayBillRequest,\n type B2BPayBillResponse,\n} from './b2b-pay-bill'\nimport { initiateB2CPayment as _initiateB2CPayment, type B2CRequest, type B2CResponse } from './b2c'\nimport {\n initiateB2CDisbursement as _initiateB2CDisbursement,\n type B2CDisbursementRequest,\n type B2CDisbursementResponse,\n} from './b2c-disbursement'\nimport {\n billManagerOptIn as _billManagerOptIn,\n cancelBulkInvoices as _cancelBulkInvoices,\n cancelInvoice as _cancelInvoice,\n reconcilePayment as _reconcilePayment,\n sendBulkInvoices as _sendBulkInvoices,\n sendSingleInvoice as _sendSingleInvoice,\n updateOptIn as _updateOptIn,\n type BillManagerBulkInvoiceRequest,\n type BillManagerBulkInvoiceResponse,\n type BillManagerCancelBulkInvoiceRequest,\n type BillManagerCancelBulkInvoiceResponse,\n type BillManagerCancelInvoiceRequest,\n type BillManagerCancelInvoiceResponse,\n type BillManagerOptInRequest,\n type BillManagerOptInResponse,\n type BillManagerReconciliationRequest,\n type BillManagerReconciliationResponse,\n type BillManagerSingleInvoiceRequest,\n type BillManagerSingleInvoiceResponse,\n type BillManagerUpdateOptInRequest,\n type BillManagerUpdateOptInResponse,\n} from './bill-manager'\nimport {\n registerC2BUrls as _registerC2BUrls,\n simulateC2B as _simulateC2B,\n type C2BRegisterUrlRequest,\n type C2BRegisterUrlResponse,\n type C2BSimulateRequest,\n type C2BSimulateResponse,\n} from './c2b'\nimport {\n generateDynamicQR as _generateDynamicQR,\n type DynamicQRRequest,\n type DynamicQRResponse,\n} from './dynamic-qr'\nimport { requestReversal, type ReversalRequest, type ReversalResponse } from './reversal'\nimport { processStkPush, queryStkPush, type StkPushRequest, type StkQueryRequest } from './stk-push'\nimport {\n remitTax as _remitTax,\n type TaxRemittanceRequest,\n type TaxRemittanceResponse,\n} from './tax-remittance'\nimport { queryTransactionStatus, type TransactionStatusRequest } from './transaction-status'\n\nimport { DARAJA_BASE_URLS, type MpesaConfig } from './types'\n\n// ── Mpesa client ──────────────────────────────────────────────────────────────\n\nexport class Mpesa {\n private readonly config: MpesaConfig\n private readonly tokenManager: TokenManager\n private readonly baseUrl: string\n readonly idempotencyManager: IdempotencyManager\n\n constructor(config: MpesaConfig) {\n if (!config.consumerKey || !config.consumerSecret) {\n throw new PesafyError({\n code: 'INVALID_CREDENTIALS',\n message: 'consumerKey and consumerSecret are required.',\n })\n }\n this.config = config\n this.baseUrl = DARAJA_BASE_URLS[config.environment]\n this.tokenManager = new TokenManager(config.consumerKey, config.consumerSecret, this.baseUrl)\n this.idempotencyManager = new IdempotencyManager(config.idempotency)\n }\n\n /** Idempotency options passed to all outbound Daraja HTTP calls. */\n private darajaHttp(): DarajaHttpOptions {\n return { idempotency: this.idempotencyManager }\n }\n\n // ── Internal helpers ────────────────────────────────────────────────────────\n\n private getToken(): Promise<string> {\n return this.tokenManager.getAccessToken()\n }\n\n private async buildSecurityCredential(): Promise<string> {\n if (this.config.securityCredential) return this.config.securityCredential\n\n if (!this.config.initiatorPassword) {\n throw new PesafyError({\n code: 'INVALID_CREDENTIALS',\n message:\n 'Provide securityCredential (pre-encrypted) ' +\n 'OR (initiatorPassword + certificatePath/certificatePem).',\n })\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 readFile(this.config.certificatePath, 'utf-8')\n } else {\n throw new PesafyError({\n code: 'INVALID_CREDENTIALS',\n message: 'certificatePath or certificatePem is required to encrypt the initiator password.',\n })\n }\n return encryptSecurityCredential(this.config.initiatorPassword, cert)\n }\n\n private requireInitiator(forApi: string): string {\n const name = this.config.initiatorName ?? ''\n if (!name) {\n throw new PesafyError({\n code: 'VALIDATION_ERROR',\n message: `initiatorName is required for ${forApi}.`,\n })\n }\n return name\n }\n\n // ── Safe wrappers ──────────────────────────────────────────────────────────\n\n async stkPushSafe(\n request: Omit<StkPushRequest, 'shortCode' | 'passKey'>,\n ): Promise<Result<Awaited<ReturnType<typeof this.stkPush>>>> {\n try {\n return ok(await this.stkPush(request))\n } catch (e) {\n return err(e as PesafyError)\n }\n }\n\n async accountBalanceSafe(\n request: AccountBalanceRequest,\n ): Promise<Result<AccountBalanceResponse>> {\n try {\n return ok(await this.accountBalance(request))\n } catch (e) {\n return err(e as PesafyError)\n }\n }\n\n // ── STK Push ───────────────────────────────────────────────────────────────\n\n async stkPush(request: Omit<StkPushRequest, 'shortCode' | 'passKey'>) {\n const shortCode = this.config.lipaNaMpesaShortCode ?? ''\n const passKey = this.config.lipaNaMpesaPassKey ?? ''\n\n if (!shortCode || !passKey) {\n throw new PesafyError({\n code: 'VALIDATION_ERROR',\n message: 'lipaNaMpesaShortCode and lipaNaMpesaPassKey are required for STK Push.',\n })\n }\n\n const token = await this.getToken()\n return processStkPush(\n this.baseUrl,\n token,\n { ...request, shortCode, passKey },\n this.darajaHttp(),\n )\n }\n\n async stkQuery(request: Omit<StkQueryRequest, 'shortCode' | 'passKey'>) {\n const shortCode = this.config.lipaNaMpesaShortCode ?? ''\n const passKey = this.config.lipaNaMpesaPassKey ?? ''\n\n if (!shortCode || !passKey) {\n throw new PesafyError({\n code: 'VALIDATION_ERROR',\n message: 'lipaNaMpesaShortCode and lipaNaMpesaPassKey are required for STK Query.',\n })\n }\n\n const token = await this.getToken()\n return queryStkPush(this.baseUrl, token, { ...request, shortCode, passKey }, this.darajaHttp())\n }\n\n // ── Transaction Status ─────────────────────────────────────────────────────\n\n async transactionStatus(request: TransactionStatusRequest) {\n const initiator = this.requireInitiator('Transaction Status')\n const [token, cred] = await Promise.all([this.getToken(), this.buildSecurityCredential()])\n return queryTransactionStatus(this.baseUrl, token, cred, initiator, request, this.darajaHttp())\n }\n\n // ── Account Balance ────────────────────────────────────────────────────────\n\n async accountBalance(request: AccountBalanceRequest): Promise<AccountBalanceResponse> {\n const initiator = this.requireInitiator('Account Balance')\n const [token, cred] = await Promise.all([this.getToken(), this.buildSecurityCredential()])\n return queryAccountBalance(this.baseUrl, token, cred, initiator, request, this.darajaHttp())\n }\n\n // ── Reversal ───────────────────────────────────────────────────────────────\n\n async reverseTransaction(request: ReversalRequest): Promise<ReversalResponse> {\n const initiator = this.requireInitiator('Reversal')\n const [token, cred] = await Promise.all([this.getToken(), this.buildSecurityCredential()])\n return requestReversal(this.baseUrl, token, cred, initiator, request, this.darajaHttp())\n }\n\n // ── Dynamic QR ─────────────────────────────────────────────────────────────\n\n async generateDynamicQR(request: DynamicQRRequest): Promise<DynamicQRResponse> {\n const token = await this.getToken()\n return _generateDynamicQR(this.baseUrl, token, request, this.darajaHttp())\n }\n\n // ── C2B ────────────────────────────────────────────────────────────────────\n\n async registerC2BUrls(request: C2BRegisterUrlRequest): Promise<C2BRegisterUrlResponse> {\n const token = await this.getToken()\n return _registerC2BUrls(this.baseUrl, token, request, this.darajaHttp())\n }\n\n async simulateC2B(request: C2BSimulateRequest): Promise<C2BSimulateResponse> {\n const token = await this.getToken()\n return _simulateC2B(this.baseUrl, token, request, this.darajaHttp())\n }\n\n // ── Tax Remittance ─────────────────────────────────────────────────────────\n\n async remitTax(request: TaxRemittanceRequest): Promise<TaxRemittanceResponse> {\n const initiator = this.requireInitiator('Tax Remittance')\n const [token, cred] = await Promise.all([this.getToken(), this.buildSecurityCredential()])\n return _remitTax(this.baseUrl, token, cred, initiator, request, this.darajaHttp())\n }\n\n // ── B2B Express Checkout ───────────────────────────────────────────────────\n\n async b2bExpressCheckout(\n request: B2BExpressCheckoutRequest,\n ): Promise<B2BExpressCheckoutResponse> {\n const token = await this.getToken()\n return _initiateB2BExpressCheckout(this.baseUrl, token, request, this.darajaHttp())\n }\n\n // ── B2B Buy Goods ──────────────────────────────────────────────────────────\n\n async b2bBuyGoods(request: B2BBuyGoodsRequest): Promise<B2BBuyGoodsResponse> {\n const initiator = this.requireInitiator('B2B Buy Goods')\n const [token, cred] = await Promise.all([this.getToken(), this.buildSecurityCredential()])\n return _initiateB2BBuyGoods(this.baseUrl, token, cred, initiator, request, this.darajaHttp())\n }\n\n // ── B2B Pay Bill ───────────────────────────────────────────────────────────\n\n async b2bPayBill(request: B2BPayBillRequest): Promise<B2BPayBillResponse> {\n const initiator = this.requireInitiator('B2B Pay Bill')\n const [token, cred] = await Promise.all([this.getToken(), this.buildSecurityCredential()])\n return _initiateB2BPayBill(this.baseUrl, token, cred, initiator, request, this.darajaHttp())\n }\n\n // ── B2C Payment (Account Top Up) ──────────────────────────────────────────\n\n async b2cPayment(request: B2CRequest): Promise<B2CResponse> {\n const initiator = this.requireInitiator('B2C Payment')\n const [token, cred] = await Promise.all([this.getToken(), this.buildSecurityCredential()])\n return _initiateB2CPayment(this.baseUrl, token, cred, initiator, request, this.darajaHttp())\n }\n\n // ── B2C Disbursement ───────────────────────────────────────────────────────\n\n async b2cDisbursement(request: B2CDisbursementRequest): Promise<B2CDisbursementResponse> {\n const initiator = this.requireInitiator('B2C Disbursement')\n const req = {\n ...request,\n originatorConversationId:\n request.originatorConversationId ?? generateOriginatorConversationId(),\n }\n const [token, cred] = await Promise.all([this.getToken(), this.buildSecurityCredential()])\n return _initiateB2CDisbursement(this.baseUrl, token, cred, initiator, req, this.darajaHttp())\n }\n\n // ── Bill Manager ───────────────────────────────────────────────────────────\n\n async billManagerOptIn(request: BillManagerOptInRequest): Promise<BillManagerOptInResponse> {\n const token = await this.getToken()\n return _billManagerOptIn(this.baseUrl, token, request, this.darajaHttp())\n }\n\n async updateOptIn(\n request: BillManagerUpdateOptInRequest,\n ): Promise<BillManagerUpdateOptInResponse> {\n const token = await this.getToken()\n return _updateOptIn(this.baseUrl, token, request, this.darajaHttp())\n }\n\n async sendInvoice(\n request: BillManagerSingleInvoiceRequest,\n ): Promise<BillManagerSingleInvoiceResponse> {\n const token = await this.getToken()\n return _sendSingleInvoice(this.baseUrl, token, request, this.darajaHttp())\n }\n\n async sendBulkInvoices(\n request: BillManagerBulkInvoiceRequest,\n ): Promise<BillManagerBulkInvoiceResponse> {\n const token = await this.getToken()\n return _sendBulkInvoices(this.baseUrl, token, request, this.darajaHttp())\n }\n\n async cancelInvoice(\n request: BillManagerCancelInvoiceRequest,\n ): Promise<BillManagerCancelInvoiceResponse> {\n const token = await this.getToken()\n return _cancelInvoice(this.baseUrl, token, request, this.darajaHttp())\n }\n\n async cancelBulkInvoices(\n request: BillManagerCancelBulkInvoiceRequest,\n ): Promise<BillManagerCancelBulkInvoiceResponse> {\n const token = await this.getToken()\n return _cancelBulkInvoices(this.baseUrl, token, request, this.darajaHttp())\n }\n\n async reconcilePayment(\n request: BillManagerReconciliationRequest,\n ): Promise<BillManagerReconciliationResponse> {\n const token = await this.getToken()\n return _reconcilePayment(this.baseUrl, token, request, this.darajaHttp())\n }\n\n // ── Utilities ──────────────────────────────────────────────────────────────\n\n clearTokenCache(): void {\n this.tokenManager.clearCache()\n }\n\n get environment() {\n return this.config.environment\n }\n}\n\n// ══════════════════════════════════════════════════════════════════════════════\n// SUBMODULE RE-EXPORTS\n// All public APIs from every M-PESA submodule are re-exported here so that\n// consumers can import directly from 'pesafy/mpesa' (if that path is added to\n// package.json exports) or so that src/index.ts can barrel from one place.\n// ══════════════════════════════════════════════════════════════════════════════\n\n// ── Types ─────────────────────────────────────────────────────────────────────\nexport type { Environment, MpesaConfig } from './types'\nexport { DARAJA_BASE_URLS } from './types'\n\n// ── Account Balance ───────────────────────────────────────────────────────────\nexport { queryAccountBalance } from './account-balance'\nexport {\n ACCOUNT_BALANCE_ERROR_CODES,\n isAccountBalanceSuccess,\n parseAccountBalance,\n getAccountBalanceParam,\n getAccountBalanceTransactionId,\n getAccountBalanceConversationId,\n getAccountBalanceOriginatorConversationId,\n getAccountBalanceCompletedTime,\n getAccountBalanceRawBalance,\n getAccountBalanceReferenceItem,\n} from './account-balance'\nexport type {\n AccountBalanceData,\n AccountBalanceErrorCode,\n AccountBalanceRequest,\n AccountBalanceResponse,\n AccountBalanceResult,\n AccountBalanceReferenceItem,\n AccountBalanceResultParameter,\n AccountBalanceResultParameterKey,\n ParsedAccount,\n} from './account-balance'\n\n// ── B2B Buy Goods ─────────────────────────────────────────────────────────────\nexport { initiateB2BBuyGoods } from './b2b-buy-goods'\nexport {\n B2B_BUY_GOODS_ERROR_CODES,\n B2B_BUY_GOODS_RESULT_CODES,\n isB2BBuyGoodsResult,\n isB2BBuyGoodsSuccess,\n isB2BBuyGoodsFailure,\n isKnownB2BBuyGoodsResultCode,\n getB2BBuyGoodsAmount,\n getB2BBuyGoodsBillReferenceNumber,\n getB2BBuyGoodsCompletedTime,\n getB2BBuyGoodsConversationId,\n getB2BBuyGoodsCurrency,\n getB2BBuyGoodsDebitAccountBalance,\n getB2BBuyGoodsDebitPartyAffectedBalance,\n getB2BBuyGoodsDebitPartyCharges,\n getB2BBuyGoodsInitiatorBalance,\n getB2BBuyGoodsOriginatorConversationId,\n getB2BBuyGoodsQueueTimeoutUrl,\n getB2BBuyGoodsReceiverName,\n getB2BBuyGoodsResultCode,\n getB2BBuyGoodsResultDesc,\n getB2BBuyGoodsResultParam,\n getB2BBuyGoodsTransactionId,\n} from './b2b-buy-goods'\nexport type {\n B2BBuyGoodsCommandID,\n B2BBuyGoodsErrorCode,\n B2BBuyGoodsErrorResponse,\n B2BBuyGoodsReferenceItem,\n B2BBuyGoodsRequest,\n B2BBuyGoodsResponse,\n B2BBuyGoodsResult,\n B2BBuyGoodsResultCode,\n B2BBuyGoodsResultParameter,\n B2BBuyGoodsResultParameterKey,\n} from './b2b-buy-goods'\n\n// ── B2B Express Checkout ──────────────────────────────────────────────────────\nexport { initiateB2BExpressCheckout } from './b2b-express-checkout'\nexport {\n B2B_RESULT_CODES,\n getB2BAmount,\n getB2BConversationId,\n getB2BPaymentReference,\n getB2BRequestId,\n getB2BResultCode,\n getB2BResultDesc,\n getB2BTransactionId,\n isB2BCheckoutCallback,\n isB2BCheckoutCancelled,\n isB2BCheckoutFailed,\n isB2BCheckoutSuccess,\n isB2BStatusSuccess,\n isKnownB2BResultCode,\n} from './b2b-express-checkout'\nexport type {\n B2BExpressCheckoutCallback,\n B2BExpressCheckoutCallbackCancelled,\n B2BExpressCheckoutCallbackFailed,\n B2BExpressCheckoutCallbackSuccess,\n B2BExpressCheckoutErrorCode,\n B2BExpressCheckoutErrorResponse,\n B2BExpressCheckoutRequest,\n B2BExpressCheckoutResponse,\n B2BResultCode,\n} from './b2b-express-checkout'\n\n// ── B2B Pay Bill ──────────────────────────────────────────────────────────────\nexport { initiateB2BPayBill } from './b2b-pay-bill'\nexport {\n B2B_PAY_BILL_ERROR_CODES,\n B2B_PAY_BILL_RESULT_CODES,\n isB2BPayBillResult,\n isB2BPayBillSuccess,\n isB2BPayBillFailure,\n isKnownB2BPayBillResultCode,\n getB2BPayBillAmount,\n getB2BPayBillBillReferenceNumber,\n getB2BPayBillCompletedTime,\n getB2BPayBillConversationId,\n getB2BPayBillCurrency,\n getB2BPayBillDebitAccountBalance,\n getB2BPayBillDebitPartyAffectedBalance,\n getB2BPayBillDebitPartyCharges,\n getB2BPayBillInitiatorBalance,\n getB2BPayBillOriginatorConversationId,\n getB2BPayBillReceiverName,\n getB2BPayBillResultCode,\n getB2BPayBillResultDesc,\n getB2BPayBillResultParam,\n getB2BPayBillTransactionId,\n} from './b2b-pay-bill'\nexport type {\n B2BPayBillCommandID,\n B2BPayBillErrorCode,\n B2BPayBillErrorResponse,\n B2BPayBillReferenceItem,\n B2BPayBillRequest,\n B2BPayBillResponse,\n B2BPayBillResult,\n B2BPayBillResultCode,\n B2BPayBillResultParameter,\n B2BPayBillResultParameterKey,\n} from './b2b-pay-bill'\n\n// ── B2C Account Top Up ────────────────────────────────────────────────────────\nexport { initiateB2CPayment } from './b2c'\nexport {\n B2C_ERROR_CODES,\n B2C_RESULT_CODES,\n getB2CAmount,\n getB2CCurrency,\n getB2CDebitAccountBalance,\n getB2CDebitPartyCharges,\n getB2CConversationId,\n getB2COriginatorConversationId,\n getB2CReceiverPublicName,\n getB2CResultDesc,\n getB2CResultParam,\n getB2CTransactionCompletedTime,\n getB2CTransactionId,\n isB2CFailure,\n isB2CResult,\n isB2CSuccess,\n isKnownB2CResultCode,\n} from './b2c'\nexport type {\n B2CCommandID,\n B2CErrorCode,\n B2CErrorResponse,\n B2CRequest,\n B2CResponse,\n B2CResult,\n B2CResultCode,\n B2CResultParameter,\n B2CResultParameterKey,\n} from './b2c'\n\n// ── B2C Disbursement ──────────────────────────────────────────────────────────\nexport { initiateB2CDisbursement } from './b2c-disbursement'\nexport {\n B2C_DISBURSEMENT_RESULT_CODES,\n getB2CDisbursementAmount,\n getB2CDisbursementCompletedTime,\n getB2CDisbursementConversationId,\n getB2CDisbursementOriginatorConversationId,\n getB2CDisbursementReceiptNumber,\n getB2CDisbursementReceiverName,\n getB2CDisbursementResultCode,\n getB2CDisbursementResultDesc,\n getB2CDisbursementResultParam,\n getB2CDisbursementTransactionId,\n getB2CDisbursementUtilityBalance,\n getB2CDisbursementWorkingBalance,\n isB2CDisbursementFailure,\n isB2CDisbursementRecipientRegistered,\n isB2CDisbursementResult,\n isB2CDisbursementSuccess,\n isKnownB2CDisbursementResultCode,\n} from './b2c-disbursement'\nexport type {\n B2CDisbursementCommandID,\n B2CDisbursementErrorResponse,\n B2CDisbursementRequest,\n B2CDisbursementResponse,\n B2CDisbursementResult,\n B2CDisbursementResultCode,\n B2CDisbursementResultParameter,\n B2CDisbursementResultParameterKey,\n} from './b2c-disbursement'\n\n// ── Bill Manager ──────────────────────────────────────────────────────────────\nexport {\n billManagerOptIn,\n updateOptIn,\n sendSingleInvoice,\n sendBulkInvoices,\n cancelInvoice,\n cancelBulkInvoices,\n reconcilePayment,\n} from './bill-manager'\nexport type {\n BillManagerBulkInvoiceRequest,\n BillManagerBulkInvoiceResponse,\n BillManagerCancelBulkInvoiceRequest,\n BillManagerCancelBulkInvoiceResponse,\n BillManagerCancelInvoiceRequest,\n BillManagerCancelInvoiceResponse,\n BillManagerInvoiceItem,\n BillManagerOptInRequest,\n BillManagerOptInResponse,\n BillManagerPaymentCallbackResponse,\n BillManagerPaymentNotification,\n BillManagerReconciliationRequest,\n BillManagerReconciliationResponse,\n BillManagerSingleInvoiceRequest,\n BillManagerSingleInvoiceResponse,\n BillManagerUpdateOptInRequest,\n BillManagerUpdateOptInResponse,\n} from './bill-manager'\n\n// ── C2B ───────────────────────────────────────────────────────────────────────\nexport { registerC2BUrls, simulateC2B } from './c2b'\nexport {\n C2B_REGISTER_URL_ERROR_CODES,\n C2B_VALIDATION_RESULT_CODES,\n acceptC2BValidation,\n acknowledgeC2BConfirmation,\n getC2BAccountRef,\n getC2BAmount,\n getC2BCustomerName,\n getC2BTransactionId,\n isBuyGoodsPayment,\n isC2BPayload,\n isPaybillPayment,\n rejectC2BValidation,\n} from './c2b'\nexport type {\n C2BApiVersion,\n C2BCommandID,\n C2BConfirmationAck,\n C2BConfirmationPayload,\n C2BRegisterUrlErrorCode,\n C2BRegisterUrlRequest,\n C2BRegisterUrlResponse,\n C2BResponseType,\n C2BSimulateRequest,\n C2BSimulateResponse,\n C2BValidationPayload,\n C2BValidationResponse,\n C2BValidationResultCode,\n} from './c2b'\n\n// ── Dynamic QR ────────────────────────────────────────────────────────────────\nexport { generateDynamicQR } from './dynamic-qr'\nexport {\n QR_TRANSACTION_CODES,\n DEFAULT_QR_SIZE,\n MAX_QR_SIZE,\n MIN_AMOUNT,\n MIN_QR_SIZE,\n validateAmount,\n validateCpi,\n validateDynamicQRRequest,\n validateMerchantName,\n validateRefNo,\n validateSize,\n validateTrxCode,\n} from './dynamic-qr'\nexport type {\n DynamicQRDarajaPayload,\n DynamicQRErrorResponse,\n DynamicQRRequest,\n DynamicQRResponse,\n QRTransactionCode,\n ValidationFail,\n ValidationOk,\n ValidationResult,\n} from './dynamic-qr'\n\n// ── Reversal ──────────────────────────────────────────────────────────────────\nexport { requestReversal } from './reversal'\nexport {\n REVERSAL_COMMAND_ID,\n REVERSAL_ERROR_CODES,\n REVERSAL_RECEIVER_IDENTIFIER_TYPE,\n REVERSAL_RESULT_CODES,\n isKnownReversalResultCode,\n isReversalFailure,\n isReversalResult,\n isReversalSuccess,\n getReversalAmount,\n getReversalCharge,\n getReversalCompletedTime,\n getReversalConversationId,\n getReversalCreditPartyPublicName,\n getReversalDebitAccountBalance,\n getReversalDebitPartyPublicName,\n getReversalOriginalTransactionId,\n getReversalOriginatorConversationId,\n getReversalResultCode,\n getReversalResultDesc,\n getReversalResultParam,\n getReversalTransactionId,\n} from './reversal'\nexport type {\n ReversalErrorCode,\n ReversalRequest,\n ReversalResponse,\n ReversalResult,\n ReversalResultCode,\n ReversalResultParameter,\n ReversalResultParameterKey,\n} from './reversal'\n\n// ── STK Push ──────────────────────────────────────────────────────────────────\nexport { processStkPush } from './stk-push'\nexport { queryStkPush } from './stk-push'\nexport {\n STK_PUSH_LIMITS,\n STK_RESULT_CODES,\n isKnownStkResultCode,\n isStkCallbackSuccess,\n getCallbackValue,\n} from './stk-push'\nexport { formatPhoneNumber, getTimestamp } from './stk-push'\nexport type {\n StkCallbackFailure,\n StkCallbackInner,\n StkCallbackMetadataItem,\n StkCallbackSuccess,\n StkPushCallback,\n StkPushRequest,\n StkPushResponse,\n StkQueryRequest,\n StkQueryResponse,\n StkResultCode,\n TransactionType,\n} from './stk-push'\n\n// ── Tax Remittance ────────────────────────────────────────────────────────────\nexport { KRA_SHORTCODE, remitTax, TAX_COMMAND_ID } from './tax-remittance'\nexport {\n isTaxRemittanceFailure,\n isTaxRemittanceResult,\n isTaxRemittanceSuccess,\n getTaxAmount,\n getTaxCompletedTime,\n getTaxConversationId,\n getTaxOriginatorConversationId,\n getTaxReceiverName,\n getTaxResultCode,\n getTaxResultDesc,\n getTaxResultParam,\n getTaxTransactionId,\n} from './tax-remittance'\nexport type {\n TaxRemittanceErrorResponse,\n TaxRemittanceRequest,\n TaxRemittanceResponse,\n TaxRemittanceResult,\n TaxRemittanceResultParameter,\n TaxRemittanceResultParameterKey,\n} from './tax-remittance'\n\n// ── Transaction Status ────────────────────────────────────────────────────────\nexport { queryTransactionStatus } from './transaction-status'\nexport {\n TRANSACTION_STATUS_ERROR_CODES,\n TRANSACTION_STATUS_RESULT_CODES,\n isTransactionStatusFailure,\n isTransactionStatusResult,\n isTransactionStatusSuccess,\n isKnownTransactionStatusResultCode,\n getTransactionStatusConversationId,\n getTransactionStatusOriginatorConversationId,\n getTransactionStatusResultCode,\n getTransactionStatusResultDesc,\n getTransactionStatusTransactionId,\n getTransactionStatusAmount,\n getTransactionStatusCreditPartyName,\n getTransactionStatusDebitAccountBalance,\n getTransactionStatusDebitPartyName,\n getTransactionStatusReceiptNo,\n getTransactionStatusResultParam,\n getTransactionStatusStatus,\n getTransactionStatusTransactionDate,\n} from './transaction-status'\nexport type {\n TransactionStatusErrorCode,\n TransactionStatusRequest,\n TransactionStatusResponse,\n TransactionStatusResult,\n TransactionStatusResultCode,\n TransactionStatusResultParameter,\n TransactionStatusResultParameterKey,\n} from './transaction-status'\n\n// ── Webhooks ──────────────────────────────────────────────────────────────────\nexport {\n retryWithBackoff,\n parseStkPushWebhook,\n SAFARICOM_IPS,\n verifyWebhookIP,\n extractAmount,\n extractPhoneNumber,\n extractTransactionId,\n handleWebhook,\n isSuccessfulCallback,\n} from './webhooks'\nexport type {\n RetryOptions,\n RetryResult,\n StkPushWebhook,\n WebhookEvent,\n WebhookEventType,\n WebhookHandlerOptions,\n WebhookHandlerResult,\n} from './webhooks'\n","import { existsSync } from 'node:fs'\nimport { readFile } from 'node:fs/promises'\nimport { resolve } from 'node:path'\nimport { Mpesa } from '../../mpesa'\nimport type { MpesaConfig } from '../../mpesa/types'\nimport { encryptSecurityCredential } from '../../core/encryption'\nimport { requireEnv } from './env'\nimport { EXIT_CONFIG } from './output'\n\nexport async function mpesaFromEnv(\n env: Record<string, string>,\n opts: { requireInitiator?: boolean } = {},\n): Promise<Mpesa> {\n requireEnv(env, 'MPESA_CONSUMER_KEY', 'MPESA_CONSUMER_SECRET', 'MPESA_ENVIRONMENT')\n\n const config: MpesaConfig = {\n consumerKey: env['MPESA_CONSUMER_KEY']!,\n consumerSecret: env['MPESA_CONSUMER_SECRET']!,\n environment: env['MPESA_ENVIRONMENT'] === 'production' ? 'production' : 'sandbox',\n lipaNaMpesaShortCode: env['MPESA_SHORTCODE'] ?? '',\n lipaNaMpesaPassKey: env['MPESA_PASSKEY'] ?? '',\n }\n\n if (opts.requireInitiator) {\n requireEnv(env, 'MPESA_INITIATOR_NAME', 'MPESA_INITIATOR_PASSWORD', 'MPESA_CERTIFICATE_PATH')\n const certPath = resolve(process.cwd(), env['MPESA_CERTIFICATE_PATH']!)\n if (!existsSync(certPath)) {\n console.error(`✖ Certificate not found: ${certPath}`)\n process.exit(EXIT_CONFIG)\n }\n const pem = await readFile(certPath, 'utf-8')\n const initiatorName = env['MPESA_INITIATOR_NAME']!\n const initiatorPassword = env['MPESA_INITIATOR_PASSWORD']!\n config.initiatorName = initiatorName\n config.initiatorPassword = initiatorPassword\n config.securityCredential = encryptSecurityCredential(initiatorPassword, pem)\n }\n\n return new Mpesa(config)\n}\n","import type { CliContext } from '../lib/context'\nimport { getPkgVersion, prompt } from '../lib/context'\nimport { getBaseUrl, requireEnv } from '../lib/env'\nimport { fetchJson, getToken } from '../lib/daraja'\nimport { b, c, dim, g, printJson, r, y } from '../lib/output'\nimport { existsSync, writeFileSync } from 'node:fs'\nimport { resolve } from 'node:path'\nimport { encryptSecurityCredential } from '../../core/encryption'\nimport { readFile } from 'node:fs/promises'\nimport { Mpesa } from '../../mpesa'\nimport { formatSafaricomPhone } from '../../utils/phone'\nimport { mpesaFromEnv } from '../lib/mpesa-from-env'\n\nexport async function cmdVersion(ctx: CliContext) {\n void ctx\n console.log(`pesafy v${getPkgVersion()}`)\n}\n","import type { CliContext } from '../lib/context'\nimport { getPkgVersion, prompt } from '../lib/context'\nimport { getBaseUrl, requireEnv } from '../lib/env'\nimport { fetchJson, getToken } from '../lib/daraja'\nimport { b, c, dim, g, printJson, r, y } from '../lib/output'\nimport { existsSync, writeFileSync } from 'node:fs'\nimport { resolve } from 'node:path'\nimport { encryptSecurityCredential } from '../../core/encryption'\nimport { readFile } from 'node:fs/promises'\nimport { Mpesa } from '../../mpesa'\nimport { formatSafaricomPhone } from '../../utils/phone'\nimport { mpesaFromEnv } from '../lib/mpesa-from-env'\n\nexport async function cmdHelp(ctx: CliContext) {\n void ctx\n console.log(`\n${b(`pesafy`)} ${dim(`v${getPkgVersion()}`)} — M-PESA Daraja SDK CLI\n\n${b('SETUP')}\n ${g('init')} Scaffold .env interactively\n ${g('doctor')} Validate .env configuration\n ${g('token')} Print Daraja OAuth token\n ${g('encrypt')} Encrypt initiator password → SecurityCredential\n ${g('validate-phone')} ${c('<phone>')} Normalise Kenyan MSISDN\n\n${b('PAYMENTS')}\n ${g('stk-push')} STK Push (M-PESA Express)\n ${g('stk-query')} ${c('<checkoutId>')} Query STK status\n ${g('b2b-checkout')} B2B Express Checkout\n ${g('qr-generate')} Dynamic QR (JSON body optional)\n\n${b('ASYNC')}\n ${g('balance')} Account balance query\n ${g('reversal')} ${c('<txId>')} Transaction reversal\n ${g('tx-status')} Transaction status query\n ${g('b2c-payment')} B2C account top-up\n ${g('b2c-disburse')} B2C disbursement\n ${g('tax-remit')} KRA tax remittance\n ${g('register-c2b-urls')} Register C2B URLs\n ${g('simulate-c2b')} C2B simulate (sandbox)\n\n${b('BILLS')}\n ${g('bills')} ${c('<optin|invoice|bulk|reconcile>')} Bill Manager API\n\n${b('FLAGS')}\n ${g('--json')} Machine-readable output\n ${g('--env-file')} ${c('<path>')} Alternate .env path\n ${g('--env')} ${c('sandbox|production')} Override MPESA_ENVIRONMENT\n\n${b('UTILS')}\n ${g('version')} Print version\n ${g('help')} Show this help\n\n${b('ENVIRONMENT')}\n All commands read from .env in the current directory.\n Required keys:\n MPESA_CONSUMER_KEY — Daraja consumer key\n MPESA_CONSUMER_SECRET — Daraja consumer secret\n MPESA_ENVIRONMENT — \"sandbox\" | \"production\"\n\n STK Push additionally requires:\n MPESA_SHORTCODE — Paybill / HO shortcode\n MPESA_PASSKEY — Lipa Na M-PESA passkey\n MPESA_CALLBACK_URL — Public callback URL\n\n Initiator APIs (Account Balance / B2C / Reversal) additionally require:\n MPESA_INITIATOR_NAME\n MPESA_INITIATOR_PASSWORD\n MPESA_CERTIFICATE_PATH — Path to .cer file\n MPESA_RESULT_URL — Public URL for async balance/reversal result\n MPESA_QUEUE_TIMEOUT_URL\n\n${b('ACCOUNT BALANCE NOTES')}\n The Account Balance API is ASYNCHRONOUS.\n The CLI submits the request and prints the OriginatorConversationID.\n Daraja will POST the actual balance data to MPESA_RESULT_URL.\n\n IdentifierType values:\n 1 = MSISDN\n 2 = Till Number\n 4 = Organisation ShortCode (default, most common)\n\n${b('EXAMPLES')}\n ${dim('$ npx pesafy init')}\n ${dim('$ npx pesafy stk-push --amount 100 --phone 254712345678')}\n ${dim('$ npx pesafy stk-query ws_CO_1234567890')}\n ${dim('$ npx pesafy balance --shortcode 600000 --identifier-type 4')}\n ${dim('$ npx pesafy token')}\n ${dim('$ npx pesafy doctor')}\n ${dim('$ npx pesafy validate-phone 0712345678')}\n`)\n}\n","import type { CliContext } from '../lib/context'\nimport { getPkgVersion, prompt } from '../lib/context'\nimport { getBaseUrl, requireEnv } from '../lib/env'\nimport { fetchJson, getToken } from '../lib/daraja'\nimport { b, c, dim, g, printJson, r, y } from '../lib/output'\nimport { existsSync, writeFileSync } from 'node:fs'\nimport { resolve } from 'node:path'\nimport { encryptSecurityCredential } from '../../core/encryption'\nimport { readFile } from 'node:fs/promises'\nimport { Mpesa } from '../../mpesa'\nimport { formatSafaricomPhone } from '../../utils/phone'\nimport { mpesaFromEnv } from '../lib/mpesa-from-env'\n\nexport async function cmdInit(ctx: CliContext) {\n void ctx\n console.log(`\\n${b('🚀 pesafy — Interactive Setup')}\\n`)\n console.log(dim('This will create a .env file in the current directory.\\n'))\n\n const env = await prompt('Environment (sandbox/production)', 'sandbox')\n const consumerKey = await prompt('Consumer Key')\n const consumerSecret = await prompt('Consumer Secret')\n const shortcode = await prompt('Lipa Na M-PESA Shortcode (for STK Push)', '174379')\n const passkey = await prompt('Lipa Na M-PESA Passkey')\n const callbackUrl = await prompt(\n 'STK Push Callback URL',\n 'https://example.com/api/mpesa/callback',\n )\n const initiatorName = await prompt(\n 'Initiator Name (leave blank if not using B2C/Reversal/Balance)',\n )\n const initiatorPassword = await prompt(\n 'Initiator Password (leave blank if not using B2C/Reversal/Balance)',\n )\n const certPath = await prompt(\n 'Certificate path (leave blank to skip)',\n './SandboxCertificate.cer',\n )\n const resultUrl = await prompt(\n 'Result URL (for async APIs — Account Balance, Reversal, B2C)',\n 'https://example.com/api/mpesa/result',\n )\n const queueTimeoutUrl = await prompt('Queue Timeout URL', 'https://example.com/api/mpesa/timeout')\n\n const content = `# pesafy — M-PESA Daraja configuration\n# Generated by: npx pesafy init\n\nMPESA_ENVIRONMENT=${env}\nMPESA_CONSUMER_KEY=${consumerKey}\nMPESA_CONSUMER_SECRET=${consumerSecret}\n\n# STK Push (M-PESA Express)\nMPESA_SHORTCODE=${shortcode}\nMPESA_PASSKEY=${passkey}\nMPESA_CALLBACK_URL=${callbackUrl}\n\n# Initiator (Account Balance / B2C / Reversal / Transaction Status / Tax Remittance)\n# Required org portal role for Account Balance: \"Balance Query ORG API\"\nMPESA_INITIATOR_NAME=${initiatorName}\nMPESA_INITIATOR_PASSWORD=${initiatorPassword}\nMPESA_CERTIFICATE_PATH=${certPath}\n\n# Async API result endpoints (Account Balance, Reversal, B2C)\nMPESA_RESULT_URL=${resultUrl}\nMPESA_QUEUE_TIMEOUT_URL=${queueTimeoutUrl}\n`\n\n const envPath = resolve(process.cwd(), '.env')\n if (existsSync(envPath)) {\n const overwrite = await prompt('.env already exists — overwrite? (y/N)', 'N')\n if (overwrite.toLowerCase() !== 'y') {\n console.log(y('⚠ Skipped — existing .env preserved.'))\n return\n }\n }\n\n writeFileSync(envPath, content)\n console.log(`\\n${g('✔ .env created')} — run ${c('npx pesafy doctor')} to validate.\\n`)\n}\n","import type { CliContext } from '../lib/context'\nimport { getPkgVersion, prompt } from '../lib/context'\nimport { getBaseUrl, requireEnv } from '../lib/env'\nimport { fetchJson, getToken } from '../lib/daraja'\nimport { b, c, dim, g, printJson, r, y } from '../lib/output'\nimport { existsSync, writeFileSync } from 'node:fs'\nimport { resolve } from 'node:path'\nimport { encryptSecurityCredential } from '../../core/encryption'\nimport { readFile } from 'node:fs/promises'\nimport { Mpesa } from '../../mpesa'\nimport { formatSafaricomPhone } from '../../utils/phone'\nimport { mpesaFromEnv } from '../lib/mpesa-from-env'\n\nexport async function cmdDoctor(ctx: CliContext) {\n console.log(`\\n${b('🩺 pesafy doctor')}\\n`)\n const env = ctx.env\n let ok = true\n\n function check(key: string, hint = '') {\n if (env[key]) {\n console.log(`${g('✔')} ${key}`)\n } else {\n console.log(`${r('✖')} ${key}${hint ? dim(` — ${hint}`) : ''}`)\n ok = false\n }\n }\n\n console.log(b('Core:'))\n check('MPESA_CONSUMER_KEY', 'Get from https://developer.safaricom.co.ke')\n check('MPESA_CONSUMER_SECRET')\n check('MPESA_ENVIRONMENT', \"'sandbox' or 'production'\")\n\n const envVal = env['MPESA_ENVIRONMENT'] ?? ''\n if (envVal && envVal !== 'sandbox' && envVal !== 'production') {\n console.log(r(` ✖ MPESA_ENVIRONMENT must be \"sandbox\" or \"production\", got \"${envVal}\"`))\n ok = false\n }\n\n console.log(`\\n${b('STK Push:')}`)\n check('MPESA_SHORTCODE')\n check('MPESA_PASSKEY')\n check('MPESA_CALLBACK_URL', 'Must be a publicly accessible HTTPS URL in production')\n\n const cb = env['MPESA_CALLBACK_URL'] ?? ''\n if (cb && envVal === 'production' && !cb.startsWith('https://')) {\n console.log(r(' ✖ MPESA_CALLBACK_URL must be HTTPS in production'))\n ok = false\n }\n if (\n cb &&\n ['mpesa', 'safaricom', '.exe', 'cmd', 'sql'].some((kw) => cb.toLowerCase().includes(kw))\n ) {\n console.log(r(' ✖ MPESA_CALLBACK_URL contains a forbidden keyword (mpesa/safaricom/etc.)'))\n ok = false\n }\n\n console.log(`\\n${b('Initiator (Account Balance / B2C / Reversal / Balance — optional):')}`)\n const hasInitiator = !!(env['MPESA_INITIATOR_NAME'] && env['MPESA_INITIATOR_PASSWORD'])\n if (!hasInitiator) {\n console.log(dim(' — Not configured (required for Account Balance, B2C, Reversal, Tax)'))\n } else {\n check('MPESA_INITIATOR_NAME')\n check('MPESA_INITIATOR_PASSWORD')\n const certPath = env['MPESA_CERTIFICATE_PATH']\n if (certPath) {\n const fullPath = resolve(process.cwd(), certPath)\n if (existsSync(fullPath)) {\n console.log(`${g('✔')} MPESA_CERTIFICATE_PATH ${dim(`(${fullPath})`)}`)\n } else {\n console.log(`${r('✖')} MPESA_CERTIFICATE_PATH — file not found: ${fullPath}`)\n ok = false\n }\n } else {\n console.log(y('⚠ MPESA_CERTIFICATE_PATH not set — download from Safaricom Daraja portal'))\n }\n }\n\n console.log(`\\n${b('Async result URLs (required for Account Balance, Reversal, B2C):')}`)\n if (env['MPESA_RESULT_URL']) {\n console.log(`${g('✔')} MPESA_RESULT_URL`)\n const resultUrl = env['MPESA_RESULT_URL']\n if (envVal === 'production' && !resultUrl.startsWith('https://')) {\n console.log(r(' ✖ MPESA_RESULT_URL must be HTTPS in production'))\n ok = false\n }\n } else {\n console.log(y('⚠ MPESA_RESULT_URL not set — required for Account Balance, Reversal, B2C'))\n }\n\n if (env['MPESA_QUEUE_TIMEOUT_URL']) {\n console.log(`${g('✔')} MPESA_QUEUE_TIMEOUT_URL`)\n } else {\n console.log(\n y('⚠ MPESA_QUEUE_TIMEOUT_URL not set — required for Account Balance, Reversal, B2C'),\n )\n }\n\n console.log('')\n if (ok) {\n console.log(`${g('✔ All checks passed!')} Your config looks good.\\n`)\n } else {\n console.log(\n `${r('✖ Some checks failed.')} Fix the issues above, then re-run ${c('npx pesafy doctor')}.\\n`,\n )\n process.exit(1)\n }\n}\n","import type { CliContext } from '../lib/context'\nimport { getPkgVersion, prompt } from '../lib/context'\nimport { getBaseUrl, requireEnv } from '../lib/env'\nimport { fetchJson, getToken } from '../lib/daraja'\nimport { b, c, dim, g, printJson, r, y } from '../lib/output'\nimport { existsSync, writeFileSync } from 'node:fs'\nimport { resolve } from 'node:path'\nimport { encryptSecurityCredential } from '../../core/encryption'\nimport { readFile } from 'node:fs/promises'\nimport { Mpesa } from '../../mpesa'\nimport { formatSafaricomPhone } from '../../utils/phone'\nimport { mpesaFromEnv } from '../lib/mpesa-from-env'\n\nexport async function cmdToken(ctx: CliContext) {\n const env = ctx.env\n requireEnv(env, 'MPESA_CONSUMER_KEY', 'MPESA_CONSUMER_SECRET', 'MPESA_ENVIRONMENT')\n const baseUrl = getBaseUrl(env)\n\n process.stdout.write(dim('Fetching token…'))\n try {\n const token = await getToken(env['MPESA_CONSUMER_KEY']!, env['MPESA_CONSUMER_SECRET']!, baseUrl)\n process.stdout.write('\\r')\n if (ctx.json) {\n printJson({ access_token: token, expires_in: 3600 })\n return\n }\n console.log(`\\n${b('Access Token:')}\\n\\n${c(token)}\\n`)\n console.log(dim('Token is valid for 3600 seconds (1 hour).'))\n } catch (e) {\n process.stdout.write('\\r')\n console.error(r(`✖ ${(e as Error).message}`))\n process.exit(1)\n }\n}\n","import type { CliContext } from '../lib/context'\nimport { getPkgVersion, prompt } from '../lib/context'\nimport { getBaseUrl, requireEnv } from '../lib/env'\nimport { fetchJson, getToken } from '../lib/daraja'\nimport { b, c, dim, g, printJson, r, y } from '../lib/output'\nimport { existsSync, writeFileSync } from 'node:fs'\nimport { resolve } from 'node:path'\nimport { encryptSecurityCredential } from '../../core/encryption'\nimport { readFile } from 'node:fs/promises'\nimport { Mpesa } from '../../mpesa'\nimport { formatSafaricomPhone } from '../../utils/phone'\nimport { mpesaFromEnv } from '../lib/mpesa-from-env'\n\nexport async function cmdEncrypt(ctx: CliContext) {\n const args = ctx.args\n const env = ctx.env\n\n let password = args[0]\n let certPath = args[1] ?? env['MPESA_CERTIFICATE_PATH']\n\n if (!password) password = await prompt('Initiator password to encrypt')\n if (!certPath) certPath = await prompt('Certificate path', './SandboxCertificate.cer')\n\n if (!existsSync(resolve(process.cwd(), certPath))) {\n console.error(r(`✖ Certificate not found: ${certPath}`))\n process.exit(1)\n }\n\n try {\n const { encryptSecurityCredential } = await import('../../core/encryption/index.js')\n const { readFile } = await import('node:fs/promises')\n const pem = await readFile(resolve(process.cwd(), certPath), 'utf-8')\n const credential = encryptSecurityCredential(password, pem)\n console.log(`\\n${b('SecurityCredential (base64):')}\\n\\n${c(credential)}\\n`)\n console.log(dim('Copy this value into MPESA_SECURITY_CREDENTIAL in your .env or config.'))\n } catch (e) {\n console.error(r(`✖ Encryption failed: ${(e as Error).message}`))\n process.exit(1)\n }\n}\n","import type { CliContext } from '../lib/context'\nimport { getPkgVersion, prompt } from '../lib/context'\nimport { getBaseUrl, requireEnv } from '../lib/env'\nimport { fetchJson, getToken } from '../lib/daraja'\nimport { b, c, dim, g, printJson, r, y } from '../lib/output'\nimport { existsSync, writeFileSync } from 'node:fs'\nimport { resolve } from 'node:path'\nimport { encryptSecurityCredential } from '../../core/encryption'\nimport { readFile } from 'node:fs/promises'\nimport { Mpesa } from '../../mpesa'\nimport { formatSafaricomPhone } from '../../utils/phone'\nimport { mpesaFromEnv } from '../lib/mpesa-from-env'\n\nexport async function cmdValidatePhone(ctx: CliContext) {\n const args = ctx.args\n let phone = args[0]\n if (!phone) phone = await prompt('Phone number to validate')\n\n try {\n const { formatSafaricomPhone } = await import('../../utils/phone/index.js')\n const normalised = formatSafaricomPhone(phone)\n console.log(`\\n${g('✔')} ${b(phone)} → ${c(normalised)}\\n`)\n } catch (e) {\n console.error(`\\n${r('✖')} Invalid: ${(e as Error).message}\\n`)\n process.exit(1)\n }\n}\n","import type { CliContext } from '../lib/context'\nimport { getPkgVersion, prompt } from '../lib/context'\nimport { getBaseUrl, requireEnv } from '../lib/env'\nimport { fetchJson, getToken } from '../lib/daraja'\nimport { b, c, dim, g, printJson, r, y } from '../lib/output'\nimport { existsSync, writeFileSync } from 'node:fs'\nimport { resolve } from 'node:path'\nimport { encryptSecurityCredential } from '../../core/encryption'\nimport { readFile } from 'node:fs/promises'\nimport { Mpesa } from '../../mpesa'\nimport { formatSafaricomPhone } from '../../utils/phone'\nimport { mpesaFromEnv } from '../lib/mpesa-from-env'\n\nexport async function cmdStkPush(ctx: CliContext) {\n const args = ctx.args\n const env = ctx.env\n requireEnv(\n env,\n 'MPESA_CONSUMER_KEY',\n 'MPESA_CONSUMER_SECRET',\n 'MPESA_ENVIRONMENT',\n 'MPESA_SHORTCODE',\n 'MPESA_PASSKEY',\n 'MPESA_CALLBACK_URL',\n )\n\n const getArg = (flag: string) => {\n const idx = args.indexOf(flag)\n return idx !== -1 ? args[idx + 1] : undefined\n }\n\n let amount = Number(getArg('--amount') ?? 0)\n let phone = getArg('--phone') ?? ''\n let accountRef = getArg('--ref') ?? ''\n let desc = getArg('--desc') ?? 'Payment'\n\n if (!amount) amount = Number(await prompt('Amount (KES)'))\n if (!phone) phone = await prompt('Phone number (e.g. 0712345678)')\n if (!accountRef)\n accountRef = await prompt('Account reference', `CLI-${Date.now().toString(36).toUpperCase()}`)\n\n const baseUrl =\n env['MPESA_ENVIRONMENT'] === 'production'\n ? 'https://api.safaricom.co.ke'\n : 'https://sandbox.safaricom.co.ke'\n\n console.log(dim('\\nFetching token…'))\n const token = await getToken(env['MPESA_CONSUMER_KEY']!, env['MPESA_CONSUMER_SECRET']!, baseUrl)\n\n const { formatSafaricomPhone } = await import('../../utils/phone/index.js')\n const msisdn = formatSafaricomPhone(phone)\n\n const { getStkPushPassword, getTimestamp } = await import('../../mpesa/stk-push/utils.js')\n const timestamp = getTimestamp()\n const password = getStkPushPassword(env['MPESA_SHORTCODE']!, env['MPESA_PASSKEY']!, timestamp)\n\n console.log(dim('Sending STK Push…\\n'))\n\n try {\n const result = (await fetchJson(\n `${baseUrl}/mpesa/stkpush/v1/processrequest`,\n 'POST',\n { Authorization: `Bearer ${token}` },\n {\n BusinessShortCode: env['MPESA_SHORTCODE'],\n Password: password,\n Timestamp: timestamp,\n TransactionType: 'CustomerPayBillOnline',\n Amount: Math.round(amount),\n PartyA: msisdn,\n PartyB: env['MPESA_SHORTCODE'],\n PhoneNumber: msisdn,\n CallBackURL: env['MPESA_CALLBACK_URL'],\n AccountReference: accountRef.slice(0, 12),\n TransactionDesc: desc.slice(0, 13),\n },\n )) as Record<string, string>\n\n if (result['ResponseCode'] === '0') {\n console.log(`${g('✔ STK Push sent successfully!')}\\n`)\n console.log(` ${b('CheckoutRequestID:')} ${c(result['CheckoutRequestID'] ?? '')}`)\n console.log(` ${b('MerchantRequestID:')} ${result['MerchantRequestID'] ?? ''}`)\n console.log(` ${b('CustomerMessage:')} ${result['CustomerMessage'] ?? ''}`)\n console.log(`\\n${dim(' Use `npx pesafy stk-query <CheckoutRequestID>` to check status.')}\\n`)\n } else {\n console.log(`${r('✖ STK Push failed:')}\\n`)\n console.log(JSON.stringify(result, null, 2))\n }\n } catch (e) {\n console.error(r(`✖ ${(e as Error).message}`))\n process.exit(1)\n }\n}\n","import type { CliContext } from '../lib/context'\nimport { getPkgVersion, prompt } from '../lib/context'\nimport { getBaseUrl, requireEnv } from '../lib/env'\nimport { fetchJson, getToken } from '../lib/daraja'\nimport { b, c, dim, g, printJson, r, y } from '../lib/output'\nimport { existsSync, writeFileSync } from 'node:fs'\nimport { resolve } from 'node:path'\nimport { encryptSecurityCredential } from '../../core/encryption'\nimport { readFile } from 'node:fs/promises'\nimport { Mpesa } from '../../mpesa'\nimport { formatSafaricomPhone } from '../../utils/phone'\nimport { mpesaFromEnv } from '../lib/mpesa-from-env'\n\nexport async function cmdStkQuery(ctx: CliContext) {\n const args = ctx.args\n const env = ctx.env\n requireEnv(\n env,\n 'MPESA_CONSUMER_KEY',\n 'MPESA_CONSUMER_SECRET',\n 'MPESA_ENVIRONMENT',\n 'MPESA_SHORTCODE',\n 'MPESA_PASSKEY',\n )\n\n let checkoutId = args[0]\n if (!checkoutId) checkoutId = await prompt('CheckoutRequestID')\n\n const baseUrl =\n env['MPESA_ENVIRONMENT'] === 'production'\n ? 'https://api.safaricom.co.ke'\n : 'https://sandbox.safaricom.co.ke'\n\n const token = await getToken(env['MPESA_CONSUMER_KEY']!, env['MPESA_CONSUMER_SECRET']!, baseUrl)\n const { getStkPushPassword, getTimestamp } = await import('../../mpesa/stk-push/utils.js')\n const timestamp = getTimestamp()\n const password = getStkPushPassword(env['MPESA_SHORTCODE']!, env['MPESA_PASSKEY']!, timestamp)\n\n try {\n const result = (await fetchJson(\n `${baseUrl}/mpesa/stkpushquery/v1/query`,\n 'POST',\n { Authorization: `Bearer ${token}` },\n {\n BusinessShortCode: env['MPESA_SHORTCODE'],\n Password: password,\n Timestamp: timestamp,\n CheckoutRequestID: checkoutId,\n },\n )) as Record<string, unknown>\n\n const code = result['ResultCode'] as number\n const desc = result['ResultDesc'] as string\n\n if (code === 0) {\n console.log(`\\n${g('✔ Payment confirmed!')} — ${desc}\\n`)\n } else {\n console.log(`\\n${y('⚠ Payment not complete')} (code ${code}) — ${desc}\\n`)\n }\n console.log(JSON.stringify(result, null, 2))\n } catch (e) {\n console.error(r(`✖ ${(e as Error).message}`))\n process.exit(1)\n }\n}\n\n/**\n * Account Balance CLI command\n *\n * Daraja API: POST /mpesa/accountbalance/v1/query\n *\n * ASYNCHRONOUS — submits the request and returns OriginatorConversationID.\n * Actual balance data arrives via POST to MPESA_RESULT_URL.\n *\n * Required env:\n * MPESA_CONSUMER_KEY, MPESA_CONSUMER_SECRET, MPESA_ENVIRONMENT\n * MPESA_INITIATOR_NAME, MPESA_INITIATOR_PASSWORD, MPESA_CERTIFICATE_PATH\n * MPESA_RESULT_URL, MPESA_QUEUE_TIMEOUT_URL\n *\n * Required org portal role: \"Balance Query ORG API\"\n *\n * Flags:\n * --shortcode <code> — PartyA shortcode to query (default: MPESA_SHORTCODE)\n * --identifier-type <type> — \"1\" MSISDN | \"2\" Till | \"4\" ShortCode (default: \"4\")\n * --remarks <text> — Optional remarks (default: \"Balance query via pesafy CLI\")\n */\n","import type { CliContext } from '../lib/context'\nimport { getPkgVersion, prompt } from '../lib/context'\nimport { getBaseUrl, requireEnv } from '../lib/env'\nimport { fetchJson, getToken } from '../lib/daraja'\nimport { b, c, dim, g, printJson, r, y } from '../lib/output'\nimport { existsSync, writeFileSync } from 'node:fs'\nimport { resolve } from 'node:path'\nimport { encryptSecurityCredential } from '../../core/encryption'\nimport { readFile } from 'node:fs/promises'\nimport { Mpesa } from '../../mpesa'\nimport { formatSafaricomPhone } from '../../utils/phone'\nimport { mpesaFromEnv } from '../lib/mpesa-from-env'\n\nexport async function cmdBalance(ctx: CliContext) {\n const args = ctx.args\n const env = ctx.env\n requireEnv(\n env,\n 'MPESA_CONSUMER_KEY',\n 'MPESA_CONSUMER_SECRET',\n 'MPESA_ENVIRONMENT',\n 'MPESA_INITIATOR_NAME',\n 'MPESA_INITIATOR_PASSWORD',\n )\n\n const getArg = (flag: string) => {\n const idx = args.indexOf(flag)\n return idx !== -1 ? args[idx + 1] : undefined\n }\n\n // PartyA — the shortcode being queried\n const partyA =\n getArg('--shortcode') ??\n args[0] ??\n env['MPESA_SHORTCODE'] ??\n (await prompt('Shortcode to query (PartyA)'))\n\n // IdentifierType — defaults to \"4\" (Organisation ShortCode) per Daraja docs\n const identifierTypeRaw = getArg('--identifier-type') ?? env['MPESA_IDENTIFIER_TYPE'] ?? '4'\n const validIdentifierTypes = ['1', '2', '4'] as const\n type IdentifierType = '1' | '2' | '4'\n if (!validIdentifierTypes.includes(identifierTypeRaw as IdentifierType)) {\n console.error(\n r(\n `✖ Invalid --identifier-type \"${identifierTypeRaw}\". Must be \"1\" (MSISDN), \"2\" (Till), or \"4\" (ShortCode).`,\n ),\n )\n process.exit(1)\n }\n const identifierType = identifierTypeRaw as IdentifierType\n\n const remarks = getArg('--remarks') ?? 'Balance query via pesafy CLI'\n\n const resultUrl = env['MPESA_RESULT_URL'] ?? (await prompt('Result URL'))\n const queueTimeoutUrl = env['MPESA_QUEUE_TIMEOUT_URL'] ?? (await prompt('Queue Timeout URL'))\n\n // Validate URLs are present\n if (!resultUrl.trim()) {\n console.error(r('✖ ResultURL is required. Set MPESA_RESULT_URL in your .env'))\n process.exit(1)\n }\n if (!queueTimeoutUrl.trim()) {\n console.error(r('✖ QueueTimeOutURL is required. Set MPESA_QUEUE_TIMEOUT_URL in your .env'))\n process.exit(1)\n }\n\n const baseUrl =\n env['MPESA_ENVIRONMENT'] === 'production'\n ? 'https://api.safaricom.co.ke'\n : 'https://sandbox.safaricom.co.ke'\n\n console.log(dim('\\nFetching token…'))\n const token = await getToken(env['MPESA_CONSUMER_KEY']!, env['MPESA_CONSUMER_SECRET']!, baseUrl)\n\n // Encrypt initiator password using the certificate\n const { encryptSecurityCredential } = await import('../../core/encryption/index.js')\n const { readFile } = await import('node:fs/promises')\n const certPath = env['MPESA_CERTIFICATE_PATH'] ?? './SandboxCertificate.cer'\n\n if (!existsSync(resolve(process.cwd(), certPath))) {\n console.error(r(`✖ Certificate not found: ${certPath}`))\n console.error(dim(' Download from Safaricom Daraja portal and set MPESA_CERTIFICATE_PATH'))\n process.exit(1)\n }\n\n const pem = await readFile(resolve(process.cwd(), certPath), 'utf-8')\n const securityCredential = encryptSecurityCredential(env['MPESA_INITIATOR_PASSWORD']!, pem)\n\n console.log(dim('Sending Account Balance query…\\n'))\n\n const identifierTypeLabel: Record<IdentifierType, string> = {\n '1': 'MSISDN',\n '2': 'Till Number',\n '4': 'Organisation ShortCode',\n }\n\n try {\n // Exactly 8 fields per Daraja Account Balance spec\n const result = (await fetchJson(\n `${baseUrl}/mpesa/accountbalance/v1/query`,\n 'POST',\n { Authorization: `Bearer ${token}` },\n {\n Initiator: env['MPESA_INITIATOR_NAME'],\n SecurityCredential: securityCredential,\n CommandID: 'AccountBalance',\n PartyA: String(partyA),\n IdentifierType: identifierType,\n ResultURL: resultUrl,\n QueueTimeOutURL: queueTimeoutUrl,\n Remarks: remarks,\n },\n )) as Record<string, string>\n\n if (result['ResponseCode'] === '0') {\n console.log(`${g('✔ Account Balance query submitted!')}\\n`)\n console.log(\n ` ${b('OriginatorConversationID:')} ${c(result['OriginatorConversationID'] ?? '')}`,\n )\n console.log(` ${b('ConversationID:')} ${result['ConversationID'] ?? ''}`)\n console.log(` ${b('ResponseDescription:')} ${result['ResponseDescription'] ?? ''}`)\n console.log(` ${b('PartyA (shortcode):')} ${partyA}`)\n console.log(\n ` ${b('IdentifierType:')} ${identifierType} (${identifierTypeLabel[identifierType]})`,\n )\n console.log()\n console.log(\n dim(` ⚠ This API is ASYNCHRONOUS. The balance data will be POSTed to:\\n ${resultUrl}\\n`),\n )\n console.log(\n dim(\n ` Save the OriginatorConversationID to correlate the async callback.\\n` +\n ` If the callback fails, check the M-PESA org portal manually.\\n`,\n ),\n )\n } else {\n console.log(`${r('✖ Account Balance query failed:')}\\n`)\n console.log(JSON.stringify(result, null, 2))\n\n // Provide helpful hints for common error codes\n const errorCode = result['errorCode'] ?? ''\n if (errorCode === '18' || result['ResponseDescription']?.includes('initiator')) {\n console.log(y('\\n Hint: Error 18 = Initiator Credential Check Failure'))\n console.log(dim(' — Verify MPESA_INITIATOR_NAME is the correct API operator username'))\n console.log(\n dim(' — Confirm the API user is active (not dormant) on the M-PESA org portal'),\n )\n console.log(dim(' — Ensure the certificate matches the environment (sandbox/production)'))\n } else if (errorCode === '20') {\n console.log(y('\\n Hint: Error 20 = Unresolved Initiator'))\n console.log(\n dim(' — The initiator username was not found. Log in to the M-PESA portal to verify.'),\n )\n } else if (errorCode === '21') {\n console.log(y('\\n Hint: Error 21 = Initiator to Primary Party Permission Failure'))\n console.log(dim(' — The API user does not have the \"Balance Query ORG API\" role.'))\n console.log(dim(' — Ask your Business Administrator to assign the correct role.'))\n } else if (errorCode === '15') {\n console.log(y('\\n Hint: Error 15 = Duplicate Detected'))\n console.log(\n dim(' — A request with the same OriginatorConversationID was already submitted.'),\n )\n }\n }\n } catch (e) {\n console.error(r(`✖ ${(e as Error).message}`))\n process.exit(1)\n }\n}\n","import type { CliContext } from '../lib/context'\nimport { getPkgVersion, prompt } from '../lib/context'\nimport { getBaseUrl, requireEnv } from '../lib/env'\nimport { fetchJson, getToken } from '../lib/daraja'\nimport { b, c, dim, g, printJson, r, y } from '../lib/output'\nimport { existsSync, writeFileSync } from 'node:fs'\nimport { resolve } from 'node:path'\nimport { encryptSecurityCredential } from '../../core/encryption'\nimport { readFile } from 'node:fs/promises'\nimport { Mpesa } from '../../mpesa'\nimport { formatSafaricomPhone } from '../../utils/phone'\nimport { mpesaFromEnv } from '../lib/mpesa-from-env'\n\nexport async function cmdRegisterC2BUrls(ctx: CliContext) {\n const args = ctx.args\n const env = ctx.env\n requireEnv(env, 'MPESA_CONSUMER_KEY', 'MPESA_CONSUMER_SECRET', 'MPESA_ENVIRONMENT')\n\n const shortCode = args[0] ?? env['MPESA_SHORTCODE'] ?? (await prompt('Shortcode'))\n const confirmationUrl = args[1] ?? (await prompt('Confirmation URL'))\n const validationUrl = args[2] ?? (await prompt('Validation URL'))\n const responseType = (args[3] ??\n (await prompt('Response type (Completed/Cancelled)', 'Completed'))) as 'Completed' | 'Cancelled'\n\n const baseUrl =\n env['MPESA_ENVIRONMENT'] === 'production'\n ? 'https://api.safaricom.co.ke'\n : 'https://sandbox.safaricom.co.ke'\n\n const token = await getToken(env['MPESA_CONSUMER_KEY']!, env['MPESA_CONSUMER_SECRET']!, baseUrl)\n\n try {\n const result = (await fetchJson(\n `${baseUrl}/mpesa/c2b/v2/registerurl`,\n 'POST',\n { Authorization: `Bearer ${token}` },\n {\n ShortCode: shortCode,\n ResponseType: responseType,\n ConfirmationURL: confirmationUrl,\n ValidationURL: validationUrl,\n },\n )) as Record<string, string>\n\n if (result['ResponseCode'] === '0') {\n console.log(`\\n${g('✔ C2B URLs registered successfully!')}\\n`)\n } else {\n console.log(r('✖ Registration failed:'))\n console.log(JSON.stringify(result, null, 2))\n }\n } catch (e) {\n console.error(r(`✖ ${(e as Error).message}`))\n process.exit(1)\n }\n}\n","import type { CliContext } from '../lib/context'\nimport { getPkgVersion, prompt } from '../lib/context'\nimport { getBaseUrl, requireEnv } from '../lib/env'\nimport { fetchJson, getToken } from '../lib/daraja'\nimport { b, c, dim, g, printJson, r, y } from '../lib/output'\nimport { existsSync, writeFileSync } from 'node:fs'\nimport { resolve } from 'node:path'\nimport { encryptSecurityCredential } from '../../core/encryption'\nimport { readFile } from 'node:fs/promises'\nimport { Mpesa } from '../../mpesa'\nimport { formatSafaricomPhone } from '../../utils/phone'\nimport { mpesaFromEnv } from '../lib/mpesa-from-env'\n\nexport async function cmdSimulateC2B(ctx: CliContext) {\n const args = ctx.args\n const env = ctx.env\n requireEnv(env, 'MPESA_CONSUMER_KEY', 'MPESA_CONSUMER_SECRET')\n if (env['MPESA_ENVIRONMENT'] === 'production') {\n console.error(r('✖ C2B simulate is only available in sandbox.'))\n process.exit(1)\n }\n\n const shortCode = args[0] ?? env['MPESA_SHORTCODE'] ?? (await prompt('Shortcode'))\n const amount = Number(args[1] ?? (await prompt('Amount (KES)')))\n const msisdn = args[2] ?? (await prompt('MSISDN', '254708374149'))\n const commandId = (args[3] ??\n (await prompt(\n 'CommandID (CustomerPayBillOnline/CustomerBuyGoodsOnline)',\n 'CustomerPayBillOnline',\n ))) as string\n const billRef =\n commandId === 'CustomerBuyGoodsOnline'\n ? undefined\n : (args[4] ?? (await prompt('BillRefNumber (account ref)', '')))\n\n const baseUrl = 'https://sandbox.safaricom.co.ke'\n const token = await getToken(env['MPESA_CONSUMER_KEY']!, env['MPESA_CONSUMER_SECRET']!, baseUrl)\n\n const payload: Record<string, unknown> = {\n ShortCode: Number(shortCode),\n CommandID: commandId,\n Amount: Math.round(amount),\n Msisdn: Number(msisdn),\n }\n if (commandId !== 'CustomerBuyGoodsOnline') payload['BillRefNumber'] = billRef ?? ''\n\n try {\n const result = (await fetchJson(\n `${baseUrl}/mpesa/c2b/v2/simulate`,\n 'POST',\n { Authorization: `Bearer ${token}` },\n payload,\n )) as Record<string, string>\n if (result['ResponseCode'] === '0') {\n console.log(`\\n${g('✔ C2B simulation submitted!')}\\n`)\n } else {\n console.log(r('✖ Simulation failed:'))\n console.log(JSON.stringify(result, null, 2))\n }\n } catch (e) {\n console.error(r(`✖ ${(e as Error).message}`))\n process.exit(1)\n }\n}\n","import type { CliContext } from '../lib/context'\nimport { getPkgVersion, prompt } from '../lib/context'\nimport { getBaseUrl, requireEnv } from '../lib/env'\nimport { fetchJson, getToken } from '../lib/daraja'\nimport { b, c, dim, g, printJson, r, y } from '../lib/output'\nimport { existsSync, writeFileSync } from 'node:fs'\nimport { resolve } from 'node:path'\nimport { encryptSecurityCredential } from '../../core/encryption'\nimport { readFile } from 'node:fs/promises'\nimport { Mpesa } from '../../mpesa'\nimport { formatSafaricomPhone } from '../../utils/phone'\nimport { mpesaFromEnv } from '../lib/mpesa-from-env'\n\nexport async function cmdReversal(ctx: CliContext) {\n const args = ctx.args\n const env = ctx.env\n requireEnv(\n env,\n 'MPESA_CONSUMER_KEY',\n 'MPESA_CONSUMER_SECRET',\n 'MPESA_ENVIRONMENT',\n 'MPESA_INITIATOR_NAME',\n 'MPESA_INITIATOR_PASSWORD',\n )\n\n const transactionId = args[0] ?? (await prompt('Transaction ID to reverse'))\n const receiverParty =\n args[1] ?? env['MPESA_SHORTCODE'] ?? (await prompt('Receiver Party (shortcode)'))\n const amount = Number(args[2] ?? (await prompt('Amount to reverse')))\n const resultUrl = env['MPESA_RESULT_URL'] ?? (await prompt('Result URL'))\n const queueTimeoutUrl = env['MPESA_QUEUE_TIMEOUT_URL'] ?? (await prompt('Queue Timeout URL'))\n\n const baseUrl =\n env['MPESA_ENVIRONMENT'] === 'production'\n ? 'https://api.safaricom.co.ke'\n : 'https://sandbox.safaricom.co.ke'\n\n const token = await getToken(env['MPESA_CONSUMER_KEY']!, env['MPESA_CONSUMER_SECRET']!, baseUrl)\n\n const { encryptSecurityCredential } = await import('../../core/encryption/index.js')\n const { readFile } = await import('node:fs/promises')\n const certPath = env['MPESA_CERTIFICATE_PATH'] ?? './SandboxCertificate.cer'\n const pem = await readFile(resolve(process.cwd(), certPath), 'utf-8')\n const cred = encryptSecurityCredential(env['MPESA_INITIATOR_PASSWORD']!, pem)\n\n try {\n const result = (await fetchJson(\n `${baseUrl}/mpesa/reversal/v1/request`,\n 'POST',\n { Authorization: `Bearer ${token}` },\n {\n Initiator: env['MPESA_INITIATOR_NAME'],\n SecurityCredential: cred,\n CommandID: 'TransactionReversal',\n TransactionID: transactionId,\n Amount: String(Math.round(amount)),\n ReceiverParty: receiverParty,\n RecieverIdentifierType: '4',\n ResultURL: resultUrl,\n QueueTimeOutURL: queueTimeoutUrl,\n Remarks: 'Reversal via pesafy CLI',\n Occasion: '',\n },\n )) as Record<string, string>\n\n if (result['ResponseCode'] === '0') {\n console.log(`\\n${g('✔ Reversal submitted!')} Result will be POSTed to:\\n ${c(resultUrl)}\\n`)\n } else {\n console.log(r('✖ Reversal failed:'))\n console.log(JSON.stringify(result, null, 2))\n }\n } catch (e) {\n console.error(r(`✖ ${(e as Error).message}`))\n process.exit(1)\n }\n}\n","import type { CliContext } from '../lib/context'\nimport { prompt } from '../lib/context'\nimport { requireEnv } from '../lib/env'\nimport { mpesaFromEnv } from '../lib/mpesa-from-env'\nimport { b, g, printJson, r } from '../lib/output'\n\nfunction getArg(args: string[], flag: string): string | undefined {\n const idx = args.indexOf(flag)\n return idx !== -1 ? args[idx + 1] : undefined\n}\n\nexport async function cmdTxStatus(ctx: CliContext): Promise<void> {\n const mpesa = await mpesaFromEnv(ctx.env, { requireInitiator: true })\n requireEnv(ctx.env, 'MPESA_RESULT_URL', 'MPESA_QUEUE_TIMEOUT_URL')\n const partyA = getArg(ctx.args, '--party-a') ?? ctx.args[0] ?? ctx.env['MPESA_SHORTCODE'] ?? ''\n const transactionId = getArg(ctx.args, '--tx') ?? ctx.args[1]\n const result = await mpesa.transactionStatus({\n ...(transactionId ? { transactionId } : {}),\n partyA,\n identifierType: (getArg(ctx.args, '--identifier-type') ?? '4') as '1' | '2' | '4',\n resultUrl: ctx.env['MPESA_RESULT_URL']!,\n queueTimeOutUrl: ctx.env['MPESA_QUEUE_TIMEOUT_URL']!,\n })\n if (ctx.json) printJson(result)\n else console.log(`${g('✔')} Transaction status query submitted\\n`, result)\n}\n\nexport async function cmdB2cPayment(ctx: CliContext): Promise<void> {\n const mpesa = await mpesaFromEnv(ctx.env, { requireInitiator: true })\n requireEnv(ctx.env, 'MPESA_RESULT_URL', 'MPESA_QUEUE_TIMEOUT_URL')\n const amount = Number(getArg(ctx.args, '--amount') ?? ctx.args[0] ?? 0)\n const partyB = getArg(ctx.args, '--phone') ?? ctx.args[1] ?? ''\n const result = await mpesa.b2cPayment({\n commandId: 'BusinessPayToBulk',\n amount,\n partyA: getArg(ctx.args, '--party-a') ?? ctx.env['MPESA_SHORTCODE'] ?? '',\n partyB,\n accountReference: getArg(ctx.args, '--ref') ?? `CLI-${Date.now()}`,\n resultUrl: ctx.env['MPESA_RESULT_URL']!,\n queueTimeOutUrl: ctx.env['MPESA_QUEUE_TIMEOUT_URL']!,\n })\n if (ctx.json) printJson(result)\n else console.log(`${g('✔')} B2C payment submitted\\n`, result)\n}\n\nexport async function cmdB2cDisburse(ctx: CliContext): Promise<void> {\n const mpesa = await mpesaFromEnv(ctx.env, { requireInitiator: true })\n requireEnv(ctx.env, 'MPESA_RESULT_URL', 'MPESA_QUEUE_TIMEOUT_URL')\n const result = await mpesa.b2cDisbursement({\n originatorConversationId:\n getArg(ctx.args, '--originator-id') ?? (await prompt('OriginatorConversationID')),\n commandId: (getArg(ctx.args, '--command') ?? 'BusinessPayment') as\n | 'BusinessPayment'\n | 'SalaryPayment'\n | 'PromotionPayment',\n amount: Number(getArg(ctx.args, '--amount') ?? ctx.args[0] ?? 0),\n partyA: getArg(ctx.args, '--party-a') ?? ctx.env['MPESA_SHORTCODE'] ?? '',\n partyB: getArg(ctx.args, '--phone') ?? ctx.args[1] ?? '',\n remarks: getArg(ctx.args, '--remarks') ?? 'pesafy CLI',\n resultUrl: ctx.env['MPESA_RESULT_URL']!,\n queueTimeOutUrl: ctx.env['MPESA_QUEUE_TIMEOUT_URL']!,\n })\n if (ctx.json) printJson(result)\n else console.log(`${g('✔')} B2C disbursement submitted\\n`, result)\n}\n\nexport async function cmdB2bCheckout(ctx: CliContext): Promise<void> {\n const mpesa = await mpesaFromEnv(ctx.env)\n const result = await mpesa.b2bExpressCheckout({\n primaryShortCode: getArg(ctx.args, '--primary') ?? ctx.env['MPESA_SHORTCODE'] ?? '',\n receiverShortCode: getArg(ctx.args, '--receiver') ?? ctx.env['MPESA_SHORTCODE'] ?? '',\n amount: Number(getArg(ctx.args, '--amount') ?? 0),\n paymentRef: getArg(ctx.args, '--ref') ?? `REF-${Date.now()}`,\n callbackUrl: getArg(ctx.args, '--callback') ?? ctx.env['MPESA_CALLBACK_URL'] ?? '',\n partnerName: getArg(ctx.args, '--partner') ?? 'pesafy',\n })\n if (ctx.json) printJson(result)\n else console.log(`${g('✔')} B2B checkout initiated\\n`, result)\n}\n\nexport async function cmdTaxRemit(ctx: CliContext): Promise<void> {\n const mpesa = await mpesaFromEnv(ctx.env, { requireInitiator: true })\n requireEnv(ctx.env, 'MPESA_RESULT_URL', 'MPESA_QUEUE_TIMEOUT_URL')\n const result = await mpesa.remitTax({\n amount: Number(getArg(ctx.args, '--amount') ?? 0),\n partyA: getArg(ctx.args, '--party-a') ?? ctx.env['MPESA_SHORTCODE'] ?? '',\n accountReference: getArg(ctx.args, '--prn') ?? (await prompt('KRA PRN')),\n resultUrl: ctx.env['MPESA_RESULT_URL']!,\n queueTimeOutUrl: ctx.env['MPESA_QUEUE_TIMEOUT_URL']!,\n })\n if (ctx.json) printJson(result)\n else console.log(`${g('✔')} Tax remittance submitted\\n`, result)\n}\n\nexport async function cmdQrGenerate(ctx: CliContext): Promise<void> {\n const mpesa = await mpesaFromEnv(ctx.env)\n const result = await mpesa.generateDynamicQR(ctx.args[0] ? JSON.parse(ctx.args[0]) : {})\n if (ctx.json) printJson(result)\n else console.log(`${g('✔')} Dynamic QR generated\\n`, result)\n}\n\nexport async function cmdBills(ctx: CliContext): Promise<void> {\n const sub = ctx.args[0]\n const mpesa = await mpesaFromEnv(ctx.env)\n const body = ctx.args[1] ? JSON.parse(ctx.args[1]) : {}\n\n let result: unknown\n switch (sub) {\n case 'optin':\n result = await mpesa.billManagerOptIn(body)\n break\n case 'invoice':\n result = await mpesa.sendInvoice(body)\n break\n case 'bulk':\n result = await mpesa.sendBulkInvoices(body)\n break\n case 'reconcile':\n result = await mpesa.reconcilePayment(body)\n break\n default:\n console.error(r('Usage: pesafy bills <optin|invoice|bulk|reconcile> [json-body]'))\n process.exit(1)\n }\n if (ctx.json) printJson(result)\n else console.log(`${g('✔')} Bill manager ${sub}\\n`, result)\n}\n","#!/usr/bin/env node\n\nimport { C, dim, r } from './lib/output'\nimport { createCliContext, getPkgVersion } from './lib/context'\nimport {\n cmdBalance,\n cmdDoctor,\n cmdEncrypt,\n cmdHelp,\n cmdInit,\n cmdRegisterC2BUrls,\n cmdReversal,\n cmdSimulateC2B,\n cmdStkPush,\n cmdStkQuery,\n cmdToken,\n cmdValidatePhone,\n cmdVersion,\n} from './commands/legacy'\nimport {\n cmdB2bCheckout,\n cmdB2cDisburse,\n cmdB2cPayment,\n cmdBills,\n cmdQrGenerate,\n cmdTaxRemit,\n cmdTxStatus,\n} from './commands/mpesa-api'\n\nconst GLOBAL_FLAGS = new Set(['--json', '--env-file', '--env'])\n\nasync function main(): Promise<void> {\n const rawArgv = process.argv.slice(2)\n const globalArgv: string[] = []\n let command = 'help'\n const commandArgs: string[] = []\n\n for (let i = 0; i < rawArgv.length; i++) {\n const a = rawArgv[i]!\n if (GLOBAL_FLAGS.has(a)) {\n globalArgv.push(a)\n if (a === '--env-file' || a === '--env') globalArgv.push(rawArgv[++i] ?? '')\n continue\n }\n if (command === 'help' && !a.startsWith('--')) {\n command = a\n continue\n }\n commandArgs.push(a)\n }\n\n const ctx = createCliContext([...globalArgv, ...commandArgs])\n\n const banner = `${C.cyan}${C.bold} pesafy${C.reset} ${dim(`v${getPkgVersion()}`)}${C.reset}`\n process.stdout.write(`\\n${banner}\\n`)\n\n const cmds: Record<string, (c: typeof ctx) => Promise<void>> = {\n init: cmdInit,\n doctor: cmdDoctor,\n token: cmdToken,\n encrypt: cmdEncrypt,\n 'validate-phone': cmdValidatePhone,\n 'stk-push': cmdStkPush,\n 'stk-query': cmdStkQuery,\n balance: cmdBalance,\n 'register-c2b-urls': cmdRegisterC2BUrls,\n 'simulate-c2b': cmdSimulateC2B,\n reversal: cmdReversal,\n 'tx-status': cmdTxStatus,\n 'b2c-payment': cmdB2cPayment,\n 'b2c-disburse': cmdB2cDisburse,\n 'b2b-checkout': cmdB2bCheckout,\n 'tax-remit': cmdTaxRemit,\n 'qr-generate': cmdQrGenerate,\n bills: cmdBills,\n version: cmdVersion,\n help: cmdHelp,\n '--help': cmdHelp,\n '-h': cmdHelp,\n '--version': cmdVersion,\n '-v': cmdVersion,\n }\n\n const handler = cmds[command]\n if (!handler) {\n console.error(r(`✖ Unknown command: \"${command}\"`))\n console.error(dim(' Run: npx pesafy help'))\n process.exit(1)\n }\n\n try {\n await handler(ctx)\n } catch (e) {\n console.error(r(`\\n✖ Unhandled error: ${(e as Error).message}\\n`))\n if (process.env['PESAFY_DEBUG']) console.error(e)\n process.exit(1)\n }\n}\n\nvoid main()\n"],"mappings":"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"}