pesafy 0.0.2

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,106 @@
1
+ import { A as TransactionStatusResult, D as MpesaConfig, T as AccountBalanceResult, b as B2BExpressCheckoutCallback, f as C2BValidationPayload, g as B2CDisbursementResult, o as ReversalResult, p as C2BValidationResponse, r as TaxRemittanceResult, s as C2BConfirmationPayload, y as B2CResult } from "../types.js";
2
+ import { t as Mpesa } from "../index.js";
3
+ import { Router } from "express";
4
+
5
+ //#region src/adapters/express.d.ts
6
+ interface MpesaExpressConfig extends MpesaConfig {
7
+ /** STK Push callback URL (required) */
8
+ callbackUrl: string;
9
+ /** Default ResultURL for async APIs (balance, reversal, tx-status, b2c, tax) */
10
+ resultUrl?: string;
11
+ /** Default QueueTimeOutURL */
12
+ queueTimeoutUrl?: string;
13
+ /** Skip Safaricom IP whitelist check (local dev only) */
14
+ skipIPCheck?: boolean;
15
+ /** Shared secret for opt-in HMAC webhook verification */
16
+ webhookSecret?: string;
17
+ /** Require valid HMAC when webhookSecret is set */
18
+ requireHMAC?: boolean;
19
+ /** Signature header name (default: x-safaricom-signature) */
20
+ signatureHeader?: string;
21
+ /** Prefix for all routes — default "" */
22
+ routePrefix?: string;
23
+ balance?: {
24
+ resultUrl?: string;
25
+ queueTimeoutUrl?: string;
26
+ shortCode?: string;
27
+ };
28
+ reversal?: {
29
+ resultUrl?: string;
30
+ queueTimeoutUrl?: string;
31
+ };
32
+ txStatus?: {
33
+ resultUrl?: string;
34
+ queueTimeoutUrl?: string;
35
+ };
36
+ tax?: {
37
+ resultUrl?: string;
38
+ queueTimeoutUrl?: string;
39
+ partyA?: string;
40
+ };
41
+ b2c?: {
42
+ resultUrl?: string;
43
+ queueTimeoutUrl?: string;
44
+ partyA?: string;
45
+ };
46
+ c2b?: {
47
+ shortCode?: string;
48
+ confirmationUrl?: string;
49
+ validationUrl?: string;
50
+ responseType?: 'Completed' | 'Cancelled';
51
+ apiVersion?: 'v1' | 'v2';
52
+ };
53
+ b2b?: {
54
+ receiverShortCode?: string;
55
+ callbackUrl?: string;
56
+ };
57
+ onStkSuccess?: (data: StkSuccessPayload) => Awaitable<void>;
58
+ onStkFailure?: (data: StkFailurePayload) => Awaitable<void>;
59
+ onC2BValidation?: (payload: C2BValidationPayload) => Awaitable<C2BValidationResponse>;
60
+ onC2BConfirmation?: (payload: C2BConfirmationPayload) => Awaitable<void>;
61
+ onAccountBalanceResult?: (result: AccountBalanceResult) => Awaitable<void>;
62
+ onReversalResult?: (result: ReversalResult) => Awaitable<void>;
63
+ onTxStatusResult?: (result: TransactionStatusResult) => Awaitable<void>;
64
+ onTaxResult?: (result: TaxRemittanceResult) => Awaitable<void>;
65
+ onB2BCheckoutCallback?: (callback: B2BExpressCheckoutCallback) => Awaitable<void>;
66
+ onB2CResult?: (result: B2CResult) => Awaitable<void>;
67
+ onB2CDisbursementResult?: (result: B2CDisbursementResult) => Awaitable<void>;
68
+ }
69
+ type Awaitable<T> = T | Promise<T>;
70
+ interface StkSuccessPayload {
71
+ receiptNumber: string | null;
72
+ amount: number | null;
73
+ phone: string | null;
74
+ checkoutRequestId: string;
75
+ merchantRequestId: string;
76
+ }
77
+ interface StkFailurePayload {
78
+ resultCode: number;
79
+ resultDesc: string;
80
+ checkoutRequestId: string;
81
+ merchantRequestId: string;
82
+ }
83
+ /**
84
+ * Creates an Express Router with all M-PESA Daraja routes mounted.
85
+ *
86
+ * @example
87
+ * import express from 'express'
88
+ * import { createMpesaRouter } from 'pesafy/adapters/express'
89
+ *
90
+ * const app = express()
91
+ * app.use(express.json())
92
+ * app.use('/api', createMpesaRouter({
93
+ * consumerKey: process.env.MPESA_CONSUMER_KEY!,
94
+ * consumerSecret: process.env.MPESA_CONSUMER_SECRET!,
95
+ * environment: 'sandbox',
96
+ * callbackUrl: 'https://yourdomain.com/api/mpesa/stk/callback',
97
+ * lipaNaMpesaShortCode: '174379',
98
+ * lipaNaMpesaPassKey: process.env.MPESA_PASSKEY!,
99
+ * }))
100
+ */
101
+ declare function createMpesaRouter(config: MpesaExpressConfig, router?: Router): Router;
102
+ declare function createMpesaExpressClient(config: MpesaExpressConfig): {
103
+ mpesa: Mpesa;
104
+ };
105
+ //#endregion
106
+ export { MpesaExpressConfig, StkFailurePayload, StkSuccessPayload, createMpesaExpressClient, createMpesaRouter as createMpesaExpressRouter, createMpesaRouter };
@@ -0,0 +1 @@
1
+ import{t as e}from"../chunk.js";import{A as t,C as n,D as r,E as i,O as a,S as o,T as s,_ as c,a as l,b as u,c as d,d as f,f as p,g as m,h,i as g,j as _,k as v,l as y,m as b,n as x,o as S,p as C,r as w,s as T,t as E,u as D,v as O,w as k,x as A,y as j}from"../webhook-guard.js";function M(e){return e.headers[`x-forwarded-for`]?.split(`,`)[0]?.trim()??e.ip??``}function N(e,t){if(!e.headersSent){if(t instanceof _){e.status(t.statusCode??400).json({ok:!1,error:t.code,message:t.message});return}e.status(500).json({ok:!1,error:`INTERNAL_ERROR`,message:`Unexpected server error`})}}function P(e,t,n,r){let i=e??t??n??``;if(!i)throw new _({code:`VALIDATION_ERROR`,message:`${r} is required. Set it in config or include it in the request body.`});return i}function F(e,t){e&&e().catch(e=>console.error(`[pesafy] ${t} hook error:`,e))}function I(e){let t=e.rawBody;if(typeof t==`string`)return t;if(t instanceof Buffer)return t.toString(`utf8`);if(e.body!==void 0)return JSON.stringify(e.body)}async function L(e,t){return E(M(e),I(e),t=>{let n=e.headers[t.toLowerCase()];return Array.isArray(n)?n[0]:n},t)}function R(E,M){let{Router:N}=e(`express`),I=M??N(),R=new x(E),B=E.routePrefix??``;return I.post(`${B}/mpesa/stk/push`,z(async(e,t)=>{let{amount:n,phoneNumber:r,accountReference:i,transactionDesc:a,transactionType:o,partyB:s}=e.body;if(!n||n<=0)throw new _({code:`VALIDATION_ERROR`,message:`amount must be > 0`});if(!r)throw new _({code:`VALIDATION_ERROR`,message:`phoneNumber is required`});let c=await R.stkPush({amount:n,phoneNumber:r,callbackUrl:E.callbackUrl,accountReference:i??`REF-${Date.now().toString(36).toUpperCase()}`,transactionDesc:a??`Payment`,...o===void 0?{}:{transactionType:o},...s===void 0?{}:{partyB:s}});t.json({ok:!0,data:c})})),I.post(`${B}/mpesa/stk/query`,z(async(e,t)=>{let{checkoutRequestId:n}=e.body;if(!n)throw new _({code:`VALIDATION_ERROR`,message:`checkoutRequestId is required`});let r=await R.stkQuery({checkoutRequestId:n});t.json({ok:!0,data:r})})),I.post(`${B}/mpesa/stk/callback`,async(e,t)=>{await L(e,E);let n=e.body,r=n?.Body?.stkCallback;if(!r)return t.json({ResultCode:0,ResultDesc:`Accepted`});if(S(n)){let e={receiptNumber:l(n),amount:w(n),phone:g(n),checkoutRequestId:r.CheckoutRequestID,merchantRequestId:r.MerchantRequestID};console.info(`[pesafy] STK success:`,e),F(E.onStkSuccess?()=>Promise.resolve(E.onStkSuccess(e)):void 0,`onStkSuccess`)}else{let e={resultCode:r.ResultCode,resultDesc:r.ResultDesc,checkoutRequestId:r.CheckoutRequestID,merchantRequestId:r.MerchantRequestID};console.warn(`[pesafy] STK failure:`,e),F(E.onStkFailure?()=>Promise.resolve(E.onStkFailure(e)):void 0,`onStkFailure`)}return t.json({ResultCode:0,ResultDesc:`Accepted`})}),I.post(`${B}/mpesa/c2b/register`,z(async(e,t)=>{let{shortCode:n=E.c2b?.shortCode,confirmationUrl:r=E.c2b?.confirmationUrl,validationUrl:i=E.c2b?.validationUrl,responseType:a=E.c2b?.responseType??`Completed`,apiVersion:o=E.c2b?.apiVersion??`v2`}=e.body;if(!n)throw new _({code:`VALIDATION_ERROR`,message:`shortCode is required`});if(!r)throw new _({code:`VALIDATION_ERROR`,message:`confirmationUrl is required`});if(!i)throw new _({code:`VALIDATION_ERROR`,message:`validationUrl is required`});let s=await R.registerC2BUrls({shortCode:n,responseType:a,confirmationUrl:r,validationUrl:i,apiVersion:o});t.json({ok:!0,data:s})})),I.post(`${B}/mpesa/c2b/simulate`,z(async(e,t)=>{let{commandId:n,amount:r,msisdn:i,billRefNumber:a,shortCode:o,apiVersion:s}=e.body;if(!n)throw new _({code:`VALIDATION_ERROR`,message:`commandId is required`});if(!r||r<=0)throw new _({code:`VALIDATION_ERROR`,message:`amount must be > 0`});if(!i)throw new _({code:`VALIDATION_ERROR`,message:`msisdn is required`});let c=await R.simulateC2B({shortCode:o??E.c2b?.shortCode??``,commandId:n,amount:r,msisdn:i,apiVersion:s??E.c2b?.apiVersion??`v2`,...a===void 0?{}:{billRefNumber:a}});t.json({ok:!0,data:c})})),I.post(`${B}/mpesa/c2b/validation`,z(async(e,t)=>{await L(e,E);let n=e.body,r=E.onC2BValidation?await E.onC2BValidation(n):b();t.json(r)})),I.post(`${B}/mpesa/c2b/confirmation`,(e,t)=>{let n=e.body;console.info(`[pesafy] C2B confirmation:`,{transactionId:n.TransID,amount:n.TransAmount,billRef:n.BillRefNumber}),F(E.onC2BConfirmation?()=>Promise.resolve(E.onC2BConfirmation(n)):void 0,`onC2BConfirmation`),t.json({ResultCode:0,ResultDesc:`Success`})}),I.post(`${B}/mpesa/balance/query`,z(async(e,t)=>{let n=e.body,r=await R.accountBalance({partyA:n.partyA??E.balance?.shortCode??``,identifierType:n.identifierType??`4`,resultUrl:P(n.resultUrl,E.balance?.resultUrl,E.resultUrl,`resultUrl`),queueTimeOutUrl:P(n.queueTimeoutUrl,E.balance?.queueTimeoutUrl,E.queueTimeoutUrl,`queueTimeoutUrl`),...n.remarks===void 0?{}:{remarks:n.remarks}});t.json({ok:!0,data:r})})),I.post(`${B}/mpesa/balance/result`,(e,n)=>{let r=e.body;if(!v(r))console.warn(`[pesafy] Account balance failed:`,r);else{let e=a(r);console.info(`[pesafy] Account balance:`,e?t(e):r)}F(E.onAccountBalanceResult?()=>Promise.resolve(E.onAccountBalanceResult(r)):void 0,`onAccountBalanceResult`),n.json({ResultCode:0,ResultDesc:`Accepted`})}),I.post(`${B}/mpesa/qr/generate`,z(async(e,t)=>{let n=await R.generateDynamicQR(e.body);t.json({ok:!0,data:n})})),I.post(`${B}/mpesa/reversal/request`,z(async(e,t)=>{let n=e.body;if(!n.transactionId)throw new _({code:`VALIDATION_ERROR`,message:`transactionId is required`});if(!n.receiverParty)throw new _({code:`VALIDATION_ERROR`,message:`receiverParty is required`});if(!n.amount||n.amount<=0)throw new _({code:`VALIDATION_ERROR`,message:`amount must be > 0`});let r=await R.reverseTransaction({transactionId:n.transactionId,receiverParty:n.receiverParty,amount:n.amount,resultUrl:P(n.resultUrl,E.reversal?.resultUrl,E.resultUrl,`resultUrl`),queueTimeOutUrl:P(n.queueTimeoutUrl,E.reversal?.queueTimeoutUrl,E.queueTimeoutUrl,`queueTimeoutUrl`),...n.remarks===void 0?{}:{remarks:n.remarks},...n.occasion===void 0?{}:{occasion:n.occasion}});t.json({ok:!0,data:r})})),I.post(`${B}/mpesa/reversal/result`,(e,t)=>{let n=e.body;p(n)&&(C(n)?console.info(`[pesafy] Reversal success:`,{txId:f(n)}):console.warn(`[pesafy] Reversal failed:`,n.Result.ResultDesc),F(E.onReversalResult?()=>Promise.resolve(E.onReversalResult(n)):void 0,`onReversalResult`)),t.json({ResultCode:0,ResultDesc:`Accepted`})}),I.post(`${B}/mpesa/tx-status/query`,z(async(e,t)=>{let n=e.body,r=await R.transactionStatus({...n.transactionId===void 0?{}:{transactionId:n.transactionId},...n.originalConversationId===void 0?{}:{originalConversationId:n.originalConversationId},partyA:n.partyA,identifierType:n.identifierType,resultUrl:P(n.resultUrl,E.txStatus?.resultUrl,E.resultUrl,`resultUrl`),queueTimeOutUrl:P(n.queueTimeoutUrl,E.txStatus?.queueTimeoutUrl,E.queueTimeoutUrl,`queueTimeoutUrl`),...n.remarks===void 0?{}:{remarks:n.remarks},...n.occasion===void 0?{}:{occasion:n.occasion}});t.json({ok:!0,data:r})})),I.post(`${B}/mpesa/tx-status/result`,(e,t)=>{let n=e.body;T(n)&&(d(n)?console.info(`[pesafy] Transaction status success:`,n.Result.TransactionID):console.warn(`[pesafy] Transaction status failed:`,n.Result.ResultDesc),F(E.onTxStatusResult?()=>Promise.resolve(E.onTxStatusResult(n)):void 0,`onTxStatusResult`)),t.json({ResultCode:0,ResultDesc:`Accepted`})}),I.post(`${B}/mpesa/tax/remit`,z(async(e,t)=>{let n=e.body;if(!n.amount||n.amount<=0)throw new _({code:`VALIDATION_ERROR`,message:`amount must be > 0`});if(!n.accountReference)throw new _({code:`VALIDATION_ERROR`,message:`accountReference (KRA PRN) is required`});let r=await R.remitTax({amount:n.amount,partyA:n.partyA??E.tax?.partyA??``,accountReference:n.accountReference,resultUrl:P(n.resultUrl,E.tax?.resultUrl,E.resultUrl,`resultUrl`),queueTimeOutUrl:P(n.queueTimeoutUrl,E.tax?.queueTimeoutUrl,E.queueTimeoutUrl,`queueTimeoutUrl`),...n.partyB===void 0?{}:{partyB:n.partyB},...n.remarks===void 0?{}:{remarks:n.remarks}});t.json({ok:!0,data:r})})),I.post(`${B}/mpesa/tax/result`,(e,t)=>{let n=e.body;y(n)&&(D(n)?console.info(`[pesafy] Tax remittance success:`,n.Result.TransactionID):console.warn(`[pesafy] Tax remittance failed:`,n.Result.ResultDesc),F(E.onTaxResult?()=>Promise.resolve(E.onTaxResult(n)):void 0,`onTaxResult`)),t.json({ResultCode:0,ResultDesc:`Accepted`})}),I.post(`${B}/mpesa/b2b/checkout`,z(async(e,t)=>{let n=e.body;if(!n.primaryShortCode)throw new _({code:`VALIDATION_ERROR`,message:`primaryShortCode is required`});if(!n.amount||n.amount<=0)throw new _({code:`VALIDATION_ERROR`,message:`amount must be > 0`});if(!n.paymentRef)throw new _({code:`VALIDATION_ERROR`,message:`paymentRef is required`});if(!n.partnerName)throw new _({code:`VALIDATION_ERROR`,message:`partnerName is required`});let r=n.receiverShortCode??E.b2b?.receiverShortCode??``;if(!r)throw new _({code:`VALIDATION_ERROR`,message:`receiverShortCode is required`});let i=n.callbackUrl??E.b2b?.callbackUrl??``;if(!i)throw new _({code:`VALIDATION_ERROR`,message:`callbackUrl is required`});let a=await R.b2bExpressCheckout({primaryShortCode:n.primaryShortCode,receiverShortCode:r,amount:n.amount,paymentRef:n.paymentRef,callbackUrl:i,partnerName:n.partnerName,...n.requestRefId===void 0?{}:{requestRefId:n.requestRefId}});t.json({ok:!0,data:a})})),I.post(`${B}/mpesa/b2b/callback`,(e,t)=>{let a=e.body;if(!s(a))return console.warn(`[pesafy] Unknown B2B callback payload`),t.json({ResultCode:0,ResultDesc:`Accepted`});let c=a;return r(c)?console.info(`[pesafy] B2B checkout success:`,{txId:k(c),conversationId:n(c),amount:o(c)}):i(c)?console.warn(`[pesafy] B2B checkout cancelled by merchant`):console.warn(`[pesafy] B2B checkout failed:`,c.resultDesc),F(E.onB2BCheckoutCallback?()=>Promise.resolve(E.onB2BCheckoutCallback(c)):void 0,`onB2BCheckoutCallback`),t.json({ResultCode:0,ResultDesc:`Accepted`})}),I.post(`${B}/mpesa/b2c/payment`,z(async(e,t)=>{let n=e.body;if(n.commandId!==`BusinessPayToBulk`)throw new _({code:`VALIDATION_ERROR`,message:`commandId must be "BusinessPayToBulk"`});if(!n.amount||n.amount<=0)throw new _({code:`VALIDATION_ERROR`,message:`amount must be > 0`});if(!n.partyB)throw new _({code:`VALIDATION_ERROR`,message:`partyB is required`});if(!n.accountReference)throw new _({code:`VALIDATION_ERROR`,message:`accountReference is required`});let r=await R.b2cPayment({commandId:`BusinessPayToBulk`,amount:n.amount,partyA:n.partyA??E.b2c?.partyA??``,partyB:n.partyB,accountReference:n.accountReference,resultUrl:P(n.resultUrl,E.b2c?.resultUrl,E.resultUrl,`resultUrl`),queueTimeOutUrl:P(n.queueTimeoutUrl,E.b2c?.queueTimeoutUrl,E.queueTimeoutUrl,`queueTimeoutUrl`),...n.requester===void 0?{}:{requester:n.requester},...n.remarks===void 0?{}:{remarks:n.remarks}});t.json({ok:!0,data:r})})),I.post(`${B}/mpesa/b2c/result`,(e,t)=>{let n=e.body;u(n)&&(A(n)?console.info(`[pesafy] B2C success:`,{txId:j(n),amount:c(n),origConvId:O(n)}):console.warn(`[pesafy] B2C failed:`,n.Result.ResultDesc),F(E.onB2CResult?()=>Promise.resolve(E.onB2CResult(n)):void 0,`onB2CResult`)),t.json({ResultCode:0,ResultDesc:`Accepted`})}),I.post(`${B}/mpesa/b2c/disburse`,z(async(e,t)=>{let n=e.body,r=await R.b2cDisbursement({originatorConversationId:n.originatorConversationId,commandId:n.commandId,amount:n.amount,partyA:n.partyA,partyB:n.partyB,remarks:n.remarks,resultUrl:P(n.resultUrl,E.b2c?.resultUrl,E.resultUrl,`resultUrl`),queueTimeOutUrl:P(n.queueTimeoutUrl,E.b2c?.queueTimeoutUrl,E.queueTimeoutUrl,`queueTimeoutUrl`),...n.occasion===void 0?{}:{occasion:n.occasion}});t.json({ok:!0,data:r})})),I.post(`${B}/mpesa/b2c/disburse/result`,(e,t)=>{let n=e.body;h(n)&&(m(n)?console.info(`[pesafy] B2C disbursement success:`,n.Result.TransactionID):console.warn(`[pesafy] B2C disbursement failed:`,n.Result.ResultDesc),F(E.onB2CDisbursementResult?()=>Promise.resolve(E.onB2CDisbursementResult(n)):void 0,`onB2CDisbursementResult`)),t.json({ResultCode:0,ResultDesc:`Accepted`})}),I.post(`${B}/mpesa/bills/optin`,z(async(e,t)=>{let n=await R.billManagerOptIn(e.body);t.json({ok:!0,data:n})})),I.patch(`${B}/mpesa/bills/optin`,z(async(e,t)=>{let n=await R.updateOptIn(e.body);t.json({ok:!0,data:n})})),I.post(`${B}/mpesa/bills/invoice`,z(async(e,t)=>{let n=await R.sendInvoice(e.body);t.json({ok:!0,data:n})})),I.post(`${B}/mpesa/bills/invoice/bulk`,z(async(e,t)=>{let n=await R.sendBulkInvoices(e.body);t.json({ok:!0,data:n})})),I.delete(`${B}/mpesa/bills/invoice`,z(async(e,t)=>{let n=await R.cancelInvoice(e.body);t.json({ok:!0,data:n})})),I.delete(`${B}/mpesa/bills/invoice/bulk`,z(async(e,t)=>{let n=await R.cancelBulkInvoices(e.body);t.json({ok:!0,data:n})})),I.post(`${B}/mpesa/bills/reconcile`,z(async(e,t)=>{let n=await R.reconcilePayment(e.body);t.json({ok:!0,data:n})})),I.get(`${B}/mpesa/health`,(e,t)=>{t.json({ok:!0,environment:R.environment,ts:new Date().toISOString()})}),I}function z(e){return(t,n,r)=>{e(t,n,r).catch(e=>{n.headersSent?r(e):N(n,e)})}}function B(e){return{mpesa:new x(e)}}export{B as createMpesaExpressClient,R as createMpesaExpressRouter,R as createMpesaRouter};
@@ -0,0 +1,82 @@
1
+ import { A as TransactionStatusResult, D as MpesaConfig, T as AccountBalanceResult, b as B2BExpressCheckoutCallback, f as C2BValidationPayload, g as B2CDisbursementResult, o as ReversalResult, p as C2BValidationResponse, r as TaxRemittanceResult, s as C2BConfirmationPayload, y as B2CResult } from "../types.js";
2
+ import { t as Mpesa } from "../index.js";
3
+ import { FastifyPluginAsync } from "fastify";
4
+
5
+ //#region src/adapters/fastify.d.ts
6
+ interface MpesaFastifyConfig extends MpesaConfig {
7
+ callbackUrl: string;
8
+ resultUrl?: string;
9
+ queueTimeoutUrl?: string;
10
+ skipIPCheck?: boolean;
11
+ webhookSecret?: string;
12
+ requireHMAC?: boolean;
13
+ signatureHeader?: string;
14
+ routePrefix?: string;
15
+ balance?: {
16
+ resultUrl?: string;
17
+ queueTimeoutUrl?: string;
18
+ partyA?: string;
19
+ };
20
+ reversal?: {
21
+ resultUrl?: string;
22
+ queueTimeoutUrl?: string;
23
+ };
24
+ txStatus?: {
25
+ resultUrl?: string;
26
+ queueTimeoutUrl?: string;
27
+ };
28
+ tax?: {
29
+ resultUrl?: string;
30
+ queueTimeoutUrl?: string;
31
+ partyA?: string;
32
+ };
33
+ b2c?: {
34
+ resultUrl?: string;
35
+ queueTimeoutUrl?: string;
36
+ partyA?: string;
37
+ };
38
+ c2b?: {
39
+ shortCode?: string;
40
+ confirmationUrl?: string;
41
+ validationUrl?: string;
42
+ responseType?: 'Completed' | 'Cancelled';
43
+ apiVersion?: 'v1' | 'v2';
44
+ };
45
+ b2b?: {
46
+ receiverShortCode?: string;
47
+ callbackUrl?: string;
48
+ };
49
+ onStkSuccess?: (data: StkSuccessPayload) => Awaitable<void>;
50
+ onStkFailure?: (data: StkFailurePayload) => Awaitable<void>;
51
+ onC2BValidation?: (payload: C2BValidationPayload) => Awaitable<C2BValidationResponse>;
52
+ onC2BConfirmation?: (payload: C2BConfirmationPayload) => Awaitable<void>;
53
+ onAccountBalanceResult?: (result: AccountBalanceResult) => Awaitable<void>;
54
+ onReversalResult?: (result: ReversalResult) => Awaitable<void>;
55
+ onTxStatusResult?: (result: TransactionStatusResult) => Awaitable<void>;
56
+ onTaxResult?: (result: TaxRemittanceResult) => Awaitable<void>;
57
+ onB2BCheckoutCallback?: (cb: B2BExpressCheckoutCallback) => Awaitable<void>;
58
+ onB2CResult?: (result: B2CResult) => Awaitable<void>;
59
+ onB2CDisbursementResult?: (result: B2CDisbursementResult) => Awaitable<void>;
60
+ }
61
+ type Awaitable<T> = T | Promise<T>;
62
+ interface StkSuccessPayload {
63
+ receiptNumber: string | null;
64
+ amount: number | null;
65
+ phone: string | null;
66
+ checkoutRequestId: string;
67
+ merchantRequestId: string;
68
+ }
69
+ interface StkFailurePayload {
70
+ resultCode: number;
71
+ resultDesc: string;
72
+ checkoutRequestId: string;
73
+ merchantRequestId: string;
74
+ }
75
+ declare const registerMpesaPlugin: FastifyPluginAsync<MpesaFastifyConfig>;
76
+ declare module 'fastify' {
77
+ interface FastifyInstance {
78
+ mpesa: Mpesa;
79
+ }
80
+ }
81
+ //#endregion
82
+ export { MpesaFastifyConfig, StkFailurePayload, StkSuccessPayload, registerMpesaPlugin };
@@ -0,0 +1 @@
1
+ import{t as e}from"../chunk.js";import{A as t,D as n,E as r,O as i,S as a,T as o,_ as s,a as c,b as l,c as u,d,f,g as p,h as m,i as h,j as g,k as _,l as v,m as y,n as b,o as x,p as S,r as C,s as w,t as T,u as E,w as D,x as O,y as k}from"../webhook-guard.js";function A(e){let t=e.headers[`x-forwarded-for`];return typeof t==`string`?t.split(`,`)[0]?.trim()??``:e.ip??``}function j(e,t){return t instanceof g?e.status(t.statusCode??400).send({ok:!1,error:t.code,message:t.message}):e.status(500).send({ok:!1,error:`INTERNAL_ERROR`,message:`Unexpected error`})}function M(...e){return e.find(e=>e?.trim())??``}function N(e,t){e?.().catch(e=>console.error(`[pesafy/fastify] ${t} hook error:`,e))}const P=e(`fastify-plugin`)(async(e,P)=>{let F=new b(P),I=P.routePrefix??``;e.decorate(`mpesa`,F),e.post(`${I}/mpesa/stk/push`,async(e,t)=>{try{let{amount:t,phoneNumber:n,accountReference:r,transactionDesc:i,transactionType:a,partyB:o}=e.body;if(!t||t<=0)throw new g({code:`VALIDATION_ERROR`,message:`amount must be > 0`});if(!n)throw new g({code:`VALIDATION_ERROR`,message:`phoneNumber is required`});return{ok:!0,data:await F.stkPush({amount:t,phoneNumber:n,callbackUrl:P.callbackUrl,accountReference:r??`REF-${Date.now().toString(36).toUpperCase()}`,transactionDesc:i??`Payment`,...a===void 0?{}:{transactionType:a},...o===void 0?{}:{partyB:o}})}}catch(e){return j(t,e)}}),e.post(`${I}/mpesa/stk/query`,async(e,t)=>{try{let{checkoutRequestId:t}=e.body;if(!t)throw new g({code:`VALIDATION_ERROR`,message:`checkoutRequestId is required`});return{ok:!0,data:await F.stkQuery({checkoutRequestId:t})}}catch(e){return j(t,e)}}),e.post(`${I}/mpesa/stk/callback`,async e=>{let t=e.rawBody??(e.body===void 0?void 0:JSON.stringify(e.body));await T(A(e),t,t=>{let n=e.headers[t.toLowerCase()];return Array.isArray(n)?n[0]:n},P);let n=e.body,r=n?.Body?.stkCallback;if(!r)return{ResultCode:0,ResultDesc:`Accepted`};if(x(n)){let t={receiptNumber:c(n),amount:C(n),phone:h(n),checkoutRequestId:r.CheckoutRequestID,merchantRequestId:r.MerchantRequestID};e.log.info(t,`[pesafy] STK success`),N(P.onStkSuccess?()=>Promise.resolve(P.onStkSuccess(t)):void 0,`onStkSuccess`)}else{let t={resultCode:r.ResultCode,resultDesc:r.ResultDesc,checkoutRequestId:r.CheckoutRequestID,merchantRequestId:r.MerchantRequestID};e.log.warn(t,`[pesafy] STK failure`),N(P.onStkFailure?()=>Promise.resolve(P.onStkFailure(t)):void 0,`onStkFailure`)}return{ResultCode:0,ResultDesc:`Accepted`}}),e.post(`${I}/mpesa/c2b/register`,async(e,t)=>{try{let{shortCode:t,confirmationUrl:n,validationUrl:r,responseType:i,apiVersion:a}=e.body,o=t??P.c2b?.shortCode??``,s=n??P.c2b?.confirmationUrl??``,c=r??P.c2b?.validationUrl??``;if(!o)throw new g({code:`VALIDATION_ERROR`,message:`shortCode is required`});if(!s)throw new g({code:`VALIDATION_ERROR`,message:`confirmationUrl is required`});if(!c)throw new g({code:`VALIDATION_ERROR`,message:`validationUrl is required`});return{ok:!0,data:await F.registerC2BUrls({shortCode:o,responseType:i??P.c2b?.responseType??`Completed`,confirmationUrl:s,validationUrl:c,apiVersion:a??P.c2b?.apiVersion??`v2`})}}catch(e){return j(t,e)}}),e.post(`${I}/mpesa/c2b/simulate`,async(e,t)=>{try{let{commandId:t,amount:n,msisdn:r,billRefNumber:i,shortCode:a}=e.body;if(!t)throw new g({code:`VALIDATION_ERROR`,message:`commandId is required`});if(!n||n<=0)throw new g({code:`VALIDATION_ERROR`,message:`amount must be > 0`});if(!r)throw new g({code:`VALIDATION_ERROR`,message:`msisdn is required`});return{ok:!0,data:await F.simulateC2B({shortCode:a??P.c2b?.shortCode??``,commandId:t,amount:n,msisdn:r,apiVersion:P.c2b?.apiVersion??`v2`,...i===void 0?{}:{billRefNumber:i}})}}catch(e){return j(t,e)}}),e.post(`${I}/mpesa/c2b/validation`,async e=>{let t=e.rawBody??(e.body===void 0?void 0:JSON.stringify(e.body));await T(A(e),t,t=>{let n=e.headers[t.toLowerCase()];return Array.isArray(n)?n[0]:n},P);let n=e.body;return P.onC2BValidation?await P.onC2BValidation(n):y()}),e.post(`${I}/mpesa/c2b/confirmation`,async e=>{let t=e.body;return e.log.info({txId:t.TransID,amount:t.TransAmount},`[pesafy] C2B confirmation`),N(P.onC2BConfirmation?()=>Promise.resolve(P.onC2BConfirmation(t)):void 0,`onC2BConfirmation`),{ResultCode:0,ResultDesc:`Success`}}),e.post(`${I}/mpesa/balance/query`,async(e,t)=>{try{let{partyA:t,identifierType:n,remarks:r,resultUrl:i,queueTimeoutUrl:a}=e.body;return{ok:!0,data:await F.accountBalance({partyA:t??P.balance?.partyA??``,identifierType:n??`4`,resultUrl:M(i,P.balance?.resultUrl,P.resultUrl),queueTimeOutUrl:M(a,P.balance?.queueTimeoutUrl,P.queueTimeoutUrl),...r===void 0?{}:{remarks:r}})}}catch(e){return j(t,e)}}),e.post(`${I}/mpesa/balance/result`,async e=>{let n=e.body;if(_(n)){let r=i(n);e.log.info(r?t(r):n,`[pesafy] Account balance result`)}else e.log.warn(n,`[pesafy] Account balance failed`);return N(P.onAccountBalanceResult?()=>Promise.resolve(P.onAccountBalanceResult(n)):void 0,`onAccountBalanceResult`),{ResultCode:0,ResultDesc:`Accepted`}}),e.post(`${I}/mpesa/qr/generate`,async(e,t)=>{try{return{ok:!0,data:await F.generateDynamicQR(e.body)}}catch(e){return j(t,e)}}),e.post(`${I}/mpesa/reversal/request`,async(e,t)=>{try{let{transactionId:t,receiverParty:n,amount:r,remarks:i,occasion:a,resultUrl:o,queueTimeoutUrl:s}=e.body;if(!t)throw new g({code:`VALIDATION_ERROR`,message:`transactionId is required`});if(!n)throw new g({code:`VALIDATION_ERROR`,message:`receiverParty is required`});if(!r||r<=0)throw new g({code:`VALIDATION_ERROR`,message:`amount must be > 0`});return{ok:!0,data:await F.reverseTransaction({transactionId:t,receiverParty:n,amount:r,resultUrl:M(o,P.reversal?.resultUrl,P.resultUrl),queueTimeOutUrl:M(s,P.reversal?.queueTimeoutUrl,P.queueTimeoutUrl),...i===void 0?{}:{remarks:i},...a===void 0?{}:{occasion:a}})}}catch(e){return j(t,e)}}),e.post(`${I}/mpesa/reversal/result`,async e=>{let t=e.body;return f(t)&&(S(t)?e.log.info({txId:d(t)},`[pesafy] Reversal success`):e.log.warn({result:t.Result.ResultDesc},`[pesafy] Reversal failed`),N(P.onReversalResult?()=>Promise.resolve(P.onReversalResult(t)):void 0,`onReversalResult`)),{ResultCode:0,ResultDesc:`Accepted`}}),e.post(`${I}/mpesa/tx-status/query`,async(e,t)=>{try{let{transactionId:t,originalConversationId:n,partyA:r,identifierType:i,remarks:a,occasion:o,resultUrl:s,queueTimeoutUrl:c}=e.body;return{ok:!0,data:await F.transactionStatus({...t===void 0?{}:{transactionId:t},...n===void 0?{}:{originalConversationId:n},partyA:r,identifierType:i,resultUrl:M(s,P.txStatus?.resultUrl,P.resultUrl),queueTimeOutUrl:M(c,P.txStatus?.queueTimeoutUrl,P.queueTimeoutUrl),...a===void 0?{}:{remarks:a},...o===void 0?{}:{occasion:o}})}}catch(e){return j(t,e)}}),e.post(`${I}/mpesa/tx-status/result`,async e=>{let t=e.body;return w(t)&&(u(t)?e.log.info({txId:t.Result.TransactionID},`[pesafy] Tx-status success`):e.log.warn({desc:t.Result.ResultDesc},`[pesafy] Tx-status failed`),N(P.onTxStatusResult?()=>Promise.resolve(P.onTxStatusResult(t)):void 0,`onTxStatusResult`)),{ResultCode:0,ResultDesc:`Accepted`}}),e.post(`${I}/mpesa/tax/remit`,async(e,t)=>{try{let{amount:t,partyA:n,accountReference:r,remarks:i,partyB:a,queueTimeoutUrl:o,resultUrl:s}=e.body;if(!t||t<=0)throw new g({code:`VALIDATION_ERROR`,message:`amount must be > 0`});if(!r)throw new g({code:`VALIDATION_ERROR`,message:`accountReference (KRA PRN) is required`});return{ok:!0,data:await F.remitTax({amount:t,partyA:n??P.tax?.partyA??``,accountReference:r,resultUrl:M(s,P.tax?.resultUrl,P.resultUrl),queueTimeOutUrl:M(o,P.tax?.queueTimeoutUrl,P.queueTimeoutUrl),...a===void 0?{}:{partyB:a},...i===void 0?{}:{remarks:i}})}}catch(e){return j(t,e)}}),e.post(`${I}/mpesa/tax/result`,async e=>{let t=e.body;return v(t)&&(E(t)?e.log.info({txId:t.Result.TransactionID},`[pesafy] Tax success`):e.log.warn({desc:t.Result.ResultDesc},`[pesafy] Tax failed`),N(P.onTaxResult?()=>Promise.resolve(P.onTaxResult(t)):void 0,`onTaxResult`)),{ResultCode:0,ResultDesc:`Accepted`}}),e.post(`${I}/mpesa/b2b/checkout`,async(e,t)=>{try{let{primaryShortCode:t,receiverShortCode:n,amount:r,paymentRef:i,partnerName:a,callbackUrl:o,requestRefId:s}=e.body;if(!t)throw new g({code:`VALIDATION_ERROR`,message:`primaryShortCode is required`});if(!r||r<=0)throw new g({code:`VALIDATION_ERROR`,message:`amount must be > 0`});let c=n??P.b2b?.receiverShortCode??``,l=o??P.b2b?.callbackUrl??``;if(!c)throw new g({code:`VALIDATION_ERROR`,message:`receiverShortCode is required`});if(!l)throw new g({code:`VALIDATION_ERROR`,message:`callbackUrl is required`});return{ok:!0,data:await F.b2bExpressCheckout({primaryShortCode:t,receiverShortCode:c,amount:r,paymentRef:i,callbackUrl:l,partnerName:a,...s===void 0?{}:{requestRefId:s}})}}catch(e){return j(t,e)}}),e.post(`${I}/mpesa/b2b/callback`,async e=>{let t=e.body;if(!o(t))return e.log.warn(`[pesafy] Unknown B2B callback payload`),{ResultCode:0,ResultDesc:`Accepted`};let i=t;return n(i)?e.log.info({txId:D(i),amount:a(i)},`[pesafy] B2B success`):r(i)?e.log.warn(`[pesafy] B2B cancelled`):e.log.warn({result:i.resultDesc},`[pesafy] B2B failed`),N(P.onB2BCheckoutCallback?()=>Promise.resolve(P.onB2BCheckoutCallback(i)):void 0,`onB2BCheckoutCallback`),{ResultCode:0,ResultDesc:`Accepted`}}),e.post(`${I}/mpesa/b2c/payment`,async(e,t)=>{try{let{commandId:t,amount:n,partyA:r,partyB:i,accountReference:a,requester:o,remarks:s,resultUrl:c,queueTimeoutUrl:l}=e.body;if(t!==`BusinessPayToBulk`)throw new g({code:`VALIDATION_ERROR`,message:`commandId must be "BusinessPayToBulk"`});if(!n||n<=0)throw new g({code:`VALIDATION_ERROR`,message:`amount must be > 0`});if(!i)throw new g({code:`VALIDATION_ERROR`,message:`partyB is required`});if(!a)throw new g({code:`VALIDATION_ERROR`,message:`accountReference is required`});return{ok:!0,data:await F.b2cPayment({commandId:t,amount:n,partyA:r??P.b2c?.partyA??``,partyB:i,accountReference:a,resultUrl:M(c,P.b2c?.resultUrl,P.resultUrl),queueTimeOutUrl:M(l,P.b2c?.queueTimeoutUrl,P.queueTimeoutUrl),...o===void 0?{}:{requester:o},...s===void 0?{}:{remarks:s}})}}catch(e){return j(t,e)}}),e.post(`${I}/mpesa/b2c/result`,async e=>{let t=e.body;return l(t)&&(O(t)?e.log.info({txId:k(t),amount:s(t)},`[pesafy] B2C success`):e.log.warn({desc:t.Result.ResultDesc},`[pesafy] B2C failed`),N(P.onB2CResult?()=>Promise.resolve(P.onB2CResult(t)):void 0,`onB2CResult`)),{ResultCode:0,ResultDesc:`Accepted`}}),e.post(`${I}/mpesa/b2c/disburse`,async(e,t)=>{try{let{queueTimeoutUrl:t,resultUrl:n,...r}=e.body;return{ok:!0,data:await F.b2cDisbursement({...r,resultUrl:M(n,P.b2c?.resultUrl,P.resultUrl),queueTimeOutUrl:M(t,P.b2c?.queueTimeoutUrl,P.queueTimeoutUrl)})}}catch(e){return j(t,e)}}),e.post(`${I}/mpesa/b2c/disburse/result`,async e=>{let t=e.body;return m(t)&&(p(t)?e.log.info({txId:t.Result.TransactionID},`[pesafy] Disbursement success`):e.log.warn({desc:t.Result.ResultDesc},`[pesafy] Disbursement failed`),N(P.onB2CDisbursementResult?()=>Promise.resolve(P.onB2CDisbursementResult(t)):void 0,`onB2CDisbursementResult`)),{ResultCode:0,ResultDesc:`Accepted`}}),e.post(`${I}/mpesa/bills/optin`,async(e,t)=>{try{return{ok:!0,data:await F.billManagerOptIn(e.body)}}catch(e){return j(t,e)}}),e.patch(`${I}/mpesa/bills/optin`,async(e,t)=>{try{return{ok:!0,data:await F.updateOptIn(e.body)}}catch(e){return j(t,e)}}),e.post(`${I}/mpesa/bills/invoice`,async(e,t)=>{try{return{ok:!0,data:await F.sendInvoice(e.body)}}catch(e){return j(t,e)}}),e.post(`${I}/mpesa/bills/invoice/bulk`,async(e,t)=>{try{return{ok:!0,data:await F.sendBulkInvoices(e.body)}}catch(e){return j(t,e)}}),e.delete(`${I}/mpesa/bills/invoice`,async(e,t)=>{try{return{ok:!0,data:await F.cancelInvoice(e.body)}}catch(e){return j(t,e)}}),e.delete(`${I}/mpesa/bills/invoice/bulk`,async(e,t)=>{try{return{ok:!0,data:await F.cancelBulkInvoices(e.body)}}catch(e){return j(t,e)}}),e.post(`${I}/mpesa/bills/reconcile`,async(e,t)=>{try{return{ok:!0,data:await F.reconcilePayment(e.body)}}catch(e){return j(t,e)}}),e.get(`${I}/mpesa/health`,async()=>({ok:!0,environment:F.environment,ts:new Date().toISOString()}))},{fastify:`>=4.0.0`,name:`pesafy-mpesa`});export{P as registerMpesaPlugin};
@@ -0,0 +1,106 @@
1
+ import { A as TransactionStatusResult, D as MpesaConfig, T as AccountBalanceResult, b as B2BExpressCheckoutCallback, f as C2BValidationPayload, g as B2CDisbursementResult, o as ReversalResult, p as C2BValidationResponse, r as TaxRemittanceResult, s as C2BConfirmationPayload, y as B2CResult } from "../types.js";
2
+ import { t as Mpesa } from "../index.js";
3
+ import { Hono, MiddlewareHandler } from "hono";
4
+
5
+ //#region src/adapters/hono.d.ts
6
+ interface MpesaHonoConfig extends MpesaConfig {
7
+ callbackUrl: string;
8
+ resultUrl?: string;
9
+ queueTimeoutUrl?: string;
10
+ skipIPCheck?: boolean;
11
+ webhookSecret?: string;
12
+ requireHMAC?: boolean;
13
+ signatureHeader?: string;
14
+ balance?: {
15
+ resultUrl?: string;
16
+ queueTimeoutUrl?: string;
17
+ partyA?: string;
18
+ };
19
+ reversal?: {
20
+ resultUrl?: string;
21
+ queueTimeoutUrl?: string;
22
+ };
23
+ txStatus?: {
24
+ resultUrl?: string;
25
+ queueTimeoutUrl?: string;
26
+ };
27
+ tax?: {
28
+ resultUrl?: string;
29
+ queueTimeoutUrl?: string;
30
+ partyA?: string;
31
+ };
32
+ b2c?: {
33
+ resultUrl?: string;
34
+ queueTimeoutUrl?: string;
35
+ partyA?: string;
36
+ };
37
+ c2b?: {
38
+ shortCode?: string;
39
+ confirmationUrl?: string;
40
+ validationUrl?: string;
41
+ responseType?: 'Completed' | 'Cancelled';
42
+ apiVersion?: 'v1' | 'v2';
43
+ };
44
+ b2b?: {
45
+ receiverShortCode?: string;
46
+ callbackUrl?: string;
47
+ };
48
+ onStkSuccess?: (data: StkSuccessPayload) => Awaitable<void>;
49
+ onStkFailure?: (data: StkFailurePayload) => Awaitable<void>;
50
+ onC2BValidation?: (payload: C2BValidationPayload) => Awaitable<C2BValidationResponse>;
51
+ onC2BConfirmation?: (payload: C2BConfirmationPayload) => Awaitable<void>;
52
+ onAccountBalanceResult?: (result: AccountBalanceResult) => Awaitable<void>;
53
+ onReversalResult?: (result: ReversalResult) => Awaitable<void>;
54
+ onTxStatusResult?: (result: TransactionStatusResult) => Awaitable<void>;
55
+ onTaxResult?: (result: TaxRemittanceResult) => Awaitable<void>;
56
+ onB2BCheckoutCallback?: (cb: B2BExpressCheckoutCallback) => Awaitable<void>;
57
+ onB2CResult?: (result: B2CResult) => Awaitable<void>;
58
+ onB2CDisbursementResult?: (result: B2CDisbursementResult) => Awaitable<void>;
59
+ }
60
+ type Awaitable<T> = T | Promise<T>;
61
+ interface StkSuccessPayload {
62
+ receiptNumber: string | null;
63
+ amount: number | null;
64
+ phone: string | null;
65
+ checkoutRequestId: string;
66
+ merchantRequestId: string;
67
+ }
68
+ interface StkFailurePayload {
69
+ resultCode: number;
70
+ resultDesc: string;
71
+ checkoutRequestId: string;
72
+ merchantRequestId: string;
73
+ }
74
+ /**
75
+ * Creates a Hono app with all M-PESA routes.
76
+ * Mount it on your main app with `app.route('/api', createMpesaHono(config))`.
77
+ *
78
+ * @example
79
+ * import { Hono } from 'hono'
80
+ * import { createMpesaHono } from 'pesafy/adapters/hono'
81
+ *
82
+ * const api = createMpesaHono({
83
+ * consumerKey: env.MPESA_CONSUMER_KEY,
84
+ * consumerSecret: env.MPESA_CONSUMER_SECRET,
85
+ * environment: 'sandbox',
86
+ * callbackUrl: 'https://yourdomain.com/api/mpesa/stk/callback',
87
+ * lipaNaMpesaShortCode: '174379',
88
+ * lipaNaMpesaPassKey: env.MPESA_PASSKEY,
89
+ * })
90
+ *
91
+ * const app = new Hono()
92
+ * app.route('/api', api)
93
+ */
94
+ declare function createMpesaHono(config: MpesaHonoConfig): Hono;
95
+ /**
96
+ * Returns a Hono middleware that injects an Mpesa instance into context.
97
+ * Use when you want to call the SDK directly inside your own route handlers.
98
+ */
99
+ declare function mpesaMiddleware(config: MpesaHonoConfig): MiddlewareHandler;
100
+ declare module 'hono' {
101
+ interface ContextVariableMap {
102
+ mpesa: Mpesa;
103
+ }
104
+ }
105
+ //#endregion
106
+ export { MpesaHonoConfig, StkFailurePayload, StkSuccessPayload, createMpesaHono, mpesaMiddleware };
@@ -0,0 +1 @@
1
+ import{t as e}from"../chunk.js";import{A as t,D as n,E as r,O as i,S as a,T as o,_ as s,a as c,b as l,c as u,d,f,g as p,h as m,i as h,j as g,k as _,l as v,m as y,n as b,o as x,p as S,r as C,s as w,t as T,u as E,w as D,x as O,y as k}from"../webhook-guard.js";function A(e){return e.req.header(`x-forwarded-for`)?.split(`,`)[0]?.trim()??e.req.header(`cf-connecting-ip`)??e.req.header(`x-real-ip`)??``}function j(e,t){return e.json({ok:!0,data:t})}function M(e,t){return t instanceof g?e.json({ok:!1,error:t.code,message:t.message},t.statusCode??400):e.json({ok:!1,error:`INTERNAL_ERROR`,message:`Unexpected error`},500)}function N(...e){return e.find(e=>e?.trim())??``}function P(e,t){e?.().catch(e=>console.error(`[pesafy/hono] ${t} hook error:`,e))}function F(F){let{Hono:I}=e(`hono`),L=new I,R=new b(F);return L.use(`*`,async(e,t)=>{e.set(`mpesa`,R),await t()}),L.post(`/mpesa/stk/push`,async e=>{try{let{amount:t,phoneNumber:n,accountReference:r,transactionDesc:i,transactionType:a,partyB:o}=await e.req.json();if(!t||t<=0)throw new g({code:`VALIDATION_ERROR`,message:`amount must be > 0`});if(!n)throw new g({code:`VALIDATION_ERROR`,message:`phoneNumber is required`});return j(e,await R.stkPush({amount:t,phoneNumber:n,callbackUrl:F.callbackUrl,accountReference:r??`REF-${Date.now().toString(36).toUpperCase()}`,transactionDesc:i??`Payment`,...a===void 0?{}:{transactionType:a},...o===void 0?{}:{partyB:o}}))}catch(t){return M(e,t)}}),L.post(`/mpesa/stk/query`,async e=>{try{let{checkoutRequestId:t}=await e.req.json();if(!t)throw new g({code:`VALIDATION_ERROR`,message:`checkoutRequestId is required`});return j(e,await R.stkQuery({checkoutRequestId:t}))}catch(t){return M(e,t)}}),L.post(`/mpesa/stk/callback`,async e=>{let t=await e.req.text();await T(A(e),t,t=>e.req.header(t),F);let n=JSON.parse(t),r=n?.Body?.stkCallback;if(!r)return e.json({ResultCode:0,ResultDesc:`Accepted`});if(x(n)){let e={receiptNumber:c(n),amount:C(n),phone:h(n),checkoutRequestId:r.CheckoutRequestID,merchantRequestId:r.MerchantRequestID};console.info(`[pesafy/hono] STK success:`,e),P(F.onStkSuccess?()=>Promise.resolve(F.onStkSuccess(e)):void 0,`onStkSuccess`)}else{let e={resultCode:r.ResultCode,resultDesc:r.ResultDesc,checkoutRequestId:r.CheckoutRequestID,merchantRequestId:r.MerchantRequestID};console.warn(`[pesafy/hono] STK failure:`,e),P(F.onStkFailure?()=>Promise.resolve(F.onStkFailure(e)):void 0,`onStkFailure`)}return e.json({ResultCode:0,ResultDesc:`Accepted`})}),L.post(`/mpesa/c2b/register`,async e=>{try{let{shortCode:t,confirmationUrl:n,validationUrl:r,responseType:i,apiVersion:a}=await e.req.json(),o=t??F.c2b?.shortCode??``,s=n??F.c2b?.confirmationUrl??``,c=r??F.c2b?.validationUrl??``;if(!o)throw new g({code:`VALIDATION_ERROR`,message:`shortCode is required`});if(!s)throw new g({code:`VALIDATION_ERROR`,message:`confirmationUrl is required`});if(!c)throw new g({code:`VALIDATION_ERROR`,message:`validationUrl is required`});return j(e,await R.registerC2BUrls({shortCode:o,responseType:i??F.c2b?.responseType??`Completed`,confirmationUrl:s,validationUrl:c,apiVersion:a??F.c2b?.apiVersion??`v2`}))}catch(t){return M(e,t)}}),L.post(`/mpesa/c2b/simulate`,async e=>{try{let{commandId:t,amount:n,msisdn:r,billRefNumber:i,shortCode:a}=await e.req.json();if(!t)throw new g({code:`VALIDATION_ERROR`,message:`commandId is required`});if(!n||n<=0)throw new g({code:`VALIDATION_ERROR`,message:`amount must be > 0`});if(!r)throw new g({code:`VALIDATION_ERROR`,message:`msisdn is required`});return j(e,await R.simulateC2B({shortCode:a??F.c2b?.shortCode??``,commandId:t,amount:n,msisdn:r,apiVersion:F.c2b?.apiVersion??`v2`,...i===void 0?{}:{billRefNumber:i}}))}catch(t){return M(e,t)}}),L.post(`/mpesa/c2b/validation`,async e=>{let t=await e.req.text();await T(A(e),t,t=>e.req.header(t),F);let n=JSON.parse(t),r=F.onC2BValidation?await F.onC2BValidation(n):y();return e.json(r)}),L.post(`/mpesa/c2b/confirmation`,async e=>{let t=await e.req.json();return console.info(`[pesafy/hono] C2B confirmation:`,{txId:t.TransID,amount:t.TransAmount}),P(F.onC2BConfirmation?()=>Promise.resolve(F.onC2BConfirmation(t)):void 0,`onC2BConfirmation`),e.json({ResultCode:0,ResultDesc:`Success`})}),L.post(`/mpesa/qr/generate`,async e=>{try{return j(e,await R.generateDynamicQR(await e.req.json()))}catch(t){return M(e,t)}}),L.post(`/mpesa/balance/query`,async e=>{try{let t=await e.req.json();return j(e,await R.accountBalance({partyA:t.partyA??F.balance?.partyA??``,identifierType:t.identifierType??`4`,resultUrl:N(t.resultUrl,F.balance?.resultUrl,F.resultUrl),queueTimeOutUrl:N(t.queueTimeoutUrl,F.balance?.queueTimeoutUrl,F.queueTimeoutUrl),...t.remarks===void 0?{}:{remarks:t.remarks}}))}catch(t){return M(e,t)}}),L.post(`/mpesa/balance/result`,async e=>{let n=await e.req.json();if(_(n)){let e=i(n);console.info(`[pesafy/hono] Balance result:`,e?t(e):n)}else console.warn(`[pesafy/hono] Balance failed:`,n);return P(F.onAccountBalanceResult?()=>Promise.resolve(F.onAccountBalanceResult(n)):void 0,`onAccountBalanceResult`),e.json({ResultCode:0,ResultDesc:`Accepted`})}),L.post(`/mpesa/reversal/request`,async e=>{try{let t=await e.req.json();if(!t.transactionId)throw new g({code:`VALIDATION_ERROR`,message:`transactionId is required`});if(!t.receiverParty)throw new g({code:`VALIDATION_ERROR`,message:`receiverParty is required`});if(!t.amount||t.amount<=0)throw new g({code:`VALIDATION_ERROR`,message:`amount must be > 0`});return j(e,await R.reverseTransaction({transactionId:t.transactionId,receiverParty:t.receiverParty,amount:t.amount,resultUrl:N(t.resultUrl,F.reversal?.resultUrl,F.resultUrl),queueTimeOutUrl:N(t.queueTimeoutUrl,F.reversal?.queueTimeoutUrl,F.queueTimeoutUrl),...t.remarks===void 0?{}:{remarks:t.remarks},...t.occasion===void 0?{}:{occasion:t.occasion}}))}catch(t){return M(e,t)}}),L.post(`/mpesa/reversal/result`,async e=>{let t=await e.req.json();return f(t)&&(S(t)?console.info(`[pesafy/hono] Reversal success:`,d(t)):console.warn(`[pesafy/hono] Reversal failed:`,t.Result.ResultDesc),P(F.onReversalResult?()=>Promise.resolve(F.onReversalResult(t)):void 0,`onReversalResult`)),e.json({ResultCode:0,ResultDesc:`Accepted`})}),L.post(`/mpesa/tx-status/query`,async e=>{try{let t=await e.req.json();return j(e,await R.transactionStatus({...t.transactionId===void 0?{}:{transactionId:t.transactionId},...t.originalConversationId===void 0?{}:{originalConversationId:t.originalConversationId},partyA:t.partyA,identifierType:t.identifierType,resultUrl:N(t.resultUrl,F.txStatus?.resultUrl,F.resultUrl),queueTimeOutUrl:N(t.queueTimeoutUrl,F.txStatus?.queueTimeoutUrl,F.queueTimeoutUrl),...t.remarks===void 0?{}:{remarks:t.remarks},...t.occasion===void 0?{}:{occasion:t.occasion}}))}catch(t){return M(e,t)}}),L.post(`/mpesa/tx-status/result`,async e=>{let t=await e.req.json();return w(t)&&(u(t)?console.info(`[pesafy/hono] Tx-status success:`,t.Result.TransactionID):console.warn(`[pesafy/hono] Tx-status failed:`,t.Result.ResultDesc),P(F.onTxStatusResult?()=>Promise.resolve(F.onTxStatusResult(t)):void 0,`onTxStatusResult`)),e.json({ResultCode:0,ResultDesc:`Accepted`})}),L.post(`/mpesa/tax/remit`,async e=>{try{let t=await e.req.json();if(!t.amount||t.amount<=0)throw new g({code:`VALIDATION_ERROR`,message:`amount must be > 0`});if(!t.accountReference)throw new g({code:`VALIDATION_ERROR`,message:`accountReference (KRA PRN) is required`});return j(e,await R.remitTax({amount:t.amount,partyA:t.partyA??F.tax?.partyA??``,accountReference:t.accountReference,resultUrl:N(t.resultUrl,F.tax?.resultUrl,F.resultUrl),queueTimeOutUrl:N(t.queueTimeoutUrl,F.tax?.queueTimeoutUrl,F.queueTimeoutUrl),...t.partyB===void 0?{}:{partyB:t.partyB},...t.remarks===void 0?{}:{remarks:t.remarks}}))}catch(t){return M(e,t)}}),L.post(`/mpesa/tax/result`,async e=>{let t=await e.req.json();return v(t)&&(E(t)?console.info(`[pesafy/hono] Tax success:`,t.Result.TransactionID):console.warn(`[pesafy/hono] Tax failed:`,t.Result.ResultDesc),P(F.onTaxResult?()=>Promise.resolve(F.onTaxResult(t)):void 0,`onTaxResult`)),e.json({ResultCode:0,ResultDesc:`Accepted`})}),L.post(`/mpesa/b2b/checkout`,async e=>{try{let t=await e.req.json();if(!t.primaryShortCode)throw new g({code:`VALIDATION_ERROR`,message:`primaryShortCode is required`});if(!t.amount||t.amount<=0)throw new g({code:`VALIDATION_ERROR`,message:`amount must be > 0`});let n=t.receiverShortCode??F.b2b?.receiverShortCode??``,r=t.callbackUrl??F.b2b?.callbackUrl??``;if(!n)throw new g({code:`VALIDATION_ERROR`,message:`receiverShortCode is required`});if(!r)throw new g({code:`VALIDATION_ERROR`,message:`callbackUrl is required`});return j(e,await R.b2bExpressCheckout({primaryShortCode:t.primaryShortCode,receiverShortCode:n,amount:t.amount,paymentRef:t.paymentRef,callbackUrl:r,partnerName:t.partnerName,...t.requestRefId===void 0?{}:{requestRefId:t.requestRefId}}))}catch(t){return M(e,t)}}),L.post(`/mpesa/b2b/callback`,async e=>{let t=await e.req.json();if(!o(t))return console.warn(`[pesafy/hono] Unknown B2B callback`),e.json({ResultCode:0,ResultDesc:`Accepted`});let i=t;return n(i)?console.info(`[pesafy/hono] B2B success:`,{txId:D(i),amount:a(i)}):r(i)?console.warn(`[pesafy/hono] B2B cancelled`):console.warn(`[pesafy/hono] B2B failed:`,i.resultDesc),P(F.onB2BCheckoutCallback?()=>Promise.resolve(F.onB2BCheckoutCallback(i)):void 0,`onB2BCheckoutCallback`),e.json({ResultCode:0,ResultDesc:`Accepted`})}),L.post(`/mpesa/b2c/payment`,async e=>{try{let t=await e.req.json();if(t.commandId!==`BusinessPayToBulk`)throw new g({code:`VALIDATION_ERROR`,message:`commandId must be "BusinessPayToBulk"`});if(!t.amount||t.amount<=0)throw new g({code:`VALIDATION_ERROR`,message:`amount must be > 0`});if(!t.partyB)throw new g({code:`VALIDATION_ERROR`,message:`partyB is required`});if(!t.accountReference)throw new g({code:`VALIDATION_ERROR`,message:`accountReference is required`});return j(e,await R.b2cPayment({commandId:`BusinessPayToBulk`,amount:t.amount,partyA:t.partyA??F.b2c?.partyA??``,partyB:t.partyB,accountReference:t.accountReference,resultUrl:N(t.resultUrl,F.b2c?.resultUrl,F.resultUrl),queueTimeOutUrl:N(t.queueTimeoutUrl,F.b2c?.queueTimeoutUrl,F.queueTimeoutUrl),...t.requester===void 0?{}:{requester:t.requester},...t.remarks===void 0?{}:{remarks:t.remarks}}))}catch(t){return M(e,t)}}),L.post(`/mpesa/b2c/result`,async e=>{let t=await e.req.json();return l(t)&&(O(t)?console.info(`[pesafy/hono] B2C success:`,{txId:k(t),amount:s(t)}):console.warn(`[pesafy/hono] B2C failed:`,t.Result.ResultDesc),P(F.onB2CResult?()=>Promise.resolve(F.onB2CResult(t)):void 0,`onB2CResult`)),e.json({ResultCode:0,ResultDesc:`Accepted`})}),L.post(`/mpesa/b2c/disburse`,async e=>{try{let{queueTimeoutUrl:t,resultUrl:n,...r}=await e.req.json();return j(e,await R.b2cDisbursement({...r,resultUrl:N(n,F.b2c?.resultUrl,F.resultUrl),queueTimeOutUrl:N(t,F.b2c?.queueTimeoutUrl,F.queueTimeoutUrl)}))}catch(t){return M(e,t)}}),L.post(`/mpesa/b2c/disburse/result`,async e=>{let t=await e.req.json();return m(t)&&(p(t)?console.info(`[pesafy/hono] Disbursement success:`,t.Result.TransactionID):console.warn(`[pesafy/hono] Disbursement failed:`,t.Result.ResultDesc),P(F.onB2CDisbursementResult?()=>Promise.resolve(F.onB2CDisbursementResult(t)):void 0,`onB2CDisbursementResult`)),e.json({ResultCode:0,ResultDesc:`Accepted`})}),L.post(`/mpesa/bills/optin`,async e=>{try{return j(e,await R.billManagerOptIn(await e.req.json()))}catch(t){return M(e,t)}}),L.patch(`/mpesa/bills/optin`,async e=>{try{return j(e,await R.updateOptIn(await e.req.json()))}catch(t){return M(e,t)}}),L.post(`/mpesa/bills/invoice`,async e=>{try{return j(e,await R.sendInvoice(await e.req.json()))}catch(t){return M(e,t)}}),L.post(`/mpesa/bills/invoice/bulk`,async e=>{try{return j(e,await R.sendBulkInvoices(await e.req.json()))}catch(t){return M(e,t)}}),L.delete(`/mpesa/bills/invoice`,async e=>{try{return j(e,await R.cancelInvoice(await e.req.json()))}catch(t){return M(e,t)}}),L.delete(`/mpesa/bills/invoice/bulk`,async e=>{try{return j(e,await R.cancelBulkInvoices(await e.req.json()))}catch(t){return M(e,t)}}),L.post(`/mpesa/bills/reconcile`,async e=>{try{return j(e,await R.reconcilePayment(await e.req.json()))}catch(t){return M(e,t)}}),L.get(`/mpesa/health`,e=>e.json({ok:!0,environment:R.environment,ts:new Date().toISOString()})),L}function I(e){let t=new b(e);return async(e,n)=>{e.set(`mpesa`,t),await n()}}export{F as createMpesaHono,I as mpesaMiddleware};
@@ -0,0 +1,183 @@
1
+ import { A as TransactionStatusResult, D as MpesaConfig, T as AccountBalanceResult, b as B2BExpressCheckoutCallback, f as C2BValidationPayload, g as B2CDisbursementResult, o as ReversalResult, p as C2BValidationResponse, r as TaxRemittanceResult, s as C2BConfirmationPayload, y as B2CResult } from "../types.js";
2
+
3
+ //#region src/adapters/nextjs.d.ts
4
+ interface MpesaNextConfig extends MpesaConfig {
5
+ callbackUrl: string;
6
+ resultUrl?: string;
7
+ queueTimeoutUrl?: string;
8
+ skipIPCheck?: boolean;
9
+ webhookSecret?: string;
10
+ requireHMAC?: boolean;
11
+ signatureHeader?: string;
12
+ balance?: {
13
+ resultUrl?: string;
14
+ queueTimeoutUrl?: string;
15
+ partyA?: string;
16
+ };
17
+ reversal?: {
18
+ resultUrl?: string;
19
+ queueTimeoutUrl?: string;
20
+ };
21
+ txStatus?: {
22
+ resultUrl?: string;
23
+ queueTimeoutUrl?: string;
24
+ };
25
+ tax?: {
26
+ resultUrl?: string;
27
+ queueTimeoutUrl?: string;
28
+ partyA?: string;
29
+ };
30
+ b2c?: {
31
+ resultUrl?: string;
32
+ queueTimeoutUrl?: string;
33
+ partyA?: string;
34
+ };
35
+ c2b?: {
36
+ shortCode?: string;
37
+ confirmationUrl?: string;
38
+ validationUrl?: string;
39
+ responseType?: 'Completed' | 'Cancelled';
40
+ apiVersion?: 'v1' | 'v2';
41
+ };
42
+ b2b?: {
43
+ receiverShortCode?: string;
44
+ callbackUrl?: string;
45
+ };
46
+ onStkSuccess?: (data: StkSuccessPayload) => Awaitable<void>;
47
+ onStkFailure?: (data: StkFailurePayload) => Awaitable<void>;
48
+ onC2BValidation?: (payload: C2BValidationPayload) => Awaitable<C2BValidationResponse>;
49
+ onC2BConfirmation?: (payload: C2BConfirmationPayload) => Awaitable<void>;
50
+ onAccountBalanceResult?: (result: AccountBalanceResult) => Awaitable<void>;
51
+ onReversalResult?: (result: ReversalResult) => Awaitable<void>;
52
+ onTxStatusResult?: (result: TransactionStatusResult) => Awaitable<void>;
53
+ onTaxResult?: (result: TaxRemittanceResult) => Awaitable<void>;
54
+ onB2BCheckoutCallback?: (cb: B2BExpressCheckoutCallback) => Awaitable<void>;
55
+ onB2CResult?: (result: B2CResult) => Awaitable<void>;
56
+ onB2CDisbursementResult?: (result: B2CDisbursementResult) => Awaitable<void>;
57
+ }
58
+ type Awaitable<T> = T | Promise<T>;
59
+ type RouteHandler = (req: Request, ctx?: {
60
+ params?: Record<string, string>;
61
+ }) => Promise<Response>;
62
+ interface StkSuccessPayload {
63
+ receiptNumber: string | null;
64
+ amount: number | null;
65
+ phone: string | null;
66
+ checkoutRequestId: string;
67
+ merchantRequestId: string;
68
+ }
69
+ interface StkFailurePayload {
70
+ resultCode: number;
71
+ resultDesc: string;
72
+ checkoutRequestId: string;
73
+ merchantRequestId: string;
74
+ }
75
+ /**
76
+ * Returns a POST Route Handler for STK Push initiation.
77
+ *
78
+ * @example
79
+ * // app/api/mpesa/stk/push/route.ts
80
+ * import { createStkPushHandler } from 'pesafy/adapters/nextjs'
81
+ * export const POST = createStkPushHandler(config)
82
+ */
83
+ declare function createStkPushHandler(config: MpesaNextConfig): RouteHandler;
84
+ /**
85
+ * Returns a POST Route Handler for STK Push Query.
86
+ *
87
+ * @example
88
+ * // app/api/mpesa/stk/query/route.ts
89
+ * export const POST = createStkQueryHandler(config)
90
+ */
91
+ declare function createStkQueryHandler(config: MpesaNextConfig): RouteHandler;
92
+ /**
93
+ * Returns a POST Route Handler for STK Push callbacks.
94
+ *
95
+ * @example
96
+ * // app/api/mpesa/stk/callback/route.ts
97
+ * export const POST = createStkCallbackHandler(config)
98
+ */
99
+ declare function createStkCallbackHandler(config: MpesaNextConfig): RouteHandler;
100
+ /**
101
+ * Returns a POST Route Handler for C2B Validation.
102
+ *
103
+ * @example
104
+ * // app/api/mpesa/c2b/validation/route.ts
105
+ * export const POST = createC2BValidationHandler(config)
106
+ */
107
+ declare function createC2BValidationHandler(config: MpesaNextConfig): RouteHandler;
108
+ /**
109
+ * Returns a POST Route Handler for C2B Confirmation.
110
+ *
111
+ * @example
112
+ * // app/api/mpesa/c2b/confirmation/route.ts
113
+ * export const POST = createC2BConfirmationHandler(config)
114
+ */
115
+ declare function createC2BConfirmationHandler(config: MpesaNextConfig): RouteHandler;
116
+ /**
117
+ * Returns POST handler for Account Balance result callback.
118
+ *
119
+ * @example
120
+ * // app/api/mpesa/balance/result/route.ts
121
+ * export const POST = createBalanceResultHandler(config)
122
+ */
123
+ declare function createBalanceResultHandler(config: MpesaNextConfig): RouteHandler;
124
+ /**
125
+ * Returns POST handler for Reversal result callback.
126
+ *
127
+ * @example
128
+ * // app/api/mpesa/reversal/result/route.ts
129
+ * export const POST = createReversalResultHandler(config)
130
+ */
131
+ declare function createReversalResultHandler(config: MpesaNextConfig): RouteHandler;
132
+ /**
133
+ * Returns POST handler for B2B Express Checkout callback.
134
+ *
135
+ * @example
136
+ * // app/api/mpesa/b2b/callback/route.ts
137
+ * export const POST = createB2BCallbackHandler(config)
138
+ */
139
+ declare function createB2BCallbackHandler(config: MpesaNextConfig): RouteHandler;
140
+ /**
141
+ * Returns POST handler for B2C result callback.
142
+ *
143
+ * @example
144
+ * // app/api/mpesa/b2c/result/route.ts
145
+ * export const POST = createB2CResultHandler(config)
146
+ */
147
+ declare function createB2CResultHandler(config: MpesaNextConfig): RouteHandler;
148
+ /**
149
+ * Returns POST handler for Tax Remittance result callback.
150
+ *
151
+ * @example
152
+ * // app/api/mpesa/tax/result/route.ts
153
+ * export const POST = createTaxResultHandler(config)
154
+ */
155
+ declare function createTaxResultHandler(config: MpesaNextConfig): RouteHandler;
156
+ /**
157
+ * Returns POST handler for B2C Disbursement result callback.
158
+ */
159
+ declare function createB2CDisbursementResultHandler(config: MpesaNextConfig): RouteHandler;
160
+ /**
161
+ * Returns POST handler for Transaction Status result callback.
162
+ */
163
+ declare function createTxStatusResultHandler(config: MpesaNextConfig): RouteHandler;
164
+ interface MpesaHandlers {
165
+ POST: RouteHandler;
166
+ GET: RouteHandler;
167
+ PATCH: RouteHandler;
168
+ DELETE: RouteHandler;
169
+ }
170
+ /**
171
+ * Creates all route handlers as a single catch-all.
172
+ * Mount at `app/api/mpesa/[[...route]]/route.ts`.
173
+ *
174
+ * The request pathname is resolved relative to the segment after `/mpesa/`.
175
+ *
176
+ * @example
177
+ * // app/api/mpesa/[[...route]]/route.ts
178
+ * import { createMpesaHandlers } from 'pesafy/adapters/nextjs'
179
+ * export const { POST, GET, PATCH, DELETE } = createMpesaHandlers(config)
180
+ */
181
+ declare function createMpesaHandlers(config: MpesaNextConfig): MpesaHandlers;
182
+ //#endregion
183
+ export { MpesaHandlers, MpesaNextConfig, StkFailurePayload, StkSuccessPayload, createB2BCallbackHandler, createB2CDisbursementResultHandler, createB2CResultHandler, createBalanceResultHandler, createC2BConfirmationHandler, createC2BValidationHandler, createMpesaHandlers, createReversalResultHandler, createStkCallbackHandler, createStkPushHandler, createStkQueryHandler, createTaxResultHandler, createTxStatusResultHandler };
@@ -0,0 +1 @@
1
+ import{A as e,D as t,E as n,O as r,S as i,T as a,_ as o,a as s,b as c,c as l,d as u,f as d,g as f,h as p,i as m,j as h,k as g,l as _,m as v,n as y,o as b,p as x,r as S,s as C,t as w,u as T,w as E,x as D,y as O}from"../webhook-guard.js";function k(e,t=200){return new Response(JSON.stringify(e),{status:t,headers:{"Content-Type":`application/json`}})}function A(e){return k({ok:!0,data:e})}function j(e){return e instanceof h?k({ok:!1,error:e.code,message:e.message},e.statusCode??400):k({ok:!1,error:`INTERNAL_ERROR`,message:`Unexpected error`},500)}function M(e){return e.headers.get(`x-forwarded-for`)?.split(`,`)[0]?.trim()??e.headers.get(`cf-connecting-ip`)??e.headers.get(`x-real-ip`)??``}function N(...e){return e.find(e=>e?.trim())??``}function P(e,t){e?.().catch(e=>console.error(`[pesafy/nextjs] ${t} hook error:`,e))}async function F(e,t,n){await w(M(e),n,t=>e.headers.get(t)??void 0,t)}function I(e){let t=new y(e);return async n=>{try{let{amount:r,phoneNumber:i,accountReference:a,transactionDesc:o,transactionType:s,partyB:c}=await n.json();if(!r||r<=0)throw new h({code:`VALIDATION_ERROR`,message:`amount must be > 0`});if(!i)throw new h({code:`VALIDATION_ERROR`,message:`phoneNumber is required`});return A(await t.stkPush({amount:r,phoneNumber:i,callbackUrl:e.callbackUrl,accountReference:a??`REF-${Date.now().toString(36).toUpperCase()}`,transactionDesc:o??`Payment`,...s===void 0?{}:{transactionType:s},...c===void 0?{}:{partyB:c}}))}catch(e){return j(e)}}}function L(e){let t=new y(e);return async e=>{try{let{checkoutRequestId:n}=await e.json();if(!n)throw new h({code:`VALIDATION_ERROR`,message:`checkoutRequestId is required`});return A(await t.stkQuery({checkoutRequestId:n}))}catch(e){return j(e)}}}function R(e){return async t=>{let n=await t.text();await F(t,e,n);let r=JSON.parse(n),i=r?.Body?.stkCallback;if(!i)return k({ResultCode:0,ResultDesc:`Accepted`});if(b(r)){let t={receiptNumber:s(r),amount:S(r),phone:m(r),checkoutRequestId:i.CheckoutRequestID,merchantRequestId:i.MerchantRequestID};console.info(`[pesafy/nextjs] STK success:`,t),P(e.onStkSuccess?()=>Promise.resolve(e.onStkSuccess(t)):void 0,`onStkSuccess`)}else{let t={resultCode:i.ResultCode,resultDesc:i.ResultDesc,checkoutRequestId:i.CheckoutRequestID,merchantRequestId:i.MerchantRequestID};console.warn(`[pesafy/nextjs] STK failure:`,t),P(e.onStkFailure?()=>Promise.resolve(e.onStkFailure(t)):void 0,`onStkFailure`)}return k({ResultCode:0,ResultDesc:`Accepted`})}}function z(e){return async t=>{let n=await t.text();await F(t,e,n);let r=JSON.parse(n);return k(e.onC2BValidation?await e.onC2BValidation(r):v())}}function B(e){return async t=>{let n=await t.json();return console.info(`[pesafy/nextjs] C2B confirmation:`,{txId:n.TransID,amount:n.TransAmount}),P(e.onC2BConfirmation?()=>Promise.resolve(e.onC2BConfirmation(n)):void 0,`onC2BConfirmation`),k({ResultCode:0,ResultDesc:`Success`})}}function V(t){return async n=>{let i=await n.json();if(g(i)){let t=r(i);console.info(`[pesafy/nextjs] Balance result:`,t?e(t):i)}else console.warn(`[pesafy/nextjs] Balance failed:`,i);return P(t.onAccountBalanceResult?()=>Promise.resolve(t.onAccountBalanceResult(i)):void 0,`onAccountBalanceResult`),k({ResultCode:0,ResultDesc:`Accepted`})}}function H(e){return async t=>{let n=await t.json();return d(n)&&(x(n)?console.info(`[pesafy/nextjs] Reversal success:`,u(n)):console.warn(`[pesafy/nextjs] Reversal failed:`,n.Result.ResultDesc),P(e.onReversalResult?()=>Promise.resolve(e.onReversalResult(n)):void 0,`onReversalResult`)),k({ResultCode:0,ResultDesc:`Accepted`})}}function U(e){return async r=>{let o=await r.json();if(!a(o))return console.warn(`[pesafy/nextjs] Unknown B2B callback`),k({ResultCode:0,ResultDesc:`Accepted`});let s=o;return t(s)?console.info(`[pesafy/nextjs] B2B success:`,{txId:E(s),amount:i(s)}):n(s)?console.warn(`[pesafy/nextjs] B2B cancelled`):console.warn(`[pesafy/nextjs] B2B failed:`,s.resultDesc),P(e.onB2BCheckoutCallback?()=>Promise.resolve(e.onB2BCheckoutCallback(s)):void 0,`onB2BCheckoutCallback`),k({ResultCode:0,ResultDesc:`Accepted`})}}function W(e){return async t=>{let n=await t.json();return c(n)&&(D(n)?console.info(`[pesafy/nextjs] B2C success:`,{txId:O(n),amount:o(n)}):console.warn(`[pesafy/nextjs] B2C failed:`,n.Result.ResultDesc),P(e.onB2CResult?()=>Promise.resolve(e.onB2CResult(n)):void 0,`onB2CResult`)),k({ResultCode:0,ResultDesc:`Accepted`})}}function G(e){return async t=>{let n=await t.json();return _(n)&&(T(n)?console.info(`[pesafy/nextjs] Tax success:`,n.Result.TransactionID):console.warn(`[pesafy/nextjs] Tax failed:`,n.Result.ResultDesc),P(e.onTaxResult?()=>Promise.resolve(e.onTaxResult(n)):void 0,`onTaxResult`)),k({ResultCode:0,ResultDesc:`Accepted`})}}function K(e){return async t=>{let n=await t.json();return p(n)&&(f(n)?console.info(`[pesafy/nextjs] Disbursement success:`,n.Result.TransactionID):console.warn(`[pesafy/nextjs] Disbursement failed:`,n.Result.ResultDesc),P(e.onB2CDisbursementResult?()=>Promise.resolve(e.onB2CDisbursementResult(n)):void 0,`onB2CDisbursementResult`)),k({ResultCode:0,ResultDesc:`Accepted`})}}function q(e){return async t=>{let n=await t.json();return C(n)&&(l(n)?console.info(`[pesafy/nextjs] Tx-status success:`,n.Result.TransactionID):console.warn(`[pesafy/nextjs] Tx-status failed:`,n.Result.ResultDesc),P(e.onTxStatusResult?()=>Promise.resolve(e.onTxStatusResult(n)):void 0,`onTxStatusResult`)),k({ResultCode:0,ResultDesc:`Accepted`})}}function J(e){let t=new y(e);async function n(n){let r=new URL(n.url).pathname.replace(/^.*?(\/mpesa\/.*)$/,`$1`).replace(/\/$/,``);switch(r){case`/mpesa/stk/push`:try{let{amount:r,phoneNumber:i,accountReference:a,transactionDesc:o,transactionType:s,partyB:c}=await n.json();if(!r||r<=0)throw new h({code:`VALIDATION_ERROR`,message:`amount must be > 0`});if(!i)throw new h({code:`VALIDATION_ERROR`,message:`phoneNumber is required`});return A(await t.stkPush({amount:r,phoneNumber:i,callbackUrl:e.callbackUrl,accountReference:a??`REF-${Date.now().toString(36).toUpperCase()}`,transactionDesc:o??`Payment`,...s===void 0?{}:{transactionType:s},...c===void 0?{}:{partyB:c}}))}catch(e){return j(e)}case`/mpesa/stk/query`:try{let{checkoutRequestId:e}=await n.json();if(!e)throw new h({code:`VALIDATION_ERROR`,message:`checkoutRequestId is required`});return A(await t.stkQuery({checkoutRequestId:e}))}catch(e){return j(e)}case`/mpesa/stk/callback`:return R(e)(n);case`/mpesa/c2b/register`:try{let{shortCode:r,confirmationUrl:i,validationUrl:a,responseType:o,apiVersion:s}=await n.json(),c=r??e.c2b?.shortCode??``,l=i??e.c2b?.confirmationUrl??``,u=a??e.c2b?.validationUrl??``;if(!c)throw new h({code:`VALIDATION_ERROR`,message:`shortCode is required`});if(!l)throw new h({code:`VALIDATION_ERROR`,message:`confirmationUrl is required`});if(!u)throw new h({code:`VALIDATION_ERROR`,message:`validationUrl is required`});return A(await t.registerC2BUrls({shortCode:c,confirmationUrl:l,validationUrl:u,responseType:o??e.c2b?.responseType??`Completed`,apiVersion:s??e.c2b?.apiVersion??`v2`}))}catch(e){return j(e)}case`/mpesa/c2b/simulate`:try{let{commandId:r,amount:i,msisdn:a,billRefNumber:o,shortCode:s}=await n.json();return A(await t.simulateC2B({shortCode:s??e.c2b?.shortCode??``,commandId:r,amount:i,msisdn:a,apiVersion:e.c2b?.apiVersion??`v2`,...o===void 0?{}:{billRefNumber:o}}))}catch(e){return j(e)}case`/mpesa/c2b/validation`:return z(e)(n);case`/mpesa/c2b/confirmation`:return B(e)(n);case`/mpesa/balance/query`:try{let r=await n.json();return A(await t.accountBalance({partyA:r.partyA??e.balance?.partyA??``,identifierType:r.identifierType??`4`,resultUrl:N(r.resultUrl,e.balance?.resultUrl,e.resultUrl),queueTimeOutUrl:N(r.queueTimeoutUrl,e.balance?.queueTimeoutUrl,e.queueTimeoutUrl),...r.remarks===void 0?{}:{remarks:r.remarks}}))}catch(e){return j(e)}case`/mpesa/balance/result`:return V(e)(n);case`/mpesa/qr/generate`:try{return A(await t.generateDynamicQR(await n.json()))}catch(e){return j(e)}case`/mpesa/reversal/request`:try{let r=await n.json();if(!r.transactionId)throw new h({code:`VALIDATION_ERROR`,message:`transactionId is required`});if(!r.receiverParty)throw new h({code:`VALIDATION_ERROR`,message:`receiverParty is required`});if(!r.amount||r.amount<=0)throw new h({code:`VALIDATION_ERROR`,message:`amount must be > 0`});return A(await t.reverseTransaction({transactionId:r.transactionId,receiverParty:r.receiverParty,amount:r.amount,resultUrl:N(r.resultUrl,e.reversal?.resultUrl,e.resultUrl),queueTimeOutUrl:N(r.queueTimeoutUrl,e.reversal?.queueTimeoutUrl,e.queueTimeoutUrl),...r.remarks===void 0?{}:{remarks:r.remarks},...r.occasion===void 0?{}:{occasion:r.occasion}}))}catch(e){return j(e)}case`/mpesa/reversal/result`:return H(e)(n);case`/mpesa/tx-status/query`:try{let r=await n.json();return A(await t.transactionStatus({...r.transactionId===void 0?{}:{transactionId:r.transactionId},...r.originalConversationId===void 0?{}:{originalConversationId:r.originalConversationId},partyA:r.partyA,identifierType:r.identifierType,resultUrl:N(r.resultUrl,e.txStatus?.resultUrl,e.resultUrl),queueTimeOutUrl:N(r.queueTimeoutUrl,e.txStatus?.queueTimeoutUrl,e.queueTimeoutUrl),...r.remarks===void 0?{}:{remarks:r.remarks},...r.occasion===void 0?{}:{occasion:r.occasion}}))}catch(e){return j(e)}case`/mpesa/tx-status/result`:return q(e)(n);case`/mpesa/tax/remit`:try{let r=await n.json();if(!r.amount||r.amount<=0)throw new h({code:`VALIDATION_ERROR`,message:`amount must be > 0`});if(!r.accountReference)throw new h({code:`VALIDATION_ERROR`,message:`accountReference (KRA PRN) is required`});return A(await t.remitTax({amount:r.amount,partyA:r.partyA??e.tax?.partyA??``,accountReference:r.accountReference,resultUrl:N(r.resultUrl,e.tax?.resultUrl,e.resultUrl),queueTimeOutUrl:N(r.queueTimeoutUrl,e.tax?.queueTimeoutUrl,e.queueTimeoutUrl),...r.partyB===void 0?{}:{partyB:r.partyB},...r.remarks===void 0?{}:{remarks:r.remarks}}))}catch(e){return j(e)}case`/mpesa/tax/result`:return G(e)(n);case`/mpesa/b2b/checkout`:try{let r=await n.json(),i=r.receiverShortCode??e.b2b?.receiverShortCode??``,a=r.callbackUrl??e.b2b?.callbackUrl??``;if(!i)throw new h({code:`VALIDATION_ERROR`,message:`receiverShortCode is required`});if(!a)throw new h({code:`VALIDATION_ERROR`,message:`callbackUrl is required`});return A(await t.b2bExpressCheckout({...r,receiverShortCode:i,callbackUrl:a}))}catch(e){return j(e)}case`/mpesa/b2b/callback`:return U(e)(n);case`/mpesa/b2c/payment`:try{let r=await n.json();if(r.commandId!==`BusinessPayToBulk`)throw new h({code:`VALIDATION_ERROR`,message:`commandId must be "BusinessPayToBulk"`});if(!r.partyB)throw new h({code:`VALIDATION_ERROR`,message:`partyB is required`});return A(await t.b2cPayment({commandId:`BusinessPayToBulk`,amount:r.amount,partyA:r.partyA??e.b2c?.partyA??``,partyB:r.partyB,accountReference:r.accountReference,resultUrl:N(r.resultUrl,e.b2c?.resultUrl,e.resultUrl),queueTimeOutUrl:N(r.queueTimeoutUrl,e.b2c?.queueTimeoutUrl,e.queueTimeoutUrl),...r.requester===void 0?{}:{requester:r.requester},...r.remarks===void 0?{}:{remarks:r.remarks}}))}catch(e){return j(e)}case`/mpesa/b2c/result`:return W(e)(n);case`/mpesa/b2c/disburse`:try{let{queueTimeoutUrl:r,resultUrl:i,...a}=await n.json();return A(await t.b2cDisbursement({...a,resultUrl:N(i,e.b2c?.resultUrl,e.resultUrl),queueTimeOutUrl:N(r,e.b2c?.queueTimeoutUrl,e.queueTimeoutUrl)}))}catch(e){return j(e)}case`/mpesa/b2c/disburse/result`:return K(e)(n);case`/mpesa/bills/optin`:try{return A(await t.billManagerOptIn(await n.json()))}catch(e){return j(e)}case`/mpesa/bills/invoice`:if(n.method===`DELETE`)try{return A(await t.cancelInvoice(await n.json()))}catch(e){return j(e)}try{return A(await t.sendInvoice(await n.json()))}catch(e){return j(e)}case`/mpesa/bills/invoice/bulk`:if(n.method===`DELETE`)try{return A(await t.cancelBulkInvoices(await n.json()))}catch(e){return j(e)}try{return A(await t.sendBulkInvoices(await n.json()))}catch(e){return j(e)}case`/mpesa/bills/reconcile`:try{return A(await t.reconcilePayment(await n.json()))}catch(e){return j(e)}case`/mpesa/health`:return k({ok:!0,environment:t.environment,ts:new Date().toISOString()});default:return k({ok:!1,error:`NOT_FOUND`,message:`No handler for ${r}`},404)}}return{POST:e=>n(e),GET:e=>n(e),PATCH:e=>n(e),DELETE:e=>n(e)}}export{U as createB2BCallbackHandler,K as createB2CDisbursementResultHandler,W as createB2CResultHandler,V as createBalanceResultHandler,B as createC2BConfirmationHandler,z as createC2BValidationHandler,J as createMpesaHandlers,H as createReversalResultHandler,R as createStkCallbackHandler,I as createStkPushHandler,L as createStkQueryHandler,G as createTaxResultHandler,q as createTxStatusResultHandler};
package/dist/chunk.js ADDED
@@ -0,0 +1 @@
1
+ var e=(e=>typeof require<`u`?require:typeof Proxy<`u`?new Proxy(e,{get:(e,t)=>(typeof require<`u`?require:e)[t]}):e)(function(e){if(typeof require<`u`)return require.apply(this,arguments);throw Error('Calling `require` for "'+e+"\" in an environment that doesn't expose the `require` function. See https://rolldown.rs/in-depth/bundling-cjs#require-external-modules for more details.")});export{e as t};