mpesa-mock 0.1.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,"sources":["../src/index.ts","../src/server.ts","../src/core/transactions.ts","../src/core/webhook-dispatcher.ts","../src/routes/oauth.ts","../src/core/id-generator.ts","../src/config/defaults.ts","../src/core/auth.ts","../src/routes/stk-push.ts","../src/schemas/index.ts","../src/core/failure-injector.ts","../src/routes/stk-query.ts","../src/routes/c2b.ts","../src/routes/b2c.ts","../src/routes/b2b.ts","../src/routes/transaction-status.ts","../src/routes/account-balance.ts","../src/routes/reversal.ts","../src/routes/dashboard.ts"],"sourcesContent":["export { createServer, defaultConfig } from \"./server.js\";\nexport { InMemoryStore } from \"./core/transactions.js\";\nexport { WebhookDispatcher } from \"./core/webhook-dispatcher.js\";\nexport { pickScenario } from \"./core/failure-injector.js\";\nexport type { MockConfig } from \"./core/failure-injector.js\";\nexport type {\n AppContext,\n AppVariables,\n CreateServerOptions,\n RecorderEntry,\n} from \"./server.js\";\nexport type {\n TransactionRecord,\n TransactionState,\n FailureScenario,\n StkPushRequestBody,\n StkPushResponse,\n StkCallbackBody,\n C2BSimulateBody,\n C2BRegisterUrlBody,\n} from \"./types/daraja.js\";\n","import { Hono } from \"hono\";\nimport { logger as honoLogger } from \"hono/logger\";\nimport { InMemoryStore } from \"./core/transactions.js\";\nimport { WebhookDispatcher } from \"./core/webhook-dispatcher.js\";\nimport { oauthRoute } from \"./routes/oauth.js\";\nimport { stkPushRoute } from \"./routes/stk-push.js\";\nimport { stkQueryRoute } from \"./routes/stk-query.js\";\nimport { c2bRoute } from \"./routes/c2b.js\";\nimport { b2cRoute } from \"./routes/b2c.js\";\nimport { b2bRoute } from \"./routes/b2b.js\";\nimport { transactionStatusRoute } from \"./routes/transaction-status.js\";\nimport { accountBalanceRoute } from \"./routes/account-balance.js\";\nimport { reversalRoute } from \"./routes/reversal.js\";\nimport { dashboardRoute } from \"./routes/dashboard.js\";\nimport type { MockConfig } from \"./core/failure-injector.js\";\nimport { DEFAULTS } from \"./config/defaults.js\";\n\nexport interface AppVariables {\n store: InMemoryStore;\n dispatcher: WebhookDispatcher;\n config: MockConfig;\n log?: (msg: string) => void;\n recorder?: (entry: RecorderEntry) => void;\n}\n\nexport interface AppContext {\n Variables: AppVariables;\n}\n\nexport interface RecorderEntry {\n ts: number;\n direction: \"request\" | \"response\" | \"callback\";\n method: string;\n path: string;\n status?: number;\n body?: unknown;\n}\n\nexport interface CreateServerOptions {\n config?: Partial<MockConfig>;\n quiet?: boolean;\n recorder?: (entry: RecorderEntry) => void;\n store?: InMemoryStore;\n}\n\nexport function defaultConfig(overrides: Partial<MockConfig> = {}): MockConfig {\n return {\n defaultCallbackDelayMs: overrides.defaultCallbackDelayMs ?? DEFAULTS.callbackDelayMs,\n scenarios: overrides.scenarios ?? {},\n webhookRetry: {\n attempts: overrides.webhookRetry?.attempts ?? DEFAULTS.callbackRetryAttempts,\n backoffMs: overrides.webhookRetry?.backoffMs ?? DEFAULTS.callbackRetryBackoffMs,\n },\n };\n}\n\nexport function createServer(opts: CreateServerOptions = {}): {\n app: Hono<AppContext>;\n store: InMemoryStore;\n dispatcher: WebhookDispatcher;\n config: MockConfig;\n} {\n const store = opts.store ?? new InMemoryStore();\n const config = defaultConfig(opts.config);\n const log = opts.quiet ? undefined : (msg: string) => console.log(msg);\n const dispatcher = new WebhookDispatcher({ store, onLog: log });\n\n const app = new Hono<AppContext>();\n\n if (!opts.quiet) {\n app.use(\"*\", honoLogger((msg) => console.log(msg)));\n }\n\n app.use(\"*\", async (c, next) => {\n c.set(\"store\", store);\n c.set(\"dispatcher\", dispatcher);\n c.set(\"config\", config);\n if (log) c.set(\"log\", log);\n if (opts.recorder) c.set(\"recorder\", opts.recorder);\n await next();\n });\n\n app.get(\"/\", (c) => c.json({ name: \"mpesa-mock\", status: \"ok\" }));\n app.get(\"/__mock__/health\", (c) => c.json({ status: \"ok\", uptime: process.uptime() }));\n\n app.route(\"/\", oauthRoute());\n app.route(\"/\", stkPushRoute());\n app.route(\"/\", stkQueryRoute());\n app.route(\"/\", c2bRoute());\n app.route(\"/\", b2cRoute());\n app.route(\"/\", b2bRoute());\n app.route(\"/\", transactionStatusRoute());\n app.route(\"/\", accountBalanceRoute());\n app.route(\"/\", reversalRoute());\n app.route(\"/\", dashboardRoute());\n\n app.notFound((c) =>\n c.json(\n {\n errorCode: \"404.000.01\",\n errorMessage: `Not found: ${c.req.method} ${c.req.path}`,\n },\n 404,\n ),\n );\n\n return { app, store, dispatcher, config };\n}\n","import { EventEmitter } from \"node:events\";\nimport type { TransactionRecord, TransactionState } from \"../types/daraja.js\";\n\nexport interface TransactionStore {\n put(record: TransactionRecord): void;\n get(checkoutRequestID: string): TransactionRecord | undefined;\n getByConversationID(id: string): TransactionRecord | undefined;\n list(limit?: number): TransactionRecord[];\n update(checkoutRequestID: string, patch: Partial<TransactionRecord>): TransactionRecord | undefined;\n clear(): void;\n}\n\nexport class InMemoryStore extends EventEmitter implements TransactionStore {\n private byCheckout = new Map<string, TransactionRecord>();\n private byConversation = new Map<string, string>();\n\n put(record: TransactionRecord): void {\n this.byCheckout.set(record.checkoutRequestID, record);\n if (record.conversationID) {\n this.byConversation.set(record.conversationID, record.checkoutRequestID);\n }\n this.emit(\"change\", record);\n }\n\n get(checkoutRequestID: string): TransactionRecord | undefined {\n return this.byCheckout.get(checkoutRequestID);\n }\n\n getByConversationID(id: string): TransactionRecord | undefined {\n const key = this.byConversation.get(id);\n return key ? this.byCheckout.get(key) : undefined;\n }\n\n list(limit?: number): TransactionRecord[] {\n const all = Array.from(this.byCheckout.values()).sort((a, b) => b.createdAt - a.createdAt);\n return typeof limit === \"number\" ? all.slice(0, limit) : all;\n }\n\n update(checkoutRequestID: string, patch: Partial<TransactionRecord>): TransactionRecord | undefined {\n const existing = this.byCheckout.get(checkoutRequestID);\n if (!existing) return undefined;\n const next = { ...existing, ...patch };\n this.byCheckout.set(checkoutRequestID, next);\n if (next.conversationID) this.byConversation.set(next.conversationID, checkoutRequestID);\n this.emit(\"change\", next);\n return next;\n }\n\n clear(): void {\n this.byCheckout.clear();\n this.byConversation.clear();\n this.emit(\"clear\");\n }\n}\n\nexport function stateToResultCode(state: TransactionState): number {\n switch (state) {\n case \"success\": return 0;\n case \"insufficient_funds\": return 1;\n case \"user_cancelled\": return 1032;\n case \"wrong_pin\": return 2001;\n case \"expired\": return 1037;\n case \"system_error\": return 1025;\n case \"pending\": return 1019;\n case \"timeout\": return 1037;\n }\n}\n\nexport function stateToResultDesc(state: TransactionState): string {\n switch (state) {\n case \"success\": return \"The service request is processed successfully.\";\n case \"insufficient_funds\": return \"The balance is insufficient for the transaction.\";\n case \"user_cancelled\": return \"Request cancelled by user.\";\n case \"wrong_pin\": return \"The initiator information is invalid.\";\n case \"expired\": return \"DS timeout. User cannot be reached.\";\n case \"system_error\": return \"An error occurred while sending a push request.\";\n case \"pending\": return \"The transaction is being processed.\";\n case \"timeout\": return \"DS timeout. User cannot be reached.\";\n }\n}\n","import { EventEmitter } from \"node:events\";\nimport pRetry, { AbortError } from \"p-retry\";\nimport type { InMemoryStore } from \"./transactions.js\";\n\nexport interface WebhookJob {\n id: string;\n url: string;\n body: unknown;\n scheduledAt: number;\n attempts: number;\n maxAttempts: number;\n backoffMs: number;\n transactionId?: string;\n failNTimesFirst?: number;\n}\n\nexport interface DispatcherOptions {\n store?: InMemoryStore;\n fetchImpl?: typeof fetch;\n onLog?: (msg: string) => void;\n}\n\nexport class WebhookDispatcher extends EventEmitter {\n private pending = new Map<string, NodeJS.Timeout>();\n private store?: InMemoryStore;\n private fetchImpl: typeof fetch;\n private onLog?: (msg: string) => void;\n\n constructor(opts: DispatcherOptions = {}) {\n super();\n this.store = opts.store;\n this.fetchImpl = opts.fetchImpl ?? fetch;\n this.onLog = opts.onLog;\n }\n\n schedule(job: WebhookJob, delayMs: number): void {\n if (delayMs < 0) {\n this.emit(\"skipped\", { id: job.id, reason: \"timeout\" });\n return;\n }\n if (this.pending.has(job.id)) {\n clearTimeout(this.pending.get(job.id));\n }\n const handle = setTimeout(() => {\n this.pending.delete(job.id);\n void this.fire(job);\n }, delayMs);\n this.pending.set(job.id, handle);\n this.emit(\"scheduled\", { id: job.id, delayMs, url: job.url });\n }\n\n cancel(id: string): boolean {\n const h = this.pending.get(id);\n if (!h) return false;\n clearTimeout(h);\n this.pending.delete(id);\n return true;\n }\n\n pendingIds(): string[] {\n return Array.from(this.pending.keys());\n }\n\n async fire(job: WebhookJob): Promise<void> {\n let attempt = 0;\n try {\n await pRetry(\n async () => {\n attempt += 1;\n if (typeof job.failNTimesFirst === \"number\" && attempt <= job.failNTimesFirst) {\n this.onLog?.(`webhook attempt ${attempt} forced-fail for ${job.url}`);\n throw new Error(`forced fail ${attempt}`);\n }\n const res = await this.fetchImpl(job.url, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify(job.body),\n });\n if (!res.ok && res.status >= 500) {\n throw new Error(`callback ${res.status}`);\n }\n if (!res.ok && res.status >= 400) {\n throw new AbortError(`callback ${res.status}`);\n }\n this.emit(\"delivered\", { id: job.id, url: job.url, attempts: attempt });\n if (this.store && job.transactionId) {\n this.store.update(job.transactionId, {\n callbackAttempts: attempt,\n callbackDeliveredAt: Date.now(),\n });\n }\n },\n {\n retries: job.maxAttempts - 1,\n minTimeout: job.backoffMs,\n factor: 2,\n onFailedAttempt: (err) => {\n this.onLog?.(`webhook ${job.url} attempt ${err.attemptNumber} failed: ${err.message}`);\n },\n },\n );\n } catch (err) {\n this.emit(\"failed\", {\n id: job.id,\n url: job.url,\n attempts: attempt,\n error: err instanceof Error ? err.message : String(err),\n });\n if (this.store && job.transactionId) {\n this.store.update(job.transactionId, { callbackAttempts: attempt });\n }\n }\n }\n\n shutdown(): void {\n for (const handle of this.pending.values()) clearTimeout(handle);\n this.pending.clear();\n }\n}\n","import { Hono } from \"hono\";\nimport { issueToken, isUsingTestCredentials, parseBasicAuth } from \"../core/auth.js\";\nimport type { AppContext } from \"../server.js\";\n\nexport function oauthRoute(): Hono<AppContext> {\n const app = new Hono<AppContext>();\n\n app.get(\"/oauth/v1/generate\", (c) => {\n const grantType = c.req.query(\"grant_type\");\n if (grantType !== \"client_credentials\") {\n return c.json(\n { requestId: \"no-request-id\", errorCode: \"400.001.01\", errorMessage: \"Invalid grant_type\" },\n 400,\n );\n }\n const parsed = parseBasicAuth(c.req.header(\"authorization\"));\n if (!parsed) {\n return c.json(\n { requestId: \"no-request-id\", errorCode: \"401.002.01\", errorMessage: \"Invalid Authentication passed\" },\n 401,\n );\n }\n if (isUsingTestCredentials(parsed.key, parsed.secret)) {\n c.get(\"log\")?.(\"warn: using default test credentials (test_key:test_secret) — fine for mock\");\n }\n return c.json(issueToken());\n });\n\n return app;\n}\n","import { randomBytes, randomInt } from \"node:crypto\";\n\nexport function generateAccessToken(): string {\n return randomBytes(24).toString(\"base64\").replace(/[^a-zA-Z0-9]/g, \"\").slice(0, 32).padEnd(32, \"0\");\n}\n\nexport function generateMerchantRequestID(): string {\n const a = randomInt(10000, 99999);\n const b = randomInt(10000000, 99999999);\n const c = randomInt(1, 9);\n return `${a}-${b}-${c}`;\n}\n\nexport function generateCheckoutRequestID(date = new Date()): string {\n const pad = (n: number, len = 2) => String(n).padStart(len, \"0\");\n const dd = pad(date.getDate());\n const mm = pad(date.getMonth() + 1);\n const yyyy = String(date.getFullYear());\n const hh = pad(date.getHours());\n const mi = pad(date.getMinutes());\n const ss = pad(date.getSeconds());\n const ms = pad(date.getMilliseconds(), 3);\n return `ws_CO_${dd}${mm}${yyyy}${hh}${mi}${ss}${ms}`;\n}\n\nexport function generateConversationID(): string {\n const a = randomInt(1000, 9999);\n const b = randomInt(100000, 999999);\n const c = randomInt(10, 99);\n return `AG_${formatDate(new Date())}_${a}${b}${c}`;\n}\n\nexport function generateOriginatorConversationID(): string {\n const a = randomInt(10000, 99999);\n const b = randomInt(1000000, 9999999);\n const c = randomInt(1, 9);\n return `${a}-${b}-${c}`;\n}\n\nexport function generateMpesaReceiptNumber(): string {\n const chars = \"ABCDEFGHJKMNPQRSTUVWXYZ23456789\";\n let out = \"\";\n for (let i = 0; i < 10; i++) {\n out += chars[randomInt(0, chars.length)];\n }\n return out;\n}\n\nexport function generateTransactionDate(date = new Date()): number {\n const pad = (n: number) => String(n).padStart(2, \"0\");\n const yyyy = date.getFullYear();\n const mm = pad(date.getMonth() + 1);\n const dd = pad(date.getDate());\n const hh = pad(date.getHours());\n const mi = pad(date.getMinutes());\n const ss = pad(date.getSeconds());\n return Number(`${yyyy}${mm}${dd}${hh}${mi}${ss}`);\n}\n\nfunction formatDate(d: Date): string {\n const pad = (n: number) => String(n).padStart(2, \"0\");\n return `${pad(d.getDate())}${pad(d.getMonth() + 1)}${d.getFullYear()}${pad(d.getHours())}${pad(d.getMinutes())}${pad(d.getSeconds())}`;\n}\n","export const DEFAULTS = {\n port: 4000,\n host: \"0.0.0.0\",\n callbackDelayMs: 8000,\n callbackRetryAttempts: 3,\n callbackRetryBackoffMs: 1000,\n oauthTokenExpiresIn: \"3599\",\n testCredentials: {\n consumerKey: \"test_key\",\n consumerSecret: \"test_secret\",\n },\n testShortcode: \"174379\",\n testTill: \"600000\",\n partyB: \"254708374149\",\n} as const;\n\nexport const RESULT_CODES = {\n SUCCESS: 0,\n INSUFFICIENT_FUNDS: 1,\n SYSTEM_ERROR: 1025,\n USER_CANCELLED: 1032,\n TRANSACTION_EXPIRED: 1037,\n WRONG_PIN: 2001,\n} as const;\n\nexport const RESULT_DESCS: Record<number, string> = {\n 0: \"The service request is processed successfully.\",\n 1: \"The balance is insufficient for the transaction.\",\n 1025: \"An error occurred while sending a push request.\",\n 1032: \"Request cancelled by user.\",\n 1037: \"DS timeout. User cannot be reached.\",\n 2001: \"The initiator information is invalid.\",\n};\n","import { generateAccessToken } from \"./id-generator.js\";\nimport { DEFAULTS } from \"../config/defaults.js\";\n\nconst issuedTokens = new Map<string, number>();\nconst TOKEN_TTL_MS = 3599 * 1000;\n\nexport function parseBasicAuth(header: string | undefined): { key: string; secret: string } | null {\n if (!header || !header.toLowerCase().startsWith(\"basic \")) return null;\n const encoded = header.slice(6).trim();\n let decoded: string;\n try {\n decoded = Buffer.from(encoded, \"base64\").toString(\"utf-8\");\n } catch {\n return null;\n }\n const idx = decoded.indexOf(\":\");\n if (idx < 0) return null;\n const key = decoded.slice(0, idx);\n const secret = decoded.slice(idx + 1);\n if (!key || !secret) return null;\n return { key, secret };\n}\n\nexport function issueToken(): { access_token: string; expires_in: string } {\n const token = generateAccessToken();\n issuedTokens.set(token, Date.now() + TOKEN_TTL_MS);\n return { access_token: token, expires_in: DEFAULTS.oauthTokenExpiresIn };\n}\n\nexport function isUsingTestCredentials(key: string, secret: string): boolean {\n return key === DEFAULTS.testCredentials.consumerKey && secret === DEFAULTS.testCredentials.consumerSecret;\n}\n\nexport function parseBearerToken(header: string | undefined): string | null {\n if (!header) return null;\n const m = header.match(/^Bearer\\s+(.+)$/i);\n return m && m[1] ? m[1].trim() : null;\n}\n\nexport function isValidToken(token: string): boolean {\n const exp = issuedTokens.get(token);\n if (!exp) {\n // Mock leniency: accept any 32-char alphanumeric token so devs who\n // hard-code one for tests don't get blocked across server restarts.\n return /^[a-zA-Z0-9]{20,}$/.test(token);\n }\n if (Date.now() > exp) {\n issuedTokens.delete(token);\n return false;\n }\n return true;\n}\n\nexport function clearTokens(): void {\n issuedTokens.clear();\n}\n","import { Hono } from \"hono\";\nimport type { AppContext } from \"../server.js\";\nimport { isValidToken, parseBearerToken } from \"../core/auth.js\";\nimport {\n generateCheckoutRequestID,\n generateMerchantRequestID,\n generateMpesaReceiptNumber,\n generateTransactionDate,\n} from \"../core/id-generator.js\";\nimport { stkPushSchema } from \"../schemas/index.js\";\nimport { callbackDelayFor, isFailure, pickScenario } from \"../core/failure-injector.js\";\nimport { stateToResultCode, stateToResultDesc } from \"../core/transactions.js\";\nimport type { FailureScenario, StkCallbackBody, TransactionRecord, TransactionState } from \"../types/daraja.js\";\n\nfunction scenarioToState(s: FailureScenario): TransactionState {\n switch (s) {\n case \"success\":\n case \"callback_retry\":\n case \"slow\":\n return \"success\";\n case \"user_cancelled\": return \"user_cancelled\";\n case \"insufficient_funds\": return \"insufficient_funds\";\n case \"wrong_pin\": return \"wrong_pin\";\n case \"expired\": return \"expired\";\n case \"system_error\": return \"system_error\";\n case \"timeout\": return \"timeout\";\n }\n}\n\nexport function stkPushRoute(): Hono<AppContext> {\n const app = new Hono<AppContext>();\n\n app.post(\"/mpesa/stkpush/v1/processrequest\", async (c) => {\n const token = parseBearerToken(c.req.header(\"authorization\"));\n if (!token || !isValidToken(token)) {\n return c.json({ errorCode: \"404.001.03\", errorMessage: \"Invalid Access Token\" }, 401);\n }\n\n const raw = await c.req.json().catch(() => null);\n const parsed = stkPushSchema.safeParse(raw);\n if (!parsed.success) {\n return c.json(\n {\n requestId: \"no-request-id\",\n errorCode: \"400.002.05\",\n errorMessage: \"Invalid request payload\",\n errors: parsed.error.flatten(),\n },\n 400,\n );\n }\n const body = parsed.data;\n\n const merchantRequestID = generateMerchantRequestID();\n const checkoutRequestID = generateCheckoutRequestID();\n const scenario = pickScenario(body.PhoneNumber, c.var.config);\n const targetState = scenarioToState(scenario);\n const delay = callbackDelayFor(scenario, c.var.config);\n\n const record: TransactionRecord = {\n checkoutRequestID,\n merchantRequestID,\n kind: \"stk\",\n amount: body.Amount,\n phoneNumber: body.PhoneNumber,\n shortCode: body.BusinessShortCode,\n callbackUrl: body.CallBackURL,\n state: \"pending\",\n createdAt: Date.now(),\n callbackAttempts: 0,\n };\n c.var.store.put(record);\n\n const failNTimesFirst = scenario === \"callback_retry\" ? 3 : undefined;\n\n const callbackBody = buildStkCallback({\n merchantRequestID,\n checkoutRequestID,\n state: targetState,\n amount: body.Amount,\n phoneNumber: body.PhoneNumber,\n });\n\n if (scenario === \"timeout\") {\n c.var.log?.(`stk-push ${checkoutRequestID}: timeout scenario, no callback will fire`);\n } else {\n c.var.dispatcher.schedule(\n {\n id: checkoutRequestID,\n url: body.CallBackURL,\n body: callbackBody,\n scheduledAt: Date.now() + delay,\n attempts: 0,\n maxAttempts: c.var.config.webhookRetry.attempts + (failNTimesFirst ?? 0),\n backoffMs: c.var.config.webhookRetry.backoffMs,\n transactionId: checkoutRequestID,\n ...(failNTimesFirst !== undefined ? { failNTimesFirst } : {}),\n },\n delay,\n );\n }\n\n setTimeout(() => {\n const cur = c.var.store.get(checkoutRequestID);\n if (cur && cur.state === \"pending\") {\n c.var.store.update(checkoutRequestID, {\n state: targetState,\n resultCode: stateToResultCode(targetState),\n resultDesc: stateToResultDesc(targetState),\n completedAt: Date.now(),\n ...(targetState === \"success\" ? { mpesaReceiptNumber: generateMpesaReceiptNumber() } : {}),\n });\n }\n }, Math.max(0, delay));\n\n return c.json({\n MerchantRequestID: merchantRequestID,\n CheckoutRequestID: checkoutRequestID,\n ResponseCode: \"0\",\n ResponseDescription: \"Success. Request accepted for processing\",\n CustomerMessage: \"Success. Request accepted for processing\",\n });\n });\n\n return app;\n}\n\nfunction buildStkCallback(params: {\n merchantRequestID: string;\n checkoutRequestID: string;\n state: TransactionState;\n amount: number;\n phoneNumber: string;\n}): StkCallbackBody {\n const resultCode = stateToResultCode(params.state);\n const resultDesc = stateToResultDesc(params.state);\n if (params.state === \"success\") {\n return {\n Body: {\n stkCallback: {\n MerchantRequestID: params.merchantRequestID,\n CheckoutRequestID: params.checkoutRequestID,\n ResultCode: resultCode,\n ResultDesc: resultDesc,\n CallbackMetadata: {\n Item: [\n { Name: \"Amount\", Value: params.amount },\n { Name: \"MpesaReceiptNumber\", Value: generateMpesaReceiptNumber() },\n { Name: \"TransactionDate\", Value: generateTransactionDate() },\n { Name: \"PhoneNumber\", Value: Number(params.phoneNumber) },\n ],\n },\n },\n },\n };\n }\n return {\n Body: {\n stkCallback: {\n MerchantRequestID: params.merchantRequestID,\n CheckoutRequestID: params.checkoutRequestID,\n ResultCode: resultCode,\n ResultDesc: resultDesc,\n },\n },\n };\n}\n","import { z } from \"zod\";\n\nexport const stkPushSchema = z.object({\n BusinessShortCode: z.string().min(1),\n Password: z.string().min(1),\n Timestamp: z.string().min(1),\n TransactionType: z.enum([\"CustomerPayBillOnline\", \"CustomerBuyGoodsOnline\"]),\n Amount: z.number().int().positive(),\n PartyA: z.string().min(1),\n PartyB: z.string().min(1),\n PhoneNumber: z.string().min(1),\n CallBackURL: z.string().url(),\n AccountReference: z.string().min(1).max(12),\n TransactionDesc: z.string().min(1).max(13),\n});\n\nexport const stkQuerySchema = z.object({\n BusinessShortCode: z.string().min(1),\n Password: z.string().min(1),\n Timestamp: z.string().min(1),\n CheckoutRequestID: z.string().min(1),\n});\n\nexport const c2bRegisterUrlSchema = z.object({\n ShortCode: z.string().min(1),\n ResponseType: z.enum([\"Completed\", \"Cancelled\"]),\n ConfirmationURL: z.string().url(),\n ValidationURL: z.string().url(),\n});\n\nexport const c2bSimulateSchema = z.object({\n ShortCode: z.string().min(1),\n CommandID: z.enum([\"CustomerPayBillOnline\", \"CustomerBuyGoodsOnline\"]),\n Amount: z.number().int().positive(),\n Msisdn: z.string().min(1),\n BillRefNumber: z.string().min(1),\n});\n\nexport const b2cSchema = z.object({\n InitiatorName: z.string().min(1),\n SecurityCredential: z.string().min(1),\n CommandID: z.enum([\"SalaryPayment\", \"BusinessPayment\", \"PromotionPayment\"]),\n Amount: z.number().int().positive(),\n PartyA: z.string().min(1),\n PartyB: z.string().min(1),\n Remarks: z.string().min(1),\n QueueTimeOutURL: z.string().url(),\n ResultURL: z.string().url(),\n Occasion: z.string().optional().default(\"\"),\n});\n\nexport const b2bSchema = z.object({\n Initiator: z.string().min(1),\n SecurityCredential: z.string().min(1),\n CommandID: z.string().min(1),\n SenderIdentifierType: z.string().min(1),\n RecieverIdentifierType: z.string().min(1),\n Amount: z.number().int().positive(),\n PartyA: z.string().min(1),\n PartyB: z.string().min(1),\n AccountReference: z.string().optional().default(\"\"),\n Remarks: z.string().min(1),\n QueueTimeOutURL: z.string().url(),\n ResultURL: z.string().url(),\n});\n\nexport const transactionStatusSchema = z.object({\n Initiator: z.string().min(1),\n SecurityCredential: z.string().min(1),\n CommandID: z.literal(\"TransactionStatusQuery\"),\n TransactionID: z.string().min(1),\n PartyA: z.string().min(1),\n IdentifierType: z.string().min(1),\n ResultURL: z.string().url(),\n QueueTimeOutURL: z.string().url(),\n Remarks: z.string().min(1),\n Occasion: z.string().optional().default(\"\"),\n});\n\nexport const accountBalanceSchema = z.object({\n Initiator: z.string().min(1),\n SecurityCredential: z.string().min(1),\n CommandID: z.literal(\"AccountBalance\"),\n PartyA: z.string().min(1),\n IdentifierType: z.string().min(1),\n Remarks: z.string().min(1),\n QueueTimeOutURL: z.string().url(),\n ResultURL: z.string().url(),\n});\n\nexport const reversalSchema = z.object({\n Initiator: z.string().min(1),\n SecurityCredential: z.string().min(1),\n CommandID: z.literal(\"TransactionReversal\"),\n TransactionID: z.string().min(1),\n Amount: z.number().int().positive(),\n ReceiverParty: z.string().min(1),\n RecieverIdentifierType: z.string().min(1),\n ResultURL: z.string().url(),\n QueueTimeOutURL: z.string().url(),\n Remarks: z.string().min(1),\n Occasion: z.string().optional().default(\"\"),\n});\n","import type { FailureScenario } from \"../types/daraja.js\";\n\nexport interface MockConfig {\n defaultCallbackDelayMs: number;\n scenarios: Record<string, FailureScenario>;\n webhookRetry: {\n attempts: number;\n backoffMs: number;\n };\n}\n\nconst SUFFIX_MAP: Record<string, FailureScenario> = {\n \"00\": \"success\",\n \"01\": \"user_cancelled\",\n \"02\": \"insufficient_funds\",\n \"03\": \"wrong_pin\",\n \"04\": \"timeout\",\n \"05\": \"callback_retry\",\n \"06\": \"expired\",\n \"07\": \"system_error\",\n \"99\": \"slow\",\n};\n\nexport function pickScenario(phoneNumber: string, config: MockConfig): FailureScenario {\n const direct = config.scenarios[phoneNumber];\n if (direct) return direct;\n const suffix = phoneNumber.slice(-2);\n return SUFFIX_MAP[suffix] ?? \"success\";\n}\n\nexport function callbackDelayFor(scenario: FailureScenario, config: MockConfig): number {\n if (scenario === \"slow\") return 30_000;\n if (scenario === \"timeout\") return -1;\n return config.defaultCallbackDelayMs;\n}\n\nexport function isFailure(scenario: FailureScenario): boolean {\n return scenario !== \"success\" && scenario !== \"callback_retry\" && scenario !== \"slow\";\n}\n","import { Hono } from \"hono\";\nimport type { AppContext } from \"../server.js\";\nimport { isValidToken, parseBearerToken } from \"../core/auth.js\";\nimport { stkQuerySchema } from \"../schemas/index.js\";\nimport { stateToResultCode, stateToResultDesc } from \"../core/transactions.js\";\n\nexport function stkQueryRoute(): Hono<AppContext> {\n const app = new Hono<AppContext>();\n\n app.post(\"/mpesa/stkpushquery/v1/query\", async (c) => {\n const token = parseBearerToken(c.req.header(\"authorization\"));\n if (!token || !isValidToken(token)) {\n return c.json({ errorCode: \"404.001.03\", errorMessage: \"Invalid Access Token\" }, 401);\n }\n\n const raw = await c.req.json().catch(() => null);\n const parsed = stkQuerySchema.safeParse(raw);\n if (!parsed.success) {\n return c.json(\n { errorCode: \"400.002.05\", errorMessage: \"Invalid request payload\", errors: parsed.error.flatten() },\n 400,\n );\n }\n\n const record = c.var.store.get(parsed.data.CheckoutRequestID);\n if (!record) {\n return c.json(\n {\n requestId: \"no-request-id\",\n errorCode: \"500.001.1001\",\n errorMessage: \"The transaction is being processed\",\n },\n 500,\n );\n }\n\n const resultCode = record.resultCode ?? stateToResultCode(record.state);\n return c.json({\n ResponseCode: \"0\",\n ResponseDescription: \"The service request has been accepted successfully\",\n MerchantRequestID: record.merchantRequestID,\n CheckoutRequestID: record.checkoutRequestID,\n ResultCode: String(resultCode),\n ResultDesc: record.resultDesc ?? stateToResultDesc(record.state),\n });\n });\n\n return app;\n}\n","import { Hono } from \"hono\";\nimport type { AppContext } from \"../server.js\";\nimport { isValidToken, parseBearerToken } from \"../core/auth.js\";\nimport { c2bRegisterUrlSchema, c2bSimulateSchema } from \"../schemas/index.js\";\nimport {\n generateConversationID,\n generateMpesaReceiptNumber,\n generateOriginatorConversationID,\n generateTransactionDate,\n} from \"../core/id-generator.js\";\nimport type { TransactionRecord } from \"../types/daraja.js\";\n\ninterface RegisteredUrl {\n shortCode: string;\n confirmationURL: string;\n validationURL: string;\n responseType: \"Completed\" | \"Cancelled\";\n}\n\nconst registry = new Map<string, RegisteredUrl>();\n\nexport function c2bRoute(): Hono<AppContext> {\n const app = new Hono<AppContext>();\n\n app.post(\"/mpesa/c2b/v1/registerurl\", async (c) => {\n const token = parseBearerToken(c.req.header(\"authorization\"));\n if (!token || !isValidToken(token)) {\n return c.json({ errorCode: \"404.001.03\", errorMessage: \"Invalid Access Token\" }, 401);\n }\n const raw = await c.req.json().catch(() => null);\n const parsed = c2bRegisterUrlSchema.safeParse(raw);\n if (!parsed.success) {\n return c.json(\n { errorCode: \"400.002.05\", errorMessage: \"Invalid request payload\", errors: parsed.error.flatten() },\n 400,\n );\n }\n registry.set(parsed.data.ShortCode, {\n shortCode: parsed.data.ShortCode,\n confirmationURL: parsed.data.ConfirmationURL,\n validationURL: parsed.data.ValidationURL,\n responseType: parsed.data.ResponseType,\n });\n return c.json({\n OriginatorCoversationID: generateOriginatorConversationID(),\n ResponseCode: \"0\",\n ResponseDescription: \"success\",\n });\n });\n\n app.post(\"/mpesa/c2b/v1/simulate\", async (c) => {\n const token = parseBearerToken(c.req.header(\"authorization\"));\n if (!token || !isValidToken(token)) {\n return c.json({ errorCode: \"404.001.03\", errorMessage: \"Invalid Access Token\" }, 401);\n }\n const raw = await c.req.json().catch(() => null);\n const parsed = c2bSimulateSchema.safeParse(raw);\n if (!parsed.success) {\n return c.json(\n { errorCode: \"400.002.05\", errorMessage: \"Invalid request payload\", errors: parsed.error.flatten() },\n 400,\n );\n }\n const result = await runC2BSimulation(c, parsed.data.ShortCode, parsed.data.Amount, parsed.data.Msisdn, parsed.data.BillRefNumber);\n if (result.kind === \"no-registration\") {\n return c.json(\n { errorCode: \"500.001.1001\", errorMessage: \"No registered URLs found for this shortcode\" },\n 500,\n );\n }\n return c.json({\n OriginatorCoversationID: result.originatorConversationID,\n ConversationID: result.conversationID,\n ResponseDescription: \"Accept the service request successfully.\",\n });\n });\n\n app.post(\"/__mock__/c2b/trigger\", async (c) => {\n const raw = (await c.req.json().catch(() => null)) as Record<string, unknown> | null;\n if (!raw) {\n return c.json({ error: \"invalid body\" }, 400);\n }\n const shortCode = String(raw.ShortCode ?? raw.shortCode ?? \"\");\n const amount = Number(raw.Amount ?? raw.amount ?? 0);\n const msisdn = String(raw.Msisdn ?? raw.msisdn ?? \"\");\n const billRef = String(raw.BillRefNumber ?? raw.billRefNumber ?? \"TEST\");\n if (!shortCode || !amount || !msisdn) {\n return c.json({ error: \"ShortCode, Amount, Msisdn required\" }, 400);\n }\n const result = await runC2BSimulation(c, shortCode, amount, msisdn, billRef);\n return c.json(result, result.kind === \"no-registration\" ? 404 : 200);\n });\n\n return app;\n}\n\nasync function runC2BSimulation(\n c: { var: AppContext[\"Variables\"]; get<K extends keyof AppContext[\"Variables\"]>(k: K): AppContext[\"Variables\"][K] },\n shortCode: string,\n amount: number,\n msisdn: string,\n billRef: string,\n): Promise<\n | { kind: \"delivered\"; originatorConversationID: string; conversationID: string }\n | { kind: \"rejected\"; originatorConversationID: string; conversationID: string }\n | { kind: \"no-registration\" }\n> {\n const reg = registry.get(shortCode);\n if (!reg) return { kind: \"no-registration\" };\n\n const conversationID = generateConversationID();\n const originatorConversationID = generateOriginatorConversationID();\n const receipt = generateMpesaReceiptNumber();\n const transactionDate = generateTransactionDate();\n\n const txn: TransactionRecord = {\n checkoutRequestID: conversationID,\n merchantRequestID: originatorConversationID,\n conversationID,\n originatorConversationID,\n kind: \"c2b\",\n amount,\n phoneNumber: msisdn,\n shortCode,\n callbackUrl: reg.confirmationURL,\n state: \"pending\",\n createdAt: Date.now(),\n callbackAttempts: 0,\n mpesaReceiptNumber: receipt,\n };\n c.var.store.put(txn);\n\n const callbackPayload = {\n TransactionType: \"Pay Bill\",\n TransID: receipt,\n TransTime: String(transactionDate),\n TransAmount: String(amount),\n BusinessShortCode: shortCode,\n BillRefNumber: billRef,\n InvoiceNumber: \"\",\n OrgAccountBalance: \"0.00\",\n ThirdPartyTransID: \"\",\n MSISDN: msisdn,\n FirstName: \"Test\",\n MiddleName: \"C2B\",\n LastName: \"Customer\",\n };\n\n let validationOk = true;\n try {\n const res = await fetch(reg.validationURL, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify(callbackPayload),\n });\n if (res.ok) {\n const body = (await res.json().catch(() => ({}))) as { ResultCode?: string };\n if (body.ResultCode && body.ResultCode !== \"0\") validationOk = false;\n }\n } catch {\n validationOk = true;\n }\n\n if (!validationOk) {\n c.var.store.update(conversationID, { state: \"user_cancelled\", resultCode: 1, resultDesc: \"Validation rejected\", completedAt: Date.now() });\n return { kind: \"rejected\", originatorConversationID, conversationID };\n }\n\n c.var.dispatcher.schedule(\n {\n id: conversationID,\n url: reg.confirmationURL,\n body: callbackPayload,\n scheduledAt: Date.now(),\n attempts: 0,\n maxAttempts: c.var.config.webhookRetry.attempts,\n backoffMs: c.var.config.webhookRetry.backoffMs,\n transactionId: conversationID,\n },\n 0,\n );\n\n c.var.store.update(conversationID, { state: \"success\", resultCode: 0, resultDesc: \"Success\", completedAt: Date.now() });\n return { kind: \"delivered\", originatorConversationID, conversationID };\n}\n\nexport function clearC2BRegistry(): void {\n registry.clear();\n}\n","import { Hono } from \"hono\";\nimport type { AppContext } from \"../server.js\";\nimport { isValidToken, parseBearerToken } from \"../core/auth.js\";\nimport { b2cSchema } from \"../schemas/index.js\";\nimport {\n generateConversationID,\n generateMpesaReceiptNumber,\n generateOriginatorConversationID,\n} from \"../core/id-generator.js\";\nimport { pickScenario } from \"../core/failure-injector.js\";\nimport type { ResultCallbackBody, TransactionRecord } from \"../types/daraja.js\";\n\nexport function b2cRoute(): Hono<AppContext> {\n const app = new Hono<AppContext>();\n\n app.post(\"/mpesa/b2c/v1/paymentrequest\", async (c) => {\n const token = parseBearerToken(c.req.header(\"authorization\"));\n if (!token || !isValidToken(token)) {\n return c.json({ errorCode: \"404.001.03\", errorMessage: \"Invalid Access Token\" }, 401);\n }\n const raw = await c.req.json().catch(() => null);\n const parsed = b2cSchema.safeParse(raw);\n if (!parsed.success) {\n return c.json(\n { errorCode: \"400.002.05\", errorMessage: \"Invalid request payload\", errors: parsed.error.flatten() },\n 400,\n );\n }\n const body = parsed.data;\n const conversationID = generateConversationID();\n const originatorConversationID = generateOriginatorConversationID();\n const scenario = pickScenario(body.PartyB, c.var.config);\n const isSuccess = scenario === \"success\" || scenario === \"slow\" || scenario === \"callback_retry\";\n\n const record: TransactionRecord = {\n checkoutRequestID: conversationID,\n merchantRequestID: originatorConversationID,\n conversationID,\n originatorConversationID,\n kind: \"b2c\",\n amount: body.Amount,\n phoneNumber: body.PartyB,\n shortCode: body.PartyA,\n callbackUrl: body.ResultURL,\n state: isSuccess ? \"success\" : \"system_error\",\n createdAt: Date.now(),\n callbackAttempts: 0,\n ...(isSuccess ? { mpesaReceiptNumber: generateMpesaReceiptNumber() } : {}),\n };\n c.var.store.put(record);\n\n const callback: ResultCallbackBody = {\n Result: {\n ResultType: 0,\n ResultCode: isSuccess ? 0 : 2001,\n ResultDesc: isSuccess\n ? \"The service request is processed successfully.\"\n : \"The initiator information is invalid.\",\n OriginatorConversationID: originatorConversationID,\n ConversationID: conversationID,\n TransactionID: record.mpesaReceiptNumber ?? \"N/A\",\n ResultParameters: {\n ResultParameter: isSuccess\n ? [\n { Key: \"TransactionAmount\", Value: body.Amount },\n { Key: \"TransactionReceipt\", Value: record.mpesaReceiptNumber ?? \"\" },\n { Key: \"B2CRecipientIsRegisteredCustomer\", Value: \"Y\" },\n { Key: \"B2CChargesPaidAccountAvailableFunds\", Value: 0 },\n { Key: \"ReceiverPartyPublicName\", Value: `${body.PartyB} - Test Recipient` },\n { Key: \"TransactionCompletedDateTime\", Value: new Date().toISOString() },\n { Key: \"B2CUtilityAccountAvailableFunds\", Value: 1000000 },\n { Key: \"B2CWorkingAccountAvailableFunds\", Value: 1000000 },\n ]\n : [],\n },\n ReferenceData: {\n ReferenceItem: { Key: \"QueueTimeoutURL\", Value: body.QueueTimeOutURL },\n },\n },\n };\n\n c.var.dispatcher.schedule(\n {\n id: conversationID,\n url: body.ResultURL,\n body: callback,\n scheduledAt: Date.now() + c.var.config.defaultCallbackDelayMs,\n attempts: 0,\n maxAttempts: c.var.config.webhookRetry.attempts,\n backoffMs: c.var.config.webhookRetry.backoffMs,\n transactionId: conversationID,\n },\n c.var.config.defaultCallbackDelayMs,\n );\n\n return c.json({\n ConversationID: conversationID,\n OriginatorConversationID: originatorConversationID,\n ResponseCode: \"0\",\n ResponseDescription: \"Accept the service request successfully.\",\n });\n });\n\n return app;\n}\n","import { Hono } from \"hono\";\nimport type { AppContext } from \"../server.js\";\nimport { isValidToken, parseBearerToken } from \"../core/auth.js\";\nimport { b2bSchema } from \"../schemas/index.js\";\nimport {\n generateConversationID,\n generateMpesaReceiptNumber,\n generateOriginatorConversationID,\n} from \"../core/id-generator.js\";\nimport type { ResultCallbackBody, TransactionRecord } from \"../types/daraja.js\";\n\nexport function b2bRoute(): Hono<AppContext> {\n const app = new Hono<AppContext>();\n\n app.post(\"/mpesa/b2b/v1/paymentrequest\", async (c) => {\n const token = parseBearerToken(c.req.header(\"authorization\"));\n if (!token || !isValidToken(token)) {\n return c.json({ errorCode: \"404.001.03\", errorMessage: \"Invalid Access Token\" }, 401);\n }\n const raw = await c.req.json().catch(() => null);\n const parsed = b2bSchema.safeParse(raw);\n if (!parsed.success) {\n return c.json(\n { errorCode: \"400.002.05\", errorMessage: \"Invalid request payload\", errors: parsed.error.flatten() },\n 400,\n );\n }\n const body = parsed.data;\n const conversationID = generateConversationID();\n const originatorConversationID = generateOriginatorConversationID();\n const receipt = generateMpesaReceiptNumber();\n\n const record: TransactionRecord = {\n checkoutRequestID: conversationID,\n merchantRequestID: originatorConversationID,\n conversationID,\n originatorConversationID,\n kind: \"b2b\",\n amount: body.Amount,\n phoneNumber: body.PartyB,\n shortCode: body.PartyA,\n callbackUrl: body.ResultURL,\n state: \"success\",\n createdAt: Date.now(),\n callbackAttempts: 0,\n mpesaReceiptNumber: receipt,\n };\n c.var.store.put(record);\n\n const callback: ResultCallbackBody = {\n Result: {\n ResultType: 0,\n ResultCode: 0,\n ResultDesc: \"The service request is processed successfully.\",\n OriginatorConversationID: originatorConversationID,\n ConversationID: conversationID,\n TransactionID: receipt,\n ResultParameters: {\n ResultParameter: [\n { Key: \"DebitAccountBalance\", Value: \"Working Account|KES|1000000.00|1000000.00|0.00|0.00\" },\n { Key: \"Amount\", Value: body.Amount },\n { Key: \"DebitPartyAffectedAccountBalance\", Value: \"Working Account|KES|1000000.00|1000000.00|0.00|0.00\" },\n { Key: \"TransCompletedTime\", Value: new Date().toISOString() },\n { Key: \"DebitPartyCharges\", Value: \"\" },\n { Key: \"ReceiverPartyPublicName\", Value: `${body.PartyB} - Test Business` },\n { Key: \"Currency\", Value: \"KES\" },\n { Key: \"InitiatorAccountCurrentBalance\", Value: \"{Amount={CurrencyCode=KES, MinimumAmount=99999900, BasicAmount=999999.00}}\" },\n ],\n },\n ReferenceData: {\n ReferenceItem: [\n { Key: \"BillReferenceNumber\", Value: body.AccountReference ?? \"\" },\n { Key: \"QueueTimeoutURL\", Value: body.QueueTimeOutURL },\n ],\n },\n },\n };\n\n c.var.dispatcher.schedule(\n {\n id: conversationID,\n url: body.ResultURL,\n body: callback,\n scheduledAt: Date.now() + c.var.config.defaultCallbackDelayMs,\n attempts: 0,\n maxAttempts: c.var.config.webhookRetry.attempts,\n backoffMs: c.var.config.webhookRetry.backoffMs,\n transactionId: conversationID,\n },\n c.var.config.defaultCallbackDelayMs,\n );\n\n return c.json({\n ConversationID: conversationID,\n OriginatorConversationID: originatorConversationID,\n ResponseCode: \"0\",\n ResponseDescription: \"Accept the service request successfully.\",\n });\n });\n\n return app;\n}\n","import { Hono } from \"hono\";\nimport type { AppContext } from \"../server.js\";\nimport { isValidToken, parseBearerToken } from \"../core/auth.js\";\nimport { transactionStatusSchema } from \"../schemas/index.js\";\nimport { generateConversationID, generateOriginatorConversationID } from \"../core/id-generator.js\";\nimport type { ResultCallbackBody } from \"../types/daraja.js\";\n\nexport function transactionStatusRoute(): Hono<AppContext> {\n const app = new Hono<AppContext>();\n\n app.post(\"/mpesa/transactionstatus/v1/query\", async (c) => {\n const token = parseBearerToken(c.req.header(\"authorization\"));\n if (!token || !isValidToken(token)) {\n return c.json({ errorCode: \"404.001.03\", errorMessage: \"Invalid Access Token\" }, 401);\n }\n const raw = await c.req.json().catch(() => null);\n const parsed = transactionStatusSchema.safeParse(raw);\n if (!parsed.success) {\n return c.json(\n { errorCode: \"400.002.05\", errorMessage: \"Invalid request payload\", errors: parsed.error.flatten() },\n 400,\n );\n }\n const body = parsed.data;\n const conversationID = generateConversationID();\n const originatorConversationID = generateOriginatorConversationID();\n\n const existing = c.var.store.get(body.TransactionID);\n\n const callback: ResultCallbackBody = {\n Result: {\n ResultType: 0,\n ResultCode: 0,\n ResultDesc: \"The service request is processed successfully.\",\n OriginatorConversationID: originatorConversationID,\n ConversationID: conversationID,\n TransactionID: body.TransactionID,\n ResultParameters: {\n ResultParameter: [\n { Key: \"ReceiptNo\", Value: existing?.mpesaReceiptNumber ?? body.TransactionID },\n { Key: \"ConversationID\", Value: existing?.conversationID ?? conversationID },\n { Key: \"FinalisedTime\", Value: new Date().toISOString() },\n { Key: \"Amount\", Value: existing?.amount ?? 0 },\n { Key: \"TransactionStatus\", Value: existing?.state === \"success\" ? \"Completed\" : \"Failed\" },\n { Key: \"ReasonType\", Value: \"Salary Payment via API\" },\n { Key: \"TransactionReason\", Value: body.Remarks },\n { Key: \"DebitPartyCharges\", Value: \"\" },\n { Key: \"DebitAccountType\", Value: \"Utility Account\" },\n { Key: \"InitiatedTime\", Value: new Date().toISOString() },\n { Key: \"OriginatorConversationID\", Value: existing?.originatorConversationID ?? originatorConversationID },\n { Key: \"CreditPartyName\", Value: existing ? `${existing.phoneNumber} - Test Recipient` : \"Test Recipient\" },\n { Key: \"DebitPartyName\", Value: existing ? `${existing.shortCode} - Test Merchant` : \"Test Merchant\" },\n ],\n },\n ReferenceData: {\n ReferenceItem: { Key: \"Occasion\", Value: body.Occasion ?? \"\" },\n },\n },\n };\n\n c.var.dispatcher.schedule(\n {\n id: conversationID,\n url: body.ResultURL,\n body: callback,\n scheduledAt: Date.now() + c.var.config.defaultCallbackDelayMs,\n attempts: 0,\n maxAttempts: c.var.config.webhookRetry.attempts,\n backoffMs: c.var.config.webhookRetry.backoffMs,\n },\n c.var.config.defaultCallbackDelayMs,\n );\n\n return c.json({\n OriginatorConversationID: originatorConversationID,\n ConversationID: conversationID,\n ResponseCode: \"0\",\n ResponseDescription: \"Accept the service request successfully.\",\n });\n });\n\n return app;\n}\n","import { Hono } from \"hono\";\nimport type { AppContext } from \"../server.js\";\nimport { isValidToken, parseBearerToken } from \"../core/auth.js\";\nimport { accountBalanceSchema } from \"../schemas/index.js\";\nimport { generateConversationID, generateOriginatorConversationID } from \"../core/id-generator.js\";\nimport type { ResultCallbackBody } from \"../types/daraja.js\";\n\nexport function accountBalanceRoute(): Hono<AppContext> {\n const app = new Hono<AppContext>();\n\n app.post(\"/mpesa/accountbalance/v1/query\", async (c) => {\n const token = parseBearerToken(c.req.header(\"authorization\"));\n if (!token || !isValidToken(token)) {\n return c.json({ errorCode: \"404.001.03\", errorMessage: \"Invalid Access Token\" }, 401);\n }\n const raw = await c.req.json().catch(() => null);\n const parsed = accountBalanceSchema.safeParse(raw);\n if (!parsed.success) {\n return c.json(\n { errorCode: \"400.002.05\", errorMessage: \"Invalid request payload\", errors: parsed.error.flatten() },\n 400,\n );\n }\n const body = parsed.data;\n const conversationID = generateConversationID();\n const originatorConversationID = generateOriginatorConversationID();\n\n const callback: ResultCallbackBody = {\n Result: {\n ResultType: 0,\n ResultCode: 0,\n ResultDesc: \"The service request is processed successfully.\",\n OriginatorConversationID: originatorConversationID,\n ConversationID: conversationID,\n TransactionID: \"BALANCE-QUERY\",\n ResultParameters: {\n ResultParameter: [\n { Key: \"AccountBalance\", Value: \"Working Account|KES|1000000.00|1000000.00|0.00|0.00&Float Account|KES|0.00|0.00|0.00|0.00&Utility Account|KES|1000000.00|1000000.00|0.00|0.00&Charges Paid Account|KES|0.00|0.00|0.00|0.00&Organization Settlement Account|KES|0.00|0.00|0.00|0.00\" },\n { Key: \"BOCompletedTime\", Value: new Date().toISOString() },\n ],\n },\n ReferenceData: {\n ReferenceItem: { Key: \"QueueTimeoutURL\", Value: body.QueueTimeOutURL },\n },\n },\n };\n\n c.var.dispatcher.schedule(\n {\n id: conversationID,\n url: body.ResultURL,\n body: callback,\n scheduledAt: Date.now() + c.var.config.defaultCallbackDelayMs,\n attempts: 0,\n maxAttempts: c.var.config.webhookRetry.attempts,\n backoffMs: c.var.config.webhookRetry.backoffMs,\n },\n c.var.config.defaultCallbackDelayMs,\n );\n\n return c.json({\n OriginatorConversationID: originatorConversationID,\n ConversationID: conversationID,\n ResponseCode: \"0\",\n ResponseDescription: \"Accept the service request successfully.\",\n });\n });\n\n return app;\n}\n","import { Hono } from \"hono\";\nimport type { AppContext } from \"../server.js\";\nimport { isValidToken, parseBearerToken } from \"../core/auth.js\";\nimport { reversalSchema } from \"../schemas/index.js\";\nimport { generateConversationID, generateOriginatorConversationID } from \"../core/id-generator.js\";\nimport type { ResultCallbackBody } from \"../types/daraja.js\";\n\nexport function reversalRoute(): Hono<AppContext> {\n const app = new Hono<AppContext>();\n\n app.post(\"/mpesa/reversal/v1/request\", async (c) => {\n const token = parseBearerToken(c.req.header(\"authorization\"));\n if (!token || !isValidToken(token)) {\n return c.json({ errorCode: \"404.001.03\", errorMessage: \"Invalid Access Token\" }, 401);\n }\n const raw = await c.req.json().catch(() => null);\n const parsed = reversalSchema.safeParse(raw);\n if (!parsed.success) {\n return c.json(\n { errorCode: \"400.002.05\", errorMessage: \"Invalid request payload\", errors: parsed.error.flatten() },\n 400,\n );\n }\n const body = parsed.data;\n const conversationID = generateConversationID();\n const originatorConversationID = generateOriginatorConversationID();\n\n const callback: ResultCallbackBody = {\n Result: {\n ResultType: 0,\n ResultCode: 0,\n ResultDesc: \"The service request is processed successfully.\",\n OriginatorConversationID: originatorConversationID,\n ConversationID: conversationID,\n TransactionID: body.TransactionID,\n ResultParameters: {\n ResultParameter: [\n { Key: \"DebitAccountBalance\", Value: \"Working Account|KES|1000000.00|1000000.00|0.00|0.00\" },\n { Key: \"Amount\", Value: body.Amount },\n { Key: \"TransCompletedTime\", Value: new Date().toISOString() },\n { Key: \"OriginalTransactionID\", Value: body.TransactionID },\n { Key: \"Charge\", Value: 0 },\n { Key: \"CreditPartyPublicName\", Value: `${body.ReceiverParty} - Test Recipient` },\n { Key: \"DebitPartyPublicName\", Value: \"Test Merchant\" },\n ],\n },\n ReferenceData: {\n ReferenceItem: { Key: \"QueueTimeoutURL\", Value: body.QueueTimeOutURL },\n },\n },\n };\n\n c.var.dispatcher.schedule(\n {\n id: conversationID,\n url: body.ResultURL,\n body: callback,\n scheduledAt: Date.now() + c.var.config.defaultCallbackDelayMs,\n attempts: 0,\n maxAttempts: c.var.config.webhookRetry.attempts,\n backoffMs: c.var.config.webhookRetry.backoffMs,\n },\n c.var.config.defaultCallbackDelayMs,\n );\n\n return c.json({\n OriginatorConversationID: originatorConversationID,\n ConversationID: conversationID,\n ResponseCode: \"0\",\n ResponseDescription: \"Accept the service request successfully.\",\n });\n });\n\n return app;\n}\n","import { Hono } from \"hono\";\nimport { streamSSE } from \"hono/streaming\";\nimport type { AppContext } from \"../server.js\";\n\nconst DASHBOARD_HTML = `<!doctype html>\n<html lang=\"en\">\n<head>\n <meta charset=\"utf-8\" />\n <title>mpesa-mock dashboard</title>\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n <script src=\"https://cdn.tailwindcss.com\"></script>\n</head>\n<body class=\"bg-slate-950 text-slate-100 font-mono min-h-screen\">\n <div class=\"max-w-6xl mx-auto p-6\">\n <header class=\"flex items-center justify-between mb-6\">\n <div>\n <h1 class=\"text-2xl font-bold\">mpesa-mock <span class=\"text-emerald-400\">●</span></h1>\n <p class=\"text-slate-400 text-sm\">Local M-Pesa Daraja emulator — live transactions</p>\n </div>\n <div class=\"text-right text-xs text-slate-500\">\n <div id=\"health\">checking…</div>\n <div>SSE: <span id=\"sse-status\">connecting</span></div>\n </div>\n </header>\n\n <section class=\"grid grid-cols-1 md:grid-cols-3 gap-4 mb-6\">\n <div class=\"bg-slate-900 rounded-lg p-4 border border-slate-800\">\n <div class=\"text-xs text-slate-500 uppercase\">Transactions</div>\n <div id=\"count-total\" class=\"text-3xl font-bold\">0</div>\n </div>\n <div class=\"bg-slate-900 rounded-lg p-4 border border-slate-800\">\n <div class=\"text-xs text-slate-500 uppercase\">Pending callbacks</div>\n <div id=\"count-pending\" class=\"text-3xl font-bold text-amber-300\">0</div>\n </div>\n <div class=\"bg-slate-900 rounded-lg p-4 border border-slate-800\">\n <div class=\"text-xs text-slate-500 uppercase\">Delivered</div>\n <div id=\"count-delivered\" class=\"text-3xl font-bold text-emerald-300\">0</div>\n </div>\n </section>\n\n <section class=\"bg-slate-900 rounded-lg border border-slate-800 overflow-hidden\">\n <table class=\"w-full text-sm\">\n <thead class=\"bg-slate-800 text-slate-400 text-xs uppercase\">\n <tr>\n <th class=\"text-left p-3\">Kind</th>\n <th class=\"text-left p-3\">CheckoutRequestID</th>\n <th class=\"text-left p-3\">Phone</th>\n <th class=\"text-right p-3\">Amount</th>\n <th class=\"text-left p-3\">State</th>\n <th class=\"text-right p-3\">Cb attempts</th>\n <th class=\"text-right p-3\">Age</th>\n </tr>\n </thead>\n <tbody id=\"rows\"></tbody>\n </table>\n </section>\n\n <footer class=\"text-center text-slate-600 text-xs mt-8\">\n mpesa-mock — not affiliated with Safaricom PLC\n </footer>\n </div>\n\n <script>\n const stateColors = {\n success: 'text-emerald-300',\n pending: 'text-amber-300',\n user_cancelled: 'text-rose-300',\n insufficient_funds: 'text-rose-400',\n wrong_pin: 'text-rose-400',\n expired: 'text-rose-400',\n system_error: 'text-rose-500',\n timeout: 'text-slate-400',\n };\n\n function renderRow(t) {\n const age = Math.round((Date.now() - t.createdAt) / 1000);\n const colorCls = stateColors[t.state] ?? 'text-slate-200';\n return \\`<tr class=\"border-t border-slate-800 hover:bg-slate-800/50\">\n <td class=\"p-3 uppercase text-xs text-slate-400\">\\${t.kind}</td>\n <td class=\"p-3 text-xs\">\\${t.checkoutRequestID}</td>\n <td class=\"p-3\">\\${t.phoneNumber}</td>\n <td class=\"p-3 text-right\">\\${t.amount.toLocaleString()}</td>\n <td class=\"p-3 \\${colorCls}\">\\${t.state}</td>\n <td class=\"p-3 text-right\">\\${t.callbackAttempts}</td>\n <td class=\"p-3 text-right text-slate-500\">\\${age}s</td>\n </tr>\\`;\n }\n\n function refresh(data) {\n const txns = data.transactions ?? [];\n document.getElementById('count-total').textContent = txns.length;\n document.getElementById('count-pending').textContent = data.pendingCallbacks ?? 0;\n document.getElementById('count-delivered').textContent = txns.filter(t => t.callbackDeliveredAt).length;\n document.getElementById('rows').innerHTML = txns.slice(0, 50).map(renderRow).join('');\n }\n\n fetch('/__mock__/health').then(r => r.json()).then(d => {\n document.getElementById('health').textContent = 'up · ' + Math.round(d.uptime) + 's';\n });\n\n fetch('/__mock__/state').then(r => r.json()).then(refresh);\n\n const es = new EventSource('/__mock__/events');\n es.onopen = () => { document.getElementById('sse-status').textContent = 'live'; };\n es.onerror = () => { document.getElementById('sse-status').textContent = 'disconnected'; };\n es.onmessage = (e) => { try { refresh(JSON.parse(e.data)); } catch {} };\n </script>\n</body>\n</html>`;\n\nexport function dashboardRoute(): Hono<AppContext> {\n const app = new Hono<AppContext>();\n\n app.get(\"/__mock__/dashboard\", (c) => c.html(DASHBOARD_HTML));\n\n app.get(\"/__mock__/state\", (c) => {\n const transactions = c.var.store.list(100);\n return c.json({\n transactions,\n pendingCallbacks: c.var.dispatcher.pendingIds().length,\n });\n });\n\n app.get(\"/__mock__/events\", (c) => {\n return streamSSE(c, async (stream) => {\n const send = async () => {\n await stream.writeSSE({\n data: JSON.stringify({\n transactions: c.var.store.list(100),\n pendingCallbacks: c.var.dispatcher.pendingIds().length,\n }),\n });\n };\n await send();\n const onChange = () => { void send(); };\n c.var.store.on(\"change\", onChange);\n c.var.store.on(\"clear\", onChange);\n const heartbeat = setInterval(() => { void send(); }, 5000);\n try {\n while (true) {\n await stream.sleep(1000);\n }\n } finally {\n clearInterval(heartbeat);\n c.var.store.off(\"change\", onChange);\n c.var.store.off(\"clear\", onChange);\n }\n });\n });\n\n app.post(\"/__mock__/clear\", (c) => {\n c.var.store.clear();\n return c.json({ cleared: true });\n });\n\n return app;\n}\n"],"mappings":"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","names":["import_hono","import_node_events","pRetry","import_hono","import_hono","import_hono","import_hono","import_hono","import_hono","import_hono","import_hono","import_hono","honoLogger"]}
@@ -0,0 +1,167 @@
1
+ import { Hono } from 'hono';
2
+ import { EventEmitter } from 'node:events';
3
+
4
+ type TransactionState = "pending" | "success" | "user_cancelled" | "insufficient_funds" | "wrong_pin" | "timeout" | "expired" | "system_error";
5
+ type FailureScenario = "success" | "user_cancelled" | "insufficient_funds" | "wrong_pin" | "timeout" | "callback_retry" | "expired" | "system_error" | "slow";
6
+ interface StkPushRequestBody {
7
+ BusinessShortCode: string;
8
+ Password: string;
9
+ Timestamp: string;
10
+ TransactionType: "CustomerPayBillOnline" | "CustomerBuyGoodsOnline";
11
+ Amount: number;
12
+ PartyA: string;
13
+ PartyB: string;
14
+ PhoneNumber: string;
15
+ CallBackURL: string;
16
+ AccountReference: string;
17
+ TransactionDesc: string;
18
+ }
19
+ interface StkPushResponse {
20
+ MerchantRequestID: string;
21
+ CheckoutRequestID: string;
22
+ ResponseCode: string;
23
+ ResponseDescription: string;
24
+ CustomerMessage: string;
25
+ }
26
+ interface StkCallbackItem {
27
+ Name: string;
28
+ Value?: string | number;
29
+ }
30
+ interface StkCallbackMetadata {
31
+ Item: StkCallbackItem[];
32
+ }
33
+ interface StkCallbackBody {
34
+ Body: {
35
+ stkCallback: {
36
+ MerchantRequestID: string;
37
+ CheckoutRequestID: string;
38
+ ResultCode: number;
39
+ ResultDesc: string;
40
+ CallbackMetadata?: StkCallbackMetadata;
41
+ };
42
+ };
43
+ }
44
+ interface C2BRegisterUrlBody {
45
+ ShortCode: string;
46
+ ResponseType: "Completed" | "Cancelled";
47
+ ConfirmationURL: string;
48
+ ValidationURL: string;
49
+ }
50
+ interface C2BSimulateBody {
51
+ ShortCode: string;
52
+ CommandID: "CustomerPayBillOnline" | "CustomerBuyGoodsOnline";
53
+ Amount: number;
54
+ Msisdn: string;
55
+ BillRefNumber: string;
56
+ }
57
+ interface TransactionRecord {
58
+ checkoutRequestID: string;
59
+ merchantRequestID: string;
60
+ conversationID?: string;
61
+ originatorConversationID?: string;
62
+ kind: "stk" | "c2b" | "b2c" | "b2b" | "status" | "balance" | "reversal";
63
+ amount: number;
64
+ phoneNumber: string;
65
+ shortCode: string;
66
+ callbackUrl?: string;
67
+ state: TransactionState;
68
+ resultCode?: number;
69
+ resultDesc?: string;
70
+ mpesaReceiptNumber?: string;
71
+ createdAt: number;
72
+ completedAt?: number;
73
+ callbackAttempts: number;
74
+ callbackDeliveredAt?: number;
75
+ }
76
+
77
+ interface TransactionStore {
78
+ put(record: TransactionRecord): void;
79
+ get(checkoutRequestID: string): TransactionRecord | undefined;
80
+ getByConversationID(id: string): TransactionRecord | undefined;
81
+ list(limit?: number): TransactionRecord[];
82
+ update(checkoutRequestID: string, patch: Partial<TransactionRecord>): TransactionRecord | undefined;
83
+ clear(): void;
84
+ }
85
+ declare class InMemoryStore extends EventEmitter implements TransactionStore {
86
+ private byCheckout;
87
+ private byConversation;
88
+ put(record: TransactionRecord): void;
89
+ get(checkoutRequestID: string): TransactionRecord | undefined;
90
+ getByConversationID(id: string): TransactionRecord | undefined;
91
+ list(limit?: number): TransactionRecord[];
92
+ update(checkoutRequestID: string, patch: Partial<TransactionRecord>): TransactionRecord | undefined;
93
+ clear(): void;
94
+ }
95
+
96
+ interface WebhookJob {
97
+ id: string;
98
+ url: string;
99
+ body: unknown;
100
+ scheduledAt: number;
101
+ attempts: number;
102
+ maxAttempts: number;
103
+ backoffMs: number;
104
+ transactionId?: string;
105
+ failNTimesFirst?: number;
106
+ }
107
+ interface DispatcherOptions {
108
+ store?: InMemoryStore;
109
+ fetchImpl?: typeof fetch;
110
+ onLog?: (msg: string) => void;
111
+ }
112
+ declare class WebhookDispatcher extends EventEmitter {
113
+ private pending;
114
+ private store?;
115
+ private fetchImpl;
116
+ private onLog?;
117
+ constructor(opts?: DispatcherOptions);
118
+ schedule(job: WebhookJob, delayMs: number): void;
119
+ cancel(id: string): boolean;
120
+ pendingIds(): string[];
121
+ fire(job: WebhookJob): Promise<void>;
122
+ shutdown(): void;
123
+ }
124
+
125
+ interface MockConfig {
126
+ defaultCallbackDelayMs: number;
127
+ scenarios: Record<string, FailureScenario>;
128
+ webhookRetry: {
129
+ attempts: number;
130
+ backoffMs: number;
131
+ };
132
+ }
133
+ declare function pickScenario(phoneNumber: string, config: MockConfig): FailureScenario;
134
+
135
+ interface AppVariables {
136
+ store: InMemoryStore;
137
+ dispatcher: WebhookDispatcher;
138
+ config: MockConfig;
139
+ log?: (msg: string) => void;
140
+ recorder?: (entry: RecorderEntry) => void;
141
+ }
142
+ interface AppContext {
143
+ Variables: AppVariables;
144
+ }
145
+ interface RecorderEntry {
146
+ ts: number;
147
+ direction: "request" | "response" | "callback";
148
+ method: string;
149
+ path: string;
150
+ status?: number;
151
+ body?: unknown;
152
+ }
153
+ interface CreateServerOptions {
154
+ config?: Partial<MockConfig>;
155
+ quiet?: boolean;
156
+ recorder?: (entry: RecorderEntry) => void;
157
+ store?: InMemoryStore;
158
+ }
159
+ declare function defaultConfig(overrides?: Partial<MockConfig>): MockConfig;
160
+ declare function createServer(opts?: CreateServerOptions): {
161
+ app: Hono<AppContext>;
162
+ store: InMemoryStore;
163
+ dispatcher: WebhookDispatcher;
164
+ config: MockConfig;
165
+ };
166
+
167
+ export { type AppContext, type AppVariables, type C2BRegisterUrlBody, type C2BSimulateBody, type CreateServerOptions, type FailureScenario, InMemoryStore, type MockConfig, type RecorderEntry, type StkCallbackBody, type StkPushRequestBody, type StkPushResponse, type TransactionRecord, type TransactionState, WebhookDispatcher, createServer, defaultConfig, pickScenario };
@@ -0,0 +1,167 @@
1
+ import { Hono } from 'hono';
2
+ import { EventEmitter } from 'node:events';
3
+
4
+ type TransactionState = "pending" | "success" | "user_cancelled" | "insufficient_funds" | "wrong_pin" | "timeout" | "expired" | "system_error";
5
+ type FailureScenario = "success" | "user_cancelled" | "insufficient_funds" | "wrong_pin" | "timeout" | "callback_retry" | "expired" | "system_error" | "slow";
6
+ interface StkPushRequestBody {
7
+ BusinessShortCode: string;
8
+ Password: string;
9
+ Timestamp: string;
10
+ TransactionType: "CustomerPayBillOnline" | "CustomerBuyGoodsOnline";
11
+ Amount: number;
12
+ PartyA: string;
13
+ PartyB: string;
14
+ PhoneNumber: string;
15
+ CallBackURL: string;
16
+ AccountReference: string;
17
+ TransactionDesc: string;
18
+ }
19
+ interface StkPushResponse {
20
+ MerchantRequestID: string;
21
+ CheckoutRequestID: string;
22
+ ResponseCode: string;
23
+ ResponseDescription: string;
24
+ CustomerMessage: string;
25
+ }
26
+ interface StkCallbackItem {
27
+ Name: string;
28
+ Value?: string | number;
29
+ }
30
+ interface StkCallbackMetadata {
31
+ Item: StkCallbackItem[];
32
+ }
33
+ interface StkCallbackBody {
34
+ Body: {
35
+ stkCallback: {
36
+ MerchantRequestID: string;
37
+ CheckoutRequestID: string;
38
+ ResultCode: number;
39
+ ResultDesc: string;
40
+ CallbackMetadata?: StkCallbackMetadata;
41
+ };
42
+ };
43
+ }
44
+ interface C2BRegisterUrlBody {
45
+ ShortCode: string;
46
+ ResponseType: "Completed" | "Cancelled";
47
+ ConfirmationURL: string;
48
+ ValidationURL: string;
49
+ }
50
+ interface C2BSimulateBody {
51
+ ShortCode: string;
52
+ CommandID: "CustomerPayBillOnline" | "CustomerBuyGoodsOnline";
53
+ Amount: number;
54
+ Msisdn: string;
55
+ BillRefNumber: string;
56
+ }
57
+ interface TransactionRecord {
58
+ checkoutRequestID: string;
59
+ merchantRequestID: string;
60
+ conversationID?: string;
61
+ originatorConversationID?: string;
62
+ kind: "stk" | "c2b" | "b2c" | "b2b" | "status" | "balance" | "reversal";
63
+ amount: number;
64
+ phoneNumber: string;
65
+ shortCode: string;
66
+ callbackUrl?: string;
67
+ state: TransactionState;
68
+ resultCode?: number;
69
+ resultDesc?: string;
70
+ mpesaReceiptNumber?: string;
71
+ createdAt: number;
72
+ completedAt?: number;
73
+ callbackAttempts: number;
74
+ callbackDeliveredAt?: number;
75
+ }
76
+
77
+ interface TransactionStore {
78
+ put(record: TransactionRecord): void;
79
+ get(checkoutRequestID: string): TransactionRecord | undefined;
80
+ getByConversationID(id: string): TransactionRecord | undefined;
81
+ list(limit?: number): TransactionRecord[];
82
+ update(checkoutRequestID: string, patch: Partial<TransactionRecord>): TransactionRecord | undefined;
83
+ clear(): void;
84
+ }
85
+ declare class InMemoryStore extends EventEmitter implements TransactionStore {
86
+ private byCheckout;
87
+ private byConversation;
88
+ put(record: TransactionRecord): void;
89
+ get(checkoutRequestID: string): TransactionRecord | undefined;
90
+ getByConversationID(id: string): TransactionRecord | undefined;
91
+ list(limit?: number): TransactionRecord[];
92
+ update(checkoutRequestID: string, patch: Partial<TransactionRecord>): TransactionRecord | undefined;
93
+ clear(): void;
94
+ }
95
+
96
+ interface WebhookJob {
97
+ id: string;
98
+ url: string;
99
+ body: unknown;
100
+ scheduledAt: number;
101
+ attempts: number;
102
+ maxAttempts: number;
103
+ backoffMs: number;
104
+ transactionId?: string;
105
+ failNTimesFirst?: number;
106
+ }
107
+ interface DispatcherOptions {
108
+ store?: InMemoryStore;
109
+ fetchImpl?: typeof fetch;
110
+ onLog?: (msg: string) => void;
111
+ }
112
+ declare class WebhookDispatcher extends EventEmitter {
113
+ private pending;
114
+ private store?;
115
+ private fetchImpl;
116
+ private onLog?;
117
+ constructor(opts?: DispatcherOptions);
118
+ schedule(job: WebhookJob, delayMs: number): void;
119
+ cancel(id: string): boolean;
120
+ pendingIds(): string[];
121
+ fire(job: WebhookJob): Promise<void>;
122
+ shutdown(): void;
123
+ }
124
+
125
+ interface MockConfig {
126
+ defaultCallbackDelayMs: number;
127
+ scenarios: Record<string, FailureScenario>;
128
+ webhookRetry: {
129
+ attempts: number;
130
+ backoffMs: number;
131
+ };
132
+ }
133
+ declare function pickScenario(phoneNumber: string, config: MockConfig): FailureScenario;
134
+
135
+ interface AppVariables {
136
+ store: InMemoryStore;
137
+ dispatcher: WebhookDispatcher;
138
+ config: MockConfig;
139
+ log?: (msg: string) => void;
140
+ recorder?: (entry: RecorderEntry) => void;
141
+ }
142
+ interface AppContext {
143
+ Variables: AppVariables;
144
+ }
145
+ interface RecorderEntry {
146
+ ts: number;
147
+ direction: "request" | "response" | "callback";
148
+ method: string;
149
+ path: string;
150
+ status?: number;
151
+ body?: unknown;
152
+ }
153
+ interface CreateServerOptions {
154
+ config?: Partial<MockConfig>;
155
+ quiet?: boolean;
156
+ recorder?: (entry: RecorderEntry) => void;
157
+ store?: InMemoryStore;
158
+ }
159
+ declare function defaultConfig(overrides?: Partial<MockConfig>): MockConfig;
160
+ declare function createServer(opts?: CreateServerOptions): {
161
+ app: Hono<AppContext>;
162
+ store: InMemoryStore;
163
+ dispatcher: WebhookDispatcher;
164
+ config: MockConfig;
165
+ };
166
+
167
+ export { type AppContext, type AppVariables, type C2BRegisterUrlBody, type C2BSimulateBody, type CreateServerOptions, type FailureScenario, InMemoryStore, type MockConfig, type RecorderEntry, type StkCallbackBody, type StkPushRequestBody, type StkPushResponse, type TransactionRecord, type TransactionState, WebhookDispatcher, createServer, defaultConfig, pickScenario };