pesafy 0.3.13 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -561,6 +561,66 @@ function getCallbackValue(callback, name) {
561
561
  return inner.CallbackMetadata.Item.find((i) => i.Name === name)?.Value;
562
562
  }
563
563
 
564
+ // src/mpesa/tax-remittance/remit-tax.ts
565
+ var KRA_SHORTCODE = "572572";
566
+ var TAX_COMMAND_ID = "PayTaxToKRA";
567
+ async function remitTax(baseUrl, accessToken, securityCredential, initiatorName, request) {
568
+ const amount = Math.round(request.amount);
569
+ if (!Number.isFinite(amount) || amount < 1) {
570
+ throw createError({
571
+ code: "VALIDATION_ERROR",
572
+ message: `amount must be a whole number \u2265 1 (got ${request.amount} which rounds to ${amount}).`
573
+ });
574
+ }
575
+ if (!request.partyA) {
576
+ throw createError({
577
+ code: "VALIDATION_ERROR",
578
+ message: "partyA is required \u2014 your M-PESA business shortcode from which tax is deducted."
579
+ });
580
+ }
581
+ if (!request.accountReference?.trim()) {
582
+ throw createError({
583
+ code: "VALIDATION_ERROR",
584
+ message: "accountReference is required \u2014 the Payment Registration Number (PRN) issued by KRA."
585
+ });
586
+ }
587
+ if (!request.resultUrl?.trim()) {
588
+ throw createError({
589
+ code: "VALIDATION_ERROR",
590
+ message: "resultUrl is required \u2014 Safaricom POSTs the tax remittance result here."
591
+ });
592
+ }
593
+ if (!request.queueTimeOutUrl?.trim()) {
594
+ throw createError({
595
+ code: "VALIDATION_ERROR",
596
+ message: "queueTimeOutUrl is required \u2014 Safaricom calls this on request timeout."
597
+ });
598
+ }
599
+ const payload = {
600
+ Initiator: initiatorName,
601
+ SecurityCredential: securityCredential,
602
+ CommandID: TAX_COMMAND_ID,
603
+ SenderIdentifierType: "4",
604
+ RecieverIdentifierType: "4",
605
+ Amount: String(amount),
606
+ PartyA: String(request.partyA),
607
+ PartyB: request.partyB ?? KRA_SHORTCODE,
608
+ AccountReference: request.accountReference,
609
+ Remarks: request.remarks ?? "Tax Remittance",
610
+ QueueTimeOutURL: request.queueTimeOutUrl,
611
+ ResultURL: request.resultUrl
612
+ };
613
+ const { data } = await httpRequest(
614
+ `${baseUrl}/mpesa/b2b/v1/remittax`,
615
+ {
616
+ method: "POST",
617
+ headers: { Authorization: `Bearer ${accessToken}` },
618
+ body: payload
619
+ }
620
+ );
621
+ return data;
622
+ }
623
+
564
624
  // src/mpesa/transaction-status/query.ts
565
625
  async function queryTransactionStatus(baseUrl, token, securityCredential, initiator, request) {
566
626
  if (!request.transactionId) {
@@ -850,6 +910,52 @@ var Mpesa = class {
850
910
  const token = await this.getToken();
851
911
  return simulateC2B(this.baseUrl, token, request);
852
912
  }
913
+ // ── Tax Remittance ────────────────────────────────────────────────────────
914
+ /**
915
+ * Tax Remittance — remits tax to Kenya Revenue Authority (KRA) via M-PESA.
916
+ *
917
+ * Requires:
918
+ * - initiatorName in config
919
+ * - initiatorPassword + certificate (or pre-computed securityCredential)
920
+ *
921
+ * This is ASYNCHRONOUS. The synchronous response only confirms receipt.
922
+ * Final details are POSTed to your resultUrl.
923
+ *
924
+ * Prerequisites (from Daraja docs):
925
+ * - Prior integration with KRA for tax declaration.
926
+ * - A Payment Registration Number (PRN) from KRA.
927
+ * - Initiator with "Tax Remittance ORG API" role on M-PESA org portal.
928
+ *
929
+ * Fixed values (set automatically — do NOT override unless Safaricom changes them):
930
+ * CommandID: "PayTaxToKRA"
931
+ * SenderIdentifierType: "4"
932
+ * RecieverIdentifierType: "4"
933
+ * PartyB: "572572" (KRA shortcode)
934
+ *
935
+ * @example
936
+ * await mpesa.remitTax({
937
+ * amount: 5000,
938
+ * partyA: "888880",
939
+ * accountReference: "PRN1234XN", // PRN from KRA
940
+ * resultUrl: "https://yourdomain.com/mpesa/tax/result",
941
+ * queueTimeOutUrl: "https://yourdomain.com/mpesa/tax/timeout",
942
+ * remarks: "Monthly PAYE remittance",
943
+ * });
944
+ */
945
+ async remitTax(request) {
946
+ const initiator = this.config.initiatorName ?? "";
947
+ if (!initiator) {
948
+ throw new PesafyError({
949
+ code: "VALIDATION_ERROR",
950
+ message: "initiatorName is required for Tax Remittance"
951
+ });
952
+ }
953
+ const [token, securityCred] = await Promise.all([
954
+ this.getToken(),
955
+ this.buildSecurityCredential()
956
+ ]);
957
+ return remitTax(this.baseUrl, token, securityCred, initiator, request);
958
+ }
853
959
  /** Force the cached OAuth token to be refreshed on the next API call */
854
960
  clearTokenCache() {
855
961
  this.tokenManager.clearCache();
@@ -975,9 +1081,11 @@ function isSuccessfulCallback(webhook) {
975
1081
  }
976
1082
 
977
1083
  exports.DARAJA_BASE_URLS = DARAJA_BASE_URLS;
1084
+ exports.KRA_SHORTCODE = KRA_SHORTCODE;
978
1085
  exports.Mpesa = Mpesa;
979
1086
  exports.PesafyError = PesafyError;
980
1087
  exports.SAFARICOM_IPS = SAFARICOM_IPS;
1088
+ exports.TAX_COMMAND_ID = TAX_COMMAND_ID;
981
1089
  exports.acceptC2BValidation = acceptC2BValidation;
982
1090
  exports.acknowledgeC2BConfirmation = acknowledgeC2BConfirmation;
983
1091
  exports.createError = createError;
@@ -1001,6 +1109,7 @@ exports.isSuccessfulCallback = isSuccessfulCallback;
1001
1109
  exports.parseStkPushWebhook = parseStkPushWebhook;
1002
1110
  exports.registerC2BUrls = registerC2BUrls;
1003
1111
  exports.rejectC2BValidation = rejectC2BValidation;
1112
+ exports.remitTax = remitTax;
1004
1113
  exports.retryWithBackoff = retryWithBackoff;
1005
1114
  exports.simulateC2B = simulateC2B;
1006
1115
  exports.verifyWebhookIP = verifyWebhookIP;