pesafy 0.3.0 → 0.3.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.

Potentially problematic release.


This version of pesafy might be problematic. Click here for more details.

package/dist/index.d.cts CHANGED
@@ -99,145 +99,127 @@ interface DynamicQRResponse {
99
99
  * C2B v1 used SHA-256 hashed MSISDN — do NOT use v1.
100
100
  */
101
101
  /**
102
- * ResponseType controls what M-Pesa does if your Validation URL is unreachable.
103
- * "Completed" = auto-complete. "Cancelled" = auto-cancel.
104
- * Note: Must be sentence case exactly as shown.
102
+ * The two valid C2B CommandID values:
103
+ * - CustomerPayBillOnline → payment to a Paybill number
104
+ * - CustomerBuyGoodsOnline payment to a Till number
105
+ */
106
+ type C2BCommandId = "CustomerPayBillOnline" | "CustomerBuyGoodsOnline";
107
+ /**
108
+ * Default action when the ValidationURL is unreachable.
109
+ * "Completed" → M-PESA auto-completes the transaction.
110
+ * "Cancelled" → M-PESA auto-cancels the transaction.
111
+ * Note: values are case-sensitive per Daraja docs.
105
112
  */
106
113
  type C2BResponseType = "Completed" | "Cancelled";
107
114
  interface C2BRegisterUrlRequest {
108
- /** Your Paybill or Till shortcode (5–6 digits). */
115
+ /** Paybill or Till short code */
109
116
  shortCode: string;
110
- /**
111
- * What M-Pesa does if your Validation URL is unreachable.
112
- * Only relevant if External Validation is enabled on your shortcode.
113
- * Defaults to "Completed".
114
- */
115
- responseType?: C2BResponseType;
116
- /**
117
- * URL that receives payment confirmation after transaction completes.
118
- * Must be HTTPS in production. HTTP is allowed in sandbox.
119
- */
117
+ /** URL that receives payment confirmation after a successful transaction */
120
118
  confirmationUrl: string;
121
- /**
122
- * URL for payment validation before M-Pesa completes the transaction.
123
- * Only called if External Validation is enabled on your shortcode.
124
- * Optional — omit if validation is not required.
125
- */
126
- validationUrl?: string;
127
- }
128
- interface C2BRegisterUrlResponse {
129
- /**
130
- * Global unique identifier for the request.
131
- * NOTE: Daraja has a typo in their field name ("Coversa" not "Conversa").
132
- * We mirror it exactly so JSON parsing works.
133
- */
134
- OriginatorCoversationID: string;
135
- ResponseCode: string;
136
- ResponseDescription: string;
119
+ /** URL that receives validation requests (only when external validation is enabled) */
120
+ validationUrl: string;
121
+ responseType?: C2BResponseType;
137
122
  }
138
- /**
139
- * CommandID controls whether this is a Paybill or Buy Goods (Till) payment.
140
- * - "CustomerPayBillOnline" → Paybill (requires BillRefNumber / account number)
141
- * - "CustomerBuyGoodsOnline" → Till/Buy Goods (BillRefNumber is null)
142
- */
143
- type C2BCommandId = "CustomerPayBillOnline" | "CustomerBuyGoodsOnline";
144
123
  interface C2BSimulateRequest {
145
- /** Your Paybill or Till shortcode. */
124
+ /** Paybill or Till short code */
146
125
  shortCode: string;
147
- /** Payment type. Paybill = "CustomerPayBillOnline", Till = "CustomerBuyGoodsOnline". */
148
- commandId: C2BCommandId;
149
- /** Amount to simulate (whole numbers only — Daraja rejects decimals). */
126
+ /**
127
+ * Transaction type.
128
+ * "CustomerPayBillOnline" → payment to a Paybill number (default)
129
+ * "CustomerBuyGoodsOnline" → payment to a Till number
130
+ */
131
+ commandId?: C2BCommandId;
132
+ /** Transaction amount (whole number, min 1 KES) */
150
133
  amount: number;
151
- /** Phone number to debit in simulation. Use sandbox test numbers. */
134
+ /** Customer's phone number any valid Kenyan MSISDN */
152
135
  phoneNumber: string;
153
136
  /**
154
- * Account number / reference for Paybill payments.
155
- * Pass null or omit for Buy Goods (Till) transactions.
137
+ * Account reference for Paybill payments.
138
+ * Must be null / omitted for CustomerBuyGoodsOnline.
156
139
  */
157
- billRefNumber?: string | null;
158
- }
159
- interface C2BSimulateResponse {
160
- /** @see C2BRegisterUrlResponse.OriginatorCoversationID (Daraja typo) */
161
- OriginatorCoversationID: string;
162
- ResponseCode: string;
163
- ResponseDescription: string;
140
+ billRefNumber?: string;
164
141
  }
165
142
  /**
166
- * C2B Confirmation/Validation callback body posted by Safaricom to your URLs.
167
- * C2B v2 returns a masked MSISDN: "2547 ***** 126"
168
- *
169
- * This type covers BOTH the confirmation and validation callbacks —
170
- * they share the same shape. The difference is:
171
- * - Validation: OrgAccountBalance is blank; you must respond Accept/Reject.
172
- * - Confirmation: OrgAccountBalance has the post-payment balance.
143
+ * Shared fields present in both Validation and Confirmation callback payloads.
173
144
  */
174
- interface C2BCallbackPayload {
175
- /** "Pay Bill" or "Buy Goods" */
145
+ interface C2BCallbackPayloadBase {
176
146
  TransactionType: string;
177
- /** Unique M-Pesa transaction ID (e.g. "RKL51ZDR4F") */
147
+ /** Unique M-PESA transaction ID (e.g. "RKL51ZDR4F") */
178
148
  TransID: string;
179
- /** Timestamp: "YYYYMMDDHHmmss" (e.g. "20231121121325") */
149
+ /** 14-digit timestamp: YYYYMMDDHHmmss */
180
150
  TransTime: string;
181
- /** Amount as string decimal (e.g. "5.00") */
151
+ /** Transaction amount as a string (e.g. "5.00") */
182
152
  TransAmount: string;
183
- /** Your Paybill or Till shortcode */
184
153
  BusinessShortCode: string;
185
- /**
186
- * Account reference / bill number entered by customer.
187
- * Applies to Paybill transactions. Empty string for Buy Goods.
188
- */
154
+ /** Account reference; empty for Till transactions */
189
155
  BillRefNumber: string;
190
- /** Invoice number if applicable (usually empty string) */
191
156
  InvoiceNumber: string;
192
- /**
193
- * Post-payment balance of your Utility Account (confirmation only).
194
- * Empty string in validation requests.
195
- */
196
157
  OrgAccountBalance: string;
197
- /**
198
- * Opaque ID the partner can echo back in the validation response.
199
- * Safaricom sends it back in the confirmation if you returned it.
200
- */
201
158
  ThirdPartyTransID: string;
202
- /**
203
- * Masked phone number: "2547 ***** 126"
204
- * C2B v2 masks for privacy. C2B v1 used SHA-256 hash.
205
- */
159
+ /** v2: masked MSISDN e.g. "2547 ***** 126" */
206
160
  MSISDN: string;
207
- /** Customer first name (may be empty) */
208
161
  FirstName: string;
209
- /** Customer middle name (may be empty) */
210
162
  MiddleName: string;
211
- /** Customer last name (may be empty) */
212
163
  LastName: string;
213
164
  }
165
+ /** Payload posted to your ValidationURL (if external validation is enabled) */
166
+ type C2BValidationPayload = C2BCallbackPayloadBase;
167
+ /** Payload posted to your ConfirmationURL after successful payment */
168
+ type C2BConfirmationPayload = C2BCallbackPayloadBase;
214
169
  /**
215
- * Your response to M-Pesa's Validation request.
216
- * You must reply within ~8 seconds or M-Pesa uses ResponseType default.
170
+ * Union of both C2B callback types.
171
+ * Use the discriminated `TransactionType` field if you need to distinguish them,
172
+ * or register separate Express routes for each URL.
217
173
  */
218
- interface C2BValidationResponse {
219
- /**
220
- * "0" to accept the payment.
221
- * Any of the C2B rejection codes to reject (e.g. "C2B00011").
222
- */
223
- ResultCode: string;
224
- /** "Accepted" or "Rejected" */
225
- ResultDesc: string;
226
- }
174
+ type C2BCallbackPayload = C2BValidationPayload | C2BConfirmationPayload;
227
175
  /**
228
- * Rejection codes for the Validation response.
229
- * Use these ResultCode values when rejecting a payment.
176
+ * Codes your ValidationURL must return to Safaricom to accept or reject
177
+ * a payment before it is processed.
230
178
  */
231
179
  declare const C2B_REJECTION_CODES: {
232
- readonly INVALID_MSISDN: "C2B00011";
233
- readonly INVALID_ACCOUNT_NUMBER: "C2B00012";
234
- readonly INVALID_AMOUNT: "C2B00013";
235
- readonly INVALID_KYC_DETAILS: "C2B00014";
236
- readonly INVALID_SHORT_CODE: "C2B00015";
237
- readonly OTHER_ERROR: "C2B00016";
180
+ /** Accept the transaction — M-PESA proceeds to process and confirm */
181
+ readonly ACCEPT: "0";
182
+ /** Reject the transaction — M-PESA cancels and notifies the customer */
183
+ readonly REJECT: "1";
238
184
  };
239
185
  type C2BRejectionCode = (typeof C2B_REJECTION_CODES)[keyof typeof C2B_REJECTION_CODES];
240
186
 
187
+ /**
188
+ * Actual response shape from Daraja.
189
+ * Note: Daraja's field name has a typo ("CoversationID" — missing the 'n').
190
+ * We match it exactly so JSON parsing is lossless.
191
+ */
192
+ interface C2BRegisterUrlResponse {
193
+ /** Global unique identifier for this registration request. Daraja typo: "Coversation" */
194
+ OriginatorCoversationID: string;
195
+ /** "0" = accepted */
196
+ ResponseCode: string;
197
+ ResponseDescription: string;
198
+ }
199
+
200
+ /**
201
+ * C2B Simulate (sandbox only)
202
+ * API: POST /mpesa/c2b/v2/simulate
203
+ *
204
+ * Simulates a customer paying a Paybill or Till number.
205
+ * This endpoint is NOT available in production — use STK Push or Dynamic QR
206
+ * for production payment initiation.
207
+ */
208
+
209
+ /**
210
+ * Actual response shape from Daraja.
211
+ * Note: Daraja's field name has a typo ("CoversationID" — missing the 'n').
212
+ * We match it exactly so JSON parsing is lossless.
213
+ * There is NO ConversationID field in this response (unlike B2C/B2B).
214
+ */
215
+ interface C2BSimulateResponse {
216
+ /** Global unique identifier for this simulate request. Daraja typo: "Coversation" */
217
+ OriginatorCoversationID: string;
218
+ /** "0" = accepted */
219
+ ResponseCode: string;
220
+ ResponseDescription: string;
221
+ }
222
+
241
223
  /** B2B (Business to Business) types */
242
224
  type B2BCommandId = "BusinessPayBill" | "BusinessBuyGoods" | "DisburseFundsToBusiness" | "BusinessToBusinessTransfer";
243
225
  interface B2BRequest {
@@ -339,6 +321,92 @@ interface StkQueryResponse {
339
321
  ResultCode: number;
340
322
  ResultDesc: string;
341
323
  }
324
+ /** A single metadata item in a successful STK callback */
325
+ interface StkCallbackMetadataItem {
326
+ Name: "Amount" | "MpesaReceiptNumber" | "TransactionDate" | "PhoneNumber";
327
+ /** Present on successful transactions; absent on failure */
328
+ Value?: number | string;
329
+ }
330
+ /** Inner callback object for a SUCCESSFUL STK Push */
331
+ interface StkCallbackSuccess {
332
+ MerchantRequestID: string;
333
+ CheckoutRequestID: string;
334
+ /** 0 = success */
335
+ ResultCode: 0;
336
+ ResultDesc: string;
337
+ CallbackMetadata: {
338
+ Item: StkCallbackMetadataItem[];
339
+ };
340
+ }
341
+ /** Inner callback object for a FAILED/CANCELLED STK Push */
342
+ interface StkCallbackFailure {
343
+ MerchantRequestID: string;
344
+ CheckoutRequestID: string;
345
+ /** Non-zero result codes: e.g. 1032 = cancelled by user */
346
+ ResultCode: number;
347
+ ResultDesc: string;
348
+ CallbackMetadata?: never;
349
+ }
350
+ type StkCallbackInner = StkCallbackSuccess | StkCallbackFailure;
351
+ /** Full wrapper that Safaricom POSTs to your CallBackURL */
352
+ interface StkPushCallback {
353
+ Body: {
354
+ stkCallback: StkCallbackInner;
355
+ };
356
+ }
357
+ /**
358
+ * Type guard — narrows an StkCallbackInner to the success shape.
359
+ * Usage:
360
+ * if (isStkCallbackSuccess(callback.Body.stkCallback)) {
361
+ * const receipt = getCallbackValue(callback, "MpesaReceiptNumber");
362
+ * }
363
+ */
364
+ declare function isStkCallbackSuccess(cb: StkCallbackInner): cb is StkCallbackSuccess;
365
+ /**
366
+ * Extracts a named value from a successful callback's metadata items.
367
+ * Returns undefined if the key is absent or the callback failed.
368
+ */
369
+ declare function getCallbackValue(callback: StkPushCallback, name: StkCallbackMetadataItem["Name"]): string | number | undefined;
370
+
371
+ /**
372
+ * Shared phone number utilities for M-Pesa API calls.
373
+ *
374
+ * Two formatters are intentionally separate:
375
+ * - formatSafaricomPhone → strict, for STK Push (only Safaricom/Airtel)
376
+ * - formatKenyanMsisdn → permissive, for C2B simulate and B2C PartyB
377
+ *
378
+ * Daraja consistently expects 12-digit MSISDN without the '+' prefix.
379
+ */
380
+ /**
381
+ * Strict formatter for STK Push.
382
+ * Accepts Safaricom (2547xx) and Airtel Kenya (2541x) numbers only.
383
+ * Throws a clear error on invalid input so Daraja never receives a bad MSISDN.
384
+ */
385
+ declare function formatSafaricomPhone(phone: string): string;
386
+ /**
387
+ * Permissive formatter for C2B simulate and B2C PartyB.
388
+ * Accepts any valid 12-digit Kenyan MSISDN (Safaricom, Airtel, Telkom, …).
389
+ * Still throws if the result is structurally impossible.
390
+ */
391
+ declare function formatKenyanMsisdn(phone: string): string;
392
+ /**
393
+ * Converts a formatted MSISDN string to the numeric form Daraja expects in
394
+ * C2B simulate requests ("Msisdn": 254708374149 — no quotes in the JSON).
395
+ */
396
+ declare function msisdnToNumber(phone: string): number;
397
+
398
+ /**
399
+ * STK Push utilities
400
+ *
401
+ * Phone formatting delegates to the shared `formatSafaricomPhone` util which
402
+ * validates Safaricom/Airtel numbers and throws a clear error on bad input.
403
+ */
404
+
405
+ /**
406
+ * Generates a Daraja-compatible timestamp: YYYYMMDDHHmmss
407
+ * Call this once per request and reuse the value.
408
+ */
409
+ declare function getTimestamp(): string;
342
410
 
343
411
  type Environment = "sandbox" | "production";
344
412
  declare const DARAJA_BASE_URLS: {
@@ -560,4 +628,4 @@ declare class PesafyError extends Error {
560
628
  }
561
629
  declare function createError(options: PesafyErrorOptions): PesafyError;
562
630
 
563
- export { type B2BRequest, type B2BResponse, type B2CRequest, type B2CResponse, type B2CWebhook, type C2BCallbackPayload, type C2BCommandId, type C2BRegisterUrlRequest, type C2BRegisterUrlResponse, type C2BRejectionCode, type C2BResponseType, type C2BSimulateRequest, type C2BSimulateResponse, type C2BValidationResponse, type C2BWebhook, C2B_REJECTION_CODES, DARAJA_BASE_URLS, type DynamicQRRequest, type DynamicQRResponse, type ErrorCode, Mpesa, type MpesaConfig, type MpesaExpressConfig, PesafyError, type RetryOptions, type RetryResult, type ReversalRequest, type ReversalResponse, type StkPushRequest, type StkPushResponse, type StkPushWebhook, type StkQueryRequest, type StkQueryResponse, TokenManager, type TransactionStatusRequest, type TransactionStatusResponse, type WebhookEvent, type WebhookEventType, type WebhookHandlerOptions, type WebhookHandlerResult, createError, createMpesaExpressClient, createMpesaExpressRouter, encryptSecurityCredential, extractAmount, extractTransactionId, handleWebhook, retryWithBackoff, verifyWebhookIP };
631
+ export { type B2BRequest, type B2BResponse, type B2CRequest, type B2CResponse, type B2CWebhook, type C2BCallbackPayload, type C2BCallbackPayloadBase, type C2BCommandId, type C2BConfirmationPayload, type C2BRegisterUrlRequest, type C2BRegisterUrlResponse, type C2BRejectionCode, type C2BSimulateRequest, type C2BSimulateResponse, type C2BValidationPayload, type C2BWebhook, C2B_REJECTION_CODES, DARAJA_BASE_URLS, type DynamicQRRequest, type DynamicQRResponse, type ErrorCode, Mpesa, type MpesaConfig, type MpesaExpressConfig, PesafyError, type RetryOptions, type RetryResult, type ReversalRequest, type ReversalResponse, type StkCallbackFailure, type StkCallbackInner, type StkCallbackMetadataItem, type StkCallbackSuccess, type StkPushCallback, type StkPushRequest, type StkPushResponse, type StkPushWebhook, type StkQueryRequest, type StkQueryResponse, TokenManager, type TransactionStatusRequest, type TransactionStatusResponse, type WebhookEvent, type WebhookEventType, type WebhookHandlerOptions, type WebhookHandlerResult, createError, createMpesaExpressClient, createMpesaExpressRouter, encryptSecurityCredential, extractAmount, extractTransactionId, formatKenyanMsisdn, formatSafaricomPhone as formatPhoneNumber, formatSafaricomPhone, getCallbackValue, getTimestamp, handleWebhook, isStkCallbackSuccess, msisdnToNumber, retryWithBackoff, verifyWebhookIP };
package/dist/index.d.ts CHANGED
@@ -99,145 +99,127 @@ interface DynamicQRResponse {
99
99
  * C2B v1 used SHA-256 hashed MSISDN — do NOT use v1.
100
100
  */
101
101
  /**
102
- * ResponseType controls what M-Pesa does if your Validation URL is unreachable.
103
- * "Completed" = auto-complete. "Cancelled" = auto-cancel.
104
- * Note: Must be sentence case exactly as shown.
102
+ * The two valid C2B CommandID values:
103
+ * - CustomerPayBillOnline → payment to a Paybill number
104
+ * - CustomerBuyGoodsOnline payment to a Till number
105
+ */
106
+ type C2BCommandId = "CustomerPayBillOnline" | "CustomerBuyGoodsOnline";
107
+ /**
108
+ * Default action when the ValidationURL is unreachable.
109
+ * "Completed" → M-PESA auto-completes the transaction.
110
+ * "Cancelled" → M-PESA auto-cancels the transaction.
111
+ * Note: values are case-sensitive per Daraja docs.
105
112
  */
106
113
  type C2BResponseType = "Completed" | "Cancelled";
107
114
  interface C2BRegisterUrlRequest {
108
- /** Your Paybill or Till shortcode (5–6 digits). */
115
+ /** Paybill or Till short code */
109
116
  shortCode: string;
110
- /**
111
- * What M-Pesa does if your Validation URL is unreachable.
112
- * Only relevant if External Validation is enabled on your shortcode.
113
- * Defaults to "Completed".
114
- */
115
- responseType?: C2BResponseType;
116
- /**
117
- * URL that receives payment confirmation after transaction completes.
118
- * Must be HTTPS in production. HTTP is allowed in sandbox.
119
- */
117
+ /** URL that receives payment confirmation after a successful transaction */
120
118
  confirmationUrl: string;
121
- /**
122
- * URL for payment validation before M-Pesa completes the transaction.
123
- * Only called if External Validation is enabled on your shortcode.
124
- * Optional — omit if validation is not required.
125
- */
126
- validationUrl?: string;
127
- }
128
- interface C2BRegisterUrlResponse {
129
- /**
130
- * Global unique identifier for the request.
131
- * NOTE: Daraja has a typo in their field name ("Coversa" not "Conversa").
132
- * We mirror it exactly so JSON parsing works.
133
- */
134
- OriginatorCoversationID: string;
135
- ResponseCode: string;
136
- ResponseDescription: string;
119
+ /** URL that receives validation requests (only when external validation is enabled) */
120
+ validationUrl: string;
121
+ responseType?: C2BResponseType;
137
122
  }
138
- /**
139
- * CommandID controls whether this is a Paybill or Buy Goods (Till) payment.
140
- * - "CustomerPayBillOnline" → Paybill (requires BillRefNumber / account number)
141
- * - "CustomerBuyGoodsOnline" → Till/Buy Goods (BillRefNumber is null)
142
- */
143
- type C2BCommandId = "CustomerPayBillOnline" | "CustomerBuyGoodsOnline";
144
123
  interface C2BSimulateRequest {
145
- /** Your Paybill or Till shortcode. */
124
+ /** Paybill or Till short code */
146
125
  shortCode: string;
147
- /** Payment type. Paybill = "CustomerPayBillOnline", Till = "CustomerBuyGoodsOnline". */
148
- commandId: C2BCommandId;
149
- /** Amount to simulate (whole numbers only — Daraja rejects decimals). */
126
+ /**
127
+ * Transaction type.
128
+ * "CustomerPayBillOnline" → payment to a Paybill number (default)
129
+ * "CustomerBuyGoodsOnline" → payment to a Till number
130
+ */
131
+ commandId?: C2BCommandId;
132
+ /** Transaction amount (whole number, min 1 KES) */
150
133
  amount: number;
151
- /** Phone number to debit in simulation. Use sandbox test numbers. */
134
+ /** Customer's phone number any valid Kenyan MSISDN */
152
135
  phoneNumber: string;
153
136
  /**
154
- * Account number / reference for Paybill payments.
155
- * Pass null or omit for Buy Goods (Till) transactions.
137
+ * Account reference for Paybill payments.
138
+ * Must be null / omitted for CustomerBuyGoodsOnline.
156
139
  */
157
- billRefNumber?: string | null;
158
- }
159
- interface C2BSimulateResponse {
160
- /** @see C2BRegisterUrlResponse.OriginatorCoversationID (Daraja typo) */
161
- OriginatorCoversationID: string;
162
- ResponseCode: string;
163
- ResponseDescription: string;
140
+ billRefNumber?: string;
164
141
  }
165
142
  /**
166
- * C2B Confirmation/Validation callback body posted by Safaricom to your URLs.
167
- * C2B v2 returns a masked MSISDN: "2547 ***** 126"
168
- *
169
- * This type covers BOTH the confirmation and validation callbacks —
170
- * they share the same shape. The difference is:
171
- * - Validation: OrgAccountBalance is blank; you must respond Accept/Reject.
172
- * - Confirmation: OrgAccountBalance has the post-payment balance.
143
+ * Shared fields present in both Validation and Confirmation callback payloads.
173
144
  */
174
- interface C2BCallbackPayload {
175
- /** "Pay Bill" or "Buy Goods" */
145
+ interface C2BCallbackPayloadBase {
176
146
  TransactionType: string;
177
- /** Unique M-Pesa transaction ID (e.g. "RKL51ZDR4F") */
147
+ /** Unique M-PESA transaction ID (e.g. "RKL51ZDR4F") */
178
148
  TransID: string;
179
- /** Timestamp: "YYYYMMDDHHmmss" (e.g. "20231121121325") */
149
+ /** 14-digit timestamp: YYYYMMDDHHmmss */
180
150
  TransTime: string;
181
- /** Amount as string decimal (e.g. "5.00") */
151
+ /** Transaction amount as a string (e.g. "5.00") */
182
152
  TransAmount: string;
183
- /** Your Paybill or Till shortcode */
184
153
  BusinessShortCode: string;
185
- /**
186
- * Account reference / bill number entered by customer.
187
- * Applies to Paybill transactions. Empty string for Buy Goods.
188
- */
154
+ /** Account reference; empty for Till transactions */
189
155
  BillRefNumber: string;
190
- /** Invoice number if applicable (usually empty string) */
191
156
  InvoiceNumber: string;
192
- /**
193
- * Post-payment balance of your Utility Account (confirmation only).
194
- * Empty string in validation requests.
195
- */
196
157
  OrgAccountBalance: string;
197
- /**
198
- * Opaque ID the partner can echo back in the validation response.
199
- * Safaricom sends it back in the confirmation if you returned it.
200
- */
201
158
  ThirdPartyTransID: string;
202
- /**
203
- * Masked phone number: "2547 ***** 126"
204
- * C2B v2 masks for privacy. C2B v1 used SHA-256 hash.
205
- */
159
+ /** v2: masked MSISDN e.g. "2547 ***** 126" */
206
160
  MSISDN: string;
207
- /** Customer first name (may be empty) */
208
161
  FirstName: string;
209
- /** Customer middle name (may be empty) */
210
162
  MiddleName: string;
211
- /** Customer last name (may be empty) */
212
163
  LastName: string;
213
164
  }
165
+ /** Payload posted to your ValidationURL (if external validation is enabled) */
166
+ type C2BValidationPayload = C2BCallbackPayloadBase;
167
+ /** Payload posted to your ConfirmationURL after successful payment */
168
+ type C2BConfirmationPayload = C2BCallbackPayloadBase;
214
169
  /**
215
- * Your response to M-Pesa's Validation request.
216
- * You must reply within ~8 seconds or M-Pesa uses ResponseType default.
170
+ * Union of both C2B callback types.
171
+ * Use the discriminated `TransactionType` field if you need to distinguish them,
172
+ * or register separate Express routes for each URL.
217
173
  */
218
- interface C2BValidationResponse {
219
- /**
220
- * "0" to accept the payment.
221
- * Any of the C2B rejection codes to reject (e.g. "C2B00011").
222
- */
223
- ResultCode: string;
224
- /** "Accepted" or "Rejected" */
225
- ResultDesc: string;
226
- }
174
+ type C2BCallbackPayload = C2BValidationPayload | C2BConfirmationPayload;
227
175
  /**
228
- * Rejection codes for the Validation response.
229
- * Use these ResultCode values when rejecting a payment.
176
+ * Codes your ValidationURL must return to Safaricom to accept or reject
177
+ * a payment before it is processed.
230
178
  */
231
179
  declare const C2B_REJECTION_CODES: {
232
- readonly INVALID_MSISDN: "C2B00011";
233
- readonly INVALID_ACCOUNT_NUMBER: "C2B00012";
234
- readonly INVALID_AMOUNT: "C2B00013";
235
- readonly INVALID_KYC_DETAILS: "C2B00014";
236
- readonly INVALID_SHORT_CODE: "C2B00015";
237
- readonly OTHER_ERROR: "C2B00016";
180
+ /** Accept the transaction — M-PESA proceeds to process and confirm */
181
+ readonly ACCEPT: "0";
182
+ /** Reject the transaction — M-PESA cancels and notifies the customer */
183
+ readonly REJECT: "1";
238
184
  };
239
185
  type C2BRejectionCode = (typeof C2B_REJECTION_CODES)[keyof typeof C2B_REJECTION_CODES];
240
186
 
187
+ /**
188
+ * Actual response shape from Daraja.
189
+ * Note: Daraja's field name has a typo ("CoversationID" — missing the 'n').
190
+ * We match it exactly so JSON parsing is lossless.
191
+ */
192
+ interface C2BRegisterUrlResponse {
193
+ /** Global unique identifier for this registration request. Daraja typo: "Coversation" */
194
+ OriginatorCoversationID: string;
195
+ /** "0" = accepted */
196
+ ResponseCode: string;
197
+ ResponseDescription: string;
198
+ }
199
+
200
+ /**
201
+ * C2B Simulate (sandbox only)
202
+ * API: POST /mpesa/c2b/v2/simulate
203
+ *
204
+ * Simulates a customer paying a Paybill or Till number.
205
+ * This endpoint is NOT available in production — use STK Push or Dynamic QR
206
+ * for production payment initiation.
207
+ */
208
+
209
+ /**
210
+ * Actual response shape from Daraja.
211
+ * Note: Daraja's field name has a typo ("CoversationID" — missing the 'n').
212
+ * We match it exactly so JSON parsing is lossless.
213
+ * There is NO ConversationID field in this response (unlike B2C/B2B).
214
+ */
215
+ interface C2BSimulateResponse {
216
+ /** Global unique identifier for this simulate request. Daraja typo: "Coversation" */
217
+ OriginatorCoversationID: string;
218
+ /** "0" = accepted */
219
+ ResponseCode: string;
220
+ ResponseDescription: string;
221
+ }
222
+
241
223
  /** B2B (Business to Business) types */
242
224
  type B2BCommandId = "BusinessPayBill" | "BusinessBuyGoods" | "DisburseFundsToBusiness" | "BusinessToBusinessTransfer";
243
225
  interface B2BRequest {
@@ -339,6 +321,92 @@ interface StkQueryResponse {
339
321
  ResultCode: number;
340
322
  ResultDesc: string;
341
323
  }
324
+ /** A single metadata item in a successful STK callback */
325
+ interface StkCallbackMetadataItem {
326
+ Name: "Amount" | "MpesaReceiptNumber" | "TransactionDate" | "PhoneNumber";
327
+ /** Present on successful transactions; absent on failure */
328
+ Value?: number | string;
329
+ }
330
+ /** Inner callback object for a SUCCESSFUL STK Push */
331
+ interface StkCallbackSuccess {
332
+ MerchantRequestID: string;
333
+ CheckoutRequestID: string;
334
+ /** 0 = success */
335
+ ResultCode: 0;
336
+ ResultDesc: string;
337
+ CallbackMetadata: {
338
+ Item: StkCallbackMetadataItem[];
339
+ };
340
+ }
341
+ /** Inner callback object for a FAILED/CANCELLED STK Push */
342
+ interface StkCallbackFailure {
343
+ MerchantRequestID: string;
344
+ CheckoutRequestID: string;
345
+ /** Non-zero result codes: e.g. 1032 = cancelled by user */
346
+ ResultCode: number;
347
+ ResultDesc: string;
348
+ CallbackMetadata?: never;
349
+ }
350
+ type StkCallbackInner = StkCallbackSuccess | StkCallbackFailure;
351
+ /** Full wrapper that Safaricom POSTs to your CallBackURL */
352
+ interface StkPushCallback {
353
+ Body: {
354
+ stkCallback: StkCallbackInner;
355
+ };
356
+ }
357
+ /**
358
+ * Type guard — narrows an StkCallbackInner to the success shape.
359
+ * Usage:
360
+ * if (isStkCallbackSuccess(callback.Body.stkCallback)) {
361
+ * const receipt = getCallbackValue(callback, "MpesaReceiptNumber");
362
+ * }
363
+ */
364
+ declare function isStkCallbackSuccess(cb: StkCallbackInner): cb is StkCallbackSuccess;
365
+ /**
366
+ * Extracts a named value from a successful callback's metadata items.
367
+ * Returns undefined if the key is absent or the callback failed.
368
+ */
369
+ declare function getCallbackValue(callback: StkPushCallback, name: StkCallbackMetadataItem["Name"]): string | number | undefined;
370
+
371
+ /**
372
+ * Shared phone number utilities for M-Pesa API calls.
373
+ *
374
+ * Two formatters are intentionally separate:
375
+ * - formatSafaricomPhone → strict, for STK Push (only Safaricom/Airtel)
376
+ * - formatKenyanMsisdn → permissive, for C2B simulate and B2C PartyB
377
+ *
378
+ * Daraja consistently expects 12-digit MSISDN without the '+' prefix.
379
+ */
380
+ /**
381
+ * Strict formatter for STK Push.
382
+ * Accepts Safaricom (2547xx) and Airtel Kenya (2541x) numbers only.
383
+ * Throws a clear error on invalid input so Daraja never receives a bad MSISDN.
384
+ */
385
+ declare function formatSafaricomPhone(phone: string): string;
386
+ /**
387
+ * Permissive formatter for C2B simulate and B2C PartyB.
388
+ * Accepts any valid 12-digit Kenyan MSISDN (Safaricom, Airtel, Telkom, …).
389
+ * Still throws if the result is structurally impossible.
390
+ */
391
+ declare function formatKenyanMsisdn(phone: string): string;
392
+ /**
393
+ * Converts a formatted MSISDN string to the numeric form Daraja expects in
394
+ * C2B simulate requests ("Msisdn": 254708374149 — no quotes in the JSON).
395
+ */
396
+ declare function msisdnToNumber(phone: string): number;
397
+
398
+ /**
399
+ * STK Push utilities
400
+ *
401
+ * Phone formatting delegates to the shared `formatSafaricomPhone` util which
402
+ * validates Safaricom/Airtel numbers and throws a clear error on bad input.
403
+ */
404
+
405
+ /**
406
+ * Generates a Daraja-compatible timestamp: YYYYMMDDHHmmss
407
+ * Call this once per request and reuse the value.
408
+ */
409
+ declare function getTimestamp(): string;
342
410
 
343
411
  type Environment = "sandbox" | "production";
344
412
  declare const DARAJA_BASE_URLS: {
@@ -560,4 +628,4 @@ declare class PesafyError extends Error {
560
628
  }
561
629
  declare function createError(options: PesafyErrorOptions): PesafyError;
562
630
 
563
- export { type B2BRequest, type B2BResponse, type B2CRequest, type B2CResponse, type B2CWebhook, type C2BCallbackPayload, type C2BCommandId, type C2BRegisterUrlRequest, type C2BRegisterUrlResponse, type C2BRejectionCode, type C2BResponseType, type C2BSimulateRequest, type C2BSimulateResponse, type C2BValidationResponse, type C2BWebhook, C2B_REJECTION_CODES, DARAJA_BASE_URLS, type DynamicQRRequest, type DynamicQRResponse, type ErrorCode, Mpesa, type MpesaConfig, type MpesaExpressConfig, PesafyError, type RetryOptions, type RetryResult, type ReversalRequest, type ReversalResponse, type StkPushRequest, type StkPushResponse, type StkPushWebhook, type StkQueryRequest, type StkQueryResponse, TokenManager, type TransactionStatusRequest, type TransactionStatusResponse, type WebhookEvent, type WebhookEventType, type WebhookHandlerOptions, type WebhookHandlerResult, createError, createMpesaExpressClient, createMpesaExpressRouter, encryptSecurityCredential, extractAmount, extractTransactionId, handleWebhook, retryWithBackoff, verifyWebhookIP };
631
+ export { type B2BRequest, type B2BResponse, type B2CRequest, type B2CResponse, type B2CWebhook, type C2BCallbackPayload, type C2BCallbackPayloadBase, type C2BCommandId, type C2BConfirmationPayload, type C2BRegisterUrlRequest, type C2BRegisterUrlResponse, type C2BRejectionCode, type C2BSimulateRequest, type C2BSimulateResponse, type C2BValidationPayload, type C2BWebhook, C2B_REJECTION_CODES, DARAJA_BASE_URLS, type DynamicQRRequest, type DynamicQRResponse, type ErrorCode, Mpesa, type MpesaConfig, type MpesaExpressConfig, PesafyError, type RetryOptions, type RetryResult, type ReversalRequest, type ReversalResponse, type StkCallbackFailure, type StkCallbackInner, type StkCallbackMetadataItem, type StkCallbackSuccess, type StkPushCallback, type StkPushRequest, type StkPushResponse, type StkPushWebhook, type StkQueryRequest, type StkQueryResponse, TokenManager, type TransactionStatusRequest, type TransactionStatusResponse, type WebhookEvent, type WebhookEventType, type WebhookHandlerOptions, type WebhookHandlerResult, createError, createMpesaExpressClient, createMpesaExpressRouter, encryptSecurityCredential, extractAmount, extractTransactionId, formatKenyanMsisdn, formatSafaricomPhone as formatPhoneNumber, formatSafaricomPhone, getCallbackValue, getTimestamp, handleWebhook, isStkCallbackSuccess, msisdnToNumber, retryWithBackoff, verifyWebhookIP };