From bfecf1c824b97c89fb04f6fb62abfff1ba139ffc Mon Sep 17 00:00:00 2001 From: Samm Date: Sun, 3 Nov 2024 09:02:41 +0300 Subject: [PATCH 01/23] Major library overhaul Updated the mpesa-node library to use latest npm packages. Added new API's from daraja 2.0, new tests with an optional result body, types for typescript users, better babel configuration for seamless es6 mocha tests, and better folder structure --- .babelrc | 4 +- .github/ISSUE_TEMPLATE/bug_report.md | 35 - .github/ISSUE_TEMPLATE/feature_request.md | 17 - .gitignore | 2 +- .prettierrc | 396 + .run/Prettier-format.run.xml | 13 + .run/QR-code-generator.run.xml | 17 + .run/b2b-Topup-Api-Test.run.xml | 14 + .run/b2c-Request-Api-Test.run.xml | 14 + .run/balance-query-Api-Test.run.xml | 14 + .run/business-Paybill-Api-Test.run.xml | 14 + .run/c2b-Register-Api-test.run.xml | 14 + .run/c2b-Simulate-Api-Test.run.xml | 14 + .run/mpesa-Query-Api-Test.run.xml | 14 + .run/mpesa-Simulate-Api-Test.run.xml | 14 + .run/tax-Remmitance-Api-Test.run.xml | 14 + .run/transaction-Status-Api-Test.run.xml | 14 + .travis.yml | 11 +- CODE_OF_CONDUCT.md | 20 +- LICENSE | 2 +- README.md | 254 - eslint.config.js | 8 + mpesa.mjs | 109 + package-lock.json | 8084 +++++++++++++++++ package.json | 55 +- src/api/endpoints/@types/b2b-Topup.d.ts | 32 + src/api/endpoints/@types/b2c-Request.d.ts | 26 + src/api/endpoints/@types/balance-Query.d.ts | 24 + .../endpoints/@types/business-Paybill.d.ts | 34 + src/api/endpoints/@types/c2b-Register.d.ts | 23 + src/api/endpoints/@types/c2b-Simulate.d.ts | 23 + src/api/endpoints/@types/index.d.ts | 194 + src/api/endpoints/@types/mpesa-Query.d.ts | 21 + src/api/endpoints/@types/mpesa-Simulate.d.ts | 29 + src/api/endpoints/@types/qr-Generate.d.ts | 24 + src/api/endpoints/@types/reversals.d.ts | 30 + src/api/endpoints/@types/tax-Remittance.d.ts | 31 + .../endpoints/@types/transaction-Status.d.ts | 31 + src/api/endpoints/b2b-Topup.js | 85 + src/api/endpoints/b2c-Request.js | 89 + src/api/endpoints/balance-Query.js | 75 + src/api/endpoints/business-Paybill.js | 88 + src/api/endpoints/c2b-Register.js | 66 + src/api/endpoints/c2b-Simulate.js | 55 + src/api/endpoints/index.js | 28 + src/api/endpoints/mpesa-Query.js | 58 + src/api/endpoints/mpesa-Simulate.js | 73 + src/api/endpoints/qr-Generate.js | 76 + src/api/endpoints/reversals.js | 84 + src/api/endpoints/tax-Remittance.js | 88 + src/api/endpoints/transaction-Status.js | 82 + src/api/endpoints/utils/constants.js | 26 + src/api/endpoints/utils/helpers.js | 91 + src/endpoints/account-balance.js | 28 - src/endpoints/b2b.js | 37 - src/endpoints/b2c.js | 33 - src/endpoints/c2b-register.js | 21 - src/endpoints/c2b-simulate.js | 23 - src/endpoints/index.js | 23 - src/endpoints/lipa-na-mpesa-online.js | 35 - src/endpoints/lipa-na-mpesa-query.js | 23 - src/endpoints/oauth.js | 10 - src/endpoints/reversal.js | 34 - src/endpoints/transaction-status.js | 33 - src/helpers/index.js | 7 - src/helpers/request.js | 13 - src/helpers/security.js | 15 - src/m-pesa.js | 95 - tests/endpoints/b2b-Topup.spec.js | 43 + tests/endpoints/b2c-Request.spec.js | 45 + tests/endpoints/balance-Query.spec.js | 44 + tests/endpoints/business-Paybill.spec.js | 42 + tests/endpoints/c2b-Register.spec.js | 40 + tests/endpoints/c2b-Simulate.spec.js | 12 + tests/endpoints/mpesa-Query.spec.js | 12 + tests/endpoints/mpesa-Simulate.spec.js | 41 + tests/endpoints/qr-Generate.spec.js | 16 + tests/endpoints/reversals.spec.js | 51 + tests/endpoints/tax-Remittance.spec.js | 41 + tests/endpoints/transaction-status.spec.js | 50 + tests/endpoints/utils/configs.js | 45 + tests/endpoints/utils/helpers.js | 35 + tests/endpoints/utils/init.js | 15 + tests/endpoints/utils/options.js | 191 + tests/endpoints/utils/server.js | 145 + tests/helpers/app.js | 119 - tests/helpers/callbacksemitter.js | 9 - tests/helpers/instance.js | 25 - tests/helpers/ngrok.js | 23 - tests/helpers/tests.env | 7 - tests/integrations/_before.test.js | 11 - tests/integrations/_init.js | 21 - tests/integrations/accountbalance.test.js | 34 - tests/integrations/b2b.test.js | 34 - tests/integrations/b2c.test.js | 35 - tests/integrations/c2b.test.js | 25 - tests/integrations/lipa-na-mpesa.test.js | 34 - tests/integrations/reversal.test.js | 33 - tests/integrations/transactionstatus.test.js | 34 - .../keys/SandboxCertificate.cer | 2 +- tests/unit/index.test.js | 54 - yarn.lock | 4066 --------- 102 files changed, 11283 insertions(+), 5334 deletions(-) create mode 100644 .prettierrc create mode 100644 .run/Prettier-format.run.xml create mode 100644 .run/QR-code-generator.run.xml create mode 100644 .run/b2b-Topup-Api-Test.run.xml create mode 100644 .run/b2c-Request-Api-Test.run.xml create mode 100644 .run/balance-query-Api-Test.run.xml create mode 100644 .run/business-Paybill-Api-Test.run.xml create mode 100644 .run/c2b-Register-Api-test.run.xml create mode 100644 .run/c2b-Simulate-Api-Test.run.xml create mode 100644 .run/mpesa-Query-Api-Test.run.xml create mode 100644 .run/mpesa-Simulate-Api-Test.run.xml create mode 100644 .run/tax-Remmitance-Api-Test.run.xml create mode 100644 .run/transaction-Status-Api-Test.run.xml delete mode 100644 README.md create mode 100644 eslint.config.js create mode 100644 mpesa.mjs create mode 100644 package-lock.json create mode 100644 src/api/endpoints/@types/b2b-Topup.d.ts create mode 100644 src/api/endpoints/@types/b2c-Request.d.ts create mode 100644 src/api/endpoints/@types/balance-Query.d.ts create mode 100644 src/api/endpoints/@types/business-Paybill.d.ts create mode 100644 src/api/endpoints/@types/c2b-Register.d.ts create mode 100644 src/api/endpoints/@types/c2b-Simulate.d.ts create mode 100644 src/api/endpoints/@types/index.d.ts create mode 100644 src/api/endpoints/@types/mpesa-Query.d.ts create mode 100644 src/api/endpoints/@types/mpesa-Simulate.d.ts create mode 100644 src/api/endpoints/@types/qr-Generate.d.ts create mode 100644 src/api/endpoints/@types/reversals.d.ts create mode 100644 src/api/endpoints/@types/tax-Remittance.d.ts create mode 100644 src/api/endpoints/@types/transaction-Status.d.ts create mode 100644 src/api/endpoints/b2b-Topup.js create mode 100644 src/api/endpoints/b2c-Request.js create mode 100644 src/api/endpoints/balance-Query.js create mode 100644 src/api/endpoints/business-Paybill.js create mode 100644 src/api/endpoints/c2b-Register.js create mode 100644 src/api/endpoints/c2b-Simulate.js create mode 100644 src/api/endpoints/index.js create mode 100644 src/api/endpoints/mpesa-Query.js create mode 100644 src/api/endpoints/mpesa-Simulate.js create mode 100644 src/api/endpoints/qr-Generate.js create mode 100644 src/api/endpoints/reversals.js create mode 100644 src/api/endpoints/tax-Remittance.js create mode 100644 src/api/endpoints/transaction-Status.js create mode 100644 src/api/endpoints/utils/constants.js create mode 100644 src/api/endpoints/utils/helpers.js delete mode 100644 src/endpoints/account-balance.js delete mode 100644 src/endpoints/b2b.js delete mode 100644 src/endpoints/b2c.js delete mode 100644 src/endpoints/c2b-register.js delete mode 100644 src/endpoints/c2b-simulate.js delete mode 100644 src/endpoints/index.js delete mode 100644 src/endpoints/lipa-na-mpesa-online.js delete mode 100644 src/endpoints/lipa-na-mpesa-query.js delete mode 100644 src/endpoints/oauth.js delete mode 100644 src/endpoints/reversal.js delete mode 100644 src/endpoints/transaction-status.js delete mode 100644 src/helpers/index.js delete mode 100644 src/helpers/request.js delete mode 100644 src/helpers/security.js delete mode 100644 src/m-pesa.js create mode 100644 tests/endpoints/b2b-Topup.spec.js create mode 100644 tests/endpoints/b2c-Request.spec.js create mode 100644 tests/endpoints/balance-Query.spec.js create mode 100644 tests/endpoints/business-Paybill.spec.js create mode 100644 tests/endpoints/c2b-Register.spec.js create mode 100644 tests/endpoints/c2b-Simulate.spec.js create mode 100644 tests/endpoints/mpesa-Query.spec.js create mode 100644 tests/endpoints/mpesa-Simulate.spec.js create mode 100644 tests/endpoints/qr-Generate.spec.js create mode 100644 tests/endpoints/reversals.spec.js create mode 100644 tests/endpoints/tax-Remittance.spec.js create mode 100644 tests/endpoints/transaction-status.spec.js create mode 100644 tests/endpoints/utils/configs.js create mode 100644 tests/endpoints/utils/helpers.js create mode 100644 tests/endpoints/utils/init.js create mode 100644 tests/endpoints/utils/options.js create mode 100644 tests/endpoints/utils/server.js delete mode 100644 tests/helpers/app.js delete mode 100644 tests/helpers/callbacksemitter.js delete mode 100644 tests/helpers/instance.js delete mode 100644 tests/helpers/ngrok.js delete mode 100644 tests/helpers/tests.env delete mode 100644 tests/integrations/_before.test.js delete mode 100644 tests/integrations/_init.js delete mode 100644 tests/integrations/accountbalance.test.js delete mode 100644 tests/integrations/b2b.test.js delete mode 100644 tests/integrations/b2c.test.js delete mode 100644 tests/integrations/c2b.test.js delete mode 100644 tests/integrations/lipa-na-mpesa.test.js delete mode 100644 tests/integrations/reversal.test.js delete mode 100644 tests/integrations/transactionstatus.test.js rename keys/sandbox-cert.cer => tests/keys/SandboxCertificate.cer (98%) delete mode 100644 tests/unit/index.test.js delete mode 100644 yarn.lock diff --git a/.babelrc b/.babelrc index 2f01e1d..1320b9a 100644 --- a/.babelrc +++ b/.babelrc @@ -1,3 +1,3 @@ { - "presets": ["env"] -} \ No newline at end of file + "presets": ["@babel/preset-env"] +} diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index b735373..e69de29 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -1,35 +0,0 @@ ---- -name: Bug report -about: Create a report to help us improve - ---- - -**Describe the bug** -A clear and concise description of what the bug is. - -**To Reproduce** -Steps to reproduce the behavior: -1. Go to '...' -2. Click on '....' -3. Scroll down to '....' -4. See error - -**Expected behavior** -A clear and concise description of what you expected to happen. - -**Screenshots** -If applicable, add screenshots to help explain your problem. - -**Desktop (please complete the following information):** - - OS: [e.g. iOS] - - Browser [e.g. chrome, safari] - - Version [e.g. 22] - -**Smartphone (please complete the following information):** - - Device: [e.g. iPhone6] - - OS: [e.g. iOS8.1] - - Browser [e.g. stock browser, safari] - - Version [e.g. 22] - -**Additional context** -Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 066b2d9..e69de29 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -1,17 +0,0 @@ ---- -name: Feature request -about: Suggest an idea for this project - ---- - -**Is your feature request related to a problem? Please describe.** -A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] - -**Describe the solution you'd like** -A clear and concise description of what you want to happen. - -**Describe alternatives you've considered** -A clear and concise description of any alternative solutions or features you've considered. - -**Additional context** -Add any other context or screenshots about the feature request here. diff --git a/.gitignore b/.gitignore index 19375eb..1a5f19d 100644 --- a/.gitignore +++ b/.gitignore @@ -49,7 +49,7 @@ typings/ .yarn-integrity # dotenv environment variables file -.env +tests/.env.local # yarn error-log yarn-error.log diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..a99bb25 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,396 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://json.schemastore.org/prettierrc.json", + "definitions": { + "optionsDefinition": { + "type": "object", + "properties": { + "arrowParens": { + "description": "Include parentheses around a sole arrow function parameter.", + "default": "always", + "oneOf": [ + { + "enum": ["always"], + "description": "Always include parens. Example: `(x) => x`" + }, + { + "enum": ["avoid"], + "description": "Omit parens when possible. Example: `x => x`" + } + ] + }, + "bracketSameLine": { + "description": "Put > of opening tags on the last line instead of on a new line.", + "default": false, + "type": "boolean" + }, + "bracketSpacing": { + "description": "Print spaces between brackets.", + "default": true, + "type": "boolean" + }, + "cursorOffset": { + "description": "Print (to stderr) where a cursor at the given position would move to after formatting.", + "default": -1, + "type": "integer" + }, + "embeddedLanguageFormatting": { + "description": "Control how Prettier formats quoted code embedded in the file.", + "default": "auto", + "oneOf": [ + { + "enum": ["auto"], + "description": "Format embedded code if Prettier can automatically identify it." + }, + { + "enum": ["off"], + "description": "Never automatically format embedded code." + } + ] + }, + "endOfLine": { + "description": "Which end of line characters to apply.", + "default": "lf", + "oneOf": [ + { + "enum": ["lf"], + "description": "Line Feed only (\\n), common on Linux and macOS as well as inside git repos" + }, + { + "enum": ["crlf"], + "description": "Carriage Return + Line Feed characters (\\r\\n), common on Windows" + }, + { + "enum": ["cr"], + "description": "Carriage Return character only (\\r), used very rarely" + }, + { + "enum": ["auto"], + "description": "Maintain existing\n(mixed values within one file are normalised by looking at what's used after the first line)" + } + ] + }, + "experimentalTernaries": { + "description": "Use curious ternaries, with the question mark after the condition.", + "default": false, + "type": "boolean" + }, + "filepath": { + "description": "Specify the input filepath. This will be used to do parser inference.", + "type": "string" + }, + "htmlWhitespaceSensitivity": { + "description": "How to handle whitespaces in HTML.", + "default": "css", + "oneOf": [ + { + "enum": ["css"], + "description": "Respect the default value of CSS display property." + }, + { + "enum": ["strict"], + "description": "Whitespaces are considered sensitive." + }, + { + "enum": ["ignore"], + "description": "Whitespaces are considered insensitive." + } + ] + }, + "insertPragma": { + "description": "Insert @format pragma into file's first docblock comment.", + "default": false, + "type": "boolean" + }, + "jsxSingleQuote": { + "description": "Use single quotes in JSX.", + "default": false, + "type": "boolean" + }, + "parser": { + "description": "Which parser to use.", + "anyOf": [ + { + "enum": ["flow"], + "description": "Flow" + }, + { + "enum": ["babel"], + "description": "JavaScript" + }, + { + "enum": ["babel-flow"], + "description": "Flow" + }, + { + "enum": ["babel-ts"], + "description": "TypeScript" + }, + { + "enum": ["typescript"], + "description": "TypeScript" + }, + { + "enum": ["acorn"], + "description": "JavaScript" + }, + { + "enum": ["espree"], + "description": "JavaScript" + }, + { + "enum": ["meriyah"], + "description": "JavaScript" + }, + { + "enum": ["css"], + "description": "CSS" + }, + { + "enum": ["less"], + "description": "Less" + }, + { + "enum": ["scss"], + "description": "SCSS" + }, + { + "enum": ["json"], + "description": "JSON" + }, + { + "enum": ["json5"], + "description": "JSON5" + }, + { + "enum": ["jsonc"], + "description": "JSON with Comments" + }, + { + "enum": ["json-stringify"], + "description": "JSON.stringify" + }, + { + "enum": ["graphql"], + "description": "GraphQL" + }, + { + "enum": ["markdown"], + "description": "Markdown" + }, + { + "enum": ["mdx"], + "description": "MDX" + }, + { + "enum": ["vue"], + "description": "Vue" + }, + { + "enum": ["yaml"], + "description": "YAML" + }, + { + "enum": ["glimmer"], + "description": "Ember / Handlebars" + }, + { + "enum": ["html"], + "description": "HTML" + }, + { + "enum": ["angular"], + "description": "Angular" + }, + { + "enum": ["lwc"], + "description": "Lightning Web Components" + }, + { + "type": "string", + "description": "Custom parser" + } + ] + }, + "plugins": { + "description": "Add a plugin. Multiple plugins can be passed as separate `--plugin`s.", + "default": [], + "type": "array", + "items": { + "type": "string" + } + }, + "printWidth": { + "description": "The line length where Prettier will try wrap.", + "default": 80, + "type": "integer" + }, + "proseWrap": { + "description": "How to wrap prose.", + "default": "preserve", + "oneOf": [ + { + "enum": ["always"], + "description": "Wrap prose if it exceeds the print width." + }, + { + "enum": ["never"], + "description": "Do not wrap prose." + }, + { + "enum": ["preserve"], + "description": "Wrap prose as-is." + } + ] + }, + "quoteProps": { + "description": "Change when properties in objects are quoted.", + "default": "as-needed", + "oneOf": [ + { + "enum": ["as-needed"], + "description": "Only add quotes around object properties where required." + }, + { + "enum": ["consistent"], + "description": "If at least one property in an object requires quotes, quote all properties." + }, + { + "enum": ["preserve"], + "description": "Respect the input use of quotes in object properties." + } + ] + }, + "rangeEnd": { + "description": "Format code ending at a given character offset (exclusive).\nThe range will extend forwards to the end of the selected statement.", + "default": null, + "type": "integer" + }, + "rangeStart": { + "description": "Format code starting at a given character offset.\nThe range will extend backwards to the start of the first line containing the selected statement.", + "default": 0, + "type": "integer" + }, + "requirePragma": { + "description": "Require either '@prettier' or '@format' to be present in the file's first docblock comment\nin order for it to be formatted.", + "default": false, + "type": "boolean" + }, + "semi": { + "description": "Print semicolons.", + "default": true, + "type": "boolean" + }, + "singleAttributePerLine": { + "description": "Enforce single attribute per line in HTML, Vue and JSX.", + "default": false, + "type": "boolean" + }, + "singleQuote": { + "description": "Use single quotes instead of double quotes.", + "default": false, + "type": "boolean" + }, + "tabWidth": { + "description": "Number of spaces per indentation level.", + "default": 2, + "type": "integer" + }, + "trailingComma": { + "description": "Print trailing commas wherever possible when multi-line.", + "default": "all", + "oneOf": [ + { + "enum": ["all"], + "description": "Trailing commas wherever possible (including function arguments)." + }, + { + "enum": ["es5"], + "description": "Trailing commas where valid in ES5 (objects, arrays, etc.)" + }, + { + "enum": ["none"], + "description": "No trailing commas." + } + ] + }, + "useTabs": { + "description": "Indent with tabs instead of spaces.", + "default": false, + "type": "boolean" + }, + "vueIndentScriptAndStyle": { + "description": "Indent script and style tags in Vue files.", + "default": false, + "type": "boolean" + } + } + }, + "overridesDefinition": { + "type": "object", + "properties": { + "overrides": { + "type": "array", + "description": "Provide a list of patterns to override prettier configuration.", + "items": { + "type": "object", + "required": ["files"], + "properties": { + "files": { + "description": "Include these files in this override.", + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + }, + "excludeFiles": { + "description": "Exclude these files from this override.", + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + }, + "options": { + "$ref": "#/definitions/optionsDefinition", + "type": "object", + "description": "The options to apply for this override." + } + }, + "additionalProperties": false + } + } + } + } + }, + "oneOf": [ + { + "type": "object", + "allOf": [ + { + "$ref": "#/definitions/optionsDefinition" + }, + { + "$ref": "#/definitions/overridesDefinition" + } + ] + }, + { + "type": "string" + } + ], + "title": "Schema for .prettierrc" +} diff --git a/.run/Prettier-format.run.xml b/.run/Prettier-format.run.xml new file mode 100644 index 0000000..96f06ee --- /dev/null +++ b/.run/Prettier-format.run.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + + + + + + + + + +
+ +

b2c-Request.js

+ + + + + + + +
+
+
import axios from "axios";
+import { CommandIDs } from "../utils/constants.js";
+import {
+  callbackTrigger,
+  encryptSecurityCredential,
+  generateOAuthToken,
+  handleCallbacks,
+  logErrorDetails,
+  originatorID,
+  throwErrorMessages, validateFormatPhone,
+  validateUrl
+} from "../utils/helpers.js";
+
+// Globals
+let callbackHandlerInitialized = false;
+let conditionalCallbackData = {};
+
+/**
+ * @name b2cRequest
+ * @description B2C API can be used in several scenarios by businesses that require to either make Salary Payments, Cashback payments, Promotional Payments(e.g. betting winning payouts), winnings, financial institutions withdrawal of funds, loan disbursements, etc.
+ * @summary B2C payments involve a business sending money to an individual. This is a direct transaction from a business shortcode to a consumer's mobile number (MSISDN).
+ * @see {@link https://developer.safaricom.co.ke/APIs/BusinessToCustomer open external link}
+ * @param {Object} options Options for the B2C payment request.
+ * @param {number} options.partyA The B2C organization shortcode sending the money.
+ * @param {string} options.partyB Customer mobile number (with country code, e.g., 254).
+ * @param {number} options.amount The amount being transacted.
+ * @param {string} options.remarks Information to be associated with the transaction.
+ * @param {string} options.occasion Information to be associated with the transaction.
+ * @param {string} options.QueueTimeOutURL URL for timeout notifications.
+ * @param {string} options.resultUrl URL for M-PESA to send payment processing notifications.
+ * @param {string} options.commandId Unique command specifying B2C transaction type (e.g., BusinessPayment).
+ * @param {string} options.initiatorName API user created by Business Administrator for B2C transactions.
+ * @param {boolean} [options.proErrorLogging=false] Logs out advanced error details - good for debugging.
+ * @return {Promise<object>} b2cRequestResponse and (optional) conditionalCallbackData. */
+async function b2cRequest({
+  partyA,
+  partyB,
+  amount,
+  QueueTimeOutURL,
+  remarks,
+  occasion,
+  resultUrl,
+  commandId,
+  initiatorName = "",
+  proErrorLogging = false,
+}) {
+  if (!callbackHandlerInitialized) {
+    console.info(
+      "\x1b[42m\x1b[30m\x1b[1m The default balance query callback handler ('handleB2cRequestCallbacks') was not called. Ignore if handling manually.\x1b[0m",
+    );
+  }
+  /**
+   * @param {string} validCommandIds - loops through object values with strings "SalaryPayment", "BusinessPayment" PromotionPayment"
+   */
+  const validCommandIds = Object.values(CommandIDs);
+  if (!validCommandIds.includes(commandId)) {
+    throw new Error(
+      `Invalid commandId provided. Must be one of: ${validCommandIds.join(", ")}`,
+    );
+  }
+  const _msisdn = validateFormatPhone(partyB)
+  try {
+    validateUrl(resultUrl, "resultUrl");
+    validateUrl(QueueTimeOutURL, "queueTimeOutUrl");
+    const OriginatorConversationID = originatorID();
+    const { accessToken, baseURL } = await generateOAuthToken();
+
+    const req = axios.create({
+      baseURL,
+      headers: {
+        Authorization: `Bearer ${accessToken}`,
+        "Content-Type": "application/json",
+      },
+    });
+
+    const responseBody = await req.post("/mpesa/b2c/v3/paymentrequest", {
+      OriginatorConversationID,
+      InitiatorName: initiatorName,
+      SecurityCredential: encryptSecurityCredential(),
+      CommandID: commandId,
+      Amount: amount,
+      PartyA: partyA,
+      PartyB: _msisdn,
+      Remarks: remarks,
+      QueueTimeOutURL: QueueTimeOutURL,
+      ResultURL: resultUrl,
+      Occasion: occasion
+    });
+
+    conditionalCallbackData = await callbackTrigger(callbackHandlerInitialized);
+
+    return { b2cRequestResponse: responseBody.data, conditionalCallbackData};
+  } catch (error) {
+    if (proErrorLogging) {
+      console.info(
+        "\x1b[35m%s\x1b[0m",
+        "Advanced error logging for b2cRequest has been initialized",
+      );
+      logErrorDetails(
+        error,
+        {
+          apiEndpoint: "/mpesa/b2c/v3/paymentrequest",
+          method: "POST",
+          payload: {
+            partyA,
+            _msisdn,
+            amount,
+            QueueTimeOutURL,
+            resultUrl,
+            commandId,
+            initiatorName,
+            occasion,
+            remarks
+          },
+        },
+        "B2C transaction error details:",
+      );
+    }
+    throw throwErrorMessages(error);
+  }
+}
+const handleB2cRequestCallbacks = async (app) => {
+  callbackHandlerInitialized = true;
+  await handleCallbacks(app, "b2cRequest");
+};
+export { b2cRequest, handleB2cRequestCallbacks };
+
+
+
+ + + + +
+ +
+ +
+ Generated by JSDoc 4.0.4 on Thu Feb 27 2025 21:37:15 GMT+0300 (East Africa Time) using the Minami theme. +
+ + + + + diff --git a/docs/b2c-Topup.js.html b/docs/b2c-Topup.js.html new file mode 100644 index 0000000..5277d13 --- /dev/null +++ b/docs/b2c-Topup.js.html @@ -0,0 +1,182 @@ + + + + + + b2c-Topup.js - Documentation + + + + + + + + + + + + + + + + + +
+ +

b2c-Topup.js

+ + + + + + + +
+
+
import axios from "axios";
+import {
+  generateOAuthToken,
+  encryptSecurityCredential,
+  throwErrorMessages,
+  logErrorDetails,
+  callbackTrigger,
+  handleCallbacks,
+  validateUrl,
+} from "../utils/helpers.js";
+
+// Globals
+let callbackHandlerInitialized = false;
+let conditionalCallbackData = {};
+
+/**
+ *@name b2cTopUp
+ *@description This API enables you to load funds to a B2C shortcode directly for disbursement. The transaction moves money from your MMF/Working account to the recipient’s utility account.
+ *@summary Transfers funds from your MMF/Working account to the recipient's utility account for disbursement to a B2C shortcode.
+ * @see {@link https://developer.safaricom.co.ke/APIs/B2CAccountTopUp open external link}
+ * @param {Object} options B2C account top-up request options.
+ * @param {string} options.initiator The M-Pesa API operator username, who needs Org Business Pay to Bulk API initiator role.
+ * @param {number} options.amount The transaction amount.
+ * @param {number} options.partyA Your shortcode. The shortcode from which money will be deducted.
+ * @param {number} options.partyB The shortcode to which money will be moved
+ * @param {string} options.remarks Information to be associated with the transaction.
+ * @param {number} options.accountReference Identifier for the transaction.
+ * @param {number} [options.requester] Optional. The consumer’s mobile number on behalf of whom you are paying.
+ * @param {string} options.QueueTimeOutURL A URL that will be used to notify your system in case the request times out.
+ * @param {string} options.resultUrl A URL that will be used to send transaction results after processing.
+ * @param {boolean} [options.proErrorLogging] Logs out advanced error details - good for debugging
+ * @return {Promise<object>} b2cTopUpResponse and (optional) conditionalCallbackData. */
+async function b2cTopUp({
+  initiator,
+  amount,
+  partyA,
+  partyB,
+  accountReference,
+  requester,
+  QueueTimeOutURL,
+  resultUrl,
+  remarks,
+  proErrorLogging = false,
+}) {
+  if (!callbackHandlerInitialized) {
+    console.info(
+      "\x1b[42m\x1b[30m\x1b[1m The default callback handler ('handleB2cTopUpCallbacks') was not called. Ignore if handling manually.\x1b[0m",
+    );
+  }
+  try {
+    validateUrl(resultUrl, "resultUrl");
+    validateUrl(QueueTimeOutURL, "QueueTimeOutUrl");
+    const { accessToken, baseURL } = await generateOAuthToken();
+    const req = axios.create({
+      baseURL,
+      headers: {
+        Authorization: `Bearer ${accessToken}`,
+        "Content-Type": "application/json",
+      },
+    });
+
+    // Default configurations
+    const config = {
+      commandId: "BusinessPayToBulk",
+      senderIdentifierType: "4",
+      receiverIdentifierType: "4",
+    };
+
+    const responseBody = await req.post("mpesa/b2b/v1/paymentrequest", {
+      Initiator: initiator,
+      SecurityCredential: encryptSecurityCredential(),
+      CommandID: config.commandId,
+      SenderIdentifierType: config.senderIdentifierType,
+      RecieverIdentifierType: config.receiverIdentifierType,
+      Amount: amount,
+      PartyA: partyA,
+      PartyB: partyB,
+      AccountReference: accountReference,
+      Requester: requester,
+      Remarks: remarks,
+      QueueTimeOutURL: QueueTimeOutURL,
+      ResultURL: resultUrl,
+    });
+
+    conditionalCallbackData = await callbackTrigger(callbackHandlerInitialized);
+
+    return { b2cTopUpResponse: responseBody.data, conditionalCallbackData };
+  } catch (error) {
+    if (proErrorLogging) {
+      console.info(
+        "\x1b[35m%s\x1b[0m",
+        "Advanced error logging for b2cTopUp has been initialized",
+      );
+      logErrorDetails(
+        error,
+        {
+          apiEndpoint: "/mpesa/b2b/v1/paymentrequest",
+          method: "POST",
+          payload: {
+            initiator,
+            amount,
+            partyA,
+            partyB,
+            accountReference,
+            requester,
+            QueueTimeOutURL,
+            resultUrl,
+            remarks
+          },
+        },
+        "B2C account top-up error details:",
+      );
+    }
+    throw throwErrorMessages(error);
+  }
+}
+
+const handleB2cTopUpCallbacks = async (app) => {
+  callbackHandlerInitialized = true;
+  await handleCallbacks(app, "b2cTopUp");
+};
+export { b2cTopUp, handleB2cTopUpCallbacks };
+
+
+
+ + + + +
+ +
+ +
+ Generated by JSDoc 4.0.4 on Thu Feb 27 2025 21:37:15 GMT+0300 (East Africa Time) using the Minami theme. +
+ + + + + diff --git a/docs/balance-Query.js.html b/docs/balance-Query.js.html new file mode 100644 index 0000000..bfc4b4d --- /dev/null +++ b/docs/balance-Query.js.html @@ -0,0 +1,173 @@ + + + + + + balance-Query.js - Documentation + + + + + + + + + + + + + + + + + +
+ +

balance-Query.js

+ + + + + + + +
+
+
import axios from "axios";
+import {
+  callbackTrigger,
+  encryptSecurityCredential,
+  generateOAuthToken,
+  handleCallbacks,
+  logErrorDetails,
+  throwErrorMessages,
+  validateUrl,
+} from "../utils/helpers.js";
+import { IdentifierTypes } from "../utils/constants.js";
+
+// Globals
+let callbackHandlerInitialized = false;
+let conditionalCallbackData = {};
+
+/**
+ * @name balanceQuery
+ * @description The Account Balance API is used to request the account balance of a short code. This can be used for both B2C, buy goods and pay bill accounts.
+ * @summary Retrieves the balance of a short code associated with the developer account, supporting B2C, buy goods, and pay bill accounts.
+ * @see {@link https://developer.safaricom.co.ke/APIs/AccountBalance open external link}
+ * @param {Object} options Options for the balance query API.
+ * @param {number} options.partyA The shortcode of the querying organization.
+ * @param {number} options.identifierType Type of the querying organization.
+ * @param {string} options.remarks Information to be associated with the transaction.
+ * @param {String} options.QueueTimeOutURL URL to receive timeout messages.
+ * @param {String} options.resultUrl URL to receive the result message.
+ * @param {String} options.initiator The credential/username for authentication.
+ * @param {boolean} [options.proErrorLogging=false] Logs out advanced error details - good for debugging
+ * @return {Promise<object>} balanceQueryResponse and (optional) conditionalCallbackData.
+ */
+async function balanceQuery({
+  partyA,
+  identifierType,
+  QueueTimeOutURL,
+  resultUrl,
+  initiator,
+  remarks,
+  proErrorLogging = false,
+}) {
+  if (!callbackHandlerInitialized) {
+    console.info(
+      "\x1b[42m\x1b[30m\x1b[1m The default balance query callback handler ('handleBalanceQueryCallbacks') was not called. Ignore if handling manually.\x1b[0m"
+    );
+  }
+  /**
+   * @param {number} validIdentifierTypes - Expected identifiers include const IdentifierTypes = { MSISDN: 1, TILL_NUMBER: 2, ORG_SHORTCODE: 4 };
+   */
+  const validIdentifierTypes = Object.values(IdentifierTypes);
+  if (!validIdentifierTypes.includes(identifierType)) {
+    throw new Error(
+      `Invalid identifierType provided. Must be one of: ${validIdentifierTypes.join(", ")}`,
+    );
+  }
+  try {
+    validateUrl(resultUrl, "resultUrl");
+    validateUrl(QueueTimeOutURL, "timeoutUrl");
+    const { accessToken, baseURL } = await generateOAuthToken();
+
+    const req = axios.create({
+      baseURL,
+      headers: {
+        Authorization: `Bearer ${accessToken}`,
+        "Content-Type": "application/json",
+      },
+    });
+
+    // Default configurations
+    const config = {
+      commandId: "AccountBalance",
+      remarks: "OK",
+    };
+
+    const responseBody = await req.post("/mpesa/accountbalance/v1/query", {
+      Initiator: initiator,
+      SecurityCredential: encryptSecurityCredential(),
+      CommandID: config.commandId,
+      PartyA: partyA,
+      IdentifierType: identifierType,
+      Remarks: remarks,
+      QueueTimeOutURL: QueueTimeOutURL,
+      ResultURL: resultUrl,
+    });
+
+    conditionalCallbackData = await callbackTrigger(callbackHandlerInitialized);
+
+    return { balanceQueryResponse: responseBody.data, conditionalCallbackData };
+  } catch (error) {
+    if (proErrorLogging) {
+      console.info(
+        "\x1b[35m%s\x1b[0m",
+        "Advanced error logging for balanceQuery has been initialized",
+      );
+      logErrorDetails(
+        error,
+        {
+          apiEndpoint: "/mpesa/accountbalance/v1/query",
+          method: "POST",
+          payload: { partyA, identifierType, QueueTimeOutURL, resultUrl, initiator, remarks },
+        },
+        "Balance query error details:",
+      );
+    }
+    throw await throwErrorMessages(error);
+  }
+}
+
+const handleBalanceQueryCallbacks = async (app) => {
+  callbackHandlerInitialized = true;
+  await handleCallbacks(app, "balanceQuery");
+};
+
+export { balanceQuery, handleBalanceQueryCallbacks };
+
+
+
+ + + + +
+ +
+ +
+ Generated by JSDoc 4.0.4 on Thu Feb 27 2025 21:37:15 GMT+0300 (East Africa Time) using the Minami theme. +
+ + + + + diff --git a/docs/business-Paybill.js.html b/docs/business-Paybill.js.html new file mode 100644 index 0000000..e012532 --- /dev/null +++ b/docs/business-Paybill.js.html @@ -0,0 +1,185 @@ + + + + + + business-Paybill.js - Documentation + + + + + + + + + + + + + + + + + +
+ +

business-Paybill.js

+ + + + + + + +
+
+
import axios from "axios";
+import {
+  callbackTrigger,
+  encryptSecurityCredential,
+  generateOAuthToken,
+  handleCallbacks,
+  logErrorDetails,
+  throwErrorMessages,
+  validateUrl,
+} from "../utils/helpers.js";
+
+// Globals
+let callbackHandlerInitialized = false;
+let conditionalCallbackData = {};
+
+/**
+ * @name businessPaybill
+ * @description This API enables you to pay bills directly from your business account to a pay bill number, or a paybill store. You can use this API to pay on behalf of a consumer/requester. The transaction moves money from your MMF/Working account to the recipient’s utility account.
+ * @summary  Facilitates bill payments from a business account to a paybill number, transferring funds to the recipient’s utility account.
+ * @see {@link https://developer.safaricom.co.ke/APIs/BusinessPayBill open external link}
+ * @param {Object} options Options for the business pay bill.
+ * @param {string} options.initiator M-Pesa API operator username with Org Business Pay Bill API role.
+ * @param {number} options.amount Transaction amount.
+ * @param {number} options.partyA Shortcode from which money will be deducted.
+ * @param {number} options.partyB Shortcode to which money will be moved.
+ * @param {string} options.remarks Information to be associated with the transaction.
+ * @param {number} options.accountReference Account number for the payment (up to 13 characters).
+ * @param {number} [options.requester] Optional consumer’s mobile number (if paying on their behalf).
+ * @param {string} options.QueueTimeOutURL URL for timeout notifications.
+ * @param {string} options.resultUrl URL for sending transaction results after processing.
+ * @param {boolean} [options.proErrorLogging] Logs out advanced error details - good for debugging
+ * @return {Promise<Object>} businessPaybillResponse and (optional) conditionalCallbackData.
+ */
+async function businessPaybill({
+  initiator,
+  amount,
+  partyA,
+  partyB,
+  remarks,
+  accountReference,
+  requester,
+  QueueTimeOutURL,
+  resultUrl,
+  proErrorLogging = false,
+}) {
+  if (!callbackHandlerInitialized) {
+    console.info(
+      "\x1b[42m\x1b[30m\x1b[1m The default callback handler ('handleBusinessPaybillCallbacks') was not called. Ignore if handling manually.\x1b[0m",
+    );
+  }
+  try {
+    validateUrl(resultUrl, "resultUrl");
+    validateUrl(QueueTimeOutURL, "timeoutUrl");
+    const { accessToken, baseURL } = await generateOAuthToken();
+    const req = axios.create({
+      baseURL,
+      headers: {
+        Authorization: `Bearer ${accessToken}`,
+        "Content-Type": "application/json",
+      },
+    });
+    // Default configurations
+    const config = {
+      commandId: "BusinessPayBill",
+      senderIdentifierType: "4",
+      receiverIdentifierType: "4",
+    };
+    const responseBody = await req.post("/mpesa/b2b/v1/paymentrequest", {
+      Initiator: initiator,
+      SecurityCredential: encryptSecurityCredential(),
+      CommandID: config.commandId,
+      SenderIdentifierType: config.senderIdentifierType,
+      RecieverIdentifierType: config.receiverIdentifierType,
+      Amount: amount,
+      PartyA: partyA,
+      PartyB: partyB,
+      AccountReference: accountReference,
+      Requester: requester,
+      Remarks: remarks,
+      QueueTimeOutURL: QueueTimeOutURL,
+      ResultURL: resultUrl,
+    });
+
+    conditionalCallbackData = await callbackTrigger(callbackHandlerInitialized);
+
+    return {
+      businessPaybillResponse: responseBody.data,
+      conditionalCallbackData,
+    };
+  } catch (error) {
+    if (proErrorLogging) {
+      console.info(
+        "\x1b[35m%s\x1b[0m",
+        "Advanced error logging for businessPaybill has been initialized",
+      );
+      logErrorDetails(
+        error,
+        {
+          apiEndpoint: "/mpesa/b2b/v1/paymentrequest",
+          method: "POST",
+          payload: {
+            initiator,
+            amount,
+            partyA,
+            partyB,
+            accountReference,
+            requester,
+            QueueTimeOutURL,
+            resultUrl,
+            remarks
+          },
+        },
+        "Business Pay Bill transaction error details:",
+      );
+    }
+    throw throwErrorMessages(error);
+  }
+}
+
+const handleBusinessPaybillCallbacks = async (app) => {
+  callbackHandlerInitialized = true;
+  await handleCallbacks(app, "businessPaybill");
+};
+
+export { businessPaybill, handleBusinessPaybillCallbacks };
+
+
+
+ + + + +
+ +
+ +
+ Generated by JSDoc 4.0.4 on Thu Feb 27 2025 21:37:15 GMT+0300 (East Africa Time) using the Minami theme. +
+ + + + + diff --git a/docs/c2b-Register.js.html b/docs/c2b-Register.js.html new file mode 100644 index 0000000..862f5c9 --- /dev/null +++ b/docs/c2b-Register.js.html @@ -0,0 +1,159 @@ + + + + + + c2b-Register.js - Documentation + + + + + + + + + + + + + + + + + +
+ +

c2b-Register.js

+ + + + + + + +
+
+
import axios from "axios";
+import {
+  callbackTrigger,
+  generateOAuthToken,
+  handleCallbacks,
+  logErrorDetails,
+  throwErrorMessages,
+  validateUrl,
+} from "../utils/helpers.js";
+import { responseTypes } from "../utils/constants.js";
+
+// Globals
+let callbackHandlerInitialized = false;
+let conditionalCallbackData = {};
+
+/**
+ * @name C2BRegister
+ * @description Register URL API works hand in hand with Customer to Business (C2B) APIs and allows receiving payment notifications to your paybill. This API enables you to register the callback URLs via which you shall receive notifications for payments to your pay bill/till numbers
+ * @summary Registers callback validation and confirmation URLs on M-Pesa to receive payment notifications for your paybill/till numbers.
+ * @see {@link https://developer.safaricom.co.ke/APIs/CustomerToBusinessRegisterURL open external link}
+ * @param {Object} options Options for the C2B Register URL.
+ * @param {string} options.confirmationUrl URL to receive confirmation upon payment completion.
+ * @param {string} options.validationUrl URL to receive validation upon payment submission (default: external validation disabled).
+ * @param {number} options.shortCode Unique M-PESA pay bill/till number.
+ * @param {string} options.responseType Action if validation URL is unreachable (values: Completed or Cancelled).
+ * @param {boolean} [options.proErrorLogging] Logs out detailed errors for debugging purposes
+ * @return {Promise<Object>} c2bRegisterResponse and (optional) conditionalCallbackData.
+ */
+async function c2bRegister({
+  confirmationUrl,
+  validationUrl,
+  shortCode,
+  responseType,
+  proErrorLogging = false,
+}) {
+  if (!callbackHandlerInitialized) {
+    console.info(
+      "\x1b[42m\x1b[30m\x1b[1m The default balance query callback handler ('handleC2bRegisterCallbacks') was not called. Ignore if handling manually.\x1b[0m",
+    );
+  }
+  /**
+   * @param {string} validResponseTypes - Should either be Completed or Cancelled
+   */
+  const validResponseTypes = Object.values(responseTypes);
+  if (!validResponseTypes.includes(responseType)) {
+    throw new Error(
+      `Invalid responseType provided. Must be one of: ${validResponseTypes.join(", ")}`,
+    );
+  }
+  try {
+    validateUrl(confirmationUrl, "confirmationUrl");
+    validateUrl(validationUrl, "validationUrl");
+    const { accessToken, baseURL } = await generateOAuthToken();
+    const req = axios.create({
+      baseURL,
+      headers: {
+        Authorization: `Bearer ${accessToken}`,
+        "Content-Type": "application/json",
+      },
+    });
+    const responseBody = await req.post("/mpesa/c2b/v1/registerurl", {
+      ShortCode: shortCode,
+      ResponseType: responseType,
+      ConfirmationURL: confirmationUrl,
+      ValidationURL: validationUrl,
+    });
+    conditionalCallbackData = await callbackTrigger(callbackHandlerInitialized);
+    return { c2bRegisterResponse: responseBody.data, conditionalCallbackData };
+  } catch (error) {
+    if (proErrorLogging) {
+      console.info(
+        "\x1b[35m%s\x1b[0m",
+        "Advanced error logging for c2bRegister has been initialized",
+      );
+      logErrorDetails(
+        error,
+        {
+          apiEndpoint: "/mpesa/c2b/v1/registerurl",
+          method: "POST",
+          payload: {
+            confirmationUrl,
+            validationUrl,
+            shortCode,
+            responseType,
+          },
+        },
+        "C2B Register transaction error details:",
+      );
+    }
+    throw throwErrorMessages(error);
+  }
+}
+
+const handleC2bRegisterCallbacks = async (app) => {
+  callbackHandlerInitialized = true;
+  await handleCallbacks(app, "c2bRegister");
+};
+
+export { c2bRegister, handleC2bRegisterCallbacks };
+
+
+
+ + + + +
+ +
+ +
+ Generated by JSDoc 4.0.4 on Thu Feb 27 2025 21:37:15 GMT+0300 (East Africa Time) using the Minami theme. +
+ + + + + diff --git a/docs/c2b-Simulate.js.html b/docs/c2b-Simulate.js.html new file mode 100644 index 0000000..5b32209 --- /dev/null +++ b/docs/c2b-Simulate.js.html @@ -0,0 +1,139 @@ + + + + + + c2b-Simulate.js - Documentation + + + + + + + + + + + + + + + + + +
+ +

c2b-Simulate.js

+ + + + + + + +
+
+
import {
+  generateOAuthToken, logErrorDetails,
+  throwErrorMessages,
+  validateFormatPhone
+} from "../utils/helpers.js";
+import axios from "axios";
+
+
+/**
+ * @name c2bSimulate
+ * @description This function simulates a C2B (Customer to Business) transaction by initiating a payment from an MSISDN to a business shortcode.
+ * @summary Simulate a transaction from an MSISDN to a business shortcode
+ * @see {@link https://developer.safaricom.co.ke/APIs/CustomerToBusinessRegisterURL open external link}
+ * @param {Object} options Options for the C2B simulation.
+ * @param {string} options.msisdn The MSISDN receiving the transaction
+ * @param {number} options.amount Transaction amount.
+ * @param {string} options.billRefNumber Bill reference number e.g. "invoice008".
+ * @param {number} options.shortCode Usually, a unique number is tagged to an M-PESA pay bill/till number of the organization
+ * @param {boolean} [options.proErrorLogging] Logs out advanced error details - good for debugging
+ * @return {Promise<Object>} c2bSimulateResponse.
+ */
+async function c2bSimulate({
+  msisdn,
+  amount,
+  billRefNumber,
+  shortCode,
+  proErrorLogging = false,
+}) {
+  /** SIMULATE SHOULD ONLY BE PERFORMED IN SANDBOX MODE, NEVER IN PRODUCTION **/
+  const { accessToken, baseURL } = await generateOAuthToken();
+  if (baseURL === "https://api.safaricom.co.ke") {
+    throw new Error(
+      "Simulation is allowed only in development or sandbox environment!",
+    );
+  }
+  try {
+    const req = axios.create({
+      baseURL,
+      headers: {
+        Authorization: `Bearer ${accessToken}`,
+        "Content-Type": "application/json",
+      },
+    });
+    const config = {
+      commandId: "CustomerPayBillOnline",
+    };
+    const responseBody = await req.post(`/mpesa/c2b/v1/simulate`, {
+      ShortCode: shortCode,
+      CommandID: config.commandId,
+      Amount: amount,
+      Msisdn: validateFormatPhone(msisdn),
+      BillRefNumber: billRefNumber,
+    });
+    return { c2bSimulateResponse: responseBody.data };
+  } catch (error) {
+    if (proErrorLogging) {
+      console.info(
+        "\x1b[35m%s\x1b[0m",
+        "Advanced error logging for c2bSimulate has been initialized",
+      );
+      logErrorDetails(
+        error,
+        {
+          apiEndpoint: "/mpesa/c2b/v1/simulate",
+          method: "POST",
+          payload: {
+            msisdn,
+            amount,
+            billRefNumber,
+            shortCode,
+          },
+        },
+        "C2B Simulation transaction error details:",
+      );
+    }
+    throw throwErrorMessages(error);
+  }
+}
+export { c2bSimulate };
+
+
+
+ + + + +
+ +
+ +
+ Generated by JSDoc 4.0.4 on Thu Feb 27 2025 21:37:15 GMT+0300 (East Africa Time) using the Minami theme. +
+ + + + + diff --git a/docs/fonts/OpenSans-Bold-webfont.eot b/docs/fonts/OpenSans-Bold-webfont.eot new file mode 100644 index 0000000000000000000000000000000000000000..5d20d916338a5890a033952e2e07ba7380f5a7d3 GIT binary patch literal 19544 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 literal 0 HcmV?d00001 diff --git a/docs/fonts/OpenSans-Bold-webfont.svg b/docs/fonts/OpenSans-Bold-webfont.svg new file mode 100644 index 0000000..3ed7be4 --- /dev/null +++ b/docs/fonts/OpenSans-Bold-webfont.svg @@ -0,0 +1,1830 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/fonts/OpenSans-Bold-webfont.woff b/docs/fonts/OpenSans-Bold-webfont.woff new file mode 100644 index 0000000000000000000000000000000000000000..1205787b0ed50db71ebd4f8a7f85d106721ff258 GIT binary patch literal 22432 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 ig3W + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/fonts/OpenSans-BoldItalic-webfont.woff b/docs/fonts/OpenSans-BoldItalic-webfont.woff new file mode 100644 index 0000000000000000000000000000000000000000..ed760c0628b6a0026041f5b8bba466a0471fd2e0 GIT binary patch literal 23048 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/fonts/OpenSans-Italic-webfont.woff b/docs/fonts/OpenSans-Italic-webfont.woff new file mode 100644 index 0000000000000000000000000000000000000000..ff652e64356b538c001423b6aedefcf1ee66cd17 GIT binary patch literal 23188 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 literal 0 HcmV?d00001 diff --git a/docs/fonts/OpenSans-Light-webfont.svg b/docs/fonts/OpenSans-Light-webfont.svg new file mode 100644 index 0000000..11a472c --- /dev/null +++ b/docs/fonts/OpenSans-Light-webfont.svg @@ -0,0 +1,1831 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/fonts/OpenSans-Light-webfont.woff b/docs/fonts/OpenSans-Light-webfont.woff new file mode 100644 index 0000000000000000000000000000000000000000..e786074813a27d0a7a249047832988d5bf0fe756 GIT binary patch literal 22248 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 literal 0 HcmV?d00001 diff --git a/docs/fonts/OpenSans-LightItalic-webfont.eot b/docs/fonts/OpenSans-LightItalic-webfont.eot new file mode 100644 index 0000000000000000000000000000000000000000..8f445929ffb03b50e98c2a2f7d831a0cb1b276a2 GIT binary patch literal 20535 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 Z%0#`NexefPFV?Xj51H#Y#AC7WXn+Jg($4?@ literal 0 HcmV?d00001 diff --git a/docs/fonts/OpenSans-LightItalic-webfont.svg b/docs/fonts/OpenSans-LightItalic-webfont.svg new file mode 100644 index 0000000..431d7e3 --- /dev/null +++ b/docs/fonts/OpenSans-LightItalic-webfont.svg @@ -0,0 +1,1835 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/fonts/OpenSans-LightItalic-webfont.woff b/docs/fonts/OpenSans-LightItalic-webfont.woff new file mode 100644 index 0000000000000000000000000000000000000000..43e8b9e6cc061ff17fd2903075cbde12715512b3 GIT binary patch literal 23400 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 + + + + + + +
+ +

Global

+ + + + + + + +
+ +
+ +

+ +

+ + +
+ +
+
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + + + + + + + + + + + +

Members

+ + + +
+

C2BRegister

+ + +

Registers callback validation and confirmation URLs on M-Pesa to receive payment notifications for your paybill/till numbers.

+ + + +
+

Register URL API works hand in hand with Customer to Business (C2B) APIs and allows receiving payment notifications to your paybill. This API enables you to register the callback URLs via which you shall receive notifications for payments to your pay bill/till numbers

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + +
See:
+
+ +
+ + + +
+ + + + + + + +
+ + + +
+

b2cRequest

+ + +

B2C payments involve a business sending money to an individual. This is a direct transaction from a business shortcode to a consumer's mobile number (MSISDN).

+ + + +
+

B2C API can be used in several scenarios by businesses that require to either make Salary Payments, Cashback payments, Promotional Payments(e.g. betting winning payouts), winnings, financial institutions withdrawal of funds, loan disbursements, etc.

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + +
See:
+
+ +
+ + + +
+ + + + + + + +
+ + + +
+

b2cTopUp

+ + +

Transfers funds from your MMF/Working account to the recipient's utility account for disbursement to a B2C shortcode.

+ + + +
+

This API enables you to load funds to a B2C shortcode directly for disbursement. The transaction moves money from your MMF/Working account to the recipient’s utility account.

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + +
See:
+
+ +
+ + + +
+ + + + + + + +
+ + + +
+

balanceQuery

+ + +

Retrieves the balance of a short code associated with the developer account, supporting B2C, buy goods, and pay bill accounts.

+ + + +
+

The Account Balance API is used to request the account balance of a short code. This can be used for both B2C, buy goods and pay bill accounts.

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + +
See:
+
+ +
+ + + +
+ + + + + + + +
+ + + +
+

businessPaybill

+ + +

Facilitates bill payments from a business account to a paybill number, transferring funds to the recipient’s utility account.

+ + + +
+

This API enables you to pay bills directly from your business account to a pay bill number, or a paybill store. You can use this API to pay on behalf of a consumer/requester. The transaction moves money from your MMF/Working account to the recipient’s utility account.

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + +
See:
+
+ +
+ + + +
+ + + + + + + +
+ + + +
+

c2bSimulate

+ + +

Simulate a transaction from an MSISDN to a business shortcode

+ + + +
+

This function simulates a C2B (Customer to Business) transaction by initiating a payment from an MSISDN to a business shortcode.

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + +
See:
+
+ +
+ + + +
+ + + + + + + +
+ + + +
+

generateQrCode

+ + +

Generates QR codes for customers using My Safaricom app

+ + + +
+

This generates a Dynamic QR which enables Safaricom M-PESA customers who have My Safaricom App or M-PESA app, to scan a QR (Quick Response) code, to capture till number and amount

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + +
See:
+
+ +
+ + + +
+ + + + + + + +
+ + + +
+

mpesaQuery

+ + +

Check transaction status of a Lipa na M-Pesa payment.

+ + + +
+

Use this API to check the status of a Lipa Na M-Pesa Online Payment.

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + +
See:
+
+ +
+ + + +
+ + + + + + + +
+ + + +
+

mpesaSimulate

+ + +

Sends a payment prompt to the customer's M-PESA registered phone number, requesting them to enter their M-PESA pin to authorize and complete payment.

+ + + +
+

Lipa na M-PESA online API also known as M-PESA express (STK Push/NI push) is a Merchant/Business initiated C2B (Customer to Business) Payment. it enables you to send a payment prompt on the customer's phone (Popularly known as STK Push Prompt) to your customer's M-PESA registered phone number requesting them to enter their M-PESA pin to authorize and complete payment.

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + +
See:
+
+ +
+ + + +
+ + + + + + + +
+ + + +
+

reversals

+ + +

Reverses an M-pesa transaction

+ + + +
+

Reverses a C2B M-Pesa transaction. Once a customer pays and there is a need to reverse the transaction, the organization will use this API to reverse the amount.

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + +
See:
+
+ +
+ + + +
+ + + + + + + +
+ + + +
+

taxRemittance

+ + +

Enables one to remit tax to Kenya Revenue Authority (KRA)

+ + + +
+

This API enables businesses to remit tax to Kenya Revenue Authority (KRA). To use this API, prior integration is required with KRA for tax declaration, payment registration number (PRN) generation, and exchange of other tax-related information.

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + +
See:
+
+ +
+ + + +
+ + + + + + + +
+ + + +
+

transactionStatus

+ + +

Check the status of a transaction.

+ + + +
+

Enables one to Check the status of an M-pesa transaction, using a unique identifier.

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + +
See:
+
+ +
+ + + +
+ + + + + + + +
+ + + + + + + + + +
+ +
+ + + + +
+ +
+ +
+ Generated by JSDoc 4.0.4 on Thu Feb 27 2025 21:37:15 GMT+0300 (East Africa Time) using the Minami theme. +
+ + + + + \ No newline at end of file diff --git a/docs/index.html b/docs/index.html new file mode 100644 index 0000000..1199ef4 --- /dev/null +++ b/docs/index.html @@ -0,0 +1,81 @@ + + + + + + Home - Documentation + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+

JSDoc Guide

+

About

+

This documentation is generated using JSDoc and is designed to help developers accurately map fields to API endpoints. It provides a clear understanding of which fields are preconfigured by default and which can be modified based on your requirements.

+

By referring to this guide, you can ensure that you're using the API correctly and efficiently. Whether you're integrating the library or extending its functionality, this documentation will serve as a valuable reference.

+

Keeping Documentation Up to Date

+

If you're contributing to the project, make sure to regenerate the documentation whenever changes are made to the API. Use the following commands:

+

Generate Documentation

+

After updating the library, you may optionally run the commands below to regenerate the documentation. However, if you prefer, you can leave this task to the repository maintainer.

+

npm:npm docs

+

yarn:yarn docs

+

Takeaways

+

Keeping the documentation updated ensures that all contributors and users have access to accurate and up-to-date information.

+
+ + + + + + +
+ +
+ +
+ Generated by JSDoc 4.0.4 on Thu Feb 27 2025 21:37:15 GMT+0300 (East Africa Time) using the Minami theme. +
+ + + + + \ No newline at end of file diff --git a/docs/mpesa-Query.js.html b/docs/mpesa-Query.js.html new file mode 100644 index 0000000..fbc3d3b --- /dev/null +++ b/docs/mpesa-Query.js.html @@ -0,0 +1,126 @@ + + + + + + mpesa-Query.js - Documentation + + + + + + + + + + + + + + + + + +
+ +

mpesa-Query.js

+ + + + + + + +
+
+
import {
+  generateMpesaCredentials,
+  generateOAuthToken,
+  logErrorDetails,
+  throwErrorMessages,
+} from "../utils/helpers.js";
+import axios from "axios";
+
+/**
+ * @name mpesaQuery
+ * @description Use this API to check the status of a Lipa Na M-Pesa Online Payment.
+ * @summary Check transaction status of a Lipa na M-Pesa payment.
+ * @see {@link https://developer.safaricom.co.ke/APIs/MpesaExpressQuery open external link}
+ * @param {Object} options Options for the Lipa Na M-Pesa query API.
+ * @param {string} options.checkoutRequestId Unique identifier for the processed checkout transaction.
+ * @param {number} options.businessShortCode Organization's shortcode (Paybill or Buygoods, 5-7 digits).
+ * @param {boolean} [options.proErrorLogging] Logs out advanced error details - good for debugging
+ * @returns {Promise<Object>} mpesaQueryResponse.
+ */
+async function mpesaQuery({
+  checkoutRequestId,
+  businessShortCode,
+  proErrorLogging = false,
+}) {
+  const { accessToken, baseURL } = await generateOAuthToken();
+  const { password, timeStamp } = generateMpesaCredentials(businessShortCode);
+  const req = axios.create({
+    baseURL,
+    headers: {
+      Authorization: `Bearer ${accessToken}`,
+      "Content-Type": "application/json",
+    },
+  });
+  try {
+    const responseBody = await req.post("/mpesa/stkpushquery/v1/query", {
+      BusinessShortCode: businessShortCode,
+      Password: password,
+      Timestamp: timeStamp,
+      CheckoutRequestID: checkoutRequestId,
+    });
+
+    return { mpesaQueryResponse: responseBody.data };
+  } catch (error) {
+    if (proErrorLogging) {
+      console.info(
+        "\x1b[35m%s\x1b[0m",
+        "Advanced error logging for mpesaQuery has been initialized",
+      );
+      logErrorDetails(
+        error,
+        {
+          apiEndpoint: "/mpesa/stkpushquery/v1/query",
+          method: "POST",
+          payload: {
+            businessShortCode,
+            checkoutRequestId,
+          },
+        },
+        "Mpesa Query transaction error details:",
+      );
+    }
+    throw throwErrorMessages(error);
+  }
+}
+
+export { mpesaQuery };
+
+
+
+ + + + +
+ +
+ +
+ Generated by JSDoc 4.0.4 on Thu Feb 27 2025 21:37:15 GMT+0300 (East Africa Time) using the Minami theme. +
+ + + + + diff --git a/docs/mpesa-Simulate.js.html b/docs/mpesa-Simulate.js.html new file mode 100644 index 0000000..4aaabee --- /dev/null +++ b/docs/mpesa-Simulate.js.html @@ -0,0 +1,172 @@ + + + + + + mpesa-Simulate.js - Documentation + + + + + + + + + + + + + + + + + +
+ +

mpesa-Simulate.js

+ + + + + + + +
+
+
import axios from "axios";
+import {
+  callbackTrigger,
+  generateMpesaCredentials,
+  generateOAuthToken,
+  handleCallbacks,
+  logErrorDetails,
+  throwErrorMessages,
+  validateFormatPhone,
+  validateUrl
+} from "../utils/helpers.js";
+
+// Globals
+let callbackHandlerInitialized = false;
+let conditionalCallbackData = {};
+
+/**
+ * @name mpesaSimulate
+ * @description Lipa na M-PESA online API also known as M-PESA express (STK Push/NI push) is a Merchant/Business initiated C2B (Customer to Business) Payment. it enables you to send a payment prompt on the customer's phone (Popularly known as STK Push Prompt) to your customer's M-PESA registered phone number requesting them to enter their M-PESA pin to authorize and complete payment.
+ * @summary Sends a payment prompt to the customer's M-PESA registered phone number, requesting them to enter their M-PESA pin to authorize and complete payment.
+ * @see {@link https://developer.safaricom.co.ke/APIs/MpesaExpressSimulate open external link}
+ * @param {Object} options Options for M-Pesa express API.
+ * @param {string} options.partyA Sender's Safaricom mobile number (M-PESA registered).
+ * @param {string} options.phoneNumber Mobile number to receive the STK Pin Prompt (can be same as partyA).
+ * @param {number} options.amount Transaction amount.
+ * @param {string} options.transactionType "CustomerPayBillOnline" for PayBill and "CustomerBuyGoodsOnline" for Till Numbers.
+ * @param {string} options.callbackUrl Secure URL for receiving notifications from M-Pesa API.
+ * @param {string} options.transactionDesc Information to be associated with the transaction.
+ * @param {string} options.accountRef Alphanumeric account reference (up to 12 characters) shown in the STK Pin Prompt (Org name).
+ * @param {number} options.partyB 5 to 6-digit number of the receiving organization.
+ * @param {boolean} [options.proErrorLogging] Logs out advanced error details - good for debugging
+ * @returns {Promise<Object>} mpesaSimulateResponse and (optional) conditionalCallbackData.
+ */
+async function mpesaSimulate({
+                               partyA,
+                               phoneNumber,
+                               amount,
+                               callbackUrl,
+                               accountRef,
+                               partyB,
+                               transactionType,
+  transactionDesc,
+                               proErrorLogging = false
+                             }) {
+  if (!callbackHandlerInitialized) {
+    console.info(
+      "\x1b[42m\x1b[30m\x1b[1m The default callback handler ('handleMpesaSimulateCallbacks') was not called. Ignore if handling manually.\x1b[0m"
+    );
+  }
+  try {
+    validateUrl(callbackUrl, "callbackUrl");
+    const { accessToken, baseURL } = await generateOAuthToken();
+    const { password, timeStamp } = generateMpesaCredentials(partyB);
+    const req = axios.create({
+      baseURL,
+      headers: {
+        Authorization: `Bearer ${accessToken}`,
+        "Content-Type": "application/json"
+      }
+
+    });
+    const responseBody = await req.post("/mpesa/stkpush/v1/processrequest", {
+      BusinessShortCode: partyB,
+      Password: password,
+      Timestamp: timeStamp,
+      Amount: amount,
+      PartyA: validateFormatPhone(partyA),
+      PartyB: partyB,
+      PhoneNumber: validateFormatPhone(phoneNumber),
+      CallBackURL: callbackUrl,
+      AccountReference: accountRef,
+      TransactionDesc: transactionDesc,
+      TransactionType: transactionType
+    });
+    conditionalCallbackData = await callbackTrigger(callbackHandlerInitialized, true);
+    return {
+      mpesaSimulateResponse: responseBody.data,
+      conditionalCallbackData
+    };
+  } catch (error) {
+    if (proErrorLogging) {
+      console.info(
+        "\x1b[35m%s\x1b[0m",
+        "Advanced error logging for mpesaSimulate has been initialized"
+      );
+      logErrorDetails(
+        error,
+        {
+          apiEndpoint: "/mpesa/stkpush/v1/processrequest",
+          method: "POST",
+          payload: {
+            partyA,
+            phoneNumber,
+            amount,
+            callbackUrl,
+            accountRef,
+            partyB,
+            transactionDesc
+          }
+        },
+        "Mpesa Simulate transaction error details:"
+      );
+    }
+    throw throwErrorMessages(error);
+  }
+}
+
+const handleMpesaSimulateCallbacks = async (app) => {
+  callbackHandlerInitialized = true;
+  await handleCallbacks(app, "mpesaSimulate");
+};
+export { mpesaSimulate, handleMpesaSimulateCallbacks };
+
+
+
+ + + + +
+ +
+ +
+ Generated by JSDoc 4.0.4 on Thu Feb 27 2025 21:37:15 GMT+0300 (East Africa Time) using the Minami theme. +
+ + + + + diff --git a/docs/qr-Generate.js.html b/docs/qr-Generate.js.html new file mode 100644 index 0000000..a0ae49c --- /dev/null +++ b/docs/qr-Generate.js.html @@ -0,0 +1,147 @@ + + + + + + qr-Generate.js - Documentation + + + + + + + + + + + + + + + + + +
+ +

qr-Generate.js

+ + + + + + + +
+
+
import axios from "axios";
+import {
+  generateOAuthToken,
+  logErrorDetails,
+  throwErrorMessages,
+} from "../utils/helpers.js";
+import { trxCodeTypes } from "../utils/constants.js";
+
+/**
+ * @name generateQrCode
+ * @description This generates a Dynamic QR which enables Safaricom M-PESA customers who have My Safaricom App or M-PESA app, to scan a QR (Quick Response) code, to capture till number and amount
+ * @summary Generates QR codes for customers using My Safaricom app
+ * @see {@link https://developer.safaricom.co.ke/APIs/DynamicQRCode open external link}
+ * @param {Object} options Options for the QR code generation request.
+ * @param {string} options.merchantName - The name of the company or M-Pesa merchant requesting the QR code.
+ * @param {string} options.refNo A unique reference number for the transaction.
+ * @param {number} options.amount The total amount for the sale or transaction.
+ * @param {string} options.trxCode Transaction type. Supported types: BG, WA, PB, SM, SB.
+ * @param {string} options.cpi Credit Party Identifier (e.g., mobile number, business number).
+ * @param {string} options.size Size of the QR code image in pixels. QR code image will always be square.
+ * @param {boolean} [options.proErrorLogging] Logs out advanced error details - good for debugging
+ * @returns {Promise<Object>} generateQrcodeResponse.
+ */
+async function generateQrCode({
+  merchantName,
+  refNo,
+  amount,
+  trxCode,
+  cpi,
+  size,
+  proErrorLogging = false,
+}) {
+  /**
+   * @param {string} validTrxCodes - Only support: buy goods: "BG" - withdraw agent till: "WA" - paybill business number: "PB" - send money msisdn: "SM" and end to business msisdn: "SB"
+   */
+  const validTrxCodes = Object.values(trxCodeTypes);
+  if (!validTrxCodes.includes(trxCode)) {
+    throw new Error(
+      `Invalid trxCode provided. Must be one of: ${validTrxCodes.join(", ")}`,
+    );
+  }
+  try {
+    const { accessToken, baseURL } = await generateOAuthToken();
+    const req = axios.create({
+      baseURL,
+      headers: {
+        Authorization: `Bearer ${accessToken}`,
+        "Content-Type": "application/json",
+      },
+    });
+    const responseBody = await req.post("/mpesa/qrcode/v1/generate", {
+      MerchantName: merchantName,
+      RefNo: refNo,
+      Amount: amount,
+      TrxCode: trxCode,
+      CPI: cpi,
+      Size: size,
+    });
+    return { generateQrcodeResponse: responseBody.data };
+  } catch (error) {
+    if (proErrorLogging) {
+      console.info(
+        "\x1b[35m%s\x1b[0m",
+        "Advanced error logging for generateQrcode has been initialized",
+      );
+      logErrorDetails(
+        error,
+        {
+          apiEndpoint: "/mpesa/qrcode/v1/generate",
+          method: "POST",
+          payload: {
+            merchantName,
+            refNo,
+            amount,
+            trxCode,
+            cpi,
+            size,
+          },
+        },
+        "QR code generation error details:",
+      );
+    }
+    throw throwErrorMessages(error);
+  }
+}
+
+export { generateQrCode };
+
+
+
+ + + + +
+ +
+ +
+ Generated by JSDoc 4.0.4 on Thu Feb 27 2025 21:37:15 GMT+0300 (East Africa Time) using the Minami theme. +
+ + + + + diff --git a/docs/reversals.js.html b/docs/reversals.js.html new file mode 100644 index 0000000..661392e --- /dev/null +++ b/docs/reversals.js.html @@ -0,0 +1,174 @@ + + + + + + reversals.js - Documentation + + + + + + + + + + + + + + + + + +
+ +

reversals.js

+ + + + + + + +
+
+
import axios from "axios";
+import {
+  callbackTrigger,
+  encryptSecurityCredential,
+  generateOAuthToken,
+  handleCallbacks,
+  logErrorDetails,
+  throwErrorMessages,
+  validateUrl,
+} from "../utils/helpers.js";
+
+// Globals
+let callbackHandlerInitialized = false;
+let conditionalCallbackData = {};
+
+/**
+ * @name reversals
+ * @description Reverses a C2B M-Pesa transaction. Once a customer pays and there is a need to reverse the transaction, the organization will use this API to reverse the amount.
+ * @summary Reverses an M-pesa transaction
+ * @see {@link https://developer.safaricom.co.ke/APIs/Reversal open external link}
+ * @param {Object} options Options for the reversal API.
+ * @param {string} options.transactionId Transaction ID for reversal (e.g., LKXXXX1234).
+ * @param {number} options.amount Amount to be reversed.
+ * @param {string} options.QueueTimeOutURL URL for timeout transaction details.
+ * @param {string} options.resultUrl URL for transaction details.
+ * @param {string} options.remarks Information to be associated with the transaction.
+ * @param {string} options.occasion Information to be associated with the transaction.
+ * @param {number} options.receiverParty Organization receiving the transaction.
+ * @param {string} options.initiator Name of the initiator of the request.
+ * @param {boolean} [options.proErrorLogging] Logs out advanced error details - good for debugging
+ * @returns {Promise<Object>} reversalsResponse and (optional) conditionalCallbackData.
+ */
+async function reversals({
+  transactionId,
+  amount,
+  QueueTimeOutURL,
+  resultUrl,
+  receiverParty,
+  initiator,
+  remarks,
+  occasion,
+  proErrorLogging = false,
+}) {
+  if (!callbackHandlerInitialized) {
+    console.info("\x1b[42m\x1b[30m\x1b[1m The default callback handler ('handleReversalCallbacks') was not called. Ignore if handling manually.\x1b[0m",
+    );
+  }
+  try {
+    validateUrl(resultUrl, "resultUrl");
+    validateUrl(QueueTimeOutURL, "timeoutUrl");
+    const { accessToken, baseURL } = await generateOAuthToken();
+    const req = axios.create({
+      baseURL,
+      headers: {
+        Authorization: `Bearer ${accessToken}`,
+        "Content-Type": "application/json",
+      },
+    });
+    // default configurations
+    const config = {
+      receiverIdType: "11",
+      commandId: "TransactionReversal",
+    };
+    const responseBody = await req.post("/mpesa/reversal/v1/request", {
+      Initiator: initiator,
+      SecurityCredential: encryptSecurityCredential(),
+      CommandID: config.commandId,
+      TransactionID: transactionId,
+      Amount: amount,
+      ReceiverParty: receiverParty,
+      RecieverIdentifierType: config.receiverIdType,
+      ResultURL: resultUrl,
+      QueueTimeOutURL: QueueTimeOutURL,
+      Remarks: remarks,
+      Occasion: occasion,
+    });
+
+    conditionalCallbackData = await callbackTrigger(callbackHandlerInitialized);
+
+    return { reversalsResponse: responseBody.data, conditionalCallbackData };
+  } catch (error) {
+    if (proErrorLogging) {
+      console.info(
+        "\x1b[35m%s\x1b[0m",
+        "Advanced error logging for reversals has been initialized",
+      );
+      logErrorDetails(
+        error,
+        {
+          apiEndpoint: "/mpesa/reversal/v1/request",
+          method: "POST",
+          payload: {
+            transactionId,
+            amount,
+            QueueTimeOutURL,
+            resultUrl,
+            receiverParty,
+            initiator,
+            remarks,
+            occasion
+          },
+        },
+        "Transaction reversal error details:",
+      );
+    }
+    throw throwErrorMessages(error);
+  }
+}
+const handleReversalCallbacks = async (app) => {
+  callbackHandlerInitialized = true;
+  await handleCallbacks(app, "reversals");
+};
+
+export { reversals, handleReversalCallbacks };
+
+
+
+ + + + +
+ +
+ +
+ Generated by JSDoc 4.0.4 on Thu Feb 27 2025 21:37:15 GMT+0300 (East Africa Time) using the Minami theme. +
+ + + + + diff --git a/docs/scripts/linenumber.js b/docs/scripts/linenumber.js new file mode 100644 index 0000000..8d52f7e --- /dev/null +++ b/docs/scripts/linenumber.js @@ -0,0 +1,25 @@ +/*global document */ +(function() { + var source = document.getElementsByClassName('prettyprint source linenums'); + var i = 0; + var lineNumber = 0; + var lineId; + var lines; + var totalLines; + var anchorHash; + + if (source && source[0]) { + anchorHash = document.location.hash.substring(1); + lines = source[0].getElementsByTagName('li'); + totalLines = lines.length; + + for (; i < totalLines; i++) { + lineNumber++; + lineId = 'line' + lineNumber; + lines[i].id = lineId; + if (lineId === anchorHash) { + lines[i].className += ' selected'; + } + } + } +})(); diff --git a/docs/scripts/prettify/Apache-License-2.0.txt b/docs/scripts/prettify/Apache-License-2.0.txt new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/docs/scripts/prettify/Apache-License-2.0.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/docs/scripts/prettify/lang-css.js b/docs/scripts/prettify/lang-css.js new file mode 100644 index 0000000..041e1f5 --- /dev/null +++ b/docs/scripts/prettify/lang-css.js @@ -0,0 +1,2 @@ +PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\f\r ]+/,null," \t\r\n "]],[["str",/^"(?:[^\n\f\r"\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*"/,null],["str",/^'(?:[^\n\f\r'\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*'/,null],["lang-css-str",/^url\(([^"')]*)\)/i],["kwd",/^(?:url|rgb|!important|@import|@page|@media|@charset|inherit)(?=[^\w-]|$)/i,null],["lang-css-kw",/^(-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*)\s*:/i],["com",/^\/\*[^*]*\*+(?:[^*/][^*]*\*+)*\//],["com", +/^(?:<\!--|--\>)/],["lit",/^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i],["lit",/^#[\da-f]{3,6}/i],["pln",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i],["pun",/^[^\s\w"']+/]]),["css"]);PR.registerLangHandler(PR.createSimpleLexer([],[["kwd",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i]]),["css-kw"]);PR.registerLangHandler(PR.createSimpleLexer([],[["str",/^[^"')]+/]]),["css-str"]); diff --git a/docs/scripts/prettify/prettify.js b/docs/scripts/prettify/prettify.js new file mode 100644 index 0000000..eef5ad7 --- /dev/null +++ b/docs/scripts/prettify/prettify.js @@ -0,0 +1,28 @@ +var q=null;window.PR_SHOULD_USE_CONTINUATION=!0; +(function(){function L(a){function m(a){var f=a.charCodeAt(0);if(f!==92)return f;var b=a.charAt(1);return(f=r[b])?f:"0"<=b&&b<="7"?parseInt(a.substring(1),8):b==="u"||b==="x"?parseInt(a.substring(2),16):a.charCodeAt(1)}function e(a){if(a<32)return(a<16?"\\x0":"\\x")+a.toString(16);a=String.fromCharCode(a);if(a==="\\"||a==="-"||a==="["||a==="]")a="\\"+a;return a}function h(a){for(var f=a.substring(1,a.length-1).match(/\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\[0-3][0-7]{0,2}|\\[0-7]{1,2}|\\[\S\s]|[^\\]/g),a= +[],b=[],o=f[0]==="^",c=o?1:0,i=f.length;c122||(d<65||j>90||b.push([Math.max(65,j)|32,Math.min(d,90)|32]),d<97||j>122||b.push([Math.max(97,j)&-33,Math.min(d,122)&-33]))}}b.sort(function(a,f){return a[0]-f[0]||f[1]-a[1]});f=[];j=[NaN,NaN];for(c=0;ci[0]&&(i[1]+1>i[0]&&b.push("-"),b.push(e(i[1])));b.push("]");return b.join("")}function y(a){for(var f=a.source.match(/\[(?:[^\\\]]|\\[\S\s])*]|\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\\d+|\\[^\dux]|\(\?[!:=]|[()^]|[^()[\\^]+/g),b=f.length,d=[],c=0,i=0;c=2&&a==="["?f[c]=h(j):a!=="\\"&&(f[c]=j.replace(/[A-Za-z]/g,function(a){a=a.charCodeAt(0);return"["+String.fromCharCode(a&-33,a|32)+"]"}));return f.join("")}for(var t=0,s=!1,l=!1,p=0,d=a.length;p=5&&"lang-"===b.substring(0,5))&&!(o&&typeof o[1]==="string"))c=!1,b="src";c||(r[f]=b)}i=d;d+=f.length;if(c){c=o[1];var j=f.indexOf(c),k=j+c.length;o[2]&&(k=f.length-o[2].length,j=k-c.length);b=b.substring(5);B(l+i,f.substring(0,j),e,p);B(l+i+j,c,C(b,c),p);B(l+i+k,f.substring(k),e,p)}else p.push(l+i,b)}a.e=p}var h={},y;(function(){for(var e=a.concat(m), +l=[],p={},d=0,g=e.length;d=0;)h[n.charAt(k)]=r;r=r[1];n=""+r;p.hasOwnProperty(n)||(l.push(r),p[n]=q)}l.push(/[\S\s]/);y=L(l)})();var t=m.length;return e}function u(a){var m=[],e=[];a.tripleQuotedStrings?m.push(["str",/^(?:'''(?:[^'\\]|\\[\S\s]|''?(?=[^']))*(?:'''|$)|"""(?:[^"\\]|\\[\S\s]|""?(?=[^"]))*(?:"""|$)|'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$))/,q,"'\""]):a.multiLineStrings?m.push(["str",/^(?:'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$)|`(?:[^\\`]|\\[\S\s])*(?:`|$))/, +q,"'\"`"]):m.push(["str",/^(?:'(?:[^\n\r'\\]|\\.)*(?:'|$)|"(?:[^\n\r"\\]|\\.)*(?:"|$))/,q,"\"'"]);a.verbatimStrings&&e.push(["str",/^@"(?:[^"]|"")*(?:"|$)/,q]);var h=a.hashComments;h&&(a.cStyleComments?(h>1?m.push(["com",/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,q,"#"]):m.push(["com",/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\n\r]*)/,q,"#"]),e.push(["str",/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,q])):m.push(["com",/^#[^\n\r]*/, +q,"#"]));a.cStyleComments&&(e.push(["com",/^\/\/[^\n\r]*/,q]),e.push(["com",/^\/\*[\S\s]*?(?:\*\/|$)/,q]));a.regexLiterals&&e.push(["lang-regex",/^(?:^^\.?|[!+-]|!=|!==|#|%|%=|&|&&|&&=|&=|\(|\*|\*=|\+=|,|-=|->|\/|\/=|:|::|;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|[?@[^]|\^=|\^\^|\^\^=|{|\||\|=|\|\||\|\|=|~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\s*(\/(?=[^*/])(?:[^/[\\]|\\[\S\s]|\[(?:[^\\\]]|\\[\S\s])*(?:]|$))+\/)/]);(h=a.types)&&e.push(["typ",h]);a=(""+a.keywords).replace(/^ | $/g, +"");a.length&&e.push(["kwd",RegExp("^(?:"+a.replace(/[\s,]+/g,"|")+")\\b"),q]);m.push(["pln",/^\s+/,q," \r\n\t\xa0"]);e.push(["lit",/^@[$_a-z][\w$@]*/i,q],["typ",/^(?:[@_]?[A-Z]+[a-z][\w$@]*|\w+_t\b)/,q],["pln",/^[$_a-z][\w$@]*/i,q],["lit",/^(?:0x[\da-f]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+-]?\d+)?)[a-z]*/i,q,"0123456789"],["pln",/^\\[\S\s]?/,q],["pun",/^.[^\s\w"-$'./@\\`]*/,q]);return x(m,e)}function D(a,m){function e(a){switch(a.nodeType){case 1:if(k.test(a.className))break;if("BR"===a.nodeName)h(a), +a.parentNode&&a.parentNode.removeChild(a);else for(a=a.firstChild;a;a=a.nextSibling)e(a);break;case 3:case 4:if(p){var b=a.nodeValue,d=b.match(t);if(d){var c=b.substring(0,d.index);a.nodeValue=c;(b=b.substring(d.index+d[0].length))&&a.parentNode.insertBefore(s.createTextNode(b),a.nextSibling);h(a);c||a.parentNode.removeChild(a)}}}}function h(a){function b(a,d){var e=d?a.cloneNode(!1):a,f=a.parentNode;if(f){var f=b(f,1),g=a.nextSibling;f.appendChild(e);for(var h=g;h;h=g)g=h.nextSibling,f.appendChild(h)}return e} +for(;!a.nextSibling;)if(a=a.parentNode,!a)return;for(var a=b(a.nextSibling,0),e;(e=a.parentNode)&&e.nodeType===1;)a=e;d.push(a)}var k=/(?:^|\s)nocode(?:\s|$)/,t=/\r\n?|\n/,s=a.ownerDocument,l;a.currentStyle?l=a.currentStyle.whiteSpace:window.getComputedStyle&&(l=s.defaultView.getComputedStyle(a,q).getPropertyValue("white-space"));var p=l&&"pre"===l.substring(0,3);for(l=s.createElement("LI");a.firstChild;)l.appendChild(a.firstChild);for(var d=[l],g=0;g=0;){var h=m[e];A.hasOwnProperty(h)?window.console&&console.warn("cannot override language handler %s",h):A[h]=a}}function C(a,m){if(!a||!A.hasOwnProperty(a))a=/^\s*=o&&(h+=2);e>=c&&(a+=2)}}catch(w){"console"in window&&console.log(w&&w.stack?w.stack:w)}}var v=["break,continue,do,else,for,if,return,while"],w=[[v,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"], +"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"],F=[w,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"],G=[w,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"], +H=[G,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"],w=[w,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"],I=[v,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"], +J=[v,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"],v=[v,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"],K=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/,N=/\S/,O=u({keywords:[F,H,w,"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END"+ +I,J,v],hashComments:!0,cStyleComments:!0,multiLineStrings:!0,regexLiterals:!0}),A={};k(O,["default-code"]);k(x([],[["pln",/^[^]*(?:>|$)/],["com",/^<\!--[\S\s]*?(?:--\>|$)/],["lang-",/^<\?([\S\s]+?)(?:\?>|$)/],["lang-",/^<%([\S\s]+?)(?:%>|$)/],["pun",/^(?:<[%?]|[%?]>)/],["lang-",/^]*>([\S\s]+?)<\/xmp\b[^>]*>/i],["lang-js",/^]*>([\S\s]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\S\s]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]), +["default-markup","htm","html","mxml","xhtml","xml","xsl"]);k(x([["pln",/^\s+/,q," \t\r\n"],["atv",/^(?:"[^"]*"?|'[^']*'?)/,q,"\"'"]],[["tag",/^^<\/?[a-z](?:[\w-.:]*\w)?|\/?>$/i],["atn",/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^\s"'>]*(?:[^\s"'/>]|\/(?=\s)))/],["pun",/^[/<->]+/],["lang-js",/^on\w+\s*=\s*"([^"]+)"/i],["lang-js",/^on\w+\s*=\s*'([^']+)'/i],["lang-js",/^on\w+\s*=\s*([^\s"'>]+)/i],["lang-css",/^style\s*=\s*"([^"]+)"/i],["lang-css",/^style\s*=\s*'([^']+)'/i],["lang-css", +/^style\s*=\s*([^\s"'>]+)/i]]),["in.tag"]);k(x([],[["atv",/^[\S\s]+/]]),["uq.val"]);k(u({keywords:F,hashComments:!0,cStyleComments:!0,types:K}),["c","cc","cpp","cxx","cyc","m"]);k(u({keywords:"null,true,false"}),["json"]);k(u({keywords:H,hashComments:!0,cStyleComments:!0,verbatimStrings:!0,types:K}),["cs"]);k(u({keywords:G,cStyleComments:!0}),["java"]);k(u({keywords:v,hashComments:!0,multiLineStrings:!0}),["bsh","csh","sh"]);k(u({keywords:I,hashComments:!0,multiLineStrings:!0,tripleQuotedStrings:!0}), +["cv","py"]);k(u({keywords:"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["perl","pl","pm"]);k(u({keywords:J,hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["rb"]);k(u({keywords:w,cStyleComments:!0,regexLiterals:!0}),["js"]);k(u({keywords:"all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes", +hashComments:3,cStyleComments:!0,multilineStrings:!0,tripleQuotedStrings:!0,regexLiterals:!0}),["coffee"]);k(x([],[["str",/^[\S\s]+/]]),["regex"]);window.prettyPrintOne=function(a,m,e){var h=document.createElement("PRE");h.innerHTML=a;e&&D(h,e);E({g:m,i:e,h:h});return h.innerHTML};window.prettyPrint=function(a){function m(){for(var e=window.PR_SHOULD_USE_CONTINUATION?l.now()+250:Infinity;p=0){var k=k.match(g),f,b;if(b= +!k){b=n;for(var o=void 0,c=b.firstChild;c;c=c.nextSibling)var i=c.nodeType,o=i===1?o?b:c:i===3?N.test(c.nodeValue)?b:o:o;b=(f=o===b?void 0:o)&&"CODE"===f.tagName}b&&(k=f.className.match(g));k&&(k=k[1]);b=!1;for(o=n.parentNode;o;o=o.parentNode)if((o.tagName==="pre"||o.tagName==="code"||o.tagName==="xmp")&&o.className&&o.className.indexOf("prettyprint")>=0){b=!0;break}b||((b=(b=n.className.match(/\blinenums\b(?::(\d+))?/))?b[1]&&b[1].length?+b[1]:!0:!1)&&D(n,b),d={g:k,h:n,i:b},E(d))}}p code { + font-size: 0.85em; +} + +.readme table { + margin-bottom: 1em; + border-collapse: collapse; + border-spacing: 0; +} + +.readme table tr { + background-color: #fff; + border-top: 1px solid #ccc; +} + +.readme table th, +.readme table td { + padding: 6px 13px; + border: 1px solid #ddd; +} + +.readme table tr:nth-child(2n) { + background-color: #f8f8f8; +} + +/** Nav **/ +nav { + float: left; + display: block; + width: 250px; + background: #fff; + overflow: auto; + position: fixed; + height: 100%; + padding: 10px; + border-right: 1px solid #eee; + /* box-shadow: 0 0 3px rgba(0,0,0,0.1); */ +} + +nav li { + list-style: none; + padding: 0; + margin: 0; +} + +.nav-heading { + margin-top: 10px; + font-weight: bold; +} + +.nav-heading a { + color: #888; + font-size: 14px; + display: inline-block; +} + +.nav-item-type { + /* margin-left: 5px; */ + width: 18px; + height: 18px; + display: inline-block; + text-align: center; + border-radius: 0.2em; + margin-right: 5px; + font-weight: bold; + line-height: 20px; + font-size: 13px; +} + +.type-function { + background: #B3E5FC; + color: #0288D1; +} + +.type-class { + background: #D1C4E9; + color: #4527A0; +} + +.type-member { + background: #C8E6C9; + color: #388E3C; +} + +.type-module { + background: #E1BEE7; + color: #7B1FA2; +} + + +/** Footer **/ +footer { + color: hsl(0, 0%, 28%); + margin-left: 250px; + display: block; + padding: 30px; + font-style: italic; + font-size: 90%; + border-top: 1px solid #eee; +} + +.ancestors { + color: #999 +} + +.ancestors a { + color: #999 !important; + text-decoration: none; +} + +.clear { + clear: both +} + +.important { + font-weight: bold; + color: #950B02; +} + +.yes-def { + text-indent: -1000px +} + +.type-signature { + color: #aaa +} + +.name, .signature { + font-family: Consolas, Monaco, 'Andale Mono', monospace +} + +.details { + margin-top: 14px; + border-left: 2px solid #DDD; + line-height: 30px; +} + +.details dt { + width: 120px; + float: left; + padding-left: 10px; +} + +.details dd { + margin-left: 70px +} + +.details ul { + margin: 0 +} + +.details ul { + list-style-type: none +} + +.details li { + margin-left: 30px +} + +.details pre.prettyprint { + margin: 0 +} + +.details .object-value { + padding-top: 0 +} + +.description { + margin-bottom: 1em; + margin-top: 1em; +} + +.code-caption { + font-style: italic; + font-size: 107%; + margin: 0; +} + +.prettyprint { + font-size: 13px; + border: 1px solid #ddd; + border-radius: 3px; + box-shadow: 0 1px 3px hsla(0, 0%, 0%, 0.05); + overflow: auto; +} + +.prettyprint.source { + width: inherit +} + +.prettyprint code { + font-size: 12px; + line-height: 18px; + display: block; + background-color: #fff; + color: #4D4E53; +} + +.prettyprint code:empty:before { + content: ''; +} + +.prettyprint > code { + padding: 15px +} + +.prettyprint .linenums code { + padding: 0 15px +} + +.prettyprint .linenums li:first-of-type code { + padding-top: 15px +} + +.prettyprint code span.line { + display: inline-block +} + +.prettyprint.linenums { + padding-left: 70px; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +.prettyprint.linenums ol { + padding-left: 0 +} + +.prettyprint.linenums li { + border-left: 3px #ddd solid +} + +.prettyprint.linenums li.selected, .prettyprint.linenums li.selected * { + background-color: lightyellow +} + +.prettyprint.linenums li * { + -webkit-user-select: text; + -moz-user-select: text; + -ms-user-select: text; + user-select: text; +} + +.params, .props { + border-spacing: 0; + border: 1px solid #ddd; + border-collapse: collapse; + border-radius: 3px; + box-shadow: 0 1px 3px rgba(0,0,0,0.1); + width: 100%; + font-size: 14px; + /* margin-left: 15px; */ +} + +.params .name, .props .name, .name code { + color: #4D4E53; + font-family: Consolas, Monaco, 'Andale Mono', monospace; + font-size: 100%; +} + +.params td, .params th, .props td, .props th { + margin: 0px; + text-align: left; + vertical-align: top; + padding: 10px; + display: table-cell; +} + +.params td { + border-top: 1px solid #eee +} + +.params thead tr, .props thead tr { + background-color: #fff; + font-weight: bold; +} + +.params .params thead tr, .props .props thead tr { + background-color: #fff; + font-weight: bold; +} + +.params td.description > p:first-child, .props td.description > p:first-child { + margin-top: 0; + padding-top: 0; +} + +.params td.description > p:last-child, .props td.description > p:last-child { + margin-bottom: 0; + padding-bottom: 0; +} + +dl.param-type { + /* border-bottom: 1px solid hsl(0, 0%, 87%); */ + margin: 0; + padding: 0; + font-size: 16px; +} + +.param-type dt, .param-type dd { + display: inline-block +} + +.param-type dd { + font-family: Consolas, Monaco, 'Andale Mono', monospace; + display: inline-block; + padding: 0; + margin: 0; + font-size: 14px; +} + +.disabled { + color: #454545 +} + +/* navicon button */ +.navicon-button { + display: none; + position: relative; + padding: 2.0625rem 1.5rem; + transition: 0.25s; + cursor: pointer; + user-select: none; + opacity: .8; +} +.navicon-button .navicon:before, .navicon-button .navicon:after { + transition: 0.25s; +} +.navicon-button:hover { + transition: 0.5s; + opacity: 1; +} +.navicon-button:hover .navicon:before, .navicon-button:hover .navicon:after { + transition: 0.25s; +} +.navicon-button:hover .navicon:before { + top: .825rem; +} +.navicon-button:hover .navicon:after { + top: -.825rem; +} + +/* navicon */ +.navicon { + position: relative; + width: 2.5em; + height: .3125rem; + background: #000; + transition: 0.3s; + border-radius: 2.5rem; +} +.navicon:before, .navicon:after { + display: block; + content: ""; + height: .3125rem; + width: 2.5rem; + background: #000; + position: absolute; + z-index: -1; + transition: 0.3s 0.25s; + border-radius: 1rem; +} +.navicon:before { + top: .625rem; +} +.navicon:after { + top: -.625rem; +} + +/* open */ +.nav-trigger:checked + label:not(.steps) .navicon:before, +.nav-trigger:checked + label:not(.steps) .navicon:after { + top: 0 !important; +} + +.nav-trigger:checked + label .navicon:before, +.nav-trigger:checked + label .navicon:after { + transition: 0.5s; +} + +/* Minus */ +.nav-trigger:checked + label { + transform: scale(0.75); +} + +/* × and + */ +.nav-trigger:checked + label.plus .navicon, +.nav-trigger:checked + label.x .navicon { + background: transparent; +} + +.nav-trigger:checked + label.plus .navicon:before, +.nav-trigger:checked + label.x .navicon:before { + transform: rotate(-45deg); + background: #FFF; +} + +.nav-trigger:checked + label.plus .navicon:after, +.nav-trigger:checked + label.x .navicon:after { + transform: rotate(45deg); + background: #FFF; +} + +.nav-trigger:checked + label.plus { + transform: scale(0.75) rotate(45deg); +} + +.nav-trigger:checked ~ nav { + left: 0 !important; +} + +.nav-trigger:checked ~ .overlay { + display: block; +} + +.nav-trigger { + position: fixed; + top: 0; + clip: rect(0, 0, 0, 0); +} + +.overlay { + display: none; + position: fixed; + top: 0; + bottom: 0; + left: 0; + right: 0; + width: 100%; + height: 100%; + background: hsla(0, 0%, 0%, 0.5); + z-index: 1; +} + +.section-method { + margin-bottom: 30px; + padding-bottom: 30px; + border-bottom: 1px solid #eee; +} + +@media only screen and (min-width: 320px) and (max-width: 680px) { + body { + overflow-x: hidden; + } + + nav { + background: #FFF; + width: 250px; + height: 100%; + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: -250px; + z-index: 3; + padding: 0 10px; + transition: left 0.2s; + } + + .navicon-button { + display: inline-block; + position: fixed; + top: 1.5em; + right: 0; + z-index: 2; + } + + #main { + width: 100%; + min-width: 360px; + } + + #main h1.page-title { + margin: 1em 0; + } + + #main section { + padding: 0; + } + + footer { + margin-left: 0; + } +} + +@media only print { + nav { + display: none; + } + + #main { + float: none; + width: 100%; + } +} diff --git a/docs/styles/prettify-jsdoc.css b/docs/styles/prettify-jsdoc.css new file mode 100644 index 0000000..834a866 --- /dev/null +++ b/docs/styles/prettify-jsdoc.css @@ -0,0 +1,111 @@ +/* JSDoc prettify.js theme */ + +/* plain text */ +.pln { + color: #000000; + font-weight: normal; + font-style: normal; +} + +/* string content */ +.str { + color: hsl(104, 100%, 24%); + font-weight: normal; + font-style: normal; +} + +/* a keyword */ +.kwd { + color: #000000; + font-weight: bold; + font-style: normal; +} + +/* a comment */ +.com { + font-weight: normal; + font-style: italic; +} + +/* a type name */ +.typ { + color: #000000; + font-weight: normal; + font-style: normal; +} + +/* a literal value */ +.lit { + color: #006400; + font-weight: normal; + font-style: normal; +} + +/* punctuation */ +.pun { + color: #000000; + font-weight: bold; + font-style: normal; +} + +/* lisp open bracket */ +.opn { + color: #000000; + font-weight: bold; + font-style: normal; +} + +/* lisp close bracket */ +.clo { + color: #000000; + font-weight: bold; + font-style: normal; +} + +/* a markup tag name */ +.tag { + color: #006400; + font-weight: normal; + font-style: normal; +} + +/* a markup attribute name */ +.atn { + color: #006400; + font-weight: normal; + font-style: normal; +} + +/* a markup attribute value */ +.atv { + color: #006400; + font-weight: normal; + font-style: normal; +} + +/* a declaration */ +.dec { + color: #000000; + font-weight: bold; + font-style: normal; +} + +/* a variable name */ +.var { + color: #000000; + font-weight: normal; + font-style: normal; +} + +/* a function name */ +.fun { + color: #000000; + font-weight: bold; + font-style: normal; +} + +/* Specify class=linenums on a pre to get line numbering */ +ol.linenums { + margin-top: 0; + margin-bottom: 0; +} diff --git a/docs/styles/prettify-tomorrow.css b/docs/styles/prettify-tomorrow.css new file mode 100644 index 0000000..81e74d1 --- /dev/null +++ b/docs/styles/prettify-tomorrow.css @@ -0,0 +1,132 @@ +/* Tomorrow Theme */ +/* Original theme - https://github.com/chriskempson/tomorrow-theme */ +/* Pretty printing styles. Used with prettify.js. */ +/* SPAN elements with the classes below are added by prettyprint. */ +/* plain text */ +.pln { + color: #4d4d4c; } + +@media screen { + /* string content */ + .str { + color: hsl(104, 100%, 24%); } + + /* a keyword */ + .kwd { + color: hsl(240, 100%, 50%); } + + /* a comment */ + .com { + color: hsl(0, 0%, 60%); } + + /* a type name */ + .typ { + color: hsl(240, 100%, 32%); } + + /* a literal value */ + .lit { + color: hsl(240, 100%, 40%); } + + /* punctuation */ + .pun { + color: #000000; } + + /* lisp open bracket */ + .opn { + color: #000000; } + + /* lisp close bracket */ + .clo { + color: #000000; } + + /* a markup tag name */ + .tag { + color: #c82829; } + + /* a markup attribute name */ + .atn { + color: #f5871f; } + + /* a markup attribute value */ + .atv { + color: #3e999f; } + + /* a declaration */ + .dec { + color: #f5871f; } + + /* a variable name */ + .var { + color: #c82829; } + + /* a function name */ + .fun { + color: #4271ae; } } +/* Use higher contrast and text-weight for printable form. */ +@media print, projection { + .str { + color: #060; } + + .kwd { + color: #006; + font-weight: bold; } + + .com { + color: #600; + font-style: italic; } + + .typ { + color: #404; + font-weight: bold; } + + .lit { + color: #044; } + + .pun, .opn, .clo { + color: #440; } + + .tag { + color: #006; + font-weight: bold; } + + .atn { + color: #404; } + + .atv { + color: #060; } } +/* Style */ +/* +pre.prettyprint { + background: white; + font-family: Consolas, Monaco, 'Andale Mono', monospace; + font-size: 12px; + line-height: 1.5; + border: 1px solid #ccc; + padding: 10px; } +*/ + +/* Specify class=linenums on a pre to get line numbering */ +ol.linenums { + margin-top: 0; + margin-bottom: 0; } + +/* IE indents via margin-left */ +li.L0, +li.L1, +li.L2, +li.L3, +li.L4, +li.L5, +li.L6, +li.L7, +li.L8, +li.L9 { + /* */ } + +/* Alternate shading for lines */ +li.L1, +li.L3, +li.L5, +li.L7, +li.L9 { + /* */ } diff --git a/docs/tax-Remittance.js.html b/docs/tax-Remittance.js.html new file mode 100644 index 0000000..b492455 --- /dev/null +++ b/docs/tax-Remittance.js.html @@ -0,0 +1,177 @@ + + + + + + tax-Remittance.js - Documentation + + + + + + + + + + + + + + + + + +
+ +

tax-Remittance.js

+ + + + + + + +
+
+
import axios from "axios";
+import {
+  generateOAuthToken,
+  encryptSecurityCredential,
+  throwErrorMessages,
+  logErrorDetails,
+  callbackTrigger,
+  validateUrl,
+  handleCallbacks,
+} from "../utils/helpers.js";
+
+// Globals
+let callbackHandlerInitialized = false;
+let conditionalCallbackData = {};
+
+/**
+ * @name taxRemittance
+ * @description This API enables businesses to remit tax to Kenya Revenue Authority (KRA). To use this API, prior integration is required with KRA for tax declaration, payment registration number (PRN) generation, and exchange of other tax-related information.
+ * @summary Enables one to remit tax to Kenya Revenue Authority (KRA)
+ * @see {@link https://developer.safaricom.co.ke/APIs/TaxRemittance open external link}
+ * @param {Object} options Options for the tax remittance API.
+ * @param {string} options.initiator The M-Pesa API operator username with the tax remittance API initiator role.
+ * @param {number} options.amount The transaction amount.
+ * @param {string} options.remarks Information to be associated with the transaction.
+ * @param {number} options.partyA This is your own shortcode from which the money will be deducted.
+ * @param {number} options.partyB The account to which money will be credited.
+ * @param {number} options.accountReference The payment registration number (PRN) issued by KRA
+ * @param {string} options.QueueTimeOutURL URL to notify in case of a request timeout before processing.
+ * @param {string} options.resultUrl URL to send transaction results after processing.
+ * @param {boolean} [options.proErrorLogging] Logs out advanced error details - good for debugging
+ * @returns {Promise<Object>} remittanceResponse and (optional) conditionalCallbackData.
+ */
+async function taxRemittance({
+  initiator,
+  amount,
+  partyA,
+  partyB,
+  accountReference,
+  QueueTimeOutURL,
+  remarks,
+  resultUrl,
+  proErrorLogging = false,
+}) {
+  if (!callbackHandlerInitialized) {
+    console.info(
+      "\x1b[42m\x1b[30m\x1b[1m The default balance query callback handler ('handleTaxRemittanceCallbacks') was not called. Ignore if handling manually.\x1b[0m",
+    );
+  }
+  try {
+    validateUrl(resultUrl, "resultUrl");
+    validateUrl(QueueTimeOutURL, "timeoutUrl");
+    const { accessToken, baseURL } = await generateOAuthToken();
+    const req = axios.create({
+      baseURL,
+      headers: {
+        Authorization: `Bearer ${accessToken}`,
+        "Content-Type": "application/json",
+      },
+    });
+
+    // Default configurations
+    const config = {
+      commandId: "PayTaxToKRA",
+      senderIdentifierType: "4",
+      receiverIdentifierType: "4",
+    };
+    const responseBody = await req.post("/mpesa/b2b/v1/remittax", {
+      Initiator: initiator,
+      SecurityCredential: encryptSecurityCredential(),
+      CommandID: config.commandId,
+      SenderIdentifierType: config.senderIdentifierType,
+      RecieverIdentifierType: config.receiverIdentifierType,
+      Amount: amount,
+      PartyA: partyA,
+      PartyB: partyB,
+      AccountReference: accountReference,
+      Remarks: remarks,
+      QueueTimeOutURL: QueueTimeOutURL,
+      ResultURL: resultUrl,
+    });
+    conditionalCallbackData = await callbackTrigger(callbackHandlerInitialized);
+    return { remittanceResponse: responseBody.data, conditionalCallbackData };
+  } catch (error) {
+    if (proErrorLogging) {
+      console.info(
+        "\x1b[35m%s\x1b[0m",
+        "Advanced error logging for taxRemittance has been initialized",
+      );
+      logErrorDetails(
+        error,
+        {
+          apiEndpoint: "/mpesa/b2b/v1/remittax",
+          method: "POST",
+          payload: {
+            initiator,
+            amount,
+            partyA,
+            partyB,
+            accountReference,
+            QueueTimeOutURL,
+            resultUrl,
+            remarks
+          },
+        },
+        "Tax remittance error details:",
+      );
+    }
+    throw throwErrorMessages(error);
+  }
+}
+
+const handleTaxRemittanceCallbacks = async (app) => {
+  callbackHandlerInitialized = true;
+  await handleCallbacks(app, "taxRemittance");
+};
+
+export { taxRemittance, handleTaxRemittanceCallbacks };
+
+
+
+ + + + +
+ +
+ +
+ Generated by JSDoc 4.0.4 on Thu Feb 27 2025 21:37:15 GMT+0300 (East Africa Time) using the Minami theme. +
+ + + + + diff --git a/docs/transaction-Status.js.html b/docs/transaction-Status.js.html new file mode 100644 index 0000000..eeca164 --- /dev/null +++ b/docs/transaction-Status.js.html @@ -0,0 +1,182 @@ + + + + + + transaction-Status.js - Documentation + + + + + + + + + + + + + + + + + +
+ +

transaction-Status.js

+ + + + + + + +
+
+
import axios from "axios";
+import {
+  callbackTrigger,
+  encryptSecurityCredential,
+  generateOAuthToken,
+  handleCallbacks,
+  logErrorDetails,
+  throwErrorMessages,
+} from "../utils/helpers.js";
+import { IdentifierTypes } from "../utils/constants.js";
+
+// Globals
+let callbackHandlerInitialized = false;
+let conditionalCallbackData = {};
+
+/**
+ * @name transactionStatus
+ * @description Enables one to Check the status of an M-pesa transaction, using a unique identifier.
+ * @summary Check the status of a transaction.
+ * @see {@link https://developer.safaricom.co.ke/APIs/TransactionStatus open external link}
+ * @param {Object} options Options for the transaction status API.
+ * @param {string} options.transactionId Unique identifier to identify a transaction on M-pesa
+ * @param {string} options.initiator The name of the initiator initiating the request.
+ * @param {number} options.partyA Organization/MSISDN receiving the transaction.
+ * @param {number} options.identifierType Type of organization receiving the transaction.
+ * @param {string} options.remarks Information to be associated with the transaction.
+ * @param {string} options.occasion Information to be associated with the transaction.
+ * @param {string} options.OriginatorConversationID This is a globally unique identifier for the transaction request returned by the API proxy upon successful submission.
+ * @param {string} options.QueueTimeOutUrl URL for storing information about timeout transactions.
+ * @param {string} options.resultUrl The path that stores information of a transaction.
+ * @param {boolean} [options.proErrorLogging] Logs out advanced error details - good for debugging
+ * @returns {Promise<Object>} transactionStatusResponse and (optional) conditionalCallbackData.
+ */
+async function transactionStatus({
+  transactionId,
+  partyA,
+  identifierType,
+  QueueTimeOutUrl,
+  OriginatorConversationID,
+  remarks,
+  occasion,
+  resultUrl,
+  initiator,
+  proErrorLogging = false,
+}) {
+  if (!callbackHandlerInitialized) {
+    console.info(
+      "\x1b[42m\x1b[30m\x1b[1m The default balance query callback handler ('handleTransactStatusCallbacks') was not called. Ignore if handling manually.\x1b[0m",
+    );
+  }
+  /**
+   * @param {number} validIdentifierTypes - Expected identifiers include const IdentifierTypes = { MSISDN: 1, TILL_NUMBER: 2, ORG_SHORTCODE: 4 };
+   */
+  const validIdentifierTypes = Object.values(IdentifierTypes);
+  if (!validIdentifierTypes.includes(identifierType)) {
+    throw new Error(
+      `Invalid identifierType provided. Must be one of: ${validIdentifierTypes.join(", ")}`,
+    );
+  }
+  try {
+    const { accessToken, baseURL } = await generateOAuthToken();
+    const req = axios.create({
+      baseURL,
+      headers: {
+        Authorization: `Bearer ${accessToken}`,
+        "Content-Type": "application/json",
+      },
+    });
+    // Default configurations
+    const config = {
+      commandId: "TransactionStatusQuery",
+    };
+    const responseBody = await req.post("/mpesa/transactionstatus/v1/query", {
+      Initiator: initiator,
+      SecurityCredential: encryptSecurityCredential(),
+      CommandID: config.commandId,
+      TransactionID: transactionId,
+      PartyA: partyA,
+      OriginatorConversationID,
+      IdentifierType: identifierType,
+      ResultURL: resultUrl,
+      QueueTimeOutURL: QueueTimeOutUrl,
+      Remarks: remarks,
+      Occasion: occasion,
+    });
+    conditionalCallbackData = await callbackTrigger(callbackHandlerInitialized);
+    return { transactStatusResponse: responseBody.data, conditionalCallbackData };
+  } catch (error) {
+    if (proErrorLogging) {
+      console.info(
+        "\x1b[35m%s\x1b[0m",
+        "Advanced error logging for transactionStatus has been initialized",
+      );
+      logErrorDetails(
+        error,
+        {
+          apiEndpoint: "/mpesa/transactionstatus/v1/query",
+          method: "POST",
+          payload: {
+            transactionId,
+            partyA,
+            identifierType,
+            QueueTimeOutUrl,
+            resultUrl,
+            initiator,
+            remarks,
+            occasion
+          },
+        },
+        "Transaction status error details:",
+      );
+    }
+    throw throwErrorMessages(error);
+  }
+}
+
+const handleTransactStatusCallbacks = async (app) => {
+  callbackHandlerInitialized = true;
+  await handleCallbacks(app, "transactionStatus");
+};
+
+export { transactionStatus, handleTransactStatusCallbacks };
+
+
+
+ + + + +
+ +
+ +
+ Generated by JSDoc 4.0.4 on Thu Feb 27 2025 21:37:15 GMT+0300 (East Africa Time) using the Minami theme. +
+ + + + + diff --git a/image.jpg b/image.jpg new file mode 100644 index 0000000000000000000000000000000000000000..44fb8c2610da82961e58c384ee921711f68965b6 GIT binary patch literal 41737 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 - b2cRequestApi(options: b2cRequestApiOptions): Promise; - c2BRegister(options: C2BRegisterOptions): Promise; - c2bSimulateApi(options: c2bSimulateApiOptions): Promise; - mpesaSimulateApi(options: MpesaSimulateApiOptions): Promise; - mpesaQueryApi(options: mpesaQueryApiOptions): Promise; - reversalsApi(options: reversalsApiOptions): Promise; - transactionStatusApi(options: transactionStatusApiOptions): Promise; - generateQrCodeApi(options: generateQrCodeApiOptions): Promise; - b2bTopUpApi(options: b2bTopUpApiOptions): Promise; - businessPayBillApi(options: businessPaybillApiOptions): Promise; - taxRemittanceApi(options: TaxRemittanceOptions): Promise; + /** + * @name balanceQuery + * @description The Account Balance API is used to request the account balance of a short code. This can be used for both B2C, buy goods and pay bill accounts. + * @summary Retrieves the balance of a short code associated with the developer account, supporting B2C, buy goods, and pay bill accounts. + * @see {@link https://developer.safaricom.co.ke/APIs/AccountBalance open external link} + */ + balanceQuery(options: balanceQueryOptions): Promise; + /** + * @name b2cRequest + * @description B2C API can be used in several scenarios by businesses that require to either make Salary Payments, Cashback payments, Promotional Payments(e.g. betting winning payouts), winnings, financial institutions withdrawal of funds, loan disbursements, etc. + * @summary B2C payments involve a business sending money to an individual. This is a direct transaction from a business shortcode to a consumer's mobile number (MSISDN). + * @see {@link https://developer.safaricom.co.ke/APIs/BusinessToCustomer open external link} + */ + b2cRequest(options: b2cRequestOptions): Promise; + /** + * @name c2BRegister + * @description Register URL API works hand in hand with Customer to Business (C2B) APIs and allows receiving payment notifications to your paybill. This API enables you to register the callback URLs via which you shall receive notifications for payments to your pay bill/till numbers + * @summary Registers callback validation and confirmation URLs on M-Pesa to receive payment notifications for your paybill/till numbers. + * @see {@link https://developer.safaricom.co.ke/APIs/CustomerToBusinessRegisterURL open external link} + */ + c2BRegister(options: c2bRegisterOptions): Promise; + /** + * @name c2bSimulate + * @description This function simulates a C2B (Customer to Business) transaction by initiating a payment from a phone number to a business shortcode. + * @summary This API initiates a payment from a phone number to a business shortcode + * @see {@link https://developer.safaricom.co.ke/c2b/apis/post/simulate open external link} + */ + c2bSimulate(options: c2bSimulateOptions): Promise; + /** + * @name mpesaSimulate + * @description Lipa na M-PESA online API also known as M-PESA express (STK Push/NI push) is a Merchant/Business initiated C2B (Customer to Business) Payment. + * @summary Enable you to send a payment prompt on the customer's phone to your customer's M-PESA registered phone number + * @see {@link https://developer.safaricom.co.ke/APIs/MpesaExpressSimulate open external link} + */ + mpesaSimulate(options: mpesaSimulateOptions): Promise; + /** + * @name mpesaQuery + * @description This API endpoint enables one to check the status of a Lipa Na M-Pesa Online Payment. + * @summary Fetches the transaction status of a Lipa na M-Pesa payment + * @see {@link https://developer.safaricom.co.ke/APIs/MpesaExpressQuery open external link} + */ + mpesaQuery(options: mpesaQueryOptions): Promise; + /** + * @name reversals + * @description Enables one to reverse a previously successful Mpesa (simulate) transaction, using a unique identifier + * @summary Reverse successful Mpesa transaction + * @see {@link https://developer.safaricom.co.ke/APIs/Reversal open external link} + */ + reversals(options: reversalsOptions): Promise; + /** + * @name transactionStatus + * @description Enables one to Check the status of an Mpesa transaction, using a unique identifier + * @summary Fetches Mpesa transaction status + * @see {@link https://developer.safaricom.co.ke/APIs/TransactionStatus open external link} + */ + transactionStatus(options: transactionStatusOptions): Promise; + /** + * @name generateQrCode + * @description This generates a Dynamic QR which enables Safaricom M-PESA customers who have My Safaricom App or M-PESA app, to scan a QR (Quick Response) code, to capture till number and amount + * @summary Generates QR codes for customers using My Safaricom app + * @see {@link https://developer.safaricom.co.ke/APIs/DynamicQRCode open external link} + */ + generateQrCode(options: generateQrCodeOptions): Promise; + /** + *@name b2cTopUp + *@description B2C Account Top-Up - Use this API to load funds to a B2C shortcode directly for disbursement. + *@summary Moves money from your MMF/Working account to the recipient’s utility account. + * @see {@link https://developer.safaricom.co.ke/APIs/B2CAccountTopUp open external link} + */ + b2cTopUp(options: b2cTopUpOptions): Promise; + /** + * @name businessPayBill + * @description This API enables you to pay bills directly from your business account to a pay bill number, or a paybill store. You can use this API to pay on behalf of a consumer/requester. The transaction moves money from your MMF/Working account to the recipient’s utility account. + * @summary Facilitates bill payments from a business account to a paybill number, transferring funds to the recipient’s utility account. + * @see {@link https://developer.safaricom.co.ke/APIs/BusinessPayBill open external link} + */ + businessPayBill(options: businessPaybillOptions): Promise; + /** + * @name taxRemittance + * @description This API enables businesses to remit tax to Kenya Revenue Authority (KRA). To use this API, prior integration is required with KRA for tax declaration, payment registration number (PRN) generation, and exchange of other tax-related information. + * @summary Enables one to remit tax to Kenya Revenue Authority (KRA) + * @see {@link https://developer.safaricom.co.ke/APIs/TaxRemittance open external link} + */ + taxRemittance(options: taxRemittanceOptions): Promise; +}; + +interface AppWithUse { + /** + * Registers a middleware function. + * @param middleware The middleware function to register. + */ + use(middleware: (...args: any[]) => void): void; +} + +declare const callbacks: { + /** + * @name handleBalanceQueryCallbacks + * @description Default callback handler for the balanceQuery API + **/ + handleBalanceQueryCallbacks: (app: AppWithUse) => void; + /** + * @name handleBusinessPaybillCallbacks + * @description Default callback handler for the businessPaybill API + **/ + handleBusinessPaybillCallbacks: (app: AppWithUse) => void; + /** + * @name handleTaxRemittanceCallbacks + * @description Default callback handler for the taxRemittance API + **/ + handleTaxRemittanceCallbacks: (app: AppWithUse) => void; + /** + * @name handleB2cTopUpCallbacks + * @description Default callback handler for the b2cTopUp API + **/ + handleB2cTopUpCallbacks: (app: AppWithUse) => void; + /** + * @name handleTransactStatusCallbacks + * @description Default callback handler for the transaction status API + **/ + handleTransactStatusCallbacks: (app: AppWithUse) => void; + /** + * @name handleB2cRequestCallbacks + * @description Default callback handler for the b2cRequest API + **/ + handleB2cRequestCallbacks: (app: AppWithUse) => void; + /** + * @name handleC2bRegisterCallbacks + * @description Default callback handler for the c2bRegister API + **/ + handleC2bRegisterCallbacks: (app: AppWithUse) => void; + /** + * @name handleReversalCallbacks + * @description Default callback handler for the reversals API + **/ + handleReversalCallbacks: (app: AppWithUse) => void; + /** + * @name handleMpesaSimulateCallbacks + * @description Default callback handler for the mpesaSimulate API + **/ + handleMpesaSimulateCallbacks: (app: AppWithUse) => void; }; -export default mpesa; +export { mpesa, callbacks }; diff --git a/index.js b/index.js index 58b383b..8f6aa09 100644 --- a/index.js +++ b/index.js @@ -1,28 +1,68 @@ -import balanceQueryApi from "./lib/core/apis/balance-Query.js"; -import b2cRequestApi from "./lib/core/apis/b2c-Request.js"; -import c2bRegisterApi from "./lib/core/apis/c2b-Register.js"; -import c2bSimulateApi from "./lib/core/apis/c2b-Simulate.js"; -import mpesaSimulateApi from "./lib/core/apis/mpesa-Simulate.js"; -import mpesaQueryApi from "./lib/core/apis/mpesa-Query.js"; -import reversalsApi from "./lib/core/apis/reversals.js"; -import generateQrCodeApi from "./lib/core/apis/qr-Generate.js"; -import transactionStatusApi from "./lib/core/apis/transaction-Status.js"; -import b2bTopUpApi from "./lib/core/apis/b2b-Topup.js"; -import businessPaybillApi from "./lib/core/apis/business-Paybill.js"; -import taxRemittanceApi from "./lib/core/apis/tax-Remittance.js"; +import { + balanceQuery, + handleBalanceQueryCallbacks, +} from "./lib/core/apis/balance-Query.js"; +import { + b2cRequest, + handleB2cRequestCallbacks, +} from "./lib/core/apis/b2c-Request.js"; +import { + c2bRegister, + handleC2bRegisterCallbacks, +} from "./lib/core/apis/c2b-Register.js"; +import { + transactionStatus, + handleTransactStatusCallbacks, +} from "./lib/core/apis/transaction-Status.js"; +import { + b2cTopUp, + handleB2cTopUpCallbacks, +} from "./lib/core/apis/b2c-Topup.js"; +import { + businessPaybill, + handleBusinessPaybillCallbacks, +} from "./lib/core/apis/business-Paybill.js"; +import { + taxRemittance, + handleTaxRemittanceCallbacks, +} from "./lib/core/apis/tax-Remittance.js"; +import { + reversals, + handleReversalCallbacks, +} from "./lib/core/apis/reversals.js"; +import { + mpesaSimulate, + handleMpesaSimulateCallbacks, +} from "./lib/core/apis/mpesa-Simulate.js"; +import { c2bSimulate } from "./lib/core/apis/c2b-Simulate.js"; +import { mpesaQuery } from "./lib/core/apis/mpesa-Query.js"; +import { generateQrCode } from "./lib/core/apis/qr-Generate.js"; const mpesa = { - balanceQueryApi, - b2cRequestApi, - c2bRegisterApi, - c2bSimulateApi, - mpesaSimulateApi, - mpesaQueryApi, - reversalsApi, - generateQrCodeApi, - transactionStatusApi, - b2bTopUpApi, - businessPaybillApi, - taxRemittanceApi, + balanceQuery, + b2cRequest, + c2bRegister, + c2bSimulate, + mpesaSimulate, + mpesaQuery, + reversals, + generateQrCode, + transactionStatus, + b2cTopUp, + businessPaybill, + taxRemittance, }; -export default mpesa; + +const callbacks = { + handleBalanceQueryCallbacks, + handleBusinessPaybillCallbacks, + handleTaxRemittanceCallbacks, + handleB2cTopUpCallbacks, + handleTransactStatusCallbacks, + handleB2cRequestCallbacks, + handleC2bRegisterCallbacks, + handleReversalCallbacks, + handleMpesaSimulateCallbacks, +}; + +export { mpesa, callbacks }; diff --git a/jsdoc.json b/jsdoc.json new file mode 100644 index 0000000..911f7e4 --- /dev/null +++ b/jsdoc.json @@ -0,0 +1,27 @@ +{ + "source": { + "include": ["lib/core/apis", "lib/core/JSDOC.md"], + "includePattern": ".js$", + "excludePattern": "(node_modules/|docs)" + }, + "tags": { + "allowUnknownTags": true, + "dictionaries": ["jsdoc"] + }, + "plugins": [ + "plugins/markdown" + ], + "opts": { + "destination": "./docs/", + "encoding": "utf8", + "private": true, + "recurse": true, + "template": "./node_modules/minami" + }, + "templates": { + "cleverLinks": false, + "monospaceLinks": true, + "useLongnameInNav": false, + "showInheritedInNav": true + } +} diff --git a/lib/core/JSDOC.md b/lib/core/JSDOC.md new file mode 100644 index 0000000..593331e --- /dev/null +++ b/lib/core/JSDOC.md @@ -0,0 +1,24 @@ +# MPESA-NODE LIBRARY +### JSDocs guide + +## About + +This documentation is generated using JSDoc and is designed to help developers accurately map fields to API endpoints. It provides a clear understanding of which fields are preconfigured by default and which can be modified based on your requirements. + +By referring to this guide, you can ensure that you're using the API correctly and efficiently. Whether you're integrating the library or extending its functionality, this documentation will serve as a valuable reference. + +## Keeping Documentation Up to Date + +If you're contributing to the project, make sure to regenerate the documentation whenever changes are made to the API. Use the following commands: + +### Generate Documentation + +After updating the library, you may **optionally run** the commands below to regenerate the documentation. However, if you prefer, you can leave this task to the repository maintainer. + +**npm**:`npm docs` + +**yarn**:`yarn docs` + +### Takeaways + +Keeping the documentation updated ensures that all contributors and users have access to accurate and up-to-date information. diff --git a/lib/core/apis/b2b-Topup.js b/lib/core/apis/b2b-Topup.js deleted file mode 100644 index e3838fc..0000000 --- a/lib/core/apis/b2b-Topup.js +++ /dev/null @@ -1,110 +0,0 @@ -import axios from "axios"; -import { - generateOAuthToken, - encryptSecurityCredential, - throwErrorMessages, -} from "../utils/helpers.js"; - -/** - * Logs error details for advanced debugging. - * @param {Error} error - The error object caught in the catch block. - * @param {Object} context - Additional context about the API request, such as endpoint, method, and payload. - */ -function logErrorDetails(error, context) { - console.error("B2b account top up error details:", { - message: error.message, - stack: error.stack, - ...(error.response && { - status: error.response.status, - data: error.response.data, - headers: error.response.headers, - }), - ...(error.request && { request: error.request }), - context, - }); -} - -/** - * B2B Account Top-Up - Use this API to load funds to a B2B shortcode for disbursement. - * @name b2bTopUpApi - * @function - * @see {@link https://developer.safaricom.co.ke/b2b/apis/post/paymentrequest|B2B Account Top-Up} - * @param {Object} options - Options for the B2B account top-up request. - * @param {string} options.initiator - The M-Pesa API operator username with the B2B API initiator role.. - * @param {string} [options.commandId='businessPaybillApi'] - Transaction command ID (e.g., 'businessPaybillApi'). - * @param {string} options.senderIdentifierType - Identifier type of the shortcode from which funds are deducted. This API supports type "4" only. - * @param {string} options.receiverIdentifierType - Identifier type of the shortcode to which funds are credited. This API supports type "4" only. - * @param {number} options.amount - The transaction amount to be moved. - * @param {number} options.partyA - Shortcode from which the funds will be deducted. - * @param {number} options.partyB - Shortcode to which the funds will be transferred. - * @param {number} options.accountReference - Account reference, usually an identifier for the transaction. - * @param {string} options.remarks - Any additional information to be associated with the transaction. - * @param {number} options.requester - Optional. The consumer’s mobile number on behalf of whom you are paying. - * @param {string} options.queueTimeOutURL - URL to notify in case the request times out before processing. - * @param {string} options.resultURL - URL to send transaction results after processing. - * @returns {Promise} - Returns a promise that resolves to the B2B transaction response. - */ - -async function b2bTopUpApi({ - initiator, - commandId = "BusinessPayToBulk", - senderIdentifierType = "4", - receiverIdentifierType = "4", - amount, - partyA, - partyB, - accountReference, - requester, - remarks = "OK!", - queueTimeOutURL, - resultURL, -}) { - const { accessToken, baseURL } = await generateOAuthToken(); - const req = axios.create({ - baseURL, - headers: { - Authorization: `Bearer ${accessToken}`, - "Content-Type": "application/json", - }, - }); - try { - const response = await req.post("mpesa/b2b/v1/paymentrequest", { - Initiator: initiator, - SecurityCredential: encryptSecurityCredential(), - CommandID: commandId, - SenderIdentifierType: senderIdentifierType, - RecieverIdentifierType: receiverIdentifierType, - Amount: amount, - PartyA: partyA, - PartyB: partyB, - AccountReference: accountReference, - Requester: requester, - Remarks: remarks, - QueueTimeOutURL: queueTimeOutURL, - ResultURL: resultURL, - }); - return response.data; - } catch (error) { - // Advanced error logging - logErrorDetails(error, { - apiEndpoint: "/mpesa/b2b/v1/paymentrequest", - method: "POST", - payload: { - initiator, - commandId, - senderIdentifierType, - receiverIdentifierType, - amount, - partyA, - partyB, - accountReference, - requester, - remarks, - queueTimeOutURL, - resultURL, - }, - }); - throwErrorMessages(error); - } -} -export default b2bTopUpApi; diff --git a/lib/core/apis/b2c-Request.js b/lib/core/apis/b2c-Request.js index 605c89a..8a67524 100644 --- a/lib/core/apis/b2c-Request.js +++ b/lib/core/apis/b2c-Request.js @@ -1,115 +1,126 @@ import axios from "axios"; import { CommandIDs } from "../utils/constants.js"; import { + callbackTrigger, encryptSecurityCredential, generateOAuthToken, + handleCallbacks, + logErrorDetails, originatorID, - throwErrorMessages, + throwErrorMessages, validateFormatPhone, + validateUrl } from "../utils/helpers.js"; -/** - * Logs detailed error information to assist in debugging. - * @param {Error} error - The error object thrown by the API request. - * @param {Object} context - Additional context about the request, such as endpoint and payload. - */ -function logErrorDetails(error, context) { - console.error("B2C transaction error details:", { - message: error.message, - stack: error.stack, - ...(error.response && { - status: error.response.status, - data: error.response.data, - headers: error.response.headers, - }), - ...(error.request && { request: error.request }), - context, - }); -} + +// Globals +let callbackHandlerInitialized = false; +let conditionalCallbackData = {}; /** - * B2C Payment Request - * @name b2cRequestApi - * @function - * @description B2C Request - Use this API to transact between an M-Pesa shortcode and a phone number registered on M-Pesa. + * @name b2cRequest + * @description B2C API can be used in several scenarios by businesses that require to either make Salary Payments, Cashback payments, Promotional Payments(e.g. betting winning payouts), winnings, financial institutions withdrawal of funds, loan disbursements, etc. * @summary B2C payments involve a business sending money to an individual. This is a direct transaction from a business shortcode to a consumer's mobile number (MSISDN). - * @see {@link https://developer.safaricom.co.ke/APIs/BusinessToCustomer Payment Request} - * @param {Object} options - Options for the B2C payment request. - * @param {number} options.senderParty - The organization (shortcode) sending the transaction. - * @param {number} options.receiverParty - The MSISDN receiving the transaction. - * @param {number} options.amount - The amount being transacted. - * @param {string} options.queueUrl - URL for timeout notification on transaction failure. - * @param {string} options.resultUrl - URL to receive transaction results from M-Pesa. - * @param {string} [options.commandId=CommandIDs.BUSINESS_PAYMENT] - Unique command for each transaction type. - * @param {string} [options.initiatorName=null] - The name of the initiator initiating the request (default: configured initiator). - * @param {string} [options.remarks='B2C Payment'] - Remarks to include with the transaction. - * @param {string} [options.occasion='Any event'] - Occasion for the payment (optional). - * @return {Promise} - Returns a promise that resolves to the transaction result. - */ -async function b2cRequestApi({ - senderParty, - receiverParty, + * @see {@link https://developer.safaricom.co.ke/APIs/BusinessToCustomer open external link} + * @param {Object} options Options for the B2C payment request. + * @param {number} options.partyA The B2C organization shortcode sending the money. + * @param {string} options.partyB Customer mobile number (with country code, e.g., 254). + * @param {number} options.amount The amount being transacted. + * @param {string} options.remarks Information to be associated with the transaction. + * @param {string} options.occasion Information to be associated with the transaction. + * @param {string} options.QueueTimeOutURL URL for timeout notifications. + * @param {string} options.resultUrl URL for M-PESA to send payment processing notifications. + * @param {string} options.commandId Unique command specifying B2C transaction type (e.g., BusinessPayment). + * @param {string} options.initiatorName API user created by Business Administrator for B2C transactions. + * @param {boolean} [options.proErrorLogging=false] Logs out advanced error details - good for debugging. + * @return {Promise} b2cRequestResponse and (optional) conditionalCallbackData. */ +async function b2cRequest({ + partyA, + partyB, amount, - queueUrl, + QueueTimeOutURL, + remarks, + occasion, resultUrl, commandId, - initiatorName, - remarks = "OK!", - occasion = "TESTING API!", + initiatorName = "", + proErrorLogging = false, }) { - // Validate commandId + if (!callbackHandlerInitialized) { + console.info( + "\x1b[42m\x1b[30m\x1b[1m The default balance query callback handler ('handleB2cRequestCallbacks') was not called. Ignore if handling manually.\x1b[0m", + ); + } + /** + * @param {string} validCommandIds - loops through object values with strings "SalaryPayment", "BusinessPayment" PromotionPayment" + */ const validCommandIds = Object.values(CommandIDs); if (!validCommandIds.includes(commandId)) { throw new Error( `Invalid commandId provided. Must be one of: ${validCommandIds.join(", ")}`, ); } + const _msisdn = validateFormatPhone(partyB) + try { + validateUrl(resultUrl, "resultUrl"); + validateUrl(QueueTimeOutURL, "queueTimeOutUrl"); + const OriginatorConversationID = originatorID(); + const { accessToken, baseURL } = await generateOAuthToken(); - const OriginatorConversationID = originatorID(); - const { accessToken, baseURL } = await generateOAuthToken(); - const req = axios.create({ - baseURL, - headers: { - Authorization: `Bearer ${accessToken}`, - "Content-Type": "application/json", - }, - }); + const req = axios.create({ + baseURL, + headers: { + Authorization: `Bearer ${accessToken}`, + "Content-Type": "application/json", + }, + }); - try { const responseBody = await req.post("/mpesa/b2c/v3/paymentrequest", { - OriginatorConversationID: OriginatorConversationID, + OriginatorConversationID, InitiatorName: initiatorName, SecurityCredential: encryptSecurityCredential(), CommandID: commandId, Amount: amount, - PartyA: senderParty, - PartyB: receiverParty, + PartyA: partyA, + PartyB: _msisdn, Remarks: remarks, - QueueTimeOutURL: queueUrl, + QueueTimeOutURL: QueueTimeOutURL, ResultURL: resultUrl, - Occasion: occasion, + Occasion: occasion }); - return responseBody.data; // Return the payload data directly - } catch (error) { - // Log detailed error information - logErrorDetails(error, { - apiEndpoint: "/mpesa/b2c/v3/paymentrequest", - method: "POST", - payload: { - senderParty, - receiverParty, - amount, - queueUrl, - resultUrl, - commandId, - initiatorName, - remarks, - occasion, - }, - }); + conditionalCallbackData = await callbackTrigger(callbackHandlerInitialized); - // Categorize and throw user-friendly error messages - throwErrorMessages(error); + return { b2cRequestResponse: responseBody.data, conditionalCallbackData}; + } catch (error) { + if (proErrorLogging) { + console.info( + "\x1b[35m%s\x1b[0m", + "Advanced error logging for b2cRequest has been initialized", + ); + logErrorDetails( + error, + { + apiEndpoint: "/mpesa/b2c/v3/paymentrequest", + method: "POST", + payload: { + partyA, + _msisdn, + amount, + QueueTimeOutURL, + resultUrl, + commandId, + initiatorName, + occasion, + remarks + }, + }, + "B2C transaction error details:", + ); + } + throw throwErrorMessages(error); } } - -export default b2cRequestApi; +const handleB2cRequestCallbacks = async (app) => { + callbackHandlerInitialized = true; + await handleCallbacks(app, "b2cRequest"); +}; +export { b2cRequest, handleB2cRequestCallbacks }; diff --git a/lib/core/apis/b2c-Topup.js b/lib/core/apis/b2c-Topup.js new file mode 100644 index 0000000..51510b4 --- /dev/null +++ b/lib/core/apis/b2c-Topup.js @@ -0,0 +1,122 @@ +import axios from "axios"; +import { + generateOAuthToken, + encryptSecurityCredential, + throwErrorMessages, + logErrorDetails, + callbackTrigger, + handleCallbacks, + validateUrl, +} from "../utils/helpers.js"; + +// Globals +let callbackHandlerInitialized = false; +let conditionalCallbackData = {}; + +/** + *@name b2cTopUp + *@description This API enables you to load funds to a B2C shortcode directly for disbursement. The transaction moves money from your MMF/Working account to the recipient’s utility account. + *@summary Transfers funds from your MMF/Working account to the recipient's utility account for disbursement to a B2C shortcode. + * @see {@link https://developer.safaricom.co.ke/APIs/B2CAccountTopUp open external link} + * @param {Object} options B2C account top-up request options. + * @param {string} options.initiator The M-Pesa API operator username, who needs Org Business Pay to Bulk API initiator role. + * @param {number} options.amount The transaction amount. + * @param {number} options.partyA Your shortcode. The shortcode from which money will be deducted. + * @param {number} options.partyB The shortcode to which money will be moved + * @param {string} options.remarks Information to be associated with the transaction. + * @param {number} options.accountReference Identifier for the transaction. + * @param {number} [options.requester] Optional. The consumer’s mobile number on behalf of whom you are paying. + * @param {string} options.QueueTimeOutURL A URL that will be used to notify your system in case the request times out. + * @param {string} options.resultUrl A URL that will be used to send transaction results after processing. + * @param {boolean} [options.proErrorLogging] Logs out advanced error details - good for debugging + * @return {Promise} b2cTopUpResponse and (optional) conditionalCallbackData. */ +async function b2cTopUp({ + initiator, + amount, + partyA, + partyB, + accountReference, + requester, + QueueTimeOutURL, + resultUrl, + remarks, + proErrorLogging = false, +}) { + if (!callbackHandlerInitialized) { + console.info( + "\x1b[42m\x1b[30m\x1b[1m The default callback handler ('handleB2cTopUpCallbacks') was not called. Ignore if handling manually.\x1b[0m", + ); + } + try { + validateUrl(resultUrl, "resultUrl"); + validateUrl(QueueTimeOutURL, "QueueTimeOutUrl"); + const { accessToken, baseURL } = await generateOAuthToken(); + const req = axios.create({ + baseURL, + headers: { + Authorization: `Bearer ${accessToken}`, + "Content-Type": "application/json", + }, + }); + + // Default configurations + const config = { + commandId: "BusinessPayToBulk", + senderIdentifierType: "4", + receiverIdentifierType: "4", + }; + + const responseBody = await req.post("mpesa/b2b/v1/paymentrequest", { + Initiator: initiator, + SecurityCredential: encryptSecurityCredential(), + CommandID: config.commandId, + SenderIdentifierType: config.senderIdentifierType, + RecieverIdentifierType: config.receiverIdentifierType, + Amount: amount, + PartyA: partyA, + PartyB: partyB, + AccountReference: accountReference, + Requester: requester, + Remarks: remarks, + QueueTimeOutURL: QueueTimeOutURL, + ResultURL: resultUrl, + }); + + conditionalCallbackData = await callbackTrigger(callbackHandlerInitialized); + + return { b2cTopUpResponse: responseBody.data, conditionalCallbackData }; + } catch (error) { + if (proErrorLogging) { + console.info( + "\x1b[35m%s\x1b[0m", + "Advanced error logging for b2cTopUp has been initialized", + ); + logErrorDetails( + error, + { + apiEndpoint: "/mpesa/b2b/v1/paymentrequest", + method: "POST", + payload: { + initiator, + amount, + partyA, + partyB, + accountReference, + requester, + QueueTimeOutURL, + resultUrl, + remarks + }, + }, + "B2C account top-up error details:", + ); + } + throw throwErrorMessages(error); + } +} + +const handleB2cTopUpCallbacks = async (app) => { + callbackHandlerInitialized = true; + await handleCallbacks(app, "b2cTopUp"); +}; +export { b2cTopUp, handleB2cTopUpCallbacks }; diff --git a/lib/core/apis/balance-Query.js b/lib/core/apis/balance-Query.js index 300df18..61e2b64 100644 --- a/lib/core/apis/balance-Query.js +++ b/lib/core/apis/balance-Query.js @@ -1,57 +1,62 @@ import axios from "axios"; import { + callbackTrigger, encryptSecurityCredential, generateOAuthToken, + handleCallbacks, + logErrorDetails, throwErrorMessages, + validateUrl, } from "../utils/helpers.js"; +import { IdentifierTypes } from "../utils/constants.js"; + +// Globals +let callbackHandlerInitialized = false; +let conditionalCallbackData = {}; + /** - * Logs error details for advanced debugging. - * @param {Error} error - The error object caught in the catch block. - * @param {Object} context - Additional context about the API request, such as endpoint, method, and payload. - */ -function logErrorDetails(error, context) { - console.error("Error Details:", { - message: error.message, - stack: error.stack, - ...(error.response && { - status: error.response.status, - data: error.response.data, - headers: error.response.headers, - }), - ...(error.request && { request: error.request }), - context, - }); -} -/** - * AccountBalance - Use this API to enquire the balance on an M-Pesa BuyGoods (Till Number). - * Simultaneously generates an OAuth token using the consumer key and secret. - * @name balanceQueryApi - * @function - * @see {@link https://developer.safaricom.co.ke/APIs/AccountBalance Balance Request} - * @param {Object} options - Options for the account balance query. - * @param {number} options.shortCode - The organization’s short code (Till Number) to check the balance for. - * @param {number} options.idType - The type of organization receiving the transaction. - * @param {String} options.queueUrl - URL for timeout notification on transaction failure. - * @param {String} options.resultUrl - URL for notification of the transaction result. - * @param {String} [options.remarks='Checking account balance'] - Optional remarks sent with the transaction. - * @param {String} [options.initiator=null] - Name of the person/system initiating the request (defaults to configured initiator). - * @return {Promise} - Returns a promise that resolves to the account balance or transaction status. + * @name balanceQuery + * @description The Account Balance API is used to request the account balance of a short code. This can be used for both B2C, buy goods and pay bill accounts. + * @summary Retrieves the balance of a short code associated with the developer account, supporting B2C, buy goods, and pay bill accounts. + * @see {@link https://developer.safaricom.co.ke/APIs/AccountBalance open external link} + * @param {Object} options Options for the balance query API. + * @param {number} options.partyA The shortcode of the querying organization. + * @param {number} options.identifierType Type of the querying organization. + * @param {string} options.remarks Information to be associated with the transaction. + * @param {String} options.QueueTimeOutURL URL to receive timeout messages. + * @param {String} options.resultUrl URL to receive the result message. + * @param {String} options.initiator The credential/username for authentication. + * @param {boolean} [options.proErrorLogging=false] Logs out advanced error details - good for debugging + * @return {Promise} balanceQueryResponse and (optional) conditionalCallbackData. */ - -async function balanceQueryApi({ - shortCode, - idType, - queueUrl, - commandId = "AccountBalance", +async function balanceQuery({ + partyA, + identifierType, + QueueTimeOutURL, resultUrl, - remarks = "Checking account balance", - initiator = null, + initiator, + remarks, + proErrorLogging = false, }) { + if (!callbackHandlerInitialized) { + console.info( + "\x1b[42m\x1b[30m\x1b[1m The default balance query callback handler ('handleBalanceQueryCallbacks') was not called. Ignore if handling manually.\x1b[0m" + ); + } + /** + * @param {number} validIdentifierTypes - Expected identifiers include const IdentifierTypes = { MSISDN: 1, TILL_NUMBER: 2, ORG_SHORTCODE: 4 }; + */ + const validIdentifierTypes = Object.values(IdentifierTypes); + if (!validIdentifierTypes.includes(identifierType)) { + throw new Error( + `Invalid identifierType provided. Must be one of: ${validIdentifierTypes.join(", ")}`, + ); + } try { - // Generate OAuth token + validateUrl(resultUrl, "resultUrl"); + validateUrl(QueueTimeOutURL, "timeoutUrl"); const { accessToken, baseURL } = await generateOAuthToken(); - // Prepare the request with OAuth token const req = axios.create({ baseURL, headers: { @@ -60,38 +65,49 @@ async function balanceQueryApi({ }, }); - // Execute the Account Balance API request + // Default configurations + const config = { + commandId: "AccountBalance", + remarks: "OK", + }; + const responseBody = await req.post("/mpesa/accountbalance/v1/query", { Initiator: initiator, SecurityCredential: encryptSecurityCredential(), - CommandID: commandId, - PartyA: shortCode, - IdentifierType: idType, + CommandID: config.commandId, + PartyA: partyA, + IdentifierType: identifierType, Remarks: remarks, - QueueTimeOutURL: queueUrl, + QueueTimeOutURL: QueueTimeOutURL, ResultURL: resultUrl, }); - return responseBody.data; // Return the response data directly - } catch (error) { - // Advanced error logging and categorization - logErrorDetails(error, { - apiEndpoint: "/mpesa/accountbalance/v1/query", - method: "POST", - payload: { - shortCode, - idType, - queueUrl, - commandId, - resultUrl, - remarks, - initiator, - }, - }); + conditionalCallbackData = await callbackTrigger(callbackHandlerInitialized); - // Categorize and handle error types - throwErrorMessages(error); + return { balanceQueryResponse: responseBody.data, conditionalCallbackData }; + } catch (error) { + if (proErrorLogging) { + console.info( + "\x1b[35m%s\x1b[0m", + "Advanced error logging for balanceQuery has been initialized", + ); + logErrorDetails( + error, + { + apiEndpoint: "/mpesa/accountbalance/v1/query", + method: "POST", + payload: { partyA, identifierType, QueueTimeOutURL, resultUrl, initiator, remarks }, + }, + "Balance query error details:", + ); + } + throw await throwErrorMessages(error); } } -export default balanceQueryApi; +const handleBalanceQueryCallbacks = async (app) => { + callbackHandlerInitialized = true; + await handleCallbacks(app, "balanceQuery"); +}; + +export { balanceQuery, handleBalanceQueryCallbacks }; diff --git a/lib/core/apis/business-Paybill.js b/lib/core/apis/business-Paybill.js index 463dd55..844aed7 100644 --- a/lib/core/apis/business-Paybill.js +++ b/lib/core/apis/business-Paybill.js @@ -1,113 +1,125 @@ import axios from "axios"; import { + callbackTrigger, encryptSecurityCredential, generateOAuthToken, + handleCallbacks, + logErrorDetails, throwErrorMessages, + validateUrl, } from "../utils/helpers.js"; -/** - * Logs detailed error information to assist in debugging. - * @param {Error} error - The error object thrown by the API request. - * @param {Object} context - Additional context about the request, such as endpoint and payload. - */ -function logErrorDetails(error, context) { - console.error("Business Pay Bill transaction error details:", { - message: error.message, - stack: error.stack, - ...(error.response && { - status: error.response.status, - data: error.response.data, - headers: error.response.headers, - }), - ...(error.request && { request: error.request }), - context, - }); -} +// Globals +let callbackHandlerInitialized = false; +let conditionalCallbackData = {}; /** - * businessPaybillApi - Use this API to pay bills directly from your business account to a pay bill number or store. - * Simultaneously generates an OAuth token using the consumer key and secret. - * @name businessPaybillApi - * @function - * @param {Object} options - Options for the business pay bill. - * @param {string} options.initiator - The M-Pesa API operator username (initiator). - * @param {string} [options.commandId='businessPaybillApi'] - Command ID for the pay bill. - * @param {string} [options.senderIdentifierType=4] - Type of identifier for the sender (4 for short code). - * @param {string} [options.receiverIdentifierType=4] - Type of identifier for the receiver (4 for short code). - * @param {number} options.amount - The transaction amount. - * @param {number} options.partyA - The shortcode from which money will be deducted. - * @param {number} options.partyB - The shortcode to which money will be sent. - * @param {number} options.accountReference - Account reference number for payment. - * @param {number} options.requester - Optional. The consumer’s mobile number on behalf of whom you are paying - * @param {string} [options.remarks='OK'] - Additional remarks for the transaction. - * @param {string} options.queueTimeOutURL - URL for timeout notification on transaction failure. - * @param {string} options.resultURL - URL for notification of the transaction result. - * @return {Promise} - Returns a promise that resolves to the transaction result or status. + * @name businessPaybill + * @description This API enables you to pay bills directly from your business account to a pay bill number, or a paybill store. You can use this API to pay on behalf of a consumer/requester. The transaction moves money from your MMF/Working account to the recipient’s utility account. + * @summary Facilitates bill payments from a business account to a paybill number, transferring funds to the recipient’s utility account. + * @see {@link https://developer.safaricom.co.ke/APIs/BusinessPayBill open external link} + * @param {Object} options Options for the business pay bill. + * @param {string} options.initiator M-Pesa API operator username with Org Business Pay Bill API role. + * @param {number} options.amount Transaction amount. + * @param {number} options.partyA Shortcode from which money will be deducted. + * @param {number} options.partyB Shortcode to which money will be moved. + * @param {string} options.remarks Information to be associated with the transaction. + * @param {number} options.accountReference Account number for the payment (up to 13 characters). + * @param {number} [options.requester] Optional consumer’s mobile number (if paying on their behalf). + * @param {string} options.QueueTimeOutURL URL for timeout notifications. + * @param {string} options.resultUrl URL for sending transaction results after processing. + * @param {boolean} [options.proErrorLogging] Logs out advanced error details - good for debugging + * @return {Promise} businessPaybillResponse and (optional) conditionalCallbackData. */ -async function businessPaybillApi({ +async function businessPaybill({ initiator, - commandId = "BusinessPayBill", - senderIdentifierType = "4", - receiverIdentifierType = "4", amount, partyA, partyB, + remarks, accountReference, requester, - remarks = "OK!", - queueTimeOutURL, - resultURL, + QueueTimeOutURL, + resultUrl, + proErrorLogging = false, }) { - const { accessToken, baseURL } = await generateOAuthToken(); - const req = axios.create({ - baseURL, - headers: { - Authorization: `Bearer ${accessToken}`, - "Content-Type": "application/json", - }, - }); - + if (!callbackHandlerInitialized) { + console.info( + "\x1b[42m\x1b[30m\x1b[1m The default callback handler ('handleBusinessPaybillCallbacks') was not called. Ignore if handling manually.\x1b[0m", + ); + } try { + validateUrl(resultUrl, "resultUrl"); + validateUrl(QueueTimeOutURL, "timeoutUrl"); + const { accessToken, baseURL } = await generateOAuthToken(); + const req = axios.create({ + baseURL, + headers: { + Authorization: `Bearer ${accessToken}`, + "Content-Type": "application/json", + }, + }); + // Default configurations + const config = { + commandId: "BusinessPayBill", + senderIdentifierType: "4", + receiverIdentifierType: "4", + }; const responseBody = await req.post("/mpesa/b2b/v1/paymentrequest", { Initiator: initiator, SecurityCredential: encryptSecurityCredential(), - CommandID: commandId, - SenderIdentifierType: senderIdentifierType, - RecieverIdentifierType: receiverIdentifierType, + CommandID: config.commandId, + SenderIdentifierType: config.senderIdentifierType, + RecieverIdentifierType: config.receiverIdentifierType, Amount: amount, PartyA: partyA, PartyB: partyB, AccountReference: accountReference, Requester: requester, Remarks: remarks, - QueueTimeOutURL: queueTimeOutURL, - ResultURL: resultURL, - }); - return responseBody.data; // Return the response data directly - } catch (error) { - // Log detailed error information - logErrorDetails(error, { - apiEndpoint: "/mpesa/b2b/v1/paymentrequest", - method: "POST", - payload: { - initiator, - commandId, - senderIdentifierType, - receiverIdentifierType, - amount, - partyA, - partyB, - accountReference, - requester, - remarks, - queueTimeOutURL, - resultURL, - }, + QueueTimeOutURL: QueueTimeOutURL, + ResultURL: resultUrl, }); - // Categorize and throw user-friendly error messages - throwErrorMessages(error); + conditionalCallbackData = await callbackTrigger(callbackHandlerInitialized); + + return { + businessPaybillResponse: responseBody.data, + conditionalCallbackData, + }; + } catch (error) { + if (proErrorLogging) { + console.info( + "\x1b[35m%s\x1b[0m", + "Advanced error logging for businessPaybill has been initialized", + ); + logErrorDetails( + error, + { + apiEndpoint: "/mpesa/b2b/v1/paymentrequest", + method: "POST", + payload: { + initiator, + amount, + partyA, + partyB, + accountReference, + requester, + QueueTimeOutURL, + resultUrl, + remarks + }, + }, + "Business Pay Bill transaction error details:", + ); + } + throw throwErrorMessages(error); } } -export default businessPaybillApi; +const handleBusinessPaybillCallbacks = async (app) => { + callbackHandlerInitialized = true; + await handleCallbacks(app, "businessPaybill"); +}; + +export { businessPaybill, handleBusinessPaybillCallbacks }; diff --git a/lib/core/apis/c2b-Register.js b/lib/core/apis/c2b-Register.js index aa419d4..943d99e 100644 --- a/lib/core/apis/c2b-Register.js +++ b/lib/core/apis/c2b-Register.js @@ -1,90 +1,99 @@ import axios from "axios"; -import { generateOAuthToken, throwErrorMessages } from "../utils/helpers.js"; +import { + callbackTrigger, + generateOAuthToken, + handleCallbacks, + logErrorDetails, + throwErrorMessages, + validateUrl, +} from "../utils/helpers.js"; import { responseTypes } from "../utils/constants.js"; -/** - * Logs detailed error information to assist in debugging. - * @param {Error} error - The error object thrown by the API request. - * @param {Object} context - Additional context about the request, such as endpoint and payload. - */ -function logErrorDetails(error, context) { - console.error("C2B Register transaction error details:", { - message: error.message, - stack: error.stack, - ...(error.response && { - status: error.response.status, - data: error.response.data, - headers: error.response.headers, - }), - ...(error.request && { request: error.request }), - context, - }); -} +// Globals +let callbackHandlerInitialized = false; +let conditionalCallbackData = {}; /** - * C2B Register URL - NOT SIMULATION * @name C2BRegister - * @function - * @description Use this API to register validation and confirmation URLs on M-Pesa. - * @summary C2B payments require registration of URLs for validation and confirmation to handle transaction notifications. - * @see {@link https://developer.safaricom.co.ke/APIs/BusinessToCustomer C2B Register URL} - * @param {Object} options - Options for the C2B Register URL. - * @param {string} options.confirmationUrl - Confirmation URL for the client. - * @param {string} options.validationUrl - Validation URL for the client. - * @param {number} [options.shortCode=null] - The short code of the organization (default: configured short code). - * @param {string} [options.responseType=responseTypes.COMPLETED] - Default response type for timeout. In case a transaction times out, M-Pesa will default to Complete or Cancel the transaction. - * @return {Promise} - Returns a promise that resolves to the registration result. + * @description Register URL API works hand in hand with Customer to Business (C2B) APIs and allows receiving payment notifications to your paybill. This API enables you to register the callback URLs via which you shall receive notifications for payments to your pay bill/till numbers + * @summary Registers callback validation and confirmation URLs on M-Pesa to receive payment notifications for your paybill/till numbers. + * @see {@link https://developer.safaricom.co.ke/APIs/CustomerToBusinessRegisterURL open external link} + * @param {Object} options Options for the C2B Register URL. + * @param {string} options.confirmationUrl URL to receive confirmation upon payment completion. + * @param {string} options.validationUrl URL to receive validation upon payment submission (default: external validation disabled). + * @param {number} options.shortCode Unique M-PESA pay bill/till number. + * @param {string} options.responseType Action if validation URL is unreachable (values: Completed or Cancelled). + * @param {boolean} [options.proErrorLogging] Logs out detailed errors for debugging purposes + * @return {Promise} c2bRegisterResponse and (optional) conditionalCallbackData. */ -async function c2bRegisterApi({ +async function c2bRegister({ confirmationUrl, validationUrl, shortCode, - responseType = responseTypes.COMPLETED, + responseType, + proErrorLogging = false, }) { - // Validate responseType + if (!callbackHandlerInitialized) { + console.info( + "\x1b[42m\x1b[30m\x1b[1m The default balance query callback handler ('handleC2bRegisterCallbacks') was not called. Ignore if handling manually.\x1b[0m", + ); + } + /** + * @param {string} validResponseTypes - Should either be Completed or Cancelled + */ const validResponseTypes = Object.values(responseTypes); if (!validResponseTypes.includes(responseType)) { throw new Error( `Invalid responseType provided. Must be one of: ${validResponseTypes.join(", ")}`, ); } - - // OAuth token generation function - const { accessToken, baseURL } = await generateOAuthToken(); - - const req = axios.create({ - baseURL, - headers: { - Authorization: `Bearer ${accessToken}`, - "Content-Type": "application/json", - }, - }); - try { - const response = await req.post("/mpesa/c2b/v1/registerurl", { + validateUrl(confirmationUrl, "confirmationUrl"); + validateUrl(validationUrl, "validationUrl"); + const { accessToken, baseURL } = await generateOAuthToken(); + const req = axios.create({ + baseURL, + headers: { + Authorization: `Bearer ${accessToken}`, + "Content-Type": "application/json", + }, + }); + const responseBody = await req.post("/mpesa/c2b/v1/registerurl", { ShortCode: shortCode, ResponseType: responseType, ConfirmationURL: confirmationUrl, ValidationURL: validationUrl, }); - - return response.data; // Return the response data directly + conditionalCallbackData = await callbackTrigger(callbackHandlerInitialized); + return { c2bRegisterResponse: responseBody.data, conditionalCallbackData }; } catch (error) { - // Log detailed error information - logErrorDetails(error, { - apiEndpoint: "/mpesa/c2b/v1/registerurl", - method: "POST", - payload: { - confirmationUrl, - validationUrl, - shortCode, - responseType, - }, - }); - - // Categorize and throw user-friendly error messages - throwErrorMessages(error); + if (proErrorLogging) { + console.info( + "\x1b[35m%s\x1b[0m", + "Advanced error logging for c2bRegister has been initialized", + ); + logErrorDetails( + error, + { + apiEndpoint: "/mpesa/c2b/v1/registerurl", + method: "POST", + payload: { + confirmationUrl, + validationUrl, + shortCode, + responseType, + }, + }, + "C2B Register transaction error details:", + ); + } + throw throwErrorMessages(error); } } -export default c2bRegisterApi; +const handleC2bRegisterCallbacks = async (app) => { + callbackHandlerInitialized = true; + await handleCallbacks(app, "c2bRegister"); +}; + +export { c2bRegister, handleC2bRegisterCallbacks }; diff --git a/lib/core/apis/c2b-Simulate.js b/lib/core/apis/c2b-Simulate.js index 597e9f3..8fdf1dd 100644 --- a/lib/core/apis/c2b-Simulate.js +++ b/lib/core/apis/c2b-Simulate.js @@ -1,90 +1,79 @@ -import { generateOAuthToken, throwErrorMessages } from "../utils/helpers.js"; +import { + generateOAuthToken, logErrorDetails, + throwErrorMessages, + validateFormatPhone +} from "../utils/helpers.js"; import axios from "axios"; -/** - * Logs detailed error information for easier debugging. - * @param {Error} error - The error object thrown by the API request. - * @param {Object} context - Additional context about the request, such as endpoint and payload. - */ -function logErrorDetails(error, context) { - console.error("C2B Simulation transaction error details:", { - message: error.message, - stack: error.stack, - ...(error.response && { - status: error.response.status, - data: error.response.data, - headers: error.response.headers, - }), - ...(error.request && { request: error.request }), - context, - }); -} /** - * C2B Simulate Transaction - * @name c2bSimulateApi - * @function - * @description Use this API to simulate a C2B transaction. - * @summary This function simulates a C2B (Customer to Business) transaction by initiating a payment from a phone number to a business shortcode. - * @see {@link https://developer.safaricom.co.ke/c2b/apis/post/simulate | C2B Simulate Transaction } - * @param {Object} options - Options for the C2B simulation. - * @param {number} options.msisdn - Phone number (MSISDN) initiating the transaction. - * @param {number} options.amount - The amount being transacted. - * @param {string} options.billRefNumber - Bill reference number. - * @param {string} [options.commandId='CustomerPayBillOnline'] - Unique command for each transaction type (default: CustomerPayBillOnline). - * @param {number} [options.shortCode=null] - Shortcode receiving the amount (default: configured short code). - * @return {Promise} - Returns a promise that resolves to the simulation result. + * @name c2bSimulate + * @description This function simulates a C2B (Customer to Business) transaction by initiating a payment from an MSISDN to a business shortcode. + * @summary Simulate a transaction from an MSISDN to a business shortcode + * @see {@link https://developer.safaricom.co.ke/APIs/CustomerToBusinessRegisterURL open external link} + * @param {Object} options Options for the C2B simulation. + * @param {string} options.msisdn The MSISDN receiving the transaction + * @param {number} options.amount Transaction amount. + * @param {string} options.billRefNumber Bill reference number e.g. "invoice008". + * @param {number} options.shortCode Usually, a unique number is tagged to an M-PESA pay bill/till number of the organization + * @param {boolean} [options.proErrorLogging] Logs out advanced error details - good for debugging + * @return {Promise} c2bSimulateResponse. */ -async function c2bSimulateApi({ +async function c2bSimulate({ msisdn, amount, billRefNumber, - commandId = "CustomerPayBillOnline", shortCode, + proErrorLogging = false, }) { /** SIMULATE SHOULD ONLY BE PERFORMED IN SANDBOX MODE, NEVER IN PRODUCTION **/ const { accessToken, baseURL } = await generateOAuthToken(); - if (baseURL === "https://api.safaricom.co.ke") { throw new Error( "Simulation is allowed only in development or sandbox environment!", ); } - - const req = axios.create({ - baseURL, - headers: { - Authorization: `Bearer ${accessToken}`, - "Content-Type": "application/json", - }, - }); - try { + const req = axios.create({ + baseURL, + headers: { + Authorization: `Bearer ${accessToken}`, + "Content-Type": "application/json", + }, + }); + const config = { + commandId: "CustomerPayBillOnline", + }; const responseBody = await req.post(`/mpesa/c2b/v1/simulate`, { ShortCode: shortCode, - CommandID: commandId, + CommandID: config.commandId, Amount: amount, - Msisdn: msisdn, + Msisdn: validateFormatPhone(msisdn), BillRefNumber: billRefNumber, }); - - return responseBody.data; // Return the response data directly + return { c2bSimulateResponse: responseBody.data }; } catch (error) { - logErrorDetails(error, { - apiEndpoint: "/mpesa/c2b/v1/simulate", - method: "POST", - payload: { - msisdn, - amount, - billRefNumber, - commandId, - shortCode, - }, - }); - - // Categorize error messages based on the response - throwErrorMessages(error); + if (proErrorLogging) { + console.info( + "\x1b[35m%s\x1b[0m", + "Advanced error logging for c2bSimulate has been initialized", + ); + logErrorDetails( + error, + { + apiEndpoint: "/mpesa/c2b/v1/simulate", + method: "POST", + payload: { + msisdn, + amount, + billRefNumber, + shortCode, + }, + }, + "C2B Simulation transaction error details:", + ); + } + throw throwErrorMessages(error); } } - -export default c2bSimulateApi; +export { c2bSimulate }; diff --git a/lib/core/apis/mpesa-Query.js b/lib/core/apis/mpesa-Query.js index b3e3e2e..3179a74 100644 --- a/lib/core/apis/mpesa-Query.js +++ b/lib/core/apis/mpesa-Query.js @@ -1,43 +1,29 @@ import { generateMpesaCredentials, generateOAuthToken, + logErrorDetails, throwErrorMessages, } from "../utils/helpers.js"; import axios from "axios"; /** - * Logs detailed error information for easier debugging. - * @param {Error} error - The error object thrown by the API request. - * @param {Object} context - Additional context about the request, such as endpoint and payload. + * @name mpesaQuery + * @description Use this API to check the status of a Lipa Na M-Pesa Online Payment. + * @summary Check transaction status of a Lipa na M-Pesa payment. + * @see {@link https://developer.safaricom.co.ke/APIs/MpesaExpressQuery open external link} + * @param {Object} options Options for the Lipa Na M-Pesa query API. + * @param {string} options.checkoutRequestId Unique identifier for the processed checkout transaction. + * @param {number} options.businessShortCode Organization's shortcode (Paybill or Buygoods, 5-7 digits). + * @param {boolean} [options.proErrorLogging] Logs out advanced error details - good for debugging + * @returns {Promise} mpesaQueryResponse. */ -function logErrorDetails(error, context) { - console.error("Mpesa Query transaction error details:", { - message: error.message, - stack: error.stack, - ...(error.response && { - status: error.response.status, - data: error.response.data, - headers: error.response.headers, - }), - ...(error.request && { request: error.request }), - context, - }); -} - -/** - * Lipa Na M-Pesa Query Request - Use this API to check the status of a Lipa Na M-Pesa Online Payment. - * @name mpesaQueryApi - * @function - * @see {@link https://developer.safaricom.co.ke/APIs/MpesaExpressQuery | Lipa Na M-Pesa Query Request} - * @param {Object} options - Options for the Lipa Na M-Pesa query request. - * @param {string} options.checkoutRequestId - Checkout RequestID obtained from the Lipa Na M-Pesa transaction initiation. - * @param {number} [options.shortCode] - Business Short Code used for the payment (defaults to configured shortcode). - * @returns {Promise} - Returns a promise that resolves to the transaction status response. - */ -async function mpesaQueryApi({ checkoutRequestId, shortCode }) { +async function mpesaQuery({ + checkoutRequestId, + businessShortCode, + proErrorLogging = false, +}) { const { accessToken, baseURL } = await generateOAuthToken(); - const { password, timeStamp } = generateMpesaCredentials(shortCode); - + const { password, timeStamp } = generateMpesaCredentials(businessShortCode); const req = axios.create({ baseURL, headers: { @@ -45,29 +31,36 @@ async function mpesaQueryApi({ checkoutRequestId, shortCode }) { "Content-Type": "application/json", }, }); - try { - const response = await req.post("/mpesa/stkpushquery/v1/query", { - BusinessShortCode: shortCode, + const responseBody = await req.post("/mpesa/stkpushquery/v1/query", { + BusinessShortCode: businessShortCode, Password: password, Timestamp: timeStamp, CheckoutRequestID: checkoutRequestId, }); - return response.data; + return { mpesaQueryResponse: responseBody.data }; } catch (error) { - logErrorDetails(error, { - apiEndpoint: "/mpesa/stkpushquery/v1/query", - method: "POST", - payload: { - shortCode, - checkoutRequestId, - }, - }); - - // Categorize error messages based on the response - throwErrorMessages(error); + if (proErrorLogging) { + console.info( + "\x1b[35m%s\x1b[0m", + "Advanced error logging for mpesaQuery has been initialized", + ); + logErrorDetails( + error, + { + apiEndpoint: "/mpesa/stkpushquery/v1/query", + method: "POST", + payload: { + businessShortCode, + checkoutRequestId, + }, + }, + "Mpesa Query transaction error details:", + ); + } + throw throwErrorMessages(error); } } -export default mpesaQueryApi; +export { mpesaQuery }; diff --git a/lib/core/apis/mpesa-Simulate.js b/lib/core/apis/mpesa-Simulate.js index b9a237e..d9be7a1 100644 --- a/lib/core/apis/mpesa-Simulate.js +++ b/lib/core/apis/mpesa-Simulate.js @@ -1,105 +1,112 @@ import axios from "axios"; import { + callbackTrigger, generateMpesaCredentials, generateOAuthToken, + handleCallbacks, + logErrorDetails, throwErrorMessages, + validateFormatPhone, + validateUrl } from "../utils/helpers.js"; -/** - * Logs error details for advanced debugging. - * @param {Error} error - The error object caught in the catch block. - * @param {Object} context - Additional context about the API request, such as endpoint, method, and payload. - */ -function logErrorDetails(error, context) { - console.error("Mpesa Simulate transaction error details:", { - message: error.message, - stack: error.stack, - ...(error.response && { - status: error.response.status, - data: error.response.data, - headers: error.response.headers, - }), - ...(error.request && { request: error.request }), - context, - }); -} +// Globals +let callbackHandlerInitialized = false; +let conditionalCallbackData = {}; /** - * Lipa Na M-Pesa Online Payment - Initiates an online payment on behalf of a customer. - * @name mpesaSimulateApi - * @function - * @see {@link https://developer.safaricom.co.ke/APIs/MpesaExpressSimulate Payment Request} - * @param {Object} options - Options for the Lipa Na M-Pesa online payment request. - * @param {number} options.msisdn1 - The MSISDN sending the funds. - * @param {number} options.msisdn2 - The MSISDN receiving the STK pin prompt, can be similar to msisdn1. - * @param {number} options.amount - The amount to be transacted. - * @param {string} options.callbackUrl - Callback URL for payment notification. - * @param {string} options.accountRef - Account Reference for the transaction. - * @param {string} [options.transactionDesc='OK!'] - Description of the transaction. - * @param {string} [options.transactionType='CustomerPayBillOnline'] - Transaction type. - * @param {number} [options.shortCode] - Organization shortcode used to receive the transaction. - * @returns {Promise} - Returns a promise that resolves to the payment response. + * @name mpesaSimulate + * @description Lipa na M-PESA online API also known as M-PESA express (STK Push/NI push) is a Merchant/Business initiated C2B (Customer to Business) Payment. it enables you to send a payment prompt on the customer's phone (Popularly known as STK Push Prompt) to your customer's M-PESA registered phone number requesting them to enter their M-PESA pin to authorize and complete payment. + * @summary Sends a payment prompt to the customer's M-PESA registered phone number, requesting them to enter their M-PESA pin to authorize and complete payment. + * @see {@link https://developer.safaricom.co.ke/APIs/MpesaExpressSimulate open external link} + * @param {Object} options Options for M-Pesa express API. + * @param {string} options.partyA Sender's Safaricom mobile number (M-PESA registered). + * @param {string} options.phoneNumber Mobile number to receive the STK Pin Prompt (can be same as partyA). + * @param {number} options.amount Transaction amount. + * @param {string} options.transactionType "CustomerPayBillOnline" for PayBill and "CustomerBuyGoodsOnline" for Till Numbers. + * @param {string} options.callbackUrl Secure URL for receiving notifications from M-Pesa API. + * @param {string} options.transactionDesc Information to be associated with the transaction. + * @param {string} options.accountRef Alphanumeric account reference (up to 12 characters) shown in the STK Pin Prompt (Org name). + * @param {number} options.partyB 5 to 6-digit number of the receiving organization. + * @param {boolean} [options.proErrorLogging] Logs out advanced error details - good for debugging + * @returns {Promise} mpesaSimulateResponse and (optional) conditionalCallbackData. */ -async function mpesaSimulateApi({ - msisdn1, - msisdn2, - amount, - callbackUrl, - accountRef, - transactionDesc = "OK!", - transactionType = "CustomerPayBillOnline", - shortCode, -}) { +async function mpesaSimulate({ + partyA, + phoneNumber, + amount, + callbackUrl, + accountRef, + partyB, + transactionType, + transactionDesc, + proErrorLogging = false + }) { + if (!callbackHandlerInitialized) { + console.info( + "\x1b[42m\x1b[30m\x1b[1m The default callback handler ('handleMpesaSimulateCallbacks') was not called. Ignore if handling manually.\x1b[0m" + ); + } try { - // Generate OAuth token + validateUrl(callbackUrl, "callbackUrl"); const { accessToken, baseURL } = await generateOAuthToken(); - const { password, timeStamp } = generateMpesaCredentials(shortCode); - - // Prepare the request with OAuth token + const { password, timeStamp } = generateMpesaCredentials(partyB); const req = axios.create({ baseURL, headers: { Authorization: `Bearer ${accessToken}`, - "Content-Type": "application/json", - }, - }); + "Content-Type": "application/json" + } - // Make the request to M-Pesa API - const response = await req.post("/mpesa/stkpush/v1/processrequest", { - BusinessShortCode: shortCode, + }); + const responseBody = await req.post("/mpesa/stkpush/v1/processrequest", { + BusinessShortCode: partyB, Password: password, Timestamp: timeStamp, Amount: amount, - PartyA: msisdn1, - PartyB: shortCode, - PhoneNumber: msisdn2, + PartyA: validateFormatPhone(partyA), + PartyB: partyB, + PhoneNumber: validateFormatPhone(phoneNumber), CallBackURL: callbackUrl, AccountReference: accountRef, TransactionDesc: transactionDesc, - TransactionType: transactionType, + TransactionType: transactionType }); - - return response.data; // Return the response data directly + conditionalCallbackData = await callbackTrigger(callbackHandlerInitialized, true); + return { + mpesaSimulateResponse: responseBody.data, + conditionalCallbackData + }; } catch (error) { - // Advanced error logging - logErrorDetails(error, { - apiEndpoint: "/mpesa/stkpush/v1/processrequest", - method: "POST", - payload: { - msisdn1, - msisdn2, - amount, - callbackUrl, - accountRef, - transactionDesc, - transactionType, - shortCode, - }, - }); - - // Categorize and handle error types - throwErrorMessages(error); + if (proErrorLogging) { + console.info( + "\x1b[35m%s\x1b[0m", + "Advanced error logging for mpesaSimulate has been initialized" + ); + logErrorDetails( + error, + { + apiEndpoint: "/mpesa/stkpush/v1/processrequest", + method: "POST", + payload: { + partyA, + phoneNumber, + amount, + callbackUrl, + accountRef, + partyB, + transactionDesc + } + }, + "Mpesa Simulate transaction error details:" + ); + } + throw throwErrorMessages(error); } } -export default mpesaSimulateApi; +const handleMpesaSimulateCallbacks = async (app) => { + callbackHandlerInitialized = true; + await handleCallbacks(app, "mpesaSimulate"); +}; +export { mpesaSimulate, handleMpesaSimulateCallbacks }; diff --git a/lib/core/apis/qr-Generate.js b/lib/core/apis/qr-Generate.js index e125d23..6929f12 100644 --- a/lib/core/apis/qr-Generate.js +++ b/lib/core/apis/qr-Generate.js @@ -1,61 +1,46 @@ import axios from "axios"; -import { generateOAuthToken, throwErrorMessages } from "../utils/helpers.js"; +import { + generateOAuthToken, + logErrorDetails, + throwErrorMessages, +} from "../utils/helpers.js"; import { trxCodeTypes } from "../utils/constants.js"; /** - * Logs error details for advanced debugging. - * @param {Error} error - The error object caught in the catch block. - * @param {Object} context - Additional context about the API request, such as endpoint, method, and payload. - */ -function logErrorDetails(error, context) { - console.error("QR code generation error details:", { - message: error.message, - stack: error.stack, - ...(error.response && { - status: error.response.status, - data: error.response.data, - headers: error.response.headers, - }), - ...(error.request && { request: error.request }), - context, - }); -} - -/** - * generateQrCodeApi - Creates a dynamic QR code for a specified transaction. - * @name generateQrCodeApi - * @function - * @see {@link https://developer.safaricom.co.ke/APIs/DynamicQRCode Generate Dynamic QR Code} - * @param {Object} options - Options for the QR code generation request. + * @name generateQrCode + * @description This generates a Dynamic QR which enables Safaricom M-PESA customers who have My Safaricom App or M-PESA app, to scan a QR (Quick Response) code, to capture till number and amount + * @summary Generates QR codes for customers using My Safaricom app + * @see {@link https://developer.safaricom.co.ke/APIs/DynamicQRCode open external link} + * @param {Object} options Options for the QR code generation request. * @param {string} options.merchantName - The name of the company or M-Pesa merchant requesting the QR code. - * @param {string} options.refNo - A unique reference number for the transaction. - * @param {number} options.amount - The total amount for the sale or transaction. - * @param {string} options.trxCode - Transaction type. Supported types: BG, WA, PB, SM, SB. - * @param {string} options.cpi - Credit Party Identifier (e.g., mobile number, business number). - * @param {string} options.size - Size of the QR code image in pixels. QR code image will always be square. - * @returns {Promise} - Returns a promise that resolves to the QR code data. + * @param {string} options.refNo A unique reference number for the transaction. + * @param {number} options.amount The total amount for the sale or transaction. + * @param {string} options.trxCode Transaction type. Supported types: BG, WA, PB, SM, SB. + * @param {string} options.cpi Credit Party Identifier (e.g., mobile number, business number). + * @param {string} options.size Size of the QR code image in pixels. QR code image will always be square. + * @param {boolean} [options.proErrorLogging] Logs out advanced error details - good for debugging + * @returns {Promise} generateQrcodeResponse. */ -async function generateQrCodeApi({ +async function generateQrCode({ merchantName, refNo, amount, trxCode, cpi, size, + proErrorLogging = false, }) { - // Validate trxCode against allowed types + /** + * @param {string} validTrxCodes - Only support: buy goods: "BG" - withdraw agent till: "WA" - paybill business number: "PB" - send money msisdn: "SM" and end to business msisdn: "SB" + */ const validTrxCodes = Object.values(trxCodeTypes); if (!validTrxCodes.includes(trxCode)) { throw new Error( `Invalid trxCode provided. Must be one of: ${validTrxCodes.join(", ")}`, ); } - try { - // Generate OAuth token const { accessToken, baseURL } = await generateOAuthToken(); - - // Set up the axios request with the generated OAuth token const req = axios.create({ baseURL, headers: { @@ -63,9 +48,7 @@ async function generateQrCodeApi({ "Content-Type": "application/json", }, }); - - // Execute the QR Code generation API request - const response = await req.post("/mpesa/qrcode/v1/generate", { + const responseBody = await req.post("/mpesa/qrcode/v1/generate", { MerchantName: merchantName, RefNo: refNo, Amount: amount, @@ -73,26 +56,32 @@ async function generateQrCodeApi({ CPI: cpi, Size: size, }); - - return response.data; // Return the response data directly + return { generateQrcodeResponse: responseBody.data }; } catch (error) { - // Advanced error logging - logErrorDetails(error, { - apiEndpoint: "/mpesa/qrcode/v1/generate", - method: "POST", - payload: { - merchantName, - refNo, - amount, - trxCode, - cpi, - size, - }, - }); - - // Categorize and handle error types - throwErrorMessages(error); + if (proErrorLogging) { + console.info( + "\x1b[35m%s\x1b[0m", + "Advanced error logging for generateQrcode has been initialized", + ); + logErrorDetails( + error, + { + apiEndpoint: "/mpesa/qrcode/v1/generate", + method: "POST", + payload: { + merchantName, + refNo, + amount, + trxCode, + cpi, + size, + }, + }, + "QR code generation error details:", + ); + } + throw throwErrorMessages(error); } } -export default generateQrCodeApi; +export { generateQrCode }; diff --git a/lib/core/apis/reversals.js b/lib/core/apis/reversals.js index b72dc7c..f16d8c6 100644 --- a/lib/core/apis/reversals.js +++ b/lib/core/apis/reversals.js @@ -1,63 +1,54 @@ import axios from "axios"; import { + callbackTrigger, encryptSecurityCredential, generateOAuthToken, + handleCallbacks, + logErrorDetails, throwErrorMessages, + validateUrl, } from "../utils/helpers.js"; -/** - * Logs error details for advanced debugging. - * @param {Error} error - The error object caught in the catch block. - * @param {Object} context - Additional context about the API request, such as endpoint, method, and payload. - */ -function logErrorDetails(error, context) { - console.error("Transaction reversal error details:", { - message: error.message, - stack: error.stack, - ...(error.response && { - status: error.response.status, - data: error.response.data, - headers: error.response.headers, - }), - ...(error.request && { request: error.request }), - context, - }); -} +// Globals +let callbackHandlerInitialized = false; +let conditionalCallbackData = {}; /** - * Reversal Request - Use this API to reverse an M-Pesa transaction C2B. - * @name reversalsApi - * @function - * @see {@link https://developer.safaricom.co.ke/APIs/Reversal Request} - * @param {Object} options - Options for the reversal request. - * @param {string} options.transactionId - The transaction ID for reversal, e.g., LKXXXX1234. - * @param {number} options.amount - The amount to be reversed. - * @param {string} options.queueUrl - The URL that stores information about timeout transactions. - * @param {string} options.resultUrl - The URL that stores information about the transaction result. - * @param {number} [options.shortCode=null] - The shortcode of the organization receiving the transaction. - * @param {string} [options.remarks='Reversal'] - Comments that are sent along with the transaction. - * @param {string} [options.occasion='Reversal'] - Optional parameter for the occasion of the reversal. - * @param {string} [options.initiator=null] - The name of the initiator initiating the request (defaults to configured initiator). - * @param {string} [options.receiverIdType='11'] - Type of organization receiving the transaction. - * @param {string} [options.commandId='TransactionReversal'] - Command ID, must be 'TransactionReversal'. - * @returns {Promise} - A promise that resolves to the reversal response. + * @name reversals + * @description Reverses a C2B M-Pesa transaction. Once a customer pays and there is a need to reverse the transaction, the organization will use this API to reverse the amount. + * @summary Reverses an M-pesa transaction + * @see {@link https://developer.safaricom.co.ke/APIs/Reversal open external link} + * @param {Object} options Options for the reversal API. + * @param {string} options.transactionId Transaction ID for reversal (e.g., LKXXXX1234). + * @param {number} options.amount Amount to be reversed. + * @param {string} options.QueueTimeOutURL URL for timeout transaction details. + * @param {string} options.resultUrl URL for transaction details. + * @param {string} options.remarks Information to be associated with the transaction. + * @param {string} options.occasion Information to be associated with the transaction. + * @param {number} options.receiverParty Organization receiving the transaction. + * @param {string} options.initiator Name of the initiator of the request. + * @param {boolean} [options.proErrorLogging] Logs out advanced error details - good for debugging + * @returns {Promise} reversalsResponse and (optional) conditionalCallbackData. */ -async function reversalsApi({ +async function reversals({ transactionId, amount, - queueUrl, + QueueTimeOutURL, resultUrl, - shortCode, - remarks = "OK!", - occasion = "TEST!", + receiverParty, initiator, - receiverIdType = "11", - commandId = "TransactionReversal", + remarks, + occasion, + proErrorLogging = false, }) { + if (!callbackHandlerInitialized) { + console.info("\x1b[42m\x1b[30m\x1b[1m The default callback handler ('handleReversalCallbacks') was not called. Ignore if handling manually.\x1b[0m", + ); + } try { - // Generate OAuth token + validateUrl(resultUrl, "resultUrl"); + validateUrl(QueueTimeOutURL, "timeoutUrl"); const { accessToken, baseURL } = await generateOAuthToken(); - const req = axios.create({ baseURL, headers: { @@ -65,45 +56,59 @@ async function reversalsApi({ "Content-Type": "application/json", }, }); - - // Execute the reversal request - const response = await req.post("/mpesa/reversal/v1/request", { + // default configurations + const config = { + receiverIdType: "11", + commandId: "TransactionReversal", + }; + const responseBody = await req.post("/mpesa/reversal/v1/request", { Initiator: initiator, SecurityCredential: encryptSecurityCredential(), - CommandID: commandId, + CommandID: config.commandId, TransactionID: transactionId, Amount: amount, - ReceiverParty: shortCode, - RecieverIdentifierType: receiverIdType, + ReceiverParty: receiverParty, + RecieverIdentifierType: config.receiverIdType, ResultURL: resultUrl, - QueueTimeOutURL: queueUrl, + QueueTimeOutURL: QueueTimeOutURL, Remarks: remarks, Occasion: occasion, }); - return response.data; // Return the response data directly - } catch (error) { - // Advanced error logging - logErrorDetails(error, { - apiEndpoint: "/mpesa/reversal/v1/request", - method: "POST", - payload: { - transactionId, - amount, - queueUrl, - resultUrl, - shortCode, - remarks, - occasion, - initiator, - receiverIdType, - commandId, - }, - }); + conditionalCallbackData = await callbackTrigger(callbackHandlerInitialized); - // Categorize and handle error types - throwErrorMessages(error); + return { reversalsResponse: responseBody.data, conditionalCallbackData }; + } catch (error) { + if (proErrorLogging) { + console.info( + "\x1b[35m%s\x1b[0m", + "Advanced error logging for reversals has been initialized", + ); + logErrorDetails( + error, + { + apiEndpoint: "/mpesa/reversal/v1/request", + method: "POST", + payload: { + transactionId, + amount, + QueueTimeOutURL, + resultUrl, + receiverParty, + initiator, + remarks, + occasion + }, + }, + "Transaction reversal error details:", + ); + } + throw throwErrorMessages(error); } } +const handleReversalCallbacks = async (app) => { + callbackHandlerInitialized = true; + await handleCallbacks(app, "reversals"); +}; -export default reversalsApi; +export { reversals, handleReversalCallbacks }; diff --git a/lib/core/apis/tax-Remittance.js b/lib/core/apis/tax-Remittance.js index ddb8e69..bfd89d8 100644 --- a/lib/core/apis/tax-Remittance.js +++ b/lib/core/apis/tax-Remittance.js @@ -3,64 +3,53 @@ import { generateOAuthToken, encryptSecurityCredential, throwErrorMessages, + logErrorDetails, + callbackTrigger, + validateUrl, + handleCallbacks, } from "../utils/helpers.js"; -/** - * Logs error details for advanced debugging. - * @param {Error} error - The error object caught in the catch block. - * @param {Object} context - Additional context about the API request, such as endpoint, method, and payload. - */ -function logErrorDetails(error, context) { - console.error("Tax remittance error details:", { - message: error.message, - stack: error.stack, - ...(error.response && { - status: error.response.status, - data: error.response.data, - headers: error.response.headers, - }), - ...(error.request && { request: error.request }), - context, - }); -} +// Globals +let callbackHandlerInitialized = false; +let conditionalCallbackData = {}; /** - * Tax Remittance API - Use this API to remit tax to the Kenya Revenue Authority (KRA). - * @name taxRemittanceApi - * @function - * @see {@link https://developer.safaricom.co.ke/APIs/TaxRemittance | KRA Tax Remittance} - * @param {Object} options - Options for the tax remittance request. - * @param {string} options.initiator - The M-Pesa API operator username with the tax remittance API initiator role. - * @param {string} [options.commandId='PayTaxToKRA'] - Transaction command ID. - * @param {string} options.senderIdentifierType - Identifier type of the shortcode from which funds are deducted. Only "4" is supported. - * @param {string} options.receiverIdentifierType - Identifier type of the shortcode to which funds are credited. Only "4" is supported. - * @param {number} options.amount - The transaction amount to be remitted. - * @param {number} options.partyA - Shortcode from which the funds will be deducted. - * @param {number} options.partyB - Shortcode to which the funds will be transferred (e.g., 572572 for KRA). - * @param {number} options.accountReference - Payment registration number (PRN) issued by KRA. - * @param {string} options.remarks - Additional information to be associated with the transaction. - * @param {string} options.queueTimeOutURL - URL to notify in case of a request timeout before processing. - * @param {string} options.resultURL - URL to send transaction results after processing. - * @returns {Promise} - Returns a promise that resolves to the tax remittance transaction response. + * @name taxRemittance + * @description This API enables businesses to remit tax to Kenya Revenue Authority (KRA). To use this API, prior integration is required with KRA for tax declaration, payment registration number (PRN) generation, and exchange of other tax-related information. + * @summary Enables one to remit tax to Kenya Revenue Authority (KRA) + * @see {@link https://developer.safaricom.co.ke/APIs/TaxRemittance open external link} + * @param {Object} options Options for the tax remittance API. + * @param {string} options.initiator The M-Pesa API operator username with the tax remittance API initiator role. + * @param {number} options.amount The transaction amount. + * @param {string} options.remarks Information to be associated with the transaction. + * @param {number} options.partyA This is your own shortcode from which the money will be deducted. + * @param {number} options.partyB The account to which money will be credited. + * @param {number} options.accountReference The payment registration number (PRN) issued by KRA + * @param {string} options.QueueTimeOutURL URL to notify in case of a request timeout before processing. + * @param {string} options.resultUrl URL to send transaction results after processing. + * @param {boolean} [options.proErrorLogging] Logs out advanced error details - good for debugging + * @returns {Promise} remittanceResponse and (optional) conditionalCallbackData. */ -async function taxRemittanceApi({ +async function taxRemittance({ initiator, - commandId = "PayTaxToKRA", - senderIdentifierType = "4", - receiverIdentifierType = "4", amount, partyA, partyB, accountReference, - remarks = "OK", - queueTimeOutURL, - resultURL, + QueueTimeOutURL, + remarks, + resultUrl, + proErrorLogging = false, }) { + if (!callbackHandlerInitialized) { + console.info( + "\x1b[42m\x1b[30m\x1b[1m The default balance query callback handler ('handleTaxRemittanceCallbacks') was not called. Ignore if handling manually.\x1b[0m", + ); + } try { - // Generate OAuth token + validateUrl(resultUrl, "resultUrl"); + validateUrl(QueueTimeOutURL, "timeoutUrl"); const { accessToken, baseURL } = await generateOAuthToken(); - - // Axios request setup const req = axios.create({ baseURL, headers: { @@ -69,46 +58,60 @@ async function taxRemittanceApi({ }, }); - // Send tax remittance request - const response = await req.post("/mpesa/b2b/v1/remittax", { + // Default configurations + const config = { + commandId: "PayTaxToKRA", + senderIdentifierType: "4", + receiverIdentifierType: "4", + }; + const responseBody = await req.post("/mpesa/b2b/v1/remittax", { Initiator: initiator, SecurityCredential: encryptSecurityCredential(), - CommandID: commandId, - SenderIdentifierType: senderIdentifierType, - RecieverIdentifierType: receiverIdentifierType, + CommandID: config.commandId, + SenderIdentifierType: config.senderIdentifierType, + RecieverIdentifierType: config.receiverIdentifierType, Amount: amount, PartyA: partyA, PartyB: partyB, AccountReference: accountReference, Remarks: remarks, - QueueTimeOutURL: queueTimeOutURL, - ResultURL: resultURL, + QueueTimeOutURL: QueueTimeOutURL, + ResultURL: resultUrl, }); - - return response.data; // Return the response data directly + conditionalCallbackData = await callbackTrigger(callbackHandlerInitialized); + return { remittanceResponse: responseBody.data, conditionalCallbackData }; } catch (error) { - // Advanced error logging - logErrorDetails(error, { - apiEndpoint: "/mpesa/b2b/v1/remittax", - method: "POST", - payload: { - initiator, - commandId, - senderIdentifierType, - receiverIdentifierType, - amount, - partyA, - partyB, - accountReference, - remarks, - queueTimeOutURL, - resultURL, - }, - }); - - // Categorize and handle error types - throwErrorMessages(error); + if (proErrorLogging) { + console.info( + "\x1b[35m%s\x1b[0m", + "Advanced error logging for taxRemittance has been initialized", + ); + logErrorDetails( + error, + { + apiEndpoint: "/mpesa/b2b/v1/remittax", + method: "POST", + payload: { + initiator, + amount, + partyA, + partyB, + accountReference, + QueueTimeOutURL, + resultUrl, + remarks + }, + }, + "Tax remittance error details:", + ); + } + throw throwErrorMessages(error); } } -export default taxRemittanceApi; +const handleTaxRemittanceCallbacks = async (app) => { + callbackHandlerInitialized = true; + await handleCallbacks(app, "taxRemittance"); +}; + +export { taxRemittance, handleTaxRemittanceCallbacks }; diff --git a/lib/core/apis/transaction-Status.js b/lib/core/apis/transaction-Status.js index e27849c..7e0d65c 100644 --- a/lib/core/apis/transaction-Status.js +++ b/lib/core/apis/transaction-Status.js @@ -1,63 +1,64 @@ import axios from "axios"; import { + callbackTrigger, encryptSecurityCredential, generateOAuthToken, + handleCallbacks, + logErrorDetails, throwErrorMessages, } from "../utils/helpers.js"; +import { IdentifierTypes } from "../utils/constants.js"; -/** - * Logs error details for advanced debugging. - * @param {Error} error - The error object caught in the catch block. - * @param {Object} context - Additional context about the API request, such as endpoint, method, and payload. - */ -function logErrorDetails(error, context) { - console.error("Transaction status error details:", { - message: error.message, - stack: error.stack, - ...(error.response && { - status: error.response.status, - data: error.response.data, - headers: error.response.headers, - }), - ...(error.request && { request: error.request }), - context, - }); -} +// Globals +let callbackHandlerInitialized = false; +let conditionalCallbackData = {}; /** - * Transaction Status Request - Use this API to check the status of a transaction on M-Pesa. - * @name transactionStatusApi - * @function - * @see {@link https://developer.safaricom.co.ke/transaction-status/apis/post/query|Transaction Status Request} - * @param {Object} options - Options for the transaction status request. - * @param {string} options.transactionId - Unique identifier for the transaction on M-Pesa. - * @param {number} options.receiverParty - Organization/MSISDN receiving the transaction. - * @param {number} options.idType - Type of organization receiving the transaction. - * @param {string} options.queueUrl - URL for storing information about timeout transactions. - * @param {string} options.resultUrl - URL for storing information about the transaction result. - * @param {string} [options.remarks='TransactionReversal'] - Comments sent along with the transaction. - * @param {string} [options.occasion='TransactionReversal'] - Optional parameter. - * @param {string} [options.initiator] - Name of the initiator initiating the request (defaults to configured initiator). - * @param {string} [options.commandId='transactionStatusApiQuery'] - Command ID, which Should be 'transactionStatusApiQuery'. - * @returns {Promise} - Returns a promise that resolves to the transaction status response. + * @name transactionStatus + * @description Enables one to Check the status of an M-pesa transaction, using a unique identifier. + * @summary Check the status of a transaction. + * @see {@link https://developer.safaricom.co.ke/APIs/TransactionStatus open external link} + * @param {Object} options Options for the transaction status API. + * @param {string} options.transactionId Unique identifier to identify a transaction on M-pesa + * @param {string} options.initiator The name of the initiator initiating the request. + * @param {number} options.partyA Organization/MSISDN receiving the transaction. + * @param {number} options.identifierType Type of organization receiving the transaction. + * @param {string} options.remarks Information to be associated with the transaction. + * @param {string} options.occasion Information to be associated with the transaction. + * @param {string} options.OriginatorConversationID This is a globally unique identifier for the transaction request returned by the API proxy upon successful submission. + * @param {string} options.QueueTimeOutUrl URL for storing information about timeout transactions. + * @param {string} options.resultUrl The path that stores information of a transaction. + * @param {boolean} [options.proErrorLogging] Logs out advanced error details - good for debugging + * @returns {Promise} transactionStatusResponse and (optional) conditionalCallbackData. */ -async function transactionStatusApi({ +async function transactionStatus({ transactionId, - receiverParty, - idType, - queueUrl, + partyA, + identifierType, + QueueTimeOutUrl, OriginatorConversationID, + remarks, + occasion, resultUrl, - remarks = "OK!", - occasion = "TEST!", initiator, - commandId = "TransactionStatusQuery", + proErrorLogging = false, }) { + if (!callbackHandlerInitialized) { + console.info( + "\x1b[42m\x1b[30m\x1b[1m The default balance query callback handler ('handleTransactStatusCallbacks') was not called. Ignore if handling manually.\x1b[0m", + ); + } + /** + * @param {number} validIdentifierTypes - Expected identifiers include const IdentifierTypes = { MSISDN: 1, TILL_NUMBER: 2, ORG_SHORTCODE: 4 }; + */ + const validIdentifierTypes = Object.values(IdentifierTypes); + if (!validIdentifierTypes.includes(identifierType)) { + throw new Error( + `Invalid identifierType provided. Must be one of: ${validIdentifierTypes.join(", ")}`, + ); + } try { - // Generate OAuth token const { accessToken, baseURL } = await generateOAuthToken(); - - // Axios request setup const req = axios.create({ baseURL, headers: { @@ -65,45 +66,57 @@ async function transactionStatusApi({ "Content-Type": "application/json", }, }); - - // Send transaction status request + // Default configurations + const config = { + commandId: "TransactionStatusQuery", + }; const responseBody = await req.post("/mpesa/transactionstatus/v1/query", { Initiator: initiator, SecurityCredential: encryptSecurityCredential(), - CommandID: commandId, + CommandID: config.commandId, TransactionID: transactionId, + PartyA: partyA, OriginatorConversationID, - PartyA: receiverParty, - IdentifierType: idType, + IdentifierType: identifierType, ResultURL: resultUrl, - QueueTimeOutURL: queueUrl, + QueueTimeOutURL: QueueTimeOutUrl, Remarks: remarks, Occasion: occasion, }); - - return responseBody.data; // Return the response data directly + conditionalCallbackData = await callbackTrigger(callbackHandlerInitialized); + return { transactStatusResponse: responseBody.data, conditionalCallbackData }; } catch (error) { - // Advanced error logging - logErrorDetails(error, { - apiEndpoint: "/mpesa/transactionstatus/v1/query", - method: "POST", - payload: { - transactionId, - receiverParty, - idType, - queueUrl, - OriginatorConversationID, - resultUrl, - remarks, - occasion, - initiator, - commandId, - }, - }); - - // Categorize and handle error types - throwErrorMessages(error); + if (proErrorLogging) { + console.info( + "\x1b[35m%s\x1b[0m", + "Advanced error logging for transactionStatus has been initialized", + ); + logErrorDetails( + error, + { + apiEndpoint: "/mpesa/transactionstatus/v1/query", + method: "POST", + payload: { + transactionId, + partyA, + identifierType, + QueueTimeOutUrl, + resultUrl, + initiator, + remarks, + occasion + }, + }, + "Transaction status error details:", + ); + } + throw throwErrorMessages(error); } } -export default transactionStatusApi; +const handleTransactStatusCallbacks = async (app) => { + callbackHandlerInitialized = true; + await handleCallbacks(app, "transactionStatus"); +}; + +export { transactionStatus, handleTransactStatusCallbacks }; diff --git a/lib/core/utils/configs.js b/lib/core/utils/configs.js index 200c5f7..e187de8 100644 --- a/lib/core/utils/configs.js +++ b/lib/core/utils/configs.js @@ -5,30 +5,24 @@ import fs from "fs"; // Function to load environment variables from a specified path using dotenv const loadEnv = (envPath) => dotenv.config({ path: envPath }); -// Define the primary and fallback paths for environment files -const primaryEnvPath = path.resolve(process.cwd(), ".env"); // Path for main .env file in project root -const fallbackEnvPath = path.resolve(process.cwd(), "./lib/tests/.env.local"); // Path for fallback .env file, typically used during testing +const fallbackAlt = fs.existsSync("./lib/tests/.env.local") + ? "./lib/tests/.env.local" + : "./lib/tests/.env"; +const primaryEnvPath = path.resolve(process.cwd(), ".env"); +const fallbackEnvPath = path.resolve(process.cwd(), fallbackAlt); // Function to check for the existence of .env files and load the appropriate one const configSwapper = () => { if (fs.existsSync(primaryEnvPath)) { - // If primary .env exists, load it and log confirmation - console.log("Using primary .env file"); loadEnv(primaryEnvPath); } else if (fs.existsSync(fallbackEnvPath)) { - // If primary .env is not found, load fallback .env and log confirmation - console.log("Primary .env file not found. Using fallback .env.local file"); loadEnv(fallbackEnvPath); } else { - // Log error if no .env files are found console.error("No .env file found. Please check your configuration."); } }; -// Execute the configSwapper function to load environment variables based on availability configSwapper(); - -// Extract key environment variables after loading them into process.env const { MPESA_CONSUMER_KEY, // Consumer key for M-Pesa API authentication MPESA_CONSUMER_SECRET, // Consumer secret for M-Pesa API authentication @@ -44,9 +38,12 @@ const baseURLSwapper = () => ENVIRONMENT === "development" || ENVIRONMENT === "sandbox" ? "https://sandbox.safaricom.co.ke" // Sandbox URL for testing : "https://api.safaricom.co.ke"; // Production URL for live requests -const envCERTSwapper= () => ENVIRONMENT === "development" || ENVIRONMENT === "sandbox" ? MPESA_CERT_PATH_DEV : MPESA_CERT_PATH_PROD +const envCERTSwapper = () => + ENVIRONMENT === "development" || ENVIRONMENT === "sandbox" + ? MPESA_CERT_PATH_DEV + : MPESA_CERT_PATH_PROD; const baseUrl = baseURLSwapper(); // Set baseUrl based on environment -const certificatePath = envCERTSwapper() // Set encryption cert based on environment +const certificatePath = envCERTSwapper(); // Set encryption cert based on environment // Define configuration object with all necessary variables for easy access throughout the application const globalConfigs = { @@ -59,5 +56,30 @@ const globalConfigs = { baseUrl, // Base URL set dynamically based on environment }; -// Export the configuration object for use in other parts of the application +// Validate required environment variables +const validateGlobalConfigs = () => { + const requiredConfigs = [ + "consumerKey", + "consumerSecret", + "securityCredential", + "passKey", + "certPath", + "environment", + "baseUrl" + ]; + + const missingConfigs = requiredConfigs.filter( + (config) => !globalConfigs[config] + ); + + if (missingConfigs.length > 0) { + throw new Error( + `Missing required environment variables: ${missingConfigs.join(", ")}` + ); + } +}; + +// Call validation function to ensure all necessary environment variables are defined +validateGlobalConfigs(); + export default globalConfigs; diff --git a/lib/core/utils/helpers.js b/lib/core/utils/helpers.js index 18d65ad..aed463b 100644 --- a/lib/core/utils/helpers.js +++ b/lib/core/utils/helpers.js @@ -4,32 +4,9 @@ import fs from "fs"; import path from "path"; import crypto from "crypto"; import globalConfigs from "./configs.js"; +import EventEmitter from "events"; -// Validate required environment variables -const validateGlobalConfigs = () => { - const requiredConfigs = [ - "consumerKey", - "consumerSecret", - "securityCredential", - "passKey", - "certPath", - "environment", - "baseUrl", - ]; - - const missingConfigs = requiredConfigs.filter( - (config) => !globalConfigs[config], - ); - - if (missingConfigs.length > 0) { - throw new Error( - `Missing required environment variables: ${missingConfigs.join(", ")}`, - ); - } -}; - -// Call validation function to ensure all necessary environment variables are defined -validateGlobalConfigs(); +const emitter = new EventEmitter(); /** * Helper to generate a unique identifier (UUID) for the originator ID @@ -47,18 +24,17 @@ export function originatorID() { export async function generateOAuthToken() { const { consumerKey, consumerSecret, baseUrl } = globalConfigs; const auth = Buffer.from(`${consumerKey}:${consumerSecret}`).toString( - "base64", + "base64" ); - try { const response = await axios.get( `${baseUrl}/oauth/v1/generate?grant_type=client_credentials`, { headers: { Authorization: `Basic ${auth}`, - "Content-Type": "application/json", - }, - }, + "Content-Type": "application/json" + } + } ); return { accessToken: response.data.access_token, baseURL: baseUrl }; } catch (error) { @@ -79,7 +55,7 @@ export function generateMpesaCredentials(shortCode) { .replace(/[^0-9]/g, "") .slice(0, -3); const password = Buffer.from(`${shortCode}${passKey}${timeStamp}`).toString( - "base64", + "base64" ); return { password, timeStamp }; } @@ -99,16 +75,16 @@ export function encryptSecurityCredential() { const encrypted = crypto.publicEncrypt( { key: privateKey, - padding: crypto.constants.RSA_PKCS1_PADDING, + padding: crypto.constants.RSA_PKCS1_PADDING }, - bufferToEncrypt, + bufferToEncrypt ); return encrypted.toString("base64"); } catch (error) { console.error("Failed to encrypt security credential:", error.message); throw new Error( - "Encryption of security credential failed. Please check the certificate path and credential.", + "Encryption of security credential failed. Please check the certificate path and credential." ); } } @@ -151,7 +127,7 @@ export function throwErrorMessages(error) { console.error("M-Pesa API Error:", { status: error.response.status, data: error.response.data, - headers: error.response.headers, + headers: error.response.headers }); // Handle specific error responses from M-Pesa API @@ -159,7 +135,7 @@ export function throwErrorMessages(error) { throw new Error("Invalid request: Please check the parameters provided."); } else if (error.response.status === 401) { throw new Error( - "Unauthorized: Invalid access token. Please verify your credentials.", + "Unauthorized: Invalid access token. Please verify your credentials." ); } else if (error.response.status >= 500) { throw new Error("M-Pesa server error: Please try again later."); @@ -167,10 +143,196 @@ export function throwErrorMessages(error) { } else if (error.request) { console.error("Network Error:", error.message); throw new Error( - "Network error: Unable to reach M-Pesa API. Please check your connection.", + "Network error: Unable to reach M-Pesa API. Please check your connection." ); } else { console.error("Unexpected Error:", error.message); throw new Error("Unexpected error occurred. Please try again later."); } + return null; +} + +/** + * Logs error details for advanced debugging. + * @param {Error} error - The error object caught in the catch block. + * @param {Object} context - Additional context about the API request, such as endpoint, method, and payload. + * @param {string} apiTypeMsg - Carries advanced error details for better debugging + * */ +export function logErrorDetails(error, context, apiTypeMsg) { + console.error(apiTypeMsg, { + message: error.message, + stack: error.stack, + ...(error.response && { + status: error.response.status, + data: error.response.data, + headers: error.response.headers + }), + ...(error.request && { request: error.request }), + context + }); +} + +/** + * Validates that a URL starts with "https://" + * @param {string} url - The URL to validate + * @param {string} name - The parameter name (for error messages) + * @throws {Error} If the URL is invalid + */ +export function validateUrl(url, name) { + if (!url || !url.startsWith("https://")) { + throw new Error(`${name} must be a valid HTTPS URL.`); + } +} + +/** + * @name validateFormatPhone + * @description Formats Kenyan phone numbers (e.g., 0711256844) to API standard (254711256844). + * @param {string} number - The phone number to be formatted. + * @returns {number} - Formatted phone number as an integer (`254XXXXXXXXX`). + * @throws {Error} - If the input is invalid or does not match the expected format. + */ +export function validateFormatPhone(number) { + if (typeof number !== "string") { + throw new Error("Invalid input: phone number must be a string."); + } + let phoneStr = String(number).trim().replace(/\D/g, ""); + if (phoneStr.length !== 10 || !phoneStr.startsWith("0")) { + throw new Error( + `Invalid phone number: must be exactly 10 digits and start with '0'}` + ); + } + return Number(`254${phoneStr.substring(1)}`); +} + +/** + * Recursively flattens a nested object into a single-level object with + * concatenated keys separated by underscores. + * + * @param {Object} obj - The object to flatten. + * @param {string} [prefix=""] - A prefix for nested keys (used for recursion). + * @returns {Object} - A new object with flattened keys. + * + * @example + * const nestedObj = { a: { b: { c: 1 }, d: 2 }, e: 3 }; + * const flattened = flattenObject(nestedObj); + * console.log(flattened); + * // Output: { "a_b_c": 1, "a_d": 2, "e": 3 } + */ +function flattenObject(obj, prefix = "") { + return Object.keys(obj).reduce((acc, key) => { + const newKey = prefix ? `${prefix}_${key}` : key; + if (typeof obj[key] === "object" && obj[key] !== null) { + Object.assign(acc, flattenObject(obj[key], newKey)); + } else { + acc[newKey] = obj[key]; + } + return acc; + }, {}); +} + +/** + * Handles M-Pesa callback registration. + * @param {Object} appInstance - Express app instance. + * @param {string} endpoint - Api endpoint name + */ +export async function handleCallbacks(appInstance, endpoint) { + + try { + if (!appInstance || typeof appInstance.post !== "function") { + throw new Error( + "handleBalanceQueryCallbacks requires a valid Express app instance." + ); + } + + let resultRoute, + timeoutRoute = ""; + + switch (endpoint) { + case "balanceQuery": + resultRoute = "/accountbalance/result"; + timeoutRoute = "/accountbalance/queuetimeouturl"; + break; + case "c2bRegister": + resultRoute = "/confirmation/result"; + timeoutRoute = "/validation/result"; + break; + case "reversals": + resultRoute = "/Reversal/result"; + timeoutRoute = "/Reversal/queuetimeouturl"; + break; + case "mpesaSimulate": + resultRoute = "/path/result"; + timeoutRoute = ""; + break; + case "transactionStatus": + resultRoute = "/TransactionStatus/result/"; + timeoutRoute = "/TransactionStatus/queue/"; + break; + case "b2cTopUp": + resultRoute = "/path/result"; + timeoutRoute = "/path/queue"; + break; + case "b2cRequest": + resultRoute = "/b2c/result"; + timeoutRoute = "/b2c/queue"; + break; + case "businessPaybill": + resultRoute = "/path/result"; + timeoutRoute = "/path/queue"; + break; + case "taxRemittance": + resultRoute = "/remittax/result"; + timeoutRoute = "/remittax/queue"; + break; + default: + throw new Error(`Invalid endpoint provided:, ${endpoint}`); + } + appInstance.post(resultRoute, (req, res) => { + const flattenedData = flattenObject(req.body); + emitter.emit("callbackResult", flattenedData); + res.status(200).send("Callback post success"); + }); + appInstance.post(timeoutRoute, (req, res) => { + const flattenedData = flattenObject(req.body); + emitter.emit("callbackTimeout", flattenedData); + res.status(200).send("Queue post success"); + }); + console.info( + "\x1b[32m\x1b[1m%s\x1b[0m", + `Callback handler for ${endpoint} has been initialized.` + ); + + } catch (error) { + throw new Error(`Callback Error: ${error}`); + } } + +export async function callbackTrigger(state, specialCase = false) { + if (state) { + return await new Promise((resolve) => { + const timeout = setTimeout(() => { + console.warn("\x1b[43m\x1b[1m\x1b[30m%s\x1b[0m", "No callback received within the time limit."); + resolve({ type: "timeout", data: {} }); + }, 15000); + + const callbackHandler = (data) => { + clearTimeout(timeout); + resolve({ type: "result", data }); + }; + + const timeoutHandler = (data) => { + clearTimeout(timeout); + resolve({ type: "timeout", data }); + }; + + if (specialCase) { + emitter.once("callbackResult", callbackHandler); + } else { + emitter.once("callbackResult", callbackHandler); + emitter.once("callbackTimeout", timeoutHandler); + } + }); + } +} + + diff --git a/lib/tests/.env.example b/lib/tests/.env.example index 1eca11a..b8cd59f 100644 --- a/lib/tests/.env.example +++ b/lib/tests/.env.example @@ -20,10 +20,11 @@ ENVIRONMENT=sandbox # Requester details REQUESTER=your_requester_phone_number_here # Phone number that will receive PIN request -MSISDN=your_msisdn_here +MSISDN=msisdn_eg_0711225578 # Lipa Na M-Pesa business shortcode BUSINESS_SHORT_CODE=your_business_shortcode_here # Account reference ACCOUNT_REFERENCE=your_account_reference_here # Certficate Path - Production or Sandbox -MPESA_CERT_PATH=path_to_credential_keys +MPESA_CERT_PATH_DEV=your_dev_cert_path +MPESA_CERT_PATH_PROD=your_prod_cert_path \ No newline at end of file diff --git a/lib/tests/Paste testing - env.local b/lib/tests/Paste testing - env.local new file mode 100644 index 0000000..e69de29 diff --git a/lib/tests/apis/b2b-Topup.spec.js b/lib/tests/apis/b2b-Topup.spec.js deleted file mode 100644 index 0a2e0f2..0000000 --- a/lib/tests/apis/b2b-Topup.spec.js +++ /dev/null @@ -1,43 +0,0 @@ -import { setupNgrokServer } from "./utils/helpers.js"; -import { b2bTopUpApi } from "./utils/init.js"; -import { createOptionsForB2bAccTopUp } from "./utils/options.js"; -import { expect } from "chai"; -import { emitter } from "./utils/server.js"; - -describe("B2B Account top up API with OAuth ", function () { - this.timeout(15000); - let NGROK_URL, teardown; - - before(async function () { - const { NGROK_URL: url, teardown: td } = await setupNgrokServer(); - NGROK_URL = url; - teardown = td; - }); - after(async function () { - // Execute teardown to disconnect ngrok and close server - await teardown(); - }); - - it("Should simulate a B2B account top up and receive a response body and result body", async function () { - const responseBody = await b2bTopUpApi( - createOptionsForB2bAccTopUp(NGROK_URL), - ); - expect(responseBody).to.be.an("object"); - console.log("RESPONSE BODY", JSON.stringify(responseBody, null, 2)); - - /** IF YOU WANT TO LOG OUT THE RESULT BODY UNCOMMENT THE CODE BELOW **/ - /* const resultBody = await new Promise((resolve, reject) => { - const timeout = setTimeout(() => reject(new Error('No callback received within time limit')), 30000) - emitter.once('b2bTopUpCallback', (data) => { - clearTimeout(timeout) - resolve(data) - }) - emitter.once('queueTimeout', (data) => { - clearTimeout(timeout) - resolve(data) - }) - }) - // Validate the received data - expect(resultBody).to.be.an('object')*/ - }); -}); diff --git a/lib/tests/apis/b2c-Request.spec.js b/lib/tests/apis/b2c-Request.spec.js deleted file mode 100644 index 05115b6..0000000 --- a/lib/tests/apis/b2c-Request.spec.js +++ /dev/null @@ -1,45 +0,0 @@ -import { expect } from "chai"; -import { setupNgrokServer } from "./utils/helpers.js"; -import { b2cRequestApi } from "./utils/init.js"; -import { createOptionsForB2c } from "./utils/options.js"; -// import { emitter } from "./utils/server.js"; - -describe("B2C Payment API with OAuth", function () { - this.timeout(15000); - let NGROK_URL, teardown; - - before(async function () { - // Set up the Ngrok server and retrieve NGROK_URL and teardown function - const { NGROK_URL: url, teardown: td } = await setupNgrokServer(); - NGROK_URL = url; - teardown = td; - }); - - after(async function () { - // Execute teardown to disconnect ngrok and close server - await teardown(); - }); - - it("Should send B2C payment and receive result or timeout callback", async function () { - // Hit the B2C Payment API - const responseBody = await b2cRequestApi(createOptionsForB2c(NGROK_URL)); - expect(responseBody).to.be.an("object"); - console.log("RESPONSE BODY:", JSON.stringify(responseBody, null, 2)); - - /** IF YOU WANT TO LOG OUT THE RESULT BODY UNCOMMENT THE CODE BELOW **/ - /*const resultBody = await new Promise((resolve, reject) => { - let timeout = setTimeout(() => reject(new Error('No callback received within time limit')), 15000) - - emitter.once('b2cRequestCallback', (data) => { - clearTimeout(timeout) - resolve({ type: 'result', data }) - }) - - emitter.once('queueTimeout', (data) => { - clearTimeout(timeout) - resolve({ type: 'timeout', data }) - }) - }) - expect(resultBody).to.be.an('object')*/ - }); -}); diff --git a/lib/tests/apis/balance-Query.spec.js b/lib/tests/apis/balance-Query.spec.js deleted file mode 100644 index e6bbb82..0000000 --- a/lib/tests/apis/balance-Query.spec.js +++ /dev/null @@ -1,47 +0,0 @@ -import { expect } from "chai"; -import { setupNgrokServer } from "./utils/helpers.js"; -import { balanceQueryApi } from "./utils/init.js"; -import { createOptionsForBalance } from "./utils/options.js"; -import { emitter } from "./utils/server.js"; - -describe("Account Balance API with OAuth", function() { - this.timeout(30000); - let NGROK_URL, teardown; - - before(async function() { - // Set up the Ngrok server and retrieve NGROK_URL and teardown function - const { NGROK_URL: url, teardown: td } = await setupNgrokServer(); - NGROK_URL = url; - teardown = td; - }); - - after(async function() { - // Execute teardown to disconnect ngrok and close server - await teardown(); - }); - it("Should fetch account balance and receive result or timeout callback", async function() { - const responseBody = await balanceQueryApi( - createOptionsForBalance(NGROK_URL) - ); - expect(responseBody).to.be.an("object"); - console.log("RESPONSE BODY:", JSON.stringify(responseBody, null, 2)); - - /** IF YOU WANT TO LOG OUT THE RESULT BODY UNCOMMENT THE CODE BELOW **/ - /* - const resultBody = await new Promise((resolve, reject) => { - const timeout = setTimeout(() => reject(new Error('No callback received within time limit')), 30000) - emitter.once('balanceQueryCallback', (data) => { - clearTimeout(timeout) - resolve({ type: 'result', data }) - }) - emitter.once('queueTimeout', (data) => { - clearTimeout(timeout) - resolve({ type: 'timeout', data }) - }) - }) - // Validate the received data - expect(resultBody).to.be.an("object"); - */ - - }); -}); diff --git a/lib/tests/apis/business-Paybill.spec.js b/lib/tests/apis/business-Paybill.spec.js deleted file mode 100644 index 9d792e4..0000000 --- a/lib/tests/apis/business-Paybill.spec.js +++ /dev/null @@ -1,43 +0,0 @@ -import { setupNgrokServer } from "./utils/helpers.js"; -import { businessPaybillApi } from "./utils/init.js"; -import { createOptionsForB2bPaybill } from "./utils/options.js"; -import { expect } from "chai"; -// import { emitter } from "./utils/server.js"; - -describe("B2B paybill API with OAuth ", function () { - this.timeout(15000); - let NGROK_URL, teardown; - - before(async function () { - const { NGROK_URL: url, teardown: td } = await setupNgrokServer(); - NGROK_URL = url; - teardown = td; - }); - after(async function () { - // Execute teardown to disconnect ngrok and close server - await teardown(); - }); - - it("Should simulate a B2B account top up and receive a response body and result body", async function () { - const responseBody = await businessPaybillApi( - createOptionsForB2bPaybill(NGROK_URL), - ); - expect(responseBody).to.be.an("object"); - console.log("RESPONSE BODY", JSON.stringify(responseBody, null, 2)); - - /** IF YOU WANT TO LOG OUT THE RESULT BODY UNCOMMENT THE CODE BELOW **/ - /*const resultBody = await new Promise((resolve, reject) => { - const timeout = setTimeout(() => reject(new Error('No callback received within time limit')), 30000) - emitter.once('b2bPaybillCallback', (data) => { - clearTimeout(timeout) - resolve(data) - }) - emitter.once('queueTimeout', (data) => { - clearTimeout(timeout) - resolve(data) - }) - }) - // Validate the received data - expect(resultBody).to.be.an('object')*/ - }); -}); diff --git a/lib/tests/apis/c2b-Register.spec.js b/lib/tests/apis/c2b-Register.spec.js deleted file mode 100644 index 7c214f9..0000000 --- a/lib/tests/apis/c2b-Register.spec.js +++ /dev/null @@ -1,40 +0,0 @@ -import { expect } from "chai"; -import { setupNgrokServer } from "./utils/helpers.js"; -import { c2bRegisterApi } from "./utils/init.js"; -import { createOptionsForC2bRegister } from "./utils/options.js"; -// import { emitter } from "./utils/server.js"; - -describe("C2B Register URL API with OAuth", function () { - this.timeout(15000); - let NGROK_URL, teardown; - - before(async function () { - // Set up the Ngrok server and retrieve NGROK_URL and teardown function - const { NGROK_URL: url, teardown: td } = await setupNgrokServer(); - NGROK_URL = url; - teardown = td; - }); - - after(async function () { - // Execute teardown to disconnect ngrok and close server - await teardown(); - }); - it("Should register C2B validation and confirmation URLs and confirm success", async function () { - // Hit the C2B Register URL API - const responseBody = await c2bRegisterApi( - createOptionsForC2bRegister(NGROK_URL), - ); - expect(responseBody).to.be.an("object"); - console.log("RESPONSE BODY:", JSON.stringify(responseBody, null, 2)); - - /** IF YOU WANT TO LOG OUT THE RESULT BODY UNCOMMENT THE CODE BELOW **/ - /* const resultBody = await new Promise((resolve, reject) => { - let timeout = setTimeout(() => reject(new Error('No callback received within time limit')), 15000) - emitter.once('c2bResult', (data) => { - clearTimeout(timeout) - resolve({ type: 'result', data }) - }) - }) - expect(resultBody).to.be.an('object')*/ - }); -}); diff --git a/lib/tests/apis/c2b-Simulate.spec.js b/lib/tests/apis/c2b-Simulate.spec.js deleted file mode 100644 index e24b92f..0000000 --- a/lib/tests/apis/c2b-Simulate.spec.js +++ /dev/null @@ -1,12 +0,0 @@ -import { createOptionsForC2bSimulate } from "./utils/options.js"; -import { c2bSimulateApi } from "./utils/init.js"; -import { expect } from "chai"; - -describe("C2B Simulate API with OAuth", function () { - this.timeout(15000); - it("Should simulate a C2B transaction", async function () { - const responseBody = await c2bSimulateApi(createOptionsForC2bSimulate()); - expect(responseBody).to.be.an("object"); - console.log("RESPONSE BODY:", JSON.stringify(responseBody, null, 2)); - }); -}); diff --git a/lib/tests/apis/dummy.spec.js b/lib/tests/apis/dummy.spec.js new file mode 100644 index 0000000..36c93d7 --- /dev/null +++ b/lib/tests/apis/dummy.spec.js @@ -0,0 +1,37 @@ +import { expect } from "chai"; +import sinon from "sinon"; +import { mpesa } from "../../../index.js"; +import { createOptionsForB2c } from "./utils/options.js"; + +describe("B2C Payment API - Mocked Test", function () { + this.timeout(5000); + + beforeEach(function () { + const stub = sinon.stub(mpesa, "b2cRequest").resolves({ + b2cRequestResponse: { + ResponseCode: "0", + ResponseDescription: "Request processed successfully", + OriginatorConversationID: "AG_20250224_201067702c0820b0b3b5", + }, + conditionalCallbackData: {}, + }); + + console.log("Is stub (mocking) working?", stub.isSinonProxy); // Should log true + }); + + afterEach(function () { + // Restore original function + sinon.restore(); + }); + + it("Should return a mocked B2C payment response", function (done) { + mpesa.b2cRequest(createOptionsForB2c("https://mock-callback.url")) + .then((responseBody) => { + expect(responseBody).to.be.an("object"); + expect(responseBody.b2cRequestResponse.ResponseCode).to.equal("0"); + console.log("MOCKED RESPONSE BODY:", JSON.stringify(responseBody, null, 2)); + done(); + }) + .catch(done); + }); +}); diff --git a/lib/tests/apis/main/b2c-Request.spec.js b/lib/tests/apis/main/b2c-Request.spec.js new file mode 100644 index 0000000..c47c2e0 --- /dev/null +++ b/lib/tests/apis/main/b2c-Request.spec.js @@ -0,0 +1,38 @@ +import { expect } from "chai"; +import { mpesa } from "../../../../index.js"; +import { setupNgrokServer } from "../utils/server.js"; +import { createOptionsForB2c } from "../utils/options.js"; + +describe("B2C Payment API with OAuth", function () { + this.timeout(24000); + let NGROK_URL, teardown; + const { b2cRequest } = mpesa; + + before(async function () { + ({ NGROK_URL, teardown } = await setupNgrokServer("b2cRequest", true)); + }); + + after(async function () { + await teardown(); + }); + + it("Should send multiple B2C payments in parallel and receive results or timeout callback", function (done) { + const urlToUse = NGROK_URL === "" ? "https://mock.url" : NGROK_URL; + // This simulates a bulk payment such as paying salaries + Promise.all([ + b2cRequest(createOptionsForB2c(urlToUse, "0789885845")), + b2cRequest(createOptionsForB2c(urlToUse, "0756284523")), + b2cRequest(createOptionsForB2c(urlToUse, "0741585228")), + ]) + .then(([responseBody1, responseBody2, responseBody3]) => { + [responseBody1, responseBody2, responseBody3].forEach((responseBody, index) => { + expect(responseBody).to.be.an("object"); + expect(responseBody?.b2cRequestResponse.ResponseCode).to.be.equal("0"); + console.log("\x1b[35m\x1b[4m\x1b[1m%s\x1b[0m", `Test ${index + 1} passed successfully`); + // console.log(`RESPONSE BODY ${index + 1}:`, JSON.stringify(responseBody, null, 2)); + }); + done(); + }) + .catch(done); + }); +}); diff --git a/lib/tests/apis/main/b2c-Topup.spec.js b/lib/tests/apis/main/b2c-Topup.spec.js new file mode 100644 index 0000000..98d81ca --- /dev/null +++ b/lib/tests/apis/main/b2c-Topup.spec.js @@ -0,0 +1,29 @@ +import { expect } from "chai"; +import { mpesa } from "../../../../index.js"; +import { setupNgrokServer } from "../utils/server.js"; +import { createOptionsForB2cAccTopUp } from "../utils/options.js"; + +describe("B2B Account Top-Up API with OAuth", function () { + this.timeout(24000); + let NGROK_URL, teardown; + const { b2cTopUp } = mpesa; + + before(async function () { + ({ NGROK_URL, teardown } = await setupNgrokServer("b2cTopUp", true)); + }); + + after(async function () { + await teardown(); + }); + + it("Should simulate a B2C account top-up and receive result or timeout callback", function (done) { + b2cTopUp(createOptionsForB2cAccTopUp(NGROK_URL === "" ? "https://mock.url" : NGROK_URL)) + .then((responseBody) => { + expect(responseBody).to.be.an("object"); + expect(responseBody?.b2cTopUpResponse.ResponseCode).to.be.equal("0"); + console.log("\x1b[35m\x1b[4m\x1b[1m%s\x1b[0m", `Test passed successfully`); + // console.log("RESPONSE BODY:", JSON.stringify(responseBody, null, 2)); + done() + }).catch(done); + }); +}); diff --git a/lib/tests/apis/main/balance-Query.spec.js b/lib/tests/apis/main/balance-Query.spec.js new file mode 100644 index 0000000..5e03752 --- /dev/null +++ b/lib/tests/apis/main/balance-Query.spec.js @@ -0,0 +1,30 @@ +import { expect } from "chai"; +import { setupNgrokServer } from "../utils/server.js"; +import { mpesa } from "../../../../index.js"; +import { createOptionsForBalance } from "../utils/options.js"; + +describe("Account Balance API with OAuth", function () { + this.timeout(24000); + let NGROK_URL, teardown; + const { balanceQuery } = mpesa; + + before(async function () { + ({ NGROK_URL, teardown } = await setupNgrokServer("balanceQuery", true)); + }); + + after(async function () { + await teardown(); + }); + + it("Should fetch account balance and receive result or timeout callback", function (done) { + balanceQuery(createOptionsForBalance(NGROK_URL === "" ? "https://mock.url" : NGROK_URL)) + .then((responseBody) => { + expect(responseBody).to.be.an("object"); + expect(responseBody?.balanceQueryResponse.ResponseCode).to.be.equal("0"); + console.log("\x1b[35m\x1b[4m\x1b[1m%s\x1b[0m", `Test passed successfully`); + // console.log("RESPONSE BODY:", JSON.stringify(responseBody, null, 2)); + done(); + }) + .catch(done); + }); +}); diff --git a/lib/tests/apis/main/business-Paybill.spec.js b/lib/tests/apis/main/business-Paybill.spec.js new file mode 100644 index 0000000..9fbdfd7 --- /dev/null +++ b/lib/tests/apis/main/business-Paybill.spec.js @@ -0,0 +1,30 @@ +import { expect } from "chai"; +import { setupNgrokServer } from "../utils/server.js"; +import { mpesa } from "../../../../index.js"; +import { createOptionsForB2bPaybill } from "../utils/options.js"; + +describe("B2B Paybill API with OAuth", function () { + this.timeout(24000); + let NGROK_URL, teardown; + const { businessPaybill } = mpesa; + + before(async function () { + ({ NGROK_URL, teardown } = await setupNgrokServer("businessPaybill", true)); + }); + + after(async function () { + await teardown(); + }); + + it("Should simulate a B2B paybill transaction and receive a response", function (done) { + businessPaybill(createOptionsForB2bPaybill(NGROK_URL === "" ? "https://mock.url" : NGROK_URL)) + .then((responseBody) => { + expect(responseBody).to.be.an("object"); + expect(responseBody?.businessPaybillResponse.ResponseCode).to.be.equal("0"); + console.log("\x1b[35m\x1b[4m\x1b[1m%s\x1b[0m", `Test passed successfully`); + /*console.log("RESPONSE BODY:", JSON.stringify(responseBody, null, 2));*/ + done(); + }) + .catch(done); + }); +}); diff --git a/lib/tests/apis/main/c2b-Register.spec.js b/lib/tests/apis/main/c2b-Register.spec.js new file mode 100644 index 0000000..e75833d --- /dev/null +++ b/lib/tests/apis/main/c2b-Register.spec.js @@ -0,0 +1,30 @@ +import { expect } from "chai"; +import { setupNgrokServer } from "../utils/server.js"; +import { mpesa } from "../../../../index.js"; +import { createOptionsForC2bRegister } from "../utils/options.js"; + +describe("C2B Register URL API with OAuth", function() { + this.timeout(15000); + let NGROK_URL, teardown; + const { c2bRegister } = mpesa; + + before(async function() { + ({ NGROK_URL, teardown } = await setupNgrokServer("c2bRegister", true)); + }); + + after(async function() { + await teardown(); + }); + + it("Should register C2B validation and confirmation URLs and confirm success", function(done) { + c2bRegister(createOptionsForC2bRegister(NGROK_URL === "" ? "https://mock.url" : NGROK_URL)) + .then((responseBody) => { + expect(responseBody).to.be.an("object"); + expect(responseBody?.c2bRegisterResponse.ResponseCode).to.be.equal("00000000"); + console.log("\x1b[35m\x1b[4m\x1b[1m%s\x1b[0m", `Test passed successfully`); + /*console.log("RESPONSE BODY:", JSON.stringify(responseBody, null, 2));*/ + done(); + }) + .catch(done); + }); +}); diff --git a/lib/tests/apis/main/c2b-Simulate.spec.js b/lib/tests/apis/main/c2b-Simulate.spec.js new file mode 100644 index 0000000..ca06b2f --- /dev/null +++ b/lib/tests/apis/main/c2b-Simulate.spec.js @@ -0,0 +1,19 @@ +import { expect } from "chai"; +import { mpesa } from "../../../../index.js"; +import { createOptionsForC2bSimulate } from "../utils/options.js"; + +describe("C2B Simulate API with OAuth", function() { + this.timeout(28000); + const { c2bSimulate } = mpesa; + it("Should simulate a C2B transaction", function(done) { + c2bSimulate(createOptionsForC2bSimulate()) + .then((responseBody) => { + expect(responseBody).to.be.an("object"); + expect(responseBody?.c2bSimulateResponse.ResponseCode).to.be.equal("0"); + console.log("\x1b[35m\x1b[4m\x1b[1m%s\x1b[0m", `Test passed successfully`); + // console.log("RESPONSE BODY:", JSON.stringify(responseBody, null, 2)); + done(); + }) + .catch(done); + }); +}); diff --git a/lib/tests/apis/main/mpesa-Query.spec.js b/lib/tests/apis/main/mpesa-Query.spec.js new file mode 100644 index 0000000..64ddb40 --- /dev/null +++ b/lib/tests/apis/main/mpesa-Query.spec.js @@ -0,0 +1,21 @@ +import { expect } from "chai"; +import { mpesa } from "../../../../index.js"; +import { createOptionsForMpesaQuery } from "../utils/options.js"; + +describe("Lipa Na M-Pesa Query API", function() { + this.timeout(24000); + const { mpesaQuery } = mpesa; + it("Should initiate Lipa Na M-Pesa payment query and return a response", function(done) { + mpesaQuery( + createOptionsForMpesaQuery("ws_CO_25022025092305896110081288") + ) + .then((responseBody) => { + expect(responseBody).to.be.an("object"); + expect(responseBody?.mpesaQueryResponse.ResponseCode).to.be.equal("0"); + console.log("\x1b[35m\x1b[4m\x1b[1m%s\x1b[0m", `Test passed successfully`); + // console.log("RESPONSE BODY:", JSON.stringify(responseBody, null, 2)); + done(); + }) + .catch(done); + }); +}); diff --git a/lib/tests/apis/main/mpesa-Simulate.spec.js b/lib/tests/apis/main/mpesa-Simulate.spec.js new file mode 100644 index 0000000..36910a1 --- /dev/null +++ b/lib/tests/apis/main/mpesa-Simulate.spec.js @@ -0,0 +1,30 @@ +import { expect } from "chai"; +import { mpesa } from "../../../../index.js"; +import { createOptionsForMpesaSimulate } from "../utils/options.js"; +import { setupNgrokServer } from "../utils/server.js"; + +describe("Lipa Na M-Pesa C2B API with OAuth", function() { + this.timeout(45000); + let NGROK_URL, teardown; + const { mpesaSimulate } = mpesa; + + before(async function() { + ({ NGROK_URL, teardown } = await setupNgrokServer("mpesaSimulate", true)); + }); + + after(async function() { + await teardown(); + }); + + it("Should initiate Lipa Na M-Pesa payment and return a response", function(done) { + mpesaSimulate(createOptionsForMpesaSimulate(NGROK_URL === "" ? "https://mock.url" : NGROK_URL)) + .then((responseBody) => { + expect(responseBody).to.be.an("object"); + expect(responseBody?.mpesaSimulateResponse.ResponseCode).to.be.equal("0"); + console.log("\x1b[35m\x1b[4m\x1b[1m%s\x1b[0m", `Test passed successfully`); + // console.log("RESPONSE BODY:", JSON.stringify(responseBody, null, 2)); + done(); + }) + .catch(done); + }); +}); diff --git a/lib/tests/apis/main/qr-Generate.spec.js b/lib/tests/apis/main/qr-Generate.spec.js new file mode 100644 index 0000000..f298b9e --- /dev/null +++ b/lib/tests/apis/main/qr-Generate.spec.js @@ -0,0 +1,19 @@ +import { expect } from "chai"; +import { mpesa } from "../../../../index.js"; +import { createOptionsForQrCode } from "../utils/options.js"; + +describe("Generate Dynamic QR Code API", function () { + this.timeout(24000); + const { generateQrCode } = mpesa; + + it("Should generate a dynamic QR code successfully", function (done) { + generateQrCode(createOptionsForQrCode()) + .then((responseBody) => { + expect(responseBody).to.be.an("object"); + console.log("\x1b[35m\x1b[4m\x1b[1m%s\x1b[0m", `Test passed successfully`); + // console.log("RESPONSE BODY:", JSON.stringify(responseBody, null, 2)); + done(); + }) + .catch(done); + }); +}); diff --git a/lib/tests/apis/main/reversals.spec.js b/lib/tests/apis/main/reversals.spec.js new file mode 100644 index 0000000..15c9029 --- /dev/null +++ b/lib/tests/apis/main/reversals.spec.js @@ -0,0 +1,30 @@ +import { expect } from "chai"; +import { setupNgrokServer } from "../utils/server.js"; +import { createOptionsForReversals } from "../utils/options.js"; +import { mpesa } from "../../../../index.js"; + +describe("Reversal API Test", function() { + this.timeout(28000); + let NGROK_URL, teardown; + const { reversals } = mpesa; + + before(async function() { + ({ NGROK_URL, teardown } = await setupNgrokServer("reversals", true)); + }); + + after(async function() { + await teardown(); + }); + + it("Should initiate a reversal and return a response", function(done) { + reversals(createOptionsForReversals(NGROK_URL === "" ? "https://mock.url" : NGROK_URL, "OEI2AK4Q16")) + .then((responseBody) => { + expect(responseBody).to.be.an("object"); + expect(responseBody?.reversalsResponse.ResponseCode).to.be.equal("0"); + console.log("\x1b[35m\x1b[4m\x1b[1m%s\x1b[0m", `Test passed successfully`); + // console.log("RESPONSE BODY:", JSON.stringify(responseBody, null, 2)); + done(); + }) + .catch(done); + }); +}); diff --git a/lib/tests/apis/main/tax-Remittance.spec.js b/lib/tests/apis/main/tax-Remittance.spec.js new file mode 100644 index 0000000..d04bfa5 --- /dev/null +++ b/lib/tests/apis/main/tax-Remittance.spec.js @@ -0,0 +1,31 @@ +import { setupNgrokServer } from "../utils/server.js"; +import { mpesa } from "../../../../index.js"; +import { createOptionsForTaxRemittance } from "../utils/options.js"; +import { expect } from "chai"; + +describe("Tax remittance API with OAuth ", function () { + this.timeout(15000); + let NGROK_URL, teardown; + const { taxRemittance } = mpesa; + before(async function () { + ({ NGROK_URL, teardown } = await setupNgrokServer("taxRemittance", true)); + }); + after(async function () { + await teardown(); + }); + + it("Should simulate tax remittance to KRA and receive a response body and result body", function (done) { + taxRemittance(createOptionsForTaxRemittance(NGROK_URL)) + .then((responseBody) => { + expect(responseBody).to.be.an("object"); + expect(responseBody?.remittanceResponse.ResponseCode).to.be.equal("0"); + console.log( + "\x1b[35m\x1b[4m\x1b[1m%s\x1b[0m", + `Test passed successfully`, + ); + // console.log("RESPONSE BODY", JSON.stringify(responseBody, null, 2)); + done(); + }) + .catch(done); + }); +}); diff --git a/lib/tests/apis/main/transaction-status.spec.js b/lib/tests/apis/main/transaction-status.spec.js new file mode 100644 index 0000000..da0531a --- /dev/null +++ b/lib/tests/apis/main/transaction-status.spec.js @@ -0,0 +1,30 @@ +import { expect } from "chai"; +import { setupNgrokServer } from "../utils/server.js"; +import { mpesa } from "../../../../index.js"; +import { createOptionsForTransactionStatus } from "../utils/options.js"; + +describe("Transaction Status API", function() { + this.timeout(24000); + let NGROK_URL, teardown; + const { transactionStatus } = mpesa; + + before(async function() { + ({ NGROK_URL, teardown } = await setupNgrokServer("transactionStatus", true)); + }); + + after(async function() { + await teardown(); + }); + + it("Should retrieve transaction status and return a response", function(done) { + transactionStatus(createOptionsForTransactionStatus(NGROK_URL === "" ? "https://mock.url" : NGROK_URL)) + .then((responseBody) => { + expect(responseBody).to.be.an("object"); + expect(responseBody?.transactStatusResponse.ResponseCode).to.be.equal("0"); + console.log("\x1b[35m\x1b[4m\x1b[1m%s\x1b[0m", `Test passed successfully`); + // console.log("RESPONSE BODY:", JSON.stringify(responseBody, null, 2)); + done(); + }) + .catch(done); + }); +}); diff --git a/lib/tests/apis/mpesa-Query.spec.js b/lib/tests/apis/mpesa-Query.spec.js deleted file mode 100644 index 916fa40..0000000 --- a/lib/tests/apis/mpesa-Query.spec.js +++ /dev/null @@ -1,14 +0,0 @@ -import { expect } from "chai"; -import { mpesaQueryApi } from "./utils/init.js"; -import { createOptionsForMpesaQuery } from "./utils/options.js"; - -// Requires you to pass the checkout ID of the previous MPESA C2B simulate API -describe("Lipa Na M-Pesa C2B query API", function () { - this.timeout(15000); - it("Should initiate Lipa Na M-pesa payment query, and receive a callback", async function () { - const responseBody = await mpesaQueryApi(createOptionsForMpesaQuery("ws_CO_03112024094702738110081288")); - expect(responseBody).to.be.an("object"); - console.log("RESPONSE BODY", JSON.stringify(responseBody, null, 2)); - }); -}); - diff --git a/lib/tests/apis/mpesa-Simulate.spec.js b/lib/tests/apis/mpesa-Simulate.spec.js deleted file mode 100644 index 00c23ae..0000000 --- a/lib/tests/apis/mpesa-Simulate.spec.js +++ /dev/null @@ -1,42 +0,0 @@ -import { expect } from "chai"; -import { mpesaSimulateApi } from "./utils/init.js"; -import { createOptionsForMpesaSimulate } from "./utils/options.js"; -import { setupNgrokServer } from "./utils/helpers.js"; -import { emitter } from "./utils/server.js"; - -describe("Lipa Na M-Pesa C2B API with OAuth", function () { - this.timeout(45000); - let NGROK_URL, teardown; - - before(async function () { - const { NGROK_URL: url, teardown: td } = await setupNgrokServer(); - NGROK_URL = url; - teardown = td; - }); - after(async function () { - // Execute teardown to disconnect ngrok and close server - await teardown(); - }); - - it("Should initiate Lipa Na M-Pesa payment and receive a callback", async function () { - const responseBody = await mpesaSimulateApi( - createOptionsForMpesaSimulate(NGROK_URL), - ); - expect(responseBody).to.be.an("object"); - console.log("Initiated payment:", JSON.stringify(responseBody, null, 2)); - - /** IF YOU WANT TO LOG OUT THE RESULT BODY UNCOMMENT THE CODE BELOW **/ - const resultBody = await new Promise((resolve, reject) => { - const timeout = setTimeout(() => reject(new Error('No callback received within time limit')), 30000) - emitter.once('mpesaSimulateCallback', (data) => { - clearTimeout(timeout) - resolve(data) - }) - emitter.once('queueTimeout', (data) => { - clearTimeout(timeout) - resolve(data) - }) - }) - expect(resultBody).to.be.an('object') - }); -}); diff --git a/lib/tests/apis/qr-Generate.spec.js b/lib/tests/apis/qr-Generate.spec.js deleted file mode 100644 index 5b5c2cc..0000000 --- a/lib/tests/apis/qr-Generate.spec.js +++ /dev/null @@ -1,16 +0,0 @@ -import { expect } from "chai"; -import { generateQrCodeApi } from "./utils/init.js"; -import { createOptionsForQrCode } from "./utils/options.js"; - -describe("Generate Dynamic QR Code API", function () { - this.timeout(15000); - - it("Should generate a dynamic QR code successfully", async function () { - const responseBody = await generateQrCodeApi(createOptionsForQrCode()); - expect(responseBody).to.be.an("object"); - console.log( - "Generated dynamic QR code:", - JSON.stringify(responseBody, null, 2), - ); - }); -}); diff --git a/lib/tests/apis/reversals.spec.js b/lib/tests/apis/reversals.spec.js deleted file mode 100644 index a1efa1c..0000000 --- a/lib/tests/apis/reversals.spec.js +++ /dev/null @@ -1,51 +0,0 @@ -import { expect } from "chai"; -import { setupNgrokServer } from "./utils/helpers.js"; -import { reversalsApi } from "./utils/init.js"; -import { createOptionsForReversals } from "./utils/options.js"; -import { emitter } from "./utils/server.js"; - -describe("Reversal API Test", function () { - this.timeout(15000); - let NGROK_URL, teardown; - - before(async function () { - // Set up Ngrok server and retrieve NGROK_URL and teardown function - const { NGROK_URL: url, teardown: td } = await setupNgrokServer(); - NGROK_URL = url; - teardown = td; - }); - - after(async function () { - // Execute teardown to disconnect ngrok and close server - await teardown(); - }); - - it("Should initiate a reversal and receive a result or timeout callback", async function (done) { - // Send the reversal request - const responseBody = await reversalsApi( - createOptionsForReversals(NGROK_URL), - ); - expect(responseBody).to.be.an("object"); - console.log( - "Initiated reversal request:", - JSON.stringify(responseBody, null, 2), - ); - /** IF YOU WANT TO LOG OUT THE RESULT BODY UNCOMMENT THE CODE BELOW **/ - const resultBody = await new Promise((resolve, reject) => { - const timeout = setTimeout( - () => reject(new Error("No callback received within time limit")), - 30000, - ); - emitter.once("reversalsCallback", (data) => { - clearTimeout(timeout); - resolve(data); - }); - emitter.once("queueTimeout", (data) => { - clearTimeout(timeout); - resolve(data); - }); - }); - - - }); -}); diff --git a/lib/tests/apis/tax-Remittance.spec.js b/lib/tests/apis/tax-Remittance.spec.js deleted file mode 100644 index 2851fa4..0000000 --- a/lib/tests/apis/tax-Remittance.spec.js +++ /dev/null @@ -1,42 +0,0 @@ -import { setupNgrokServer } from "./utils/helpers.js"; -import { taxRemittanceApi } from "./utils/init.js"; -import { createOptionsForTaxRemittance } from "./utils/options.js"; -// import { emitter } from "./utils/server.js"; -import { expect } from "chai"; - -describe("Tax remittance API with OAuth ", function () { - this.timeout(15000); - let NGROK_URL, teardown; - - before(async function () { - const { NGROK_URL: url, teardown: td } = await setupNgrokServer(); - NGROK_URL = url; - teardown = td; - }); - after(async function () { - // Execute teardown to disconnect ngrok and close server - await teardown(); - }); - - it("Should simulate tax remittance to KRA and receive a response body and result body", async function () { - const responseBody = await taxRemittanceApi( - createOptionsForTaxRemittance(NGROK_URL), - ); - expect(responseBody).to.be.an("object"); - console.log("RESPONSE BODY", JSON.stringify(responseBody, null, 2)); - - /** IF YOU WANT TO LOG OUT THE RESULT BODY UNCOMMENT THE CODE BELOW **/ - /*const resultBody = await new Promise((resolve, reject) => { - const timeout = setTimeout(() => reject(new Error('No callback received within time limit')), 30000) - emitter.once('taxRemittanceCallback', (data) => { - clearTimeout(timeout) - resolve(data) - }) - emitter.once('queueTimeout', (data) => { - clearTimeout(timeout) - resolve(data) - }) - }) - expect(resultBody).to.be.an('object')*/ - }); -}); diff --git a/lib/tests/apis/transaction-status.spec.js b/lib/tests/apis/transaction-status.spec.js deleted file mode 100644 index dd45337..0000000 --- a/lib/tests/apis/transaction-status.spec.js +++ /dev/null @@ -1,50 +0,0 @@ -import { expect } from "chai"; -import { setupNgrokServer } from "./utils/helpers.js"; -import { transactionStatusApi } from "./utils/init.js"; -import { createOptionsForTransactionStatusApi } from "./utils/options.js"; -// import { emitter } from "./utils/server.js"; - -describe("Transaction Status API", function () { - this.timeout(15000); - let NGROK_URL, teardown; - - before(async function () { - // Set up the Ngrok server - const { NGROK_URL: url, teardown: td } = await setupNgrokServer(); - NGROK_URL = url; - teardown = td; - }); - - after(async function () { - // Execute teardown to disconnect ngrok and close server - await teardown(); - }); - - it("Should retrieve transaction status and receive result or timeout callback", async function () { - // Send the transaction status request - const responseBody = await transactionStatusApi( - createOptionsForTransactionStatusApi(NGROK_URL), - ); - expect(responseBody).to.be.an("object"); - console.log( - "Initiated transaction status:", - JSON.stringify(responseBody, null, 2), - ); - /** IF YOU WANT TO LOG OUT THE RESULT BODY UNCOMMENT THE CODE BELOW **/ - /* const resultBody = await new Promise((resolve, reject) => { - const timeout = setTimeout( - () => reject(new Error("No callback received within time limit")), - 30000 - ); - emitter.once("transactionStatusCallback", (data) => { - clearTimeout(timeout); - resolve({ type: "result", data }); - }); - emitter.once("queueTimeout", (data) => { - clearTimeout(timeout); - resolve({ type: "timeout", data }); - }); - }); - expect(resultBody).to.be.an("object");*/ - }); -}); diff --git a/lib/tests/apis/utils/callbacks.js b/lib/tests/apis/utils/callbacks.js new file mode 100644 index 0000000..3e4b93f --- /dev/null +++ b/lib/tests/apis/utils/callbacks.js @@ -0,0 +1,53 @@ +import { callbacks } from "../../../../index.js"; + +// Destructure all available callbackHandlers + +const { + handleB2cRequestCallbacks, + handleB2cTopUpCallbacks, + handleBalanceQueryCallbacks, + handleBusinessPaybillCallbacks, + handleC2bRegisterCallbacks, + handleTaxRemittanceCallbacks, + handleTransactStatusCallbacks, + handleReversalCallbacks, + handleMpesaSimulateCallbacks, +} = callbacks; + +// Nest all callbacks into one single exportable arrow function + +const runSingleCallbacks = async (testName, app) => { + switch (testName) { + case "balanceQuery": + await handleBalanceQueryCallbacks(app); + break; + case "transactionStatus": + await handleTransactStatusCallbacks(app); + break; + case "c2bRegister": + await handleC2bRegisterCallbacks(app); + break; + case "reversals": + await handleReversalCallbacks(app); + break; + case "mpesaSimulate": + await handleMpesaSimulateCallbacks(app); + break; + case "b2cTopUp": + await handleB2cTopUpCallbacks(app); + break; + case "b2cRequest": + await handleB2cRequestCallbacks(app); + break; + case "taxRemittance": + await handleTaxRemittanceCallbacks(app); + break; + case "businessPaybill": + await handleBusinessPaybillCallbacks(app); + break; + default: + //null + } +}; + +export { runSingleCallbacks }; diff --git a/lib/tests/apis/utils/helpers.js b/lib/tests/apis/utils/helpers.js deleted file mode 100644 index 8abc4fb..0000000 --- a/lib/tests/apis/utils/helpers.js +++ /dev/null @@ -1,45 +0,0 @@ -import http from "http"; -import ngrok from "ngrok"; -import { app } from "./server.js"; - -export async function setupNgrokServer() { - const SERVER = http.createServer(app); - SERVER.listen(0); - const port = SERVER.address().port; - const NGROK_URL = await ngrok.connect(port); - - console.log(`ngrok tunnel created: ${NGROK_URL}`); - - // Teardown server and ngrok after dist - async function teardown() { - await ngrok.disconnect(); - SERVER.close((err) => { - if (err) { - console.error("Error while closing server:", err); - process.exit(1); // Forceful shutdown if an error occurs - } else { - console.log("Server closed gracefully"); - } - }); - - // Set a timeout to force stop if graceful shutdown takes too long - setTimeout(() => { - console.warn("Exiting the test now..."); - process.exit(0); - }, 30000); - } - - return { NGROK_URL, teardown }; -} - -export function flattenObject(obj, prefix = "") { - return Object.keys(obj).reduce((acc, key) => { - const newKey = prefix ? `${prefix}_${key}` : key; - if (typeof obj[key] === "object" && obj[key] !== null) { - Object.assign(acc, flattenObject(obj[key], newKey)); - } else { - acc[newKey] = obj[key]; - } - return acc; - }, {}); -} diff --git a/lib/tests/apis/utils/init.js b/lib/tests/apis/utils/init.js deleted file mode 100644 index 1f1a8f0..0000000 --- a/lib/tests/apis/utils/init.js +++ /dev/null @@ -1,15 +0,0 @@ -import mpesaAPIs from "../../../../index.js"; -export const { - balanceQueryApi, - b2cRequestApi, - c2bRegisterApi, - mpesaSimulateApi, - c2bSimulateApi, - mpesaQueryApi, - b2bTopUpApi, - reversalsApi, - generateQrCodeApi, - transactionStatusApi, - businessPaybillApi, - taxRemittanceApi, -} = mpesaAPIs; diff --git a/lib/tests/apis/utils/options.js b/lib/tests/apis/utils/options.js index b4839b6..e2a93a5 100644 --- a/lib/tests/apis/utils/options.js +++ b/lib/tests/apis/utils/options.js @@ -3,7 +3,7 @@ import { CommandIDs, IdentifierTypes, responseTypes, - trxCodeTypes, + trxCodeTypes } from "../../../core/utils/constants.js"; export function createOptionsForQrCode() { @@ -13,118 +13,125 @@ export function createOptionsForQrCode() { amount: 2000, trxCode: trxCodeTypes.BUY_GOODS, cpi: "174379", - size: "300", + size: "300" }; } export function createOptionsForMpesaQuery(passRequestIdHere) { return { - shortCode: parseInt(configs.businessShortCode, 10), - // passKey: "", - checkoutRequestId: `${passRequestIdHere}`, + businessShortCode: parseInt(configs.businessShortCode), + checkoutRequestId: `${passRequestIdHere}` }; } -export function createOptionsForB2c(NGROK_URL) { +export function createOptionsForB2c(NGROK_URL, msisdn) { return { - senderParty: parseInt(configs.partyA, 10), - receiverParty: parseInt(configs.msisdn, 10), + partyA: parseInt(configs.partyA), + partyB: msisdn, initiatorName: configs.initiatorName, amount: parseInt("1000"), commandId: CommandIDs.SALARY_PAYMENT, - queueUrl: `${NGROK_URL}/b2c/queue`, + QueueTimeOutURL: `${NGROK_URL}/b2c/queue`, resultUrl: `${NGROK_URL}/b2c/result`, - }; -} - -export function createOptionsForC2bRegister(NGROK_URL) { - return { - shortCode: parseInt(configs.partyA, 10), - responseType: responseTypes.COMPLETED, - confirmationUrl: `${NGROK_URL}/confirmation/result`, - validationUrl: `${NGROK_URL}/validation/result`, + occasion: "TEST", + remarks: "TEST" }; } export function createOptionsForC2bSimulate() { return { - shortCode: parseInt(configs.partyA, 10), - msisdn: parseInt(configs.msisdn, 10), - amount: parseInt("100", 10), - billRefNumber: "invoice008", + shortCode: parseInt(configs.partyA), + msisdn: configs.msisdn, + amount: parseInt("100"), + billRefNumber: "invoice008" }; } export function createOptionsForBalance(NGROK_URL) { return { - idType: parseInt(IdentifierTypes.TILL_NUMBER, 10), - shortCode: parseInt(configs.partyA, 10), + identifierType: IdentifierTypes.TILL_NUMBER, + partyA: parseInt(configs.partyA), initiator: configs.initiatorName, - queueUrl: `${NGROK_URL}/accountbalance/queuetimeouturl`, + QueueTimeOutURL: `${NGROK_URL}/accountbalance/queuetimeouturl`, resultUrl: `${NGROK_URL}/accountbalance/result`, + remarks: "TEST" }; } export function createOptionsForMpesaSimulate(NGROK_URL) { return { - passKey: configs.passKey, - shortCode: parseInt(configs.businessShortCode, 10), - msisdn1: parseInt(configs.msisdn, 10), - msisdn2: parseInt(configs.msisdn, 10), + partyB: parseInt(configs.partyB), + partyA: configs.msisdn, + phoneNumber: configs.msisdn, + transactionType: "CustomerPayBillOnline", + transactionDesc:"TEST", amount: 1, callbackUrl: `${NGROK_URL}/path/result`, - accountRef: "Test", + accountRef: configs.accountRef }; } -export function createOptionsForReversals(NGROK_URL) { +export function createOptionsForReversals(NGROK_URL, transId) { return { - shortCode: parseInt(configs.partyA, 10), + receiverParty: parseInt(configs.partyA), initiator: configs.initiatorName, - transactionId: "OEI2AK4Q16", - amount: parseInt("100", 10), - queueUrl: `${NGROK_URL}/Reversal/queuetimeouturl`, + transactionId: transId, + amount: parseInt("100"), + QueueTimeOutURL: `${NGROK_URL}/Reversal/queuetimeouturl`, resultUrl: `${NGROK_URL}/Reversal/result`, - remarks: "Reversal Test", - occasion: "Test Occasion", + remarks: "TEST", + occasion: "TEST" }; } -export function createOptionsForTransactionStatusApi(NGROK_URL) { +export function createOptionsForTransactionStatus(NGROK_URL) { return { - idType: parseInt(IdentifierTypes.ORG_SHORTCODE, 10), - receiverParty: parseInt(configs.partyA, 10), - transactionId: "NEF61H8J60", + identifierType: parseInt(IdentifierTypes.ORG_SHORTCODE), + OriginatorConversationID: "AG_20190826_0000777ab7d848b9e721", + transactionId: "OEI2AK4Q16", initiator: configs.initiatorName, - OriginatorConversationID: "AG_20241103_20106c21d3528ac5d558", - queueUrl: `${NGROK_URL}/transactionStatusApi/queue`, - resultUrl: `${NGROK_URL}/transactionStatusApi/result`, + partyA: parseInt(configs.partyA), + resultUrl: `${NGROK_URL}/TransactionStatus/result/`, + QueueTimeOutUrl: `${NGROK_URL}/TransactionStatus/queue/`, + remarks: "TEST", + occasion: "TEST" }; } -export function createOptionsForB2bAccTopUp(NGROK_URL) { +export function createOptionsForB2cAccTopUp(NGROK_URL) { return { - accountReference: parseInt(configs.accountRef, 10), - partyA: parseInt(configs.partyA, 10), - partyB: parseInt(configs.partyB, 10), - requester: parseInt(configs.requester, 10), //optional + accountReference: parseInt(configs.accountRef), + partyA: parseInt(configs.partyA), + partyB: parseInt(configs.partyB), + requester: parseInt(configs.requester), //optional initiator: configs.initiatorName, - amount: parseInt("100", 10), - resultURL: `${NGROK_URL}/path/result`, - queueTimeOutURL: `${NGROK_URL}/path/queue`, + amount: parseInt("100"), + resultUrl: `${NGROK_URL}/path/result`, + QueueTimeOutURL: `${NGROK_URL}/path/queue`, + remarks: "TEST", + }; +} + +export function createOptionsForC2bRegister(NGROK_URL) { + return { + confirmationUrl: `${NGROK_URL}/confirmation/result`, + validationUrl: `${NGROK_URL}/validation/result`, + shortCode: parseInt(configs.businessShortCode), + responseType: responseTypes.CANCELLED }; } export function createOptionsForB2bPaybill(NGROK_URL) { return { - accountReference: parseInt(configs.accountRef, 10), - partyA: parseInt(configs.partyA, 10), - partyB: parseInt(configs.partyB, 10), - requester: parseInt(configs.requester, 10), //optional initiator: configs.initiatorName, - amount: parseInt("100", 10), - resultURL: `${NGROK_URL}/path/result`, - queueTimeOutURL: `${NGROK_URL}/path/queue`, + partyA: parseInt(configs.partyA), + partyB: parseInt(configs.partyB), + amount: parseInt("100"), + accountReference: parseInt(configs.accountRef), + requester: parseInt(configs.requester), //optional + resultUrl: `${NGROK_URL}/path/result`, + QueueTimeOutURL: `${NGROK_URL}/path/queue`, + remarks: "TEST" }; } @@ -135,7 +142,8 @@ export function createOptionsForTaxRemittance(NGROK_URL) { partyB: parseInt(configs.partyB, 10), initiator: configs.initiatorName, amount: parseInt("100", 10), - resultURL: `${NGROK_URL}/remittax/result`, - queueTimeOutURL: `${NGROK_URL}/remittax/queue`, + resultUrl: `${NGROK_URL}/remittax/result`, + QueueTimeOutURL: `${NGROK_URL}/remittax/queue`, + remarks: "TEST" }; } diff --git a/lib/tests/apis/utils/server.js b/lib/tests/apis/utils/server.js index b37c252..4ef1b68 100644 --- a/lib/tests/apis/utils/server.js +++ b/lib/tests/apis/utils/server.js @@ -1,145 +1,53 @@ +import http from "http"; +import ngrok from "ngrok"; import express from "express"; -import EventEmitter from "events"; -import { flattenObject } from "./helpers.js"; +import { runSingleCallbacks } from "./callbacks.js"; +// Initialize Express app const app = express(); -const emitter = new EventEmitter(); app.use(express.json()); -/** ACCOUNT BALANCE QUERY **/ -app.post("/accountbalance/result", (req, res) => { - const resultData = req.body; - const unpackedData = flattenObject(resultData); - console.log("Success:", unpackedData); - - emitter.emit("balanceQueryCallback", unpackedData); - - res.status(200).send("Callback received"); -}); -app.post("/accountbalance/queuetimeouturl", (req, res) => { - console.log("Timeout:", req.body); - - emitter.emit("queueTimeout", req.body); - - res.status(200).send("Queue timeout callback received"); -}); - -/** B2C REQUEST CALLBACK **/ -app.post("/b2c/result", (req, res) => { - const resultData = req.body; - console.log("Success:", resultData); - - emitter.emit("b2cRequestCallback", resultData); - - res.status(200).send("Callback received"); -}); -app.post("/b2c/queue", (req, res) => { - console.log("Timeout:", req.body); - - emitter.emit("queueTimeout", req.body); - - res.status(200).send("Queue timeout callback received"); -}); - -/** MPESA SIMULATE CALLBACK **/ -app.post("/path/result", (req, res) => { - const resultData = req.body; - const unpackedData = flattenObject(resultData); - console.log("Success:", unpackedData); - - emitter.emit("mpesaSimulateCallback", unpackedData); - - res.status(200).send("Callback received"); -}); - -/** REVERSALS CALLBACK **/ -app.post("/Reversal/result", (req, res) => { - const resultData = req.body; - const unpackedData = flattenObject(resultData); - console.log("Success:", unpackedData); - - emitter.emit("reversalsCallback", unpackedData); - - res.status(200).send("Callback received"); -}); -app.post("/Reversal/queuetimeouturl", (req, res) => { - console.log("Timeout:", req.body); - - emitter.emit("queueTimeout", req.body); - - res.status(200).send("Queue timeout callback received"); -}); - -/** TRANSACTION STATUS CALLBACK **/ -app.post("/transactionStatusApi/result", (req, res) => { - const resultData = req.body; - const unpackedData = flattenObject(resultData); - console.log("Success:", unpackedData); - emitter.emit("transactionStatusCallback", unpackedData); - res.status(200).send("Callback received"); -}); -app.post("/transactionStatusApi/queue", (req, res) => { - console.log("Timeout:", req.body); - - emitter.emit("queueTimeout", req.body); - - res.status(200).send("Queue timeout callback received"); -}); - -/** B2B TOP UP CALLBACK **/ -app.post("/result", (req, res) => { - const resultData = req.body; - const unpackedData = flattenObject(resultData); - console.log("Success:", unpackedData); - emitter.emit("b2bTopUpCallback", unpackedData); - res.status(200).send("Callback received"); -}); -app.post("/queue", (req, res) => { - console.log("Timeout:", req.body); - emitter.emit("queueTimeout", req.body); - res.status(200).send("Queue timeout callback received"); -}); - -/** B2B PAYBILL CALLBACK **/ -app.post("/result", (req, res) => { - const resultData = req.body; - const unpackedData = flattenObject(resultData); - console.log("Success:", unpackedData); - emitter.emit("b2bPaybillCallback", unpackedData); - res.status(200).send("Callback received"); -}); -app.post("/queue", (req, res) => { - console.log("Timeout:", req.body); - emitter.emit("queueTimeout", req.body); - res.status(200).send("Queue timeout callback received"); -}); - -/** TAX REMITTANCE **/ -app.post("/remittax/result", (req, res) => { - const resultData = req.body; - const unpackedData = flattenObject(resultData); - console.log("Success:", unpackedData); - emitter.emit("taxRemittanceCallback", unpackedData); - res.status(200).send("Callback received"); -}); -app.post("/remittax/queue", (req, res) => { - console.log("Timeout:", req.body); - emitter.emit("queueTimeout", req.body); - res.status(200).send("Queue timeout callback received"); -}); - -// BY DEFAULT VALIDATION IS DISABLED - PERFORMING CALLBACK ON CONFIRMATION -/*app.post('/confirmation/result', (req, res) => { - const resultData = req.body - const unpackedData = flattenObject(resultData) - - console.log('Successfully simulated a customer to business confirmation:', unpackedData) - - // Emit the result data for the test to capture - emitter.emit('c2bResult', unpackedData) - - // Send a response back to acknowledge - res.status(200).send('Callback received') -})*/ - -export { app, emitter }; +async function setupNgrokServer(name, disabled = true) { + if (disabled) { + console.info("\x1b[43m\x1b[30m\x1b[1m%s\x1b[0m", " Ngrok server will not be initiated " ); + return { + NGROK_URL: "", teardown: async () => { + } + }; + } + // Create and start the HTTP server on a random available port + const SERVER = http.createServer(app); + SERVER.listen(0); + const port = SERVER.address().port; + + // Start ngrok tunnel + const NGROK_URL = await ngrok.connect(port); + + // Initialize callback handlers + await runSingleCallbacks(name, app); + + // Teardown function to clean up resources + async function teardown() { + console.info("\x1b[43m\x1b[30m\x1b[1m%s\x1b[0m", "Closing exposed port"); + try { + await ngrok.disconnect(); + await new Promise((resolve, reject) => { + SERVER.close((err) => (err ? reject(err) : resolve())); + }); + console.log("\x1b[32m\x1b[1m%s\x1b[0m", "Server closed gracefully"); + } catch (err) { + console.error( + "\x1b[31m\x1b[1m%s\x1b[0m", + "Error while closing server:", err + ); + } finally { + setTimeout(() => { + process.exit(1) + }, 2000) + } + } + + return { NGROK_URL, teardown }; +} + +export { setupNgrokServer }; diff --git a/package.json b/package.json index 785aca3..685e732 100644 --- a/package.json +++ b/package.json @@ -1,9 +1,8 @@ { - "name": "mpesa-node-local", + "name": "mpesa-node", "version": "0.1.3", "description": "Node M-Pesa Library", - "main": "index.js", - "types": "index.d.ts", + "main": "./index.js", "repository": { "type": "git", "url": "https://github.com/safaricom/mpesa-node-library" @@ -12,17 +11,16 @@ "exports": { ".": { "types": "./index.d.ts", - "require": "./index.js", "import": "./index.js", "default": "./index.js" } }, "dependencies": { "axios": "^1.5.0", - "globals": "^15.11.0", + "dotenv": "^16.4.7", "express": "^4.21.2", + "globals": "^15.11.0", "ngrok": "^5.0.0-beta.2", - "dotenv": "^16.4.7", "uuid": "^11.0.5" }, "devDependencies": { @@ -31,17 +29,21 @@ "@babel/polyfill": "7.12.1", "@babel/preset-env": "^7.25.9", "@babel/register": "^7.25.9", - "prettier": "^3.3.3", "chai": "^5.1.2", "eslint": "^9.19.0", - "got": "^14.4.6", + "html-minifier-terser": "^7.2.0", "jsdoc": "^4.0.4", + "minami": "^1.2.3", "mocha": "^11.1.0", + "prettier": "^3.3.3", + "sinon": "^19.0.2", + "taffydb": "^2.7.3", "typescript-eslint": "^8.22.0" }, "scripts": { - "test": "mocha --require @babel/polyfill --require @babel/register 'lib/tests/apis/**/*.spec.js'", - "format": "prettier --write ." + "test": "mocha --require @babel/polyfill --require @babel/register lib/tests/apis/main/**/*.spec.js", + "format": "prettier --write .", + "docs": "jsdoc -c jsdoc.json" }, "keywords": [ "mpesa", diff --git a/qodana.yaml b/qodana.yaml new file mode 100644 index 0000000..cf20b0d --- /dev/null +++ b/qodana.yaml @@ -0,0 +1,30 @@ +--- +#-------------------------------------------------------------------------------# +# Qodana analysis is configured by qodana.yaml file # +# https://www.jetbrains.com/help/qodana/qodana-yaml.html # +#-------------------------------------------------------------------------------# +version: "1.0" + +#Specify inspection profile for code analysis +profile: + name: qodana.starter + +#Enable inspections +#include: +# - name: + +#Disable inspections +#exclude: +# - name: +# paths: +# - + +#Execute shell command before Qodana execution (Applied in CI/CD pipeline) +#bootstrap: sh ./prepare-qodana.sh + +#Install IDE plugins before Qodana execution (Applied in CI/CD pipeline) +#plugins: +# - id: #(plugin id can be found at https://plugins.jetbrains.com) + +#Specify Qodana linter for analysis (Applied in CI/CD pipeline) +linter: jetbrains/qodana-js:2024.3 From 19f7f85d317604c9634e62513607b71be19ee0c0 Mon Sep 17 00:00:00 2001 From: Samm Date: Fri, 28 Feb 2025 09:27:35 +0300 Subject: [PATCH 17/23] Fixed naming errors, especially when it comes to the term Url. Now it's explicitly URL. This helps avoid confusions in realtime usage --- README.md | 38 ++++++++++++------------ index.d.ts | 46 ++++++++++++++--------------- lib/core/apis/b2c-Request.js | 14 ++++----- lib/core/apis/b2c-Topup.js | 10 +++---- lib/core/apis/balance-Query.js | 10 +++---- lib/core/apis/business-Paybill.js | 10 +++---- lib/core/apis/c2b-Register.js | 20 ++++++------- lib/core/apis/mpesa-Simulate.js | 10 +++---- lib/core/apis/reversals.js | 10 +++---- lib/core/apis/tax-Remittance.js | 10 +++---- lib/core/apis/transaction-Status.js | 16 +++++----- lib/tests/apis/utils/options.js | 22 +++++++------- 12 files changed, 108 insertions(+), 108 deletions(-) diff --git a/README.md b/README.md index a4f110e..8e29fe3 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ [![Made in Africa](https://img.shields.io/badge/Africa's%20Rising-%E2%9C%93-green.svg)](https://github.com/collections/made-in-africa) [![Known Vulnerabilities](https://snyk.io/test/github/safaricom/mpesa-node-library/badge.svg?targetFile=package.json)](https://snyk.io/test/github/safaricom/mpesa-node-library?targetFile=package.json) [![npm downloads](https://img.shields.io/npm/dt/your-package-name.svg)](https://www.npmjs.com/package/mpesa-node) -[![License](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) +[![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0) ## Prerequisites @@ -83,7 +83,7 @@ callback handler (optional) `handleBalanceQueryCallbacks` **TypeScript Support:** This library has a `.d.ts` for each **API endpoint**, providing seamless integration and type checking for TypeScript projects. -Below is an example of how to setup the account balance api endpoint `balanceQuery`: +Below is an example of how to set up the account balance api endpoint `balanceQuery`: ```js import { mpesa } from "mpesa-node"; @@ -108,8 +108,8 @@ async function checkAccountBalance() { idType: 2, // Example: 2 (Till Number) shortCode: 600977, initiator: INITIATOR_NAME, - queueUrl: `${VALID_HTTPS_URL}/accountbalance/queuetimeouturl`, - resultUrl: `${VALID_HTTPS_URL}/accountbalance/result`, + queueURL: `${VALID_HTTPS_URL}/accountbalance/queuetimeouturl`, + resultURL: `${VALID_HTTPS_URL}/accountbalance/result`, }); //do something ... console.log("Account Balance Response:", JSON.stringify(response, null, 2)); @@ -154,7 +154,7 @@ const PORT = process.env.PORT || 3000; app.listen(PORT, () => { console.log(`Server running on port ${PORT}`); console.log(`To expose this locally, run: ngrok http ${PORT}`); - console.log(`Ensure the public URL provided by ngrok is set as the 'resultUrl' and 'queueUrl' in your M-Pesa request`); + console.log(`Ensure the public URL provided by ngrok is set as the 'resultURL' and 'queueURL' in your M-Pesa request`); }); ``` @@ -165,8 +165,8 @@ When using the default **callback handlers**, the response includes the main API idType: 2, // Example: 2 (Till Number) shortCode: 600977, initiator: INITIATOR_NAME, - queueUrl: `${VALID_HTTPS_URL}/accountbalance/queuetimeouturl`, - resultUrl: `${VALID_HTTPS_URL}/accountbalance/result`, + queueURL: `${VALID_HTTPS_URL}/accountbalance/queuetimeouturl`, + resultURL: `${VALID_HTTPS_URL}/accountbalance/result`, }); //do something ... console.log("Account Balance Response:", JSON.stringify(response, null, 2)); @@ -236,8 +236,8 @@ Here is a comprehensive list of all supported APIs along with their respective * export interface balanceQueryOptions { partyA: number; identifierType: number; - QueueTimeOutUrl: string; - resultUrl: string; + QueueTimeOutURL: string; + resultURL: string; initiator: string; remarks: string; } @@ -246,8 +246,8 @@ export interface b2cRequestOptions { partyA: number; partyB: string; amount: number; - QueueTimeOutUrl: string; - resultUrl: string; + QueueTimeOutURL: string; + resultURL: string; commandId: string; initiatorName: string; remarks: string; @@ -255,8 +255,8 @@ export interface b2cRequestOptions { } export interface c2bRegisterOptions { - confirmationUrl: string; - validationUrl: string; + confirmationURL: string; + validationURL: string; shortCode: number; responseType: string; } @@ -272,7 +272,7 @@ export interface mpesaSimulateOptions { partyA: string; phoneNumber: string; amount: number; - callbackUrl: string; + callbackURL: string; accountRef: string; transactionType: string; partyB: number; @@ -287,8 +287,8 @@ export interface mpesaQueryOptions { export interface reversalsOptions { transactionId: string; amount: number; - QueueTimeOutUrl: string; - resultUrl: string; + QueueTimeOutURL: string; + resultURL: string; receiverParty: string; initiator: string; receiverIdType: string; @@ -300,8 +300,8 @@ export interface transactionStatusOptions { transactionId: string; partyA: number; identifierType: number; - QueueTimeOutUrl: string; - resultUrl: string; + QueueTimeOutURL: string; + resultURL: string; initiator: string; OriginatorConversationID: string; remarks: string; @@ -479,4 +479,4 @@ We welcome **contributions**! Follow these steps to get started: ## License -MIT +**APACHE 2.0** diff --git a/index.d.ts b/index.d.ts index c0f49b8..4f13dad 100644 --- a/index.d.ts +++ b/index.d.ts @@ -4,15 +4,15 @@ * @param {number} options.partyA The shortcode of the querying organization. * @param {number} options.identifierType Type of the querying organization. * @param {String} options.QueueTimeOutURL URL to receive timeout messages. - * @param {String} options.resultUrl URL to receive the result message. + * @param {String} options.resultURL URL to receive the result message. * @param {string} options.remarks Information to be associated with the transaction. * @param {String} options.initiator The credential/username for authentication. */ export interface balanceQueryOptions { partyA: number; identifierType: number; - QueueTimeOutUrl: string; - resultUrl: string; + QueueTimeOutURL: string; + resultURL: string; initiator: string; remarks: string; } @@ -26,7 +26,7 @@ export interface balanceQueryOptions { * @param {string} options.remarks Information to be associated with the transaction. * @param {string} options.occasion Information to be associated with the transaction. * @param {string} options.QueueTimeOutURL URL for timeout notifications. - * @param {string} options.resultUrl URL for M-PESA to send payment processing notifications. + * @param {string} options.resultURL URL for M-PESA to send payment processing notifications. * @param {string} options.commandId Unique command specifying B2C transaction type (e.g., BusinessPayment). * @param {string} options.initiatorName API user created by Business Administrator for B2C transactions. */ @@ -34,8 +34,8 @@ export interface b2cRequestOptions { partyA: number; partyB: string; amount: number; - QueueTimeOutUrl: string; - resultUrl: string; + QueueTimeOutURL: string; + resultURL: string; commandId: string; initiatorName: string; remarks: string; @@ -45,14 +45,14 @@ export interface b2cRequestOptions { /** * @name c2bRegisterOptions * @param {Object} options Options for the C2B Register URL. - * @param {string} options.confirmationUrl URL to receive confirmation upon payment completion. - * @param {string} options.validationUrl URL to receive validation upon payment submission (default: external validation disabled). + * @param {string} options.confirmationURL URL to receive confirmation upon payment completion. + * @param {string} options.validationURL URL to receive validation upon payment submission (default: external validation disabled). * @param {number} options.shortCode Unique M-PESA pay bill/till number. * @param {string} options.responseType Action if validation URL is unreachable (values: Completed or Cancelled). */ export interface c2bRegisterOptions { - confirmationUrl: string; - validationUrl: string; + confirmationURL: string; + validationURL: string; shortCode: number; responseType: string; } @@ -78,7 +78,7 @@ export interface c2bSimulateOptions { * @param {string} options.phoneNumber Mobile number to receive the STK Pin Prompt (can be same as partyA). * @param {number} options.amount Transaction amount. * @param {string} options.transactionType "CustomerPayBillOnline" for PayBill and "CustomerBuyGoodsOnline" for Till Numbers. - * @param {string} options.callbackUrl Secure URL for receiving notifications from M-Pesa API. + * @param {string} options.callbackURL Secure URL for receiving notifications from M-Pesa API. * @param {string} options.transactionDesc Information to be associated with the transaction. * @param {string} options.accountRef Alphanumeric account reference (up to 12 characters) shown in the STK Pin Prompt. * @param {number} options.partyB 5 to 6-digit number of the receiving organization. @@ -87,7 +87,7 @@ export interface mpesaSimulateOptions { partyA: string; phoneNumber: string; amount: number; - callbackUrl: string; + callbackURL: string; accountRef: string; transactionType: string; partyB: number; @@ -113,15 +113,15 @@ export interface mpesaQueryOptions { * @param {string} options.QueueTimeOutURL URL for timeout transaction details. * @param {string} options.remarks Information to be associated with the transaction. * @param {string} options.occasion Information to be associated with the transaction. - * @param {string} options.resultUrl URL for transaction details. + * @param {string} options.resultURL URL for transaction details. * @param {number} options.receiverParty Organization receiving the transaction. * @param {string} options.initiator Name of the initiator of the request. */ export interface reversalsOptions { transactionId: string; amount: number; - QueueTimeOutUrl: string; - resultUrl: string; + QueueTimeOutURL: string; + resultURL: string; receiverParty: string; initiator: string; receiverIdType: string; @@ -139,15 +139,15 @@ export interface reversalsOptions { * @param {string} options.occasion Information to be associated with the transaction. * @param {number} options.identifierType Type of organization receiving the transaction. * @param {string} options.OriginatorConversationID This is a globally unique identifier for the transaction request returned by the API proxy upon successful submission. - * @param {string} options.QueueTimeOutUrl URL for timeout transaction details. - * @param {string} options.resultUrl URL for transaction details. + * @param {string} options.QueueTimeOutURL URL for timeout transaction details. + * @param {string} options.resultURL URL for transaction details. */ export interface transactionStatusOptions { transactionId: string; partyA: number; identifierType: number; - QueueTimeOutUrl: string; - resultUrl: string; + QueueTimeOutURL: string; + resultURL: string; initiator: string; OriginatorConversationID: string; remarks: string; @@ -183,7 +183,7 @@ export interface generateQrCodeOptions { * @param {string} options.remarks Information to be associated with the transaction. * @param {number} options.accountReference Transaction identifier. * @param {number} [options.requester] Optional consumer’s mobile number (if paying on their behalf). * @param {string} options.QueueTimeOutURL URL for timeout notifications. - * @param {string} options.resultUrl URL for sending transaction results after processing. + * @param {string} options.resultURL URL for sending transaction results after processing. */ export interface b2cTopUpOptions { initiator: string; @@ -207,8 +207,8 @@ export interface b2cTopUpOptions { * @param {number} options.partyB Shortcode to which money will be moved. * @param {number} options.accountReference Account number for the payment (up to 13 characters). * @param {number} [options.requester] Optional consumer’s mobile number (if paying on their behalf). - * @param {string} options.QueueTimeoutUrl URL for timeout notifications. - * @param {string} options.resultUrl URL for sending transaction results after processing. + * @param {string} options.QueueTimeOutURL URL for timeout notifications. + * @param {string} options.resultURL URL for sending transaction results after processing. */ export interface businessPaybillOptions { initiator: string; @@ -232,7 +232,7 @@ export interface businessPaybillOptions { * @param {number} options.partyB Account to which money will be credited. * @param {number} options.accountReference Payment registration number (PRN) from KRA. * @param {string} options.QueueTimeOutURL URL for timeout notifications before processing. - * @param {string} options.resultUrl URL for sending transaction results after processing. + * @param {string} options.resultURL URL for sending transaction results after processing. */ export interface taxRemittanceOptions { initiator: string; diff --git a/lib/core/apis/b2c-Request.js b/lib/core/apis/b2c-Request.js index 8a67524..ed3dede 100644 --- a/lib/core/apis/b2c-Request.js +++ b/lib/core/apis/b2c-Request.js @@ -27,7 +27,7 @@ let conditionalCallbackData = {}; * @param {string} options.remarks Information to be associated with the transaction. * @param {string} options.occasion Information to be associated with the transaction. * @param {string} options.QueueTimeOutURL URL for timeout notifications. - * @param {string} options.resultUrl URL for M-PESA to send payment processing notifications. + * @param {string} options.resultURL URL for M-PESA to send payment processing notifications. * @param {string} options.commandId Unique command specifying B2C transaction type (e.g., BusinessPayment). * @param {string} options.initiatorName API user created by Business Administrator for B2C transactions. * @param {boolean} [options.proErrorLogging=false] Logs out advanced error details - good for debugging. @@ -39,9 +39,9 @@ async function b2cRequest({ QueueTimeOutURL, remarks, occasion, - resultUrl, + resultURL, commandId, - initiatorName = "", + initiatorName, proErrorLogging = false, }) { if (!callbackHandlerInitialized) { @@ -60,8 +60,8 @@ async function b2cRequest({ } const _msisdn = validateFormatPhone(partyB) try { - validateUrl(resultUrl, "resultUrl"); - validateUrl(QueueTimeOutURL, "queueTimeOutUrl"); + validateUrl(resultURL, "resultURL"); + validateUrl(QueueTimeOutURL, "QueueTimeOutURL"); const OriginatorConversationID = originatorID(); const { accessToken, baseURL } = await generateOAuthToken(); @@ -83,7 +83,7 @@ async function b2cRequest({ PartyB: _msisdn, Remarks: remarks, QueueTimeOutURL: QueueTimeOutURL, - ResultURL: resultUrl, + ResultURL: resultURL, Occasion: occasion }); @@ -106,7 +106,7 @@ async function b2cRequest({ _msisdn, amount, QueueTimeOutURL, - resultUrl, + resultURL, commandId, initiatorName, occasion, diff --git a/lib/core/apis/b2c-Topup.js b/lib/core/apis/b2c-Topup.js index 51510b4..000e03f 100644 --- a/lib/core/apis/b2c-Topup.js +++ b/lib/core/apis/b2c-Topup.js @@ -27,7 +27,7 @@ let conditionalCallbackData = {}; * @param {number} options.accountReference Identifier for the transaction. * @param {number} [options.requester] Optional. The consumer’s mobile number on behalf of whom you are paying. * @param {string} options.QueueTimeOutURL A URL that will be used to notify your system in case the request times out. - * @param {string} options.resultUrl A URL that will be used to send transaction results after processing. + * @param {string} options.resultURL A URL that will be used to send transaction results after processing. * @param {boolean} [options.proErrorLogging] Logs out advanced error details - good for debugging * @return {Promise} b2cTopUpResponse and (optional) conditionalCallbackData. */ async function b2cTopUp({ @@ -38,7 +38,7 @@ async function b2cTopUp({ accountReference, requester, QueueTimeOutURL, - resultUrl, + resultURL, remarks, proErrorLogging = false, }) { @@ -48,7 +48,7 @@ async function b2cTopUp({ ); } try { - validateUrl(resultUrl, "resultUrl"); + validateUrl(resultURL, "resultURL"); validateUrl(QueueTimeOutURL, "QueueTimeOutUrl"); const { accessToken, baseURL } = await generateOAuthToken(); const req = axios.create({ @@ -79,7 +79,7 @@ async function b2cTopUp({ Requester: requester, Remarks: remarks, QueueTimeOutURL: QueueTimeOutURL, - ResultURL: resultUrl, + ResultURL: resultURL, }); conditionalCallbackData = await callbackTrigger(callbackHandlerInitialized); @@ -104,7 +104,7 @@ async function b2cTopUp({ accountReference, requester, QueueTimeOutURL, - resultUrl, + resultURL, remarks }, }, diff --git a/lib/core/apis/balance-Query.js b/lib/core/apis/balance-Query.js index 61e2b64..d11ec6a 100644 --- a/lib/core/apis/balance-Query.js +++ b/lib/core/apis/balance-Query.js @@ -24,7 +24,7 @@ let conditionalCallbackData = {}; * @param {number} options.identifierType Type of the querying organization. * @param {string} options.remarks Information to be associated with the transaction. * @param {String} options.QueueTimeOutURL URL to receive timeout messages. - * @param {String} options.resultUrl URL to receive the result message. + * @param {String} options.resultURL URL to receive the result message. * @param {String} options.initiator The credential/username for authentication. * @param {boolean} [options.proErrorLogging=false] Logs out advanced error details - good for debugging * @return {Promise} balanceQueryResponse and (optional) conditionalCallbackData. @@ -33,7 +33,7 @@ async function balanceQuery({ partyA, identifierType, QueueTimeOutURL, - resultUrl, + resultURL, initiator, remarks, proErrorLogging = false, @@ -53,7 +53,7 @@ async function balanceQuery({ ); } try { - validateUrl(resultUrl, "resultUrl"); + validateUrl(resultURL, "resultURL"); validateUrl(QueueTimeOutURL, "timeoutUrl"); const { accessToken, baseURL } = await generateOAuthToken(); @@ -79,7 +79,7 @@ async function balanceQuery({ IdentifierType: identifierType, Remarks: remarks, QueueTimeOutURL: QueueTimeOutURL, - ResultURL: resultUrl, + ResultURL: resultURL, }); conditionalCallbackData = await callbackTrigger(callbackHandlerInitialized); @@ -96,7 +96,7 @@ async function balanceQuery({ { apiEndpoint: "/mpesa/accountbalance/v1/query", method: "POST", - payload: { partyA, identifierType, QueueTimeOutURL, resultUrl, initiator, remarks }, + payload: { partyA, identifierType, QueueTimeOutURL, resultURL, initiator, remarks }, }, "Balance query error details:", ); diff --git a/lib/core/apis/business-Paybill.js b/lib/core/apis/business-Paybill.js index 844aed7..e63e940 100644 --- a/lib/core/apis/business-Paybill.js +++ b/lib/core/apis/business-Paybill.js @@ -27,7 +27,7 @@ let conditionalCallbackData = {}; * @param {number} options.accountReference Account number for the payment (up to 13 characters). * @param {number} [options.requester] Optional consumer’s mobile number (if paying on their behalf). * @param {string} options.QueueTimeOutURL URL for timeout notifications. - * @param {string} options.resultUrl URL for sending transaction results after processing. + * @param {string} options.resultURL URL for sending transaction results after processing. * @param {boolean} [options.proErrorLogging] Logs out advanced error details - good for debugging * @return {Promise} businessPaybillResponse and (optional) conditionalCallbackData. */ @@ -40,7 +40,7 @@ async function businessPaybill({ accountReference, requester, QueueTimeOutURL, - resultUrl, + resultURL, proErrorLogging = false, }) { if (!callbackHandlerInitialized) { @@ -49,7 +49,7 @@ async function businessPaybill({ ); } try { - validateUrl(resultUrl, "resultUrl"); + validateUrl(resultURL, "resultURL"); validateUrl(QueueTimeOutURL, "timeoutUrl"); const { accessToken, baseURL } = await generateOAuthToken(); const req = axios.create({ @@ -78,7 +78,7 @@ async function businessPaybill({ Requester: requester, Remarks: remarks, QueueTimeOutURL: QueueTimeOutURL, - ResultURL: resultUrl, + ResultURL: resultURL, }); conditionalCallbackData = await callbackTrigger(callbackHandlerInitialized); @@ -106,7 +106,7 @@ async function businessPaybill({ accountReference, requester, QueueTimeOutURL, - resultUrl, + resultURL, remarks }, }, diff --git a/lib/core/apis/c2b-Register.js b/lib/core/apis/c2b-Register.js index 943d99e..0334b2f 100644 --- a/lib/core/apis/c2b-Register.js +++ b/lib/core/apis/c2b-Register.js @@ -19,16 +19,16 @@ let conditionalCallbackData = {}; * @summary Registers callback validation and confirmation URLs on M-Pesa to receive payment notifications for your paybill/till numbers. * @see {@link https://developer.safaricom.co.ke/APIs/CustomerToBusinessRegisterURL open external link} * @param {Object} options Options for the C2B Register URL. - * @param {string} options.confirmationUrl URL to receive confirmation upon payment completion. - * @param {string} options.validationUrl URL to receive validation upon payment submission (default: external validation disabled). + * @param {string} options.confirmationURL URL to receive confirmation upon payment completion. + * @param {string} options.validationURL URL to receive validation upon payment submission (default: external validation disabled). * @param {number} options.shortCode Unique M-PESA pay bill/till number. * @param {string} options.responseType Action if validation URL is unreachable (values: Completed or Cancelled). * @param {boolean} [options.proErrorLogging] Logs out detailed errors for debugging purposes * @return {Promise} c2bRegisterResponse and (optional) conditionalCallbackData. */ async function c2bRegister({ - confirmationUrl, - validationUrl, + confirmationURL, + validationURL, shortCode, responseType, proErrorLogging = false, @@ -48,8 +48,8 @@ async function c2bRegister({ ); } try { - validateUrl(confirmationUrl, "confirmationUrl"); - validateUrl(validationUrl, "validationUrl"); + validateUrl(confirmationURL, "confirmationURL"); + validateUrl(validationURL, "validationURL"); const { accessToken, baseURL } = await generateOAuthToken(); const req = axios.create({ baseURL, @@ -61,8 +61,8 @@ async function c2bRegister({ const responseBody = await req.post("/mpesa/c2b/v1/registerurl", { ShortCode: shortCode, ResponseType: responseType, - ConfirmationURL: confirmationUrl, - ValidationURL: validationUrl, + ConfirmationURL: confirmationURL, + ValidationURL: validationURL, }); conditionalCallbackData = await callbackTrigger(callbackHandlerInitialized); return { c2bRegisterResponse: responseBody.data, conditionalCallbackData }; @@ -78,8 +78,8 @@ async function c2bRegister({ apiEndpoint: "/mpesa/c2b/v1/registerurl", method: "POST", payload: { - confirmationUrl, - validationUrl, + confirmationURL, + validationURL, shortCode, responseType, }, diff --git a/lib/core/apis/mpesa-Simulate.js b/lib/core/apis/mpesa-Simulate.js index d9be7a1..733811d 100644 --- a/lib/core/apis/mpesa-Simulate.js +++ b/lib/core/apis/mpesa-Simulate.js @@ -24,7 +24,7 @@ let conditionalCallbackData = {}; * @param {string} options.phoneNumber Mobile number to receive the STK Pin Prompt (can be same as partyA). * @param {number} options.amount Transaction amount. * @param {string} options.transactionType "CustomerPayBillOnline" for PayBill and "CustomerBuyGoodsOnline" for Till Numbers. - * @param {string} options.callbackUrl Secure URL for receiving notifications from M-Pesa API. + * @param {string} options.callbackURL Secure URL for receiving notifications from M-Pesa API. * @param {string} options.transactionDesc Information to be associated with the transaction. * @param {string} options.accountRef Alphanumeric account reference (up to 12 characters) shown in the STK Pin Prompt (Org name). * @param {number} options.partyB 5 to 6-digit number of the receiving organization. @@ -35,7 +35,7 @@ async function mpesaSimulate({ partyA, phoneNumber, amount, - callbackUrl, + callbackURL, accountRef, partyB, transactionType, @@ -48,7 +48,7 @@ async function mpesaSimulate({ ); } try { - validateUrl(callbackUrl, "callbackUrl"); + validateUrl(callbackURL, "callbackURL"); const { accessToken, baseURL } = await generateOAuthToken(); const { password, timeStamp } = generateMpesaCredentials(partyB); const req = axios.create({ @@ -67,7 +67,7 @@ async function mpesaSimulate({ PartyA: validateFormatPhone(partyA), PartyB: partyB, PhoneNumber: validateFormatPhone(phoneNumber), - CallBackURL: callbackUrl, + CallBackURL: callbackURL, AccountReference: accountRef, TransactionDesc: transactionDesc, TransactionType: transactionType @@ -92,7 +92,7 @@ async function mpesaSimulate({ partyA, phoneNumber, amount, - callbackUrl, + callbackURL, accountRef, partyB, transactionDesc diff --git a/lib/core/apis/reversals.js b/lib/core/apis/reversals.js index f16d8c6..6170b88 100644 --- a/lib/core/apis/reversals.js +++ b/lib/core/apis/reversals.js @@ -22,7 +22,7 @@ let conditionalCallbackData = {}; * @param {string} options.transactionId Transaction ID for reversal (e.g., LKXXXX1234). * @param {number} options.amount Amount to be reversed. * @param {string} options.QueueTimeOutURL URL for timeout transaction details. - * @param {string} options.resultUrl URL for transaction details. + * @param {string} options.resultURL URL for transaction details. * @param {string} options.remarks Information to be associated with the transaction. * @param {string} options.occasion Information to be associated with the transaction. * @param {number} options.receiverParty Organization receiving the transaction. @@ -34,7 +34,7 @@ async function reversals({ transactionId, amount, QueueTimeOutURL, - resultUrl, + resultURL, receiverParty, initiator, remarks, @@ -46,7 +46,7 @@ async function reversals({ ); } try { - validateUrl(resultUrl, "resultUrl"); + validateUrl(resultURL, "resultURL"); validateUrl(QueueTimeOutURL, "timeoutUrl"); const { accessToken, baseURL } = await generateOAuthToken(); const req = axios.create({ @@ -69,7 +69,7 @@ async function reversals({ Amount: amount, ReceiverParty: receiverParty, RecieverIdentifierType: config.receiverIdType, - ResultURL: resultUrl, + ResultURL: resultURL, QueueTimeOutURL: QueueTimeOutURL, Remarks: remarks, Occasion: occasion, @@ -93,7 +93,7 @@ async function reversals({ transactionId, amount, QueueTimeOutURL, - resultUrl, + resultURL, receiverParty, initiator, remarks, diff --git a/lib/core/apis/tax-Remittance.js b/lib/core/apis/tax-Remittance.js index bfd89d8..c8488fd 100644 --- a/lib/core/apis/tax-Remittance.js +++ b/lib/core/apis/tax-Remittance.js @@ -26,7 +26,7 @@ let conditionalCallbackData = {}; * @param {number} options.partyB The account to which money will be credited. * @param {number} options.accountReference The payment registration number (PRN) issued by KRA * @param {string} options.QueueTimeOutURL URL to notify in case of a request timeout before processing. - * @param {string} options.resultUrl URL to send transaction results after processing. + * @param {string} options.resultURL URL to send transaction results after processing. * @param {boolean} [options.proErrorLogging] Logs out advanced error details - good for debugging * @returns {Promise} remittanceResponse and (optional) conditionalCallbackData. */ @@ -38,7 +38,7 @@ async function taxRemittance({ accountReference, QueueTimeOutURL, remarks, - resultUrl, + resultURL, proErrorLogging = false, }) { if (!callbackHandlerInitialized) { @@ -47,7 +47,7 @@ async function taxRemittance({ ); } try { - validateUrl(resultUrl, "resultUrl"); + validateUrl(resultURL, "resultURL"); validateUrl(QueueTimeOutURL, "timeoutUrl"); const { accessToken, baseURL } = await generateOAuthToken(); const req = axios.create({ @@ -76,7 +76,7 @@ async function taxRemittance({ AccountReference: accountReference, Remarks: remarks, QueueTimeOutURL: QueueTimeOutURL, - ResultURL: resultUrl, + ResultURL: resultURL, }); conditionalCallbackData = await callbackTrigger(callbackHandlerInitialized); return { remittanceResponse: responseBody.data, conditionalCallbackData }; @@ -98,7 +98,7 @@ async function taxRemittance({ partyB, accountReference, QueueTimeOutURL, - resultUrl, + resultURL, remarks }, }, diff --git a/lib/core/apis/transaction-Status.js b/lib/core/apis/transaction-Status.js index 7e0d65c..d4acc69 100644 --- a/lib/core/apis/transaction-Status.js +++ b/lib/core/apis/transaction-Status.js @@ -26,8 +26,8 @@ let conditionalCallbackData = {}; * @param {string} options.remarks Information to be associated with the transaction. * @param {string} options.occasion Information to be associated with the transaction. * @param {string} options.OriginatorConversationID This is a globally unique identifier for the transaction request returned by the API proxy upon successful submission. - * @param {string} options.QueueTimeOutUrl URL for storing information about timeout transactions. - * @param {string} options.resultUrl The path that stores information of a transaction. + * @param {string} options.QueueTimeOutURL URL for storing information about timeout transactions. + * @param {string} options.resultURL The path that stores information of a transaction. * @param {boolean} [options.proErrorLogging] Logs out advanced error details - good for debugging * @returns {Promise} transactionStatusResponse and (optional) conditionalCallbackData. */ @@ -35,11 +35,11 @@ async function transactionStatus({ transactionId, partyA, identifierType, - QueueTimeOutUrl, + QueueTimeOutURL, OriginatorConversationID, remarks, occasion, - resultUrl, + resultURL, initiator, proErrorLogging = false, }) { @@ -78,8 +78,8 @@ async function transactionStatus({ PartyA: partyA, OriginatorConversationID, IdentifierType: identifierType, - ResultURL: resultUrl, - QueueTimeOutURL: QueueTimeOutUrl, + ResultURL: resultURL, + QueueTimeOutURL: QueueTimeOutURL, Remarks: remarks, Occasion: occasion, }); @@ -100,8 +100,8 @@ async function transactionStatus({ transactionId, partyA, identifierType, - QueueTimeOutUrl, - resultUrl, + QueueTimeOutURL, + resultURL, initiator, remarks, occasion diff --git a/lib/tests/apis/utils/options.js b/lib/tests/apis/utils/options.js index e2a93a5..44ad960 100644 --- a/lib/tests/apis/utils/options.js +++ b/lib/tests/apis/utils/options.js @@ -32,7 +32,7 @@ export function createOptionsForB2c(NGROK_URL, msisdn) { amount: parseInt("1000"), commandId: CommandIDs.SALARY_PAYMENT, QueueTimeOutURL: `${NGROK_URL}/b2c/queue`, - resultUrl: `${NGROK_URL}/b2c/result`, + resultURL: `${NGROK_URL}/b2c/result`, occasion: "TEST", remarks: "TEST" }; @@ -53,7 +53,7 @@ export function createOptionsForBalance(NGROK_URL) { partyA: parseInt(configs.partyA), initiator: configs.initiatorName, QueueTimeOutURL: `${NGROK_URL}/accountbalance/queuetimeouturl`, - resultUrl: `${NGROK_URL}/accountbalance/result`, + resultURL: `${NGROK_URL}/accountbalance/result`, remarks: "TEST" }; } @@ -66,7 +66,7 @@ export function createOptionsForMpesaSimulate(NGROK_URL) { transactionType: "CustomerPayBillOnline", transactionDesc:"TEST", amount: 1, - callbackUrl: `${NGROK_URL}/path/result`, + callbackURL: `${NGROK_URL}/path/result`, accountRef: configs.accountRef }; } @@ -78,7 +78,7 @@ export function createOptionsForReversals(NGROK_URL, transId) { transactionId: transId, amount: parseInt("100"), QueueTimeOutURL: `${NGROK_URL}/Reversal/queuetimeouturl`, - resultUrl: `${NGROK_URL}/Reversal/result`, + resultURL: `${NGROK_URL}/Reversal/result`, remarks: "TEST", occasion: "TEST" }; @@ -91,8 +91,8 @@ export function createOptionsForTransactionStatus(NGROK_URL) { transactionId: "OEI2AK4Q16", initiator: configs.initiatorName, partyA: parseInt(configs.partyA), - resultUrl: `${NGROK_URL}/TransactionStatus/result/`, - QueueTimeOutUrl: `${NGROK_URL}/TransactionStatus/queue/`, + resultURL: `${NGROK_URL}/TransactionStatus/result/`, + QueueTimeOutURL: `${NGROK_URL}/TransactionStatus/queue/`, remarks: "TEST", occasion: "TEST" }; @@ -106,7 +106,7 @@ export function createOptionsForB2cAccTopUp(NGROK_URL) { requester: parseInt(configs.requester), //optional initiator: configs.initiatorName, amount: parseInt("100"), - resultUrl: `${NGROK_URL}/path/result`, + resultURL: `${NGROK_URL}/path/result`, QueueTimeOutURL: `${NGROK_URL}/path/queue`, remarks: "TEST", }; @@ -114,8 +114,8 @@ export function createOptionsForB2cAccTopUp(NGROK_URL) { export function createOptionsForC2bRegister(NGROK_URL) { return { - confirmationUrl: `${NGROK_URL}/confirmation/result`, - validationUrl: `${NGROK_URL}/validation/result`, + confirmationURL: `${NGROK_URL}/confirmation/result`, + validationURL: `${NGROK_URL}/validation/result`, shortCode: parseInt(configs.businessShortCode), responseType: responseTypes.CANCELLED }; @@ -129,7 +129,7 @@ export function createOptionsForB2bPaybill(NGROK_URL) { amount: parseInt("100"), accountReference: parseInt(configs.accountRef), requester: parseInt(configs.requester), //optional - resultUrl: `${NGROK_URL}/path/result`, + resultURL: `${NGROK_URL}/path/result`, QueueTimeOutURL: `${NGROK_URL}/path/queue`, remarks: "TEST" }; @@ -142,7 +142,7 @@ export function createOptionsForTaxRemittance(NGROK_URL) { partyB: parseInt(configs.partyB, 10), initiator: configs.initiatorName, amount: parseInt("100", 10), - resultUrl: `${NGROK_URL}/remittax/result`, + resultURL: `${NGROK_URL}/remittax/result`, QueueTimeOutURL: `${NGROK_URL}/remittax/queue`, remarks: "TEST" }; From 705778a5a8074cfa0269024c8159b1ca6bce1133 Mon Sep 17 00:00:00 2001 From: Samm Date: Fri, 28 Feb 2025 11:29:46 +0300 Subject: [PATCH 18/23] This commit contains an update specifically on the tests portion. I have omitted, taxRemittance and c2bRegister because of the extra external configurations, required for them to perform properly. I have combined M-pesa query and M-pesa express into one test file, since the CheckoutRequestID released by M-pesa express, is expected to be passed down to M-pesa query. I have also reformatted the whole code base and updated the docs --- .github/ISSUE_TEMPLATE/bug_report.md | 9 +- .github/ISSUE_TEMPLATE/feature_request.md | 6 +- CODE_OF_CONDUCT.md | 21 +- README.md | 83 +- docs/b2c-Request.js.html | 173 ++-- docs/b2c-Topup.js.html | 169 ++-- docs/balance-Query.js.html | 183 +++-- docs/business-Paybill.js.html | 169 ++-- docs/c2b-Register.js.html | 193 +++-- docs/c2b-Simulate.js.html | 171 ++-- docs/global.html | 2 +- docs/index.html | 230 ++++-- docs/mpesa-Query.js.html | 171 ++-- docs/mpesa-Simulate.js.html | 183 +++-- docs/qr-Generate.js.html | 173 ++-- docs/reversals.js.html | 183 +++-- docs/scripts/linenumber.js | 40 +- docs/scripts/prettify/lang-css.js | 38 +- docs/scripts/prettify/prettify.js | 768 +++++++++++++++++- docs/styles/jsdoc-default.css | 251 +++--- docs/styles/prettify-tomorrow.css | 87 +- docs/tax-Remittance.js.html | 183 +++-- docs/transaction-Status.js.html | 175 ++-- jsdoc.json | 14 +- lib/core/JSDOC.md | 1 + lib/core/apis/b2c-Request.js | 13 +- lib/core/apis/b2c-Topup.js | 2 +- lib/core/apis/balance-Query.js | 11 +- lib/core/apis/business-Paybill.js | 2 +- lib/core/apis/c2b-Simulate.js | 6 +- lib/core/apis/mpesa-Simulate.js | 44 +- lib/core/apis/reversals.js | 5 +- lib/core/apis/tax-Remittance.js | 2 +- lib/core/apis/transaction-Status.js | 7 +- lib/core/utils/configs.js | 6 +- lib/core/utils/helpers.js | 41 +- lib/tests/apis/dummy.spec.js | 8 +- lib/tests/apis/main/b2c-Request.spec.js | 19 +- lib/tests/apis/main/b2c-Topup.spec.js | 16 +- lib/tests/apis/main/balance-Query.spec.js | 15 +- lib/tests/apis/main/business-Paybill.spec.js | 15 +- lib/tests/apis/main/c2b-Register.spec.js | 30 - lib/tests/apis/main/c2b-Simulate.spec.js | 9 +- lib/tests/apis/main/mpesa-Express.spec.js | 69 ++ lib/tests/apis/main/mpesa-Query.spec.js | 21 - lib/tests/apis/main/mpesa-Simulate.spec.js | 30 - lib/tests/apis/main/qr-Generate.spec.js | 5 +- lib/tests/apis/main/reversals.spec.js | 20 +- lib/tests/apis/main/tax-Remittance.spec.js | 31 - .../apis/main/transaction-status.spec.js | 28 +- lib/tests/apis/utils/options.js | 44 +- lib/tests/apis/utils/server.js | 16 +- 52 files changed, 3011 insertions(+), 1180 deletions(-) delete mode 100644 lib/tests/apis/main/c2b-Register.spec.js create mode 100644 lib/tests/apis/main/mpesa-Express.spec.js delete mode 100644 lib/tests/apis/main/mpesa-Query.spec.js delete mode 100644 lib/tests/apis/main/mpesa-Simulate.spec.js delete mode 100644 lib/tests/apis/main/tax-Remittance.spec.js diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 9e020c3..9e67458 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -5,32 +5,39 @@ labels: [bug, needs-triage] --- ## Bug Description + A clear and concise description of the issue. ## Steps to Reproduce + 1. Go to '...' 2. Click on '...' 3. Scroll down to '...' 4. Observe the issue ## Expected Behavior + A clear and concise description of what should happen instead. ## Screenshots (if applicable) + Attach screenshots or screen recordings to illustrate the issue. ## System Information + ### Desktop: + - OS: [e.g. Windows 11, macOS Ventura] - Browser: [e.g. Chrome, Safari] - Version: [e.g. 120.0.1] ### Mobile: + - Device: [e.g. iPhone 13, Samsung Galaxy S22] - OS: [e.g. iOS 17, Android 13] - Browser: [e.g. Chrome, Safari] - Version: [e.g. 120.0.1] ## Additional Context -Add any other relevant details, logs, or error messages. +Add any other relevant details, logs, or error messages. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 7b7f549..64b4b83 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -5,17 +5,21 @@ labels: [enhancement, needs-triage] --- ## Feature Description + Provide a clear and concise description of the feature or improvement you are suggesting. ## Problem Statement + Is your feature request addressing a specific problem? Clearly describe the issue or limitation. Example: "It is frustrating when [...]" ## Proposed Solution + Describe the solution you would like to see implemented. Be as detailed as possible. ## Alternative Solutions + List any alternative approaches or workarounds you have considered. ## Additional Context -Include any other relevant details, references, or screenshots that can help clarify the request. +Include any other relevant details, references, or screenshots that can help clarify the request. diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 6c5948e..a97f6c1 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -1,4 +1,3 @@ - # Contributor Covenant Code of Conduct ## Our Pledge @@ -18,23 +17,23 @@ diverse, inclusive, and healthy community. Examples of behavior that contributes to a positive environment for our community include: -* Demonstrating empathy and kindness toward other people -* Being respectful of differing opinions, viewpoints, and experiences -* Giving and gracefully accepting constructive feedback -* Accepting responsibility and apologizing to those affected by our mistakes, +- Demonstrating empathy and kindness toward other people +- Being respectful of differing opinions, viewpoints, and experiences +- Giving and gracefully accepting constructive feedback +- Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience -* Focusing on what is best not just for us as individuals, but for the overall +- Focusing on what is best not just for us as individuals, but for the overall community Examples of unacceptable behavior include: -* The use of sexualized language or imagery, and sexual attention or advances of +- The use of sexualized language or imagery, and sexual attention or advances of any kind -* Trolling, insulting or derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or email address, +- Trolling, insulting or derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information, such as a physical or email address, without their explicit permission -* Other conduct which could reasonably be considered inappropriate in a +- Other conduct which could reasonably be considered inappropriate in a professional setting ## Enforcement Responsibilities diff --git a/README.md b/README.md index 8e29fe3..4774983 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,8 @@ **M-Pesa Library for Node.js using REST API** -![Node Mpesa Rest API](https://i.imghippo.com/files/fQO9155Kic.jpg -) +![Node Mpesa Rest API](https://i.imghippo.com/files/fQO9155Kic.jpg) +
JavaScript Logo
@@ -15,9 +15,9 @@ ## Prerequisites -* **Node.js v20+** – Ensure you have Node.js version 20 or later installed for improved performance, security, and +- **Node.js v20+** – Ensure you have Node.js version 20 or later installed for improved performance, security, and compatibility. -* **Ngrok CLI** – Install the [**Ngrok CLI**](https://download.ngrok.com/) to expose your local server for testing M-Pesa +- **Ngrok CLI** – Install the [**Ngrok CLI**](https://download.ngrok.com/) to expose your local server for testing M-Pesa callbacks. Ensure you have followed the official guide on how to setup Ngrok ## Installation @@ -98,8 +98,8 @@ import { mpesa } from "mpesa-node"; const { balanceQuery } = mpesa; // Ensure you replace these placeholders with valid values -// For the url, check on how to handle callbacks, paste the url provided below -const VALID_HTTPS_URL = "paste here"; +// For the url, check on how to handle callbacks, paste the url provided below +const VALID_HTTPS_URL = "paste here"; const INITIATOR_NAME = "yourInitiatorUsername"; async function checkAccountBalance() { @@ -154,23 +154,25 @@ const PORT = process.env.PORT || 3000; app.listen(PORT, () => { console.log(`Server running on port ${PORT}`); console.log(`To expose this locally, run: ngrok http ${PORT}`); - console.log(`Ensure the public URL provided by ngrok is set as the 'resultURL' and 'queueURL' in your M-Pesa request`); + console.log( + `Ensure the public URL provided by ngrok is set as the 'resultURL' and 'queueURL' in your M-Pesa request`, + ); }); ``` When using the default **callback handlers**, the response includes the main API `balanceQuery` response body, along with either a **result** or **queue** callback body. ```js - const response = await balanceQuery({ - idType: 2, // Example: 2 (Till Number) - shortCode: 600977, - initiator: INITIATOR_NAME, - queueURL: `${VALID_HTTPS_URL}/accountbalance/queuetimeouturl`, - resultURL: `${VALID_HTTPS_URL}/accountbalance/result`, - }); - //do something ... - console.log("Account Balance Response:", JSON.stringify(response, null, 2)); - /* +const response = await balanceQuery({ + idType: 2, // Example: 2 (Till Number) + shortCode: 600977, + initiator: INITIATOR_NAME, + queueURL: `${VALID_HTTPS_URL}/accountbalance/queuetimeouturl`, + resultURL: `${VALID_HTTPS_URL}/accountbalance/result`, +}); +//do something ... +console.log("Account Balance Response:", JSON.stringify(response, null, 2)); +/* if callback handlers are used expect such a json response... "Account balance response": { "balanceResponse": { @@ -194,7 +196,6 @@ When using the default **callback handlers**, the response includes the main API **NOTE**: At all costs avoid using URLs offered by **Ngrok** for **production** or **going live** - ## Supported API endpoints: #### Caution! @@ -231,7 +232,9 @@ Here is a comprehensive list of all supported API endpoints with their respectiv Developers are strongly encouraged to consult the [**JsDocs**](./docs/global.html) (_which comes bundled with the library_) for detailed information on how the required fields are mapped. This documentation clearly outlines the necessary configurations for successfully initiating any endpoint, ensuring a smooth integration process. ### Options for each API + Here is a comprehensive list of all supported APIs along with their respective **options**. Use this as a reference when configuring the parameters for your chosen API. + ```ts export interface balanceQueryOptions { partyA: number; @@ -352,10 +355,13 @@ export interface taxRemittanceOptions { remarks: string; } ``` + ### MSISDN formatting + When working with APIs that require an `msisdn` (a **phone number**), always provide it as a `string` in the format: `0708374149`. The library automatically processes the number into the required format, so no additional configuration is needed. ### External configurations + Certain endpoints require external configurations to function correctly, particularly when working in a production environment. For seamless integration and optimal performance, it is crucial to review the API documentation thoroughly. Some APIs, such as **taxRemittance**, **b2cRequest** and **c2bRegister**, may depend on additional setup or external parameters that are necessary for proper functionality. In a **production development** setting, these configurations are especially critical to ensure that all aspects of the API perform as expected. It is highly recommended that developers pay close attention to the specific requirements outlined in the [**official documentation**](https://developer.safaricom.co.ke/APIs) for each API. Relying on the most up-to-date and detailed guidelines from the official sources will help mitigate potential issues and ensure smooth integration. @@ -370,10 +376,10 @@ works reliably in actual usage scenarios. **BDD approach** + **on integration tests**, can help: -* Catch authentication issues (**OAuth failures**) -* Verify actual API responses (**instead of mocked ones**) -* Check if callbacks are received & handled properly -* Detect network timeouts or incorrect response formats +- Catch authentication issues (**OAuth failures**) +- Verify actual API responses (**instead of mocked ones**) +- Check if callbacks are received & handled properly +- Detect network timeouts or incorrect response formats To run tests, first, **clone this repository**. @@ -386,7 +392,7 @@ connection** to accurately simulate **real API interactions** over **HTTPS**. ### Activating callback handlers in tests -Callback handlers in **testing** are automatically configured but **disabled** by default. +Callback handlers in **testing** are automatically configured but **disabled** by default. Below is an example of a `c2bSimulate` **mocha** test. To activate callback handling swap `true` to `false`. @@ -396,23 +402,27 @@ import { mpesa } from "../../../../index.js"; import { setupNgrokServer } from "../utils/server.js"; import { createOptionsForC2bSimulate } from "../utils/options.js"; -describe("C2B Simulate API with OAuth", function() { +describe("C2B Simulate API with OAuth", function () { this.timeout(28000); let NGROK_URL, teardown; const { c2bSimulate } = mpesa; // To enable callback handling swap true to false - before(async function() { - // ({ NGROK_URL, teardown } = await setupNgrokServer("c2bSimulate", true)); - ({ NGROK_URL, teardown } = await setupNgrokServer("c2bSimulate", false)); + before(async function () { + // ({ NGROK_URL, teardown } = await setupNgrokServer("c2bSimulate", true)); + ({ NGROK_URL, teardown } = await setupNgrokServer("c2bSimulate", false)); }); - after(async function() { + after(async function () { await teardown(); }); - it("Should simulate a C2B transaction", function(done) { - c2bSimulate(createOptionsForC2bSimulate(NGROK_URL === "" ? "https://mock.url" : NGROK_URL)) + it("Should simulate a C2B transaction", function (done) { + c2bSimulate( + createOptionsForC2bSimulate( + NGROK_URL === "" ? "https://mock.url" : NGROK_URL, + ), + ) .then((responseBody) => { expect(responseBody).to.be.an("object"); console.log("RESPONSE BODY:", JSON.stringify(responseBody, null, 2)); @@ -421,16 +431,15 @@ describe("C2B Simulate API with OAuth", function() { .catch(done); }); }); - ``` ### Temporary port exposure Once the callback handler is enabled, the boolean option (`false` allows the test to temporarily: -* Spawn a local server -* Expose it via Ngrok -* Fetch responses from API endpoint servers +- Spawn a local server +- Expose it via Ngrok +- Fetch responses from API endpoint servers This setup ensures that callbacks are properly handled during testing. @@ -473,9 +482,9 @@ We welcome **contributions**! Follow these steps to get started: **Contributors** -* [DGatere](https://github.com/DGatere) -* [geofmureithi](https://github.com/geofmureithi) -* [Waturu Samm](https://github.com/tu-ru/) +- [DGatere](https://github.com/DGatere) +- [geofmureithi](https://github.com/geofmureithi) +- [Waturu Samm](https://github.com/tu-ru/) ## License diff --git a/docs/b2c-Request.js.html b/docs/b2c-Request.js.html index bc2f369..bcba7de 100644 --- a/docs/b2c-Request.js.html +++ b/docs/b2c-Request.js.html @@ -1,8 +1,8 @@ - + - - - + + + b2c-Request.js - Documentation @@ -10,36 +10,111 @@ - - - - - + + + + + + + - - + - + - +
+

b2c-Request.js

-
- -

b2c-Request.js

- - - - - - - -
+
-
import axios from "axios";
+          
import axios from "axios";
 import { CommandIDs } from "../utils/constants.js";
 import {
   callbackTrigger,
@@ -68,7 +143,7 @@ 

b2c-Request.js

* @param {string} options.remarks Information to be associated with the transaction. * @param {string} options.occasion Information to be associated with the transaction. * @param {string} options.QueueTimeOutURL URL for timeout notifications. - * @param {string} options.resultUrl URL for M-PESA to send payment processing notifications. + * @param {string} options.resultURL URL for M-PESA to send payment processing notifications. * @param {string} options.commandId Unique command specifying B2C transaction type (e.g., BusinessPayment). * @param {string} options.initiatorName API user created by Business Administrator for B2C transactions. * @param {boolean} [options.proErrorLogging=false] Logs out advanced error details - good for debugging. @@ -80,9 +155,9 @@

b2c-Request.js

QueueTimeOutURL, remarks, occasion, - resultUrl, + resultURL, commandId, - initiatorName = "", + initiatorName, proErrorLogging = false, }) { if (!callbackHandlerInitialized) { @@ -101,8 +176,8 @@

b2c-Request.js

} const _msisdn = validateFormatPhone(partyB) try { - validateUrl(resultUrl, "resultUrl"); - validateUrl(QueueTimeOutURL, "queueTimeOutUrl"); + validateUrl(resultURL, "resultURL"); + validateUrl(QueueTimeOutURL, "QueueTimeOutURL"); const OriginatorConversationID = originatorID(); const { accessToken, baseURL } = await generateOAuthToken(); @@ -124,7 +199,7 @@

b2c-Request.js

PartyB: _msisdn, Remarks: remarks, QueueTimeOutURL: QueueTimeOutURL, - ResultURL: resultUrl, + ResultURL: resultURL, Occasion: occasion }); @@ -147,7 +222,7 @@

b2c-Request.js

_msisdn, amount, QueueTimeOutURL, - resultUrl, + resultURL, commandId, initiatorName, occasion, @@ -167,20 +242,20 @@

b2c-Request.js

export { b2cRequest, handleB2cRequestCallbacks };
-
- - - - -
+ +
-
+
-
- Generated by JSDoc 4.0.4 on Thu Feb 27 2025 21:37:15 GMT+0300 (East Africa Time) using the Minami theme. -
+
+ Generated by JSDoc 4.0.4 on + Fri Feb 28 2025 10:29:32 GMT+0300 (East Africa Time) using the Minami + theme. +
- - - + + + diff --git a/docs/b2c-Topup.js.html b/docs/b2c-Topup.js.html index 5277d13..5fe7b5b 100644 --- a/docs/b2c-Topup.js.html +++ b/docs/b2c-Topup.js.html @@ -1,8 +1,8 @@ - + - - - + + + b2c-Topup.js - Documentation @@ -10,36 +10,111 @@ - - - - - + + + + + + + - - + - + - +
+

b2c-Topup.js

-
- -

b2c-Topup.js

- - - - - - - -
+
-
import axios from "axios";
+          
import axios from "axios";
 import {
   generateOAuthToken,
   encryptSecurityCredential,
@@ -68,7 +143,7 @@ 

b2c-Topup.js

* @param {number} options.accountReference Identifier for the transaction. * @param {number} [options.requester] Optional. The consumer’s mobile number on behalf of whom you are paying. * @param {string} options.QueueTimeOutURL A URL that will be used to notify your system in case the request times out. - * @param {string} options.resultUrl A URL that will be used to send transaction results after processing. + * @param {string} options.resultURL A URL that will be used to send transaction results after processing. * @param {boolean} [options.proErrorLogging] Logs out advanced error details - good for debugging * @return {Promise<object>} b2cTopUpResponse and (optional) conditionalCallbackData. */ async function b2cTopUp({ @@ -79,7 +154,7 @@

b2c-Topup.js

accountReference, requester, QueueTimeOutURL, - resultUrl, + resultURL, remarks, proErrorLogging = false, }) { @@ -89,7 +164,7 @@

b2c-Topup.js

); } try { - validateUrl(resultUrl, "resultUrl"); + validateUrl(resultURL, "resultURL"); validateUrl(QueueTimeOutURL, "QueueTimeOutUrl"); const { accessToken, baseURL } = await generateOAuthToken(); const req = axios.create({ @@ -120,7 +195,7 @@

b2c-Topup.js

Requester: requester, Remarks: remarks, QueueTimeOutURL: QueueTimeOutURL, - ResultURL: resultUrl, + ResultURL: resultURL, }); conditionalCallbackData = await callbackTrigger(callbackHandlerInitialized); @@ -145,7 +220,7 @@

b2c-Topup.js

accountReference, requester, QueueTimeOutURL, - resultUrl, + resultURL, remarks }, }, @@ -163,20 +238,20 @@

b2c-Topup.js

export { b2cTopUp, handleB2cTopUpCallbacks };
-
- - - - -
+ +
-
+
-
- Generated by JSDoc 4.0.4 on Thu Feb 27 2025 21:37:15 GMT+0300 (East Africa Time) using the Minami theme. -
+
+ Generated by JSDoc 4.0.4 on + Fri Feb 28 2025 10:29:32 GMT+0300 (East Africa Time) using the Minami + theme. +
- - - + + + diff --git a/docs/balance-Query.js.html b/docs/balance-Query.js.html index bfc4b4d..8e0957a 100644 --- a/docs/balance-Query.js.html +++ b/docs/balance-Query.js.html @@ -1,8 +1,8 @@ - + - - - + + + balance-Query.js - Documentation @@ -10,36 +10,111 @@ - - - - - - - - - - - - - -
- -

balance-Query.js

- - - - - - - -
+ + + + + + + + + + + + +
+

balance-Query.js

+ +
-
import axios from "axios";
+          
import axios from "axios";
 import {
   callbackTrigger,
   encryptSecurityCredential,
@@ -65,7 +140,7 @@ 

balance-Query.js

* @param {number} options.identifierType Type of the querying organization. * @param {string} options.remarks Information to be associated with the transaction. * @param {String} options.QueueTimeOutURL URL to receive timeout messages. - * @param {String} options.resultUrl URL to receive the result message. + * @param {String} options.resultURL URL to receive the result message. * @param {String} options.initiator The credential/username for authentication. * @param {boolean} [options.proErrorLogging=false] Logs out advanced error details - good for debugging * @return {Promise<object>} balanceQueryResponse and (optional) conditionalCallbackData. @@ -74,7 +149,7 @@

balance-Query.js

partyA, identifierType, QueueTimeOutURL, - resultUrl, + resultURL, initiator, remarks, proErrorLogging = false, @@ -94,7 +169,7 @@

balance-Query.js

); } try { - validateUrl(resultUrl, "resultUrl"); + validateUrl(resultURL, "resultURL"); validateUrl(QueueTimeOutURL, "timeoutUrl"); const { accessToken, baseURL } = await generateOAuthToken(); @@ -120,7 +195,7 @@

balance-Query.js

IdentifierType: identifierType, Remarks: remarks, QueueTimeOutURL: QueueTimeOutURL, - ResultURL: resultUrl, + ResultURL: resultURL, }); conditionalCallbackData = await callbackTrigger(callbackHandlerInitialized); @@ -137,7 +212,7 @@

balance-Query.js

{ apiEndpoint: "/mpesa/accountbalance/v1/query", method: "POST", - payload: { partyA, identifierType, QueueTimeOutURL, resultUrl, initiator, remarks }, + payload: { partyA, identifierType, QueueTimeOutURL, resultURL, initiator, remarks }, }, "Balance query error details:", ); @@ -154,20 +229,20 @@

balance-Query.js

export { balanceQuery, handleBalanceQueryCallbacks };
-
- - - - -
- -
- -
- Generated by JSDoc 4.0.4 on Thu Feb 27 2025 21:37:15 GMT+0300 (East Africa Time) using the Minami theme. -
- - - - +
+
+ +
+ +
+ Generated by JSDoc 4.0.4 on + Fri Feb 28 2025 10:29:32 GMT+0300 (East Africa Time) using the Minami + theme. +
+ + + + diff --git a/docs/business-Paybill.js.html b/docs/business-Paybill.js.html index e012532..3753140 100644 --- a/docs/business-Paybill.js.html +++ b/docs/business-Paybill.js.html @@ -1,8 +1,8 @@ - + - - - + + + business-Paybill.js - Documentation @@ -10,36 +10,111 @@ - - - - - + + + + + + + - - + - + - +
+

business-Paybill.js

-
- -

business-Paybill.js

- - - - - - - -
+
-
import axios from "axios";
+          
import axios from "axios";
 import {
   callbackTrigger,
   encryptSecurityCredential,
@@ -68,7 +143,7 @@ 

business-Paybill.js

* @param {number} options.accountReference Account number for the payment (up to 13 characters). * @param {number} [options.requester] Optional consumer’s mobile number (if paying on their behalf). * @param {string} options.QueueTimeOutURL URL for timeout notifications. - * @param {string} options.resultUrl URL for sending transaction results after processing. + * @param {string} options.resultURL URL for sending transaction results after processing. * @param {boolean} [options.proErrorLogging] Logs out advanced error details - good for debugging * @return {Promise<Object>} businessPaybillResponse and (optional) conditionalCallbackData. */ @@ -81,7 +156,7 @@

business-Paybill.js

accountReference, requester, QueueTimeOutURL, - resultUrl, + resultURL, proErrorLogging = false, }) { if (!callbackHandlerInitialized) { @@ -90,7 +165,7 @@

business-Paybill.js

); } try { - validateUrl(resultUrl, "resultUrl"); + validateUrl(resultURL, "resultURL"); validateUrl(QueueTimeOutURL, "timeoutUrl"); const { accessToken, baseURL } = await generateOAuthToken(); const req = axios.create({ @@ -119,7 +194,7 @@

business-Paybill.js

Requester: requester, Remarks: remarks, QueueTimeOutURL: QueueTimeOutURL, - ResultURL: resultUrl, + ResultURL: resultURL, }); conditionalCallbackData = await callbackTrigger(callbackHandlerInitialized); @@ -147,7 +222,7 @@

business-Paybill.js

accountReference, requester, QueueTimeOutURL, - resultUrl, + resultURL, remarks }, }, @@ -166,20 +241,20 @@

business-Paybill.js

export { businessPaybill, handleBusinessPaybillCallbacks };
-
- - - - -
+ +
-
+
-
- Generated by JSDoc 4.0.4 on Thu Feb 27 2025 21:37:15 GMT+0300 (East Africa Time) using the Minami theme. -
+
+ Generated by JSDoc 4.0.4 on + Fri Feb 28 2025 10:29:32 GMT+0300 (East Africa Time) using the Minami + theme. +
- - - + + + diff --git a/docs/c2b-Register.js.html b/docs/c2b-Register.js.html index 862f5c9..c91a63f 100644 --- a/docs/c2b-Register.js.html +++ b/docs/c2b-Register.js.html @@ -1,8 +1,8 @@ - + - - - + + + c2b-Register.js - Documentation @@ -10,36 +10,111 @@ - - - - - - - - - - - - - -
- -

c2b-Register.js

- - - - - - - -
+ + + + + + + + + + + + +
+

c2b-Register.js

+ +
-
import axios from "axios";
+          
import axios from "axios";
 import {
   callbackTrigger,
   generateOAuthToken,
@@ -60,16 +135,16 @@ 

c2b-Register.js

* @summary Registers callback validation and confirmation URLs on M-Pesa to receive payment notifications for your paybill/till numbers. * @see {@link https://developer.safaricom.co.ke/APIs/CustomerToBusinessRegisterURL open external link} * @param {Object} options Options for the C2B Register URL. - * @param {string} options.confirmationUrl URL to receive confirmation upon payment completion. - * @param {string} options.validationUrl URL to receive validation upon payment submission (default: external validation disabled). + * @param {string} options.confirmationURL URL to receive confirmation upon payment completion. + * @param {string} options.validationURL URL to receive validation upon payment submission (default: external validation disabled). * @param {number} options.shortCode Unique M-PESA pay bill/till number. * @param {string} options.responseType Action if validation URL is unreachable (values: Completed or Cancelled). * @param {boolean} [options.proErrorLogging] Logs out detailed errors for debugging purposes * @return {Promise<Object>} c2bRegisterResponse and (optional) conditionalCallbackData. */ async function c2bRegister({ - confirmationUrl, - validationUrl, + confirmationURL, + validationURL, shortCode, responseType, proErrorLogging = false, @@ -89,8 +164,8 @@

c2b-Register.js

); } try { - validateUrl(confirmationUrl, "confirmationUrl"); - validateUrl(validationUrl, "validationUrl"); + validateUrl(confirmationURL, "confirmationURL"); + validateUrl(validationURL, "validationURL"); const { accessToken, baseURL } = await generateOAuthToken(); const req = axios.create({ baseURL, @@ -102,8 +177,8 @@

c2b-Register.js

const responseBody = await req.post("/mpesa/c2b/v1/registerurl", { ShortCode: shortCode, ResponseType: responseType, - ConfirmationURL: confirmationUrl, - ValidationURL: validationUrl, + ConfirmationURL: confirmationURL, + ValidationURL: validationURL, }); conditionalCallbackData = await callbackTrigger(callbackHandlerInitialized); return { c2bRegisterResponse: responseBody.data, conditionalCallbackData }; @@ -119,8 +194,8 @@

c2b-Register.js

apiEndpoint: "/mpesa/c2b/v1/registerurl", method: "POST", payload: { - confirmationUrl, - validationUrl, + confirmationURL, + validationURL, shortCode, responseType, }, @@ -140,20 +215,20 @@

c2b-Register.js

export { c2bRegister, handleC2bRegisterCallbacks };
-
- - - - -
- -
- -
- Generated by JSDoc 4.0.4 on Thu Feb 27 2025 21:37:15 GMT+0300 (East Africa Time) using the Minami theme. -
- - - - +
+
+ +
+ +
+ Generated by JSDoc 4.0.4 on + Fri Feb 28 2025 10:29:32 GMT+0300 (East Africa Time) using the Minami + theme. +
+ + + + diff --git a/docs/c2b-Simulate.js.html b/docs/c2b-Simulate.js.html index 5b32209..0316fc6 100644 --- a/docs/c2b-Simulate.js.html +++ b/docs/c2b-Simulate.js.html @@ -1,8 +1,8 @@ - + - - - + + + c2b-Simulate.js - Documentation @@ -10,36 +10,109 @@ - - - - - - - - - - - - - -
- -

c2b-Simulate.js

- - - - - - - -
+ + + + + + + + + + + + +
+

c2b-Simulate.js

+ +
-
import {
+          
import {
   generateOAuthToken, logErrorDetails,
   throwErrorMessages,
   validateFormatPhone
@@ -120,20 +193,20 @@ 

c2b-Simulate.js

export { c2bSimulate };
-
- - - - -
- -
- -
- Generated by JSDoc 4.0.4 on Thu Feb 27 2025 21:37:15 GMT+0300 (East Africa Time) using the Minami theme. -
- - - - +
+
+ +
+ +
+ Generated by JSDoc 4.0.4 on + Fri Feb 28 2025 10:29:32 GMT+0300 (East Africa Time) using the Minami + theme. +
+ + + + diff --git a/docs/global.html b/docs/global.html index c4f9f6c..f377339 100644 --- a/docs/global.html +++ b/docs/global.html @@ -1005,7 +1005,7 @@

tran
- Generated by JSDoc 4.0.4 on Thu Feb 27 2025 21:37:15 GMT+0300 (East Africa Time) using the Minami theme. + Generated by JSDoc 4.0.4 on Fri Feb 28 2025 10:29:32 GMT+0300 (East Africa Time) using the Minami theme.
diff --git a/docs/index.html b/docs/index.html index 1199ef4..ab53111 100644 --- a/docs/index.html +++ b/docs/index.html @@ -1,8 +1,8 @@ - + - - - + + + Home - Documentation @@ -10,72 +10,156 @@ - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - -
-

JSDoc Guide

-

About

-

This documentation is generated using JSDoc and is designed to help developers accurately map fields to API endpoints. It provides a clear understanding of which fields are preconfigured by default and which can be modified based on your requirements.

-

By referring to this guide, you can ensure that you're using the API correctly and efficiently. Whether you're integrating the library or extending its functionality, this documentation will serve as a valuable reference.

-

Keeping Documentation Up to Date

-

If you're contributing to the project, make sure to regenerate the documentation whenever changes are made to the API. Use the following commands:

-

Generate Documentation

-

After updating the library, you may optionally run the commands below to regenerate the documentation. However, if you prefer, you can leave this task to the repository maintainer.

-

npm:npm docs

-

yarn:yarn docs

-

Takeaways

-

Keeping the documentation updated ensures that all contributors and users have access to accurate and up-to-date information.

-
- - - - - - -
- -
- -
- Generated by JSDoc 4.0.4 on Thu Feb 27 2025 21:37:15 GMT+0300 (East Africa Time) using the Minami theme. -
- - - - - \ No newline at end of file + + + + + + + + + + + + +
+
+
+

MPESA-NODE LIBRARY

+

JSDocs guide

+

About

+

+ This documentation is generated using JSDoc and is designed to help + developers accurately map fields to API endpoints. It provides a + clear understanding of which fields are preconfigured by default and + which can be modified based on your requirements. +

+

+ By referring to this guide, you can ensure that you're using the API + correctly and efficiently. Whether you're integrating the library or + extending its functionality, this documentation will serve as a + valuable reference. +

+

Keeping Documentation Up to Date

+

+ If you're contributing to the project, make sure to regenerate the + documentation whenever changes are made to the API. Use the + following commands: +

+

Generate Documentation

+

+ After updating the library, you may + optionally run the commands below to regenerate the + documentation. However, if you prefer, you can leave this task to + the repository maintainer. +

+

npm:npm docs

+

yarn:yarn docs

+

Takeaways

+

+ Keeping the documentation updated ensures that all contributors and + users have access to accurate and up-to-date information. +

+
+
+
+ +
+ +
+ Generated by JSDoc 4.0.4 on + Fri Feb 28 2025 10:29:32 GMT+0300 (East Africa Time) using the Minami + theme. +
+ + + + + diff --git a/docs/mpesa-Query.js.html b/docs/mpesa-Query.js.html index fbc3d3b..084d5bc 100644 --- a/docs/mpesa-Query.js.html +++ b/docs/mpesa-Query.js.html @@ -1,8 +1,8 @@ - + - - - + + + mpesa-Query.js - Documentation @@ -10,36 +10,109 @@ - - - - - - - - - - - - - -
- -

mpesa-Query.js

- - - - - - - -
+ + + + + + + + + + + + +
+

mpesa-Query.js

+ +
-
import {
+          
import {
   generateMpesaCredentials,
   generateOAuthToken,
   logErrorDetails,
@@ -107,20 +180,20 @@ 

mpesa-Query.js

export { mpesaQuery };
-
- - - - -
- -
- -
- Generated by JSDoc 4.0.4 on Thu Feb 27 2025 21:37:15 GMT+0300 (East Africa Time) using the Minami theme. -
- - - - +
+
+ +
+ +
+ Generated by JSDoc 4.0.4 on + Fri Feb 28 2025 10:29:32 GMT+0300 (East Africa Time) using the Minami + theme. +
+ + + + diff --git a/docs/mpesa-Simulate.js.html b/docs/mpesa-Simulate.js.html index 4aaabee..d0a43d0 100644 --- a/docs/mpesa-Simulate.js.html +++ b/docs/mpesa-Simulate.js.html @@ -1,8 +1,8 @@ - + - - - + + + mpesa-Simulate.js - Documentation @@ -10,36 +10,111 @@ - - - - - - - - - - - - - -
- -

mpesa-Simulate.js

- - - - - - - -
+ + + + + + + + + + + + +
+

mpesa-Simulate.js

+ +
-
import axios from "axios";
+          
import axios from "axios";
 import {
   callbackTrigger,
   generateMpesaCredentials,
@@ -65,7 +140,7 @@ 

mpesa-Simulate.js

* @param {string} options.phoneNumber Mobile number to receive the STK Pin Prompt (can be same as partyA). * @param {number} options.amount Transaction amount. * @param {string} options.transactionType "CustomerPayBillOnline" for PayBill and "CustomerBuyGoodsOnline" for Till Numbers. - * @param {string} options.callbackUrl Secure URL for receiving notifications from M-Pesa API. + * @param {string} options.callbackURL Secure URL for receiving notifications from M-Pesa API. * @param {string} options.transactionDesc Information to be associated with the transaction. * @param {string} options.accountRef Alphanumeric account reference (up to 12 characters) shown in the STK Pin Prompt (Org name). * @param {number} options.partyB 5 to 6-digit number of the receiving organization. @@ -76,7 +151,7 @@

mpesa-Simulate.js

partyA, phoneNumber, amount, - callbackUrl, + callbackURL, accountRef, partyB, transactionType, @@ -89,7 +164,7 @@

mpesa-Simulate.js

); } try { - validateUrl(callbackUrl, "callbackUrl"); + validateUrl(callbackURL, "callbackURL"); const { accessToken, baseURL } = await generateOAuthToken(); const { password, timeStamp } = generateMpesaCredentials(partyB); const req = axios.create({ @@ -108,7 +183,7 @@

mpesa-Simulate.js

PartyA: validateFormatPhone(partyA), PartyB: partyB, PhoneNumber: validateFormatPhone(phoneNumber), - CallBackURL: callbackUrl, + CallBackURL: callbackURL, AccountReference: accountRef, TransactionDesc: transactionDesc, TransactionType: transactionType @@ -133,7 +208,7 @@

mpesa-Simulate.js

partyA, phoneNumber, amount, - callbackUrl, + callbackURL, accountRef, partyB, transactionDesc @@ -153,20 +228,20 @@

mpesa-Simulate.js

export { mpesaSimulate, handleMpesaSimulateCallbacks };
-
- - - - -
- -
- -
- Generated by JSDoc 4.0.4 on Thu Feb 27 2025 21:37:15 GMT+0300 (East Africa Time) using the Minami theme. -
- - - - +
+
+ +
+ +
+ Generated by JSDoc 4.0.4 on + Fri Feb 28 2025 10:29:32 GMT+0300 (East Africa Time) using the Minami + theme. +
+ + + + diff --git a/docs/qr-Generate.js.html b/docs/qr-Generate.js.html index a0ae49c..3792c23 100644 --- a/docs/qr-Generate.js.html +++ b/docs/qr-Generate.js.html @@ -1,8 +1,8 @@ - + - - - + + + qr-Generate.js - Documentation @@ -10,36 +10,111 @@ - - - - - - - - - - - - - -
- -

qr-Generate.js

- - - - - - - -
+ + + + + + + + + + + + +
+

qr-Generate.js

+ +
-
import axios from "axios";
+          
import axios from "axios";
 import {
   generateOAuthToken,
   logErrorDetails,
@@ -128,20 +203,20 @@ 

qr-Generate.js

export { generateQrCode };
-
- - - - -
- -
- -
- Generated by JSDoc 4.0.4 on Thu Feb 27 2025 21:37:15 GMT+0300 (East Africa Time) using the Minami theme. -
- - - - +
+
+ +
+ +
+ Generated by JSDoc 4.0.4 on + Fri Feb 28 2025 10:29:32 GMT+0300 (East Africa Time) using the Minami + theme. +
+ + + + diff --git a/docs/reversals.js.html b/docs/reversals.js.html index 661392e..c16f83a 100644 --- a/docs/reversals.js.html +++ b/docs/reversals.js.html @@ -1,8 +1,8 @@ - + - - - + + + reversals.js - Documentation @@ -10,36 +10,111 @@ - - - - - - - - - - - - - -
- -

reversals.js

- - - - - - - -
+ + + + + + + + + + + + +
+

reversals.js

+ +
-
import axios from "axios";
+          
import axios from "axios";
 import {
   callbackTrigger,
   encryptSecurityCredential,
@@ -63,7 +138,7 @@ 

reversals.js

* @param {string} options.transactionId Transaction ID for reversal (e.g., LKXXXX1234). * @param {number} options.amount Amount to be reversed. * @param {string} options.QueueTimeOutURL URL for timeout transaction details. - * @param {string} options.resultUrl URL for transaction details. + * @param {string} options.resultURL URL for transaction details. * @param {string} options.remarks Information to be associated with the transaction. * @param {string} options.occasion Information to be associated with the transaction. * @param {number} options.receiverParty Organization receiving the transaction. @@ -75,7 +150,7 @@

reversals.js

transactionId, amount, QueueTimeOutURL, - resultUrl, + resultURL, receiverParty, initiator, remarks, @@ -87,7 +162,7 @@

reversals.js

); } try { - validateUrl(resultUrl, "resultUrl"); + validateUrl(resultURL, "resultURL"); validateUrl(QueueTimeOutURL, "timeoutUrl"); const { accessToken, baseURL } = await generateOAuthToken(); const req = axios.create({ @@ -110,7 +185,7 @@

reversals.js

Amount: amount, ReceiverParty: receiverParty, RecieverIdentifierType: config.receiverIdType, - ResultURL: resultUrl, + ResultURL: resultURL, QueueTimeOutURL: QueueTimeOutURL, Remarks: remarks, Occasion: occasion, @@ -134,7 +209,7 @@

reversals.js

transactionId, amount, QueueTimeOutURL, - resultUrl, + resultURL, receiverParty, initiator, remarks, @@ -155,20 +230,20 @@

reversals.js

export { reversals, handleReversalCallbacks };
-
- - - - -
- -
- -
- Generated by JSDoc 4.0.4 on Thu Feb 27 2025 21:37:15 GMT+0300 (East Africa Time) using the Minami theme. -
- - - - +
+
+ +
+ +
+ Generated by JSDoc 4.0.4 on + Fri Feb 28 2025 10:29:32 GMT+0300 (East Africa Time) using the Minami + theme. +
+ + + + diff --git a/docs/scripts/linenumber.js b/docs/scripts/linenumber.js index 8d52f7e..20b6b09 100644 --- a/docs/scripts/linenumber.js +++ b/docs/scripts/linenumber.js @@ -1,25 +1,25 @@ /*global document */ -(function() { - var source = document.getElementsByClassName('prettyprint source linenums'); - var i = 0; - var lineNumber = 0; - var lineId; - var lines; - var totalLines; - var anchorHash; +(function () { + var source = document.getElementsByClassName("prettyprint source linenums"); + var i = 0; + var lineNumber = 0; + var lineId; + var lines; + var totalLines; + var anchorHash; - if (source && source[0]) { - anchorHash = document.location.hash.substring(1); - lines = source[0].getElementsByTagName('li'); - totalLines = lines.length; + if (source && source[0]) { + anchorHash = document.location.hash.substring(1); + lines = source[0].getElementsByTagName("li"); + totalLines = lines.length; - for (; i < totalLines; i++) { - lineNumber++; - lineId = 'line' + lineNumber; - lines[i].id = lineId; - if (lineId === anchorHash) { - lines[i].className += ' selected'; - } - } + for (; i < totalLines; i++) { + lineNumber++; + lineId = "line" + lineNumber; + lines[i].id = lineId; + if (lineId === anchorHash) { + lines[i].className += " selected"; + } } + } })(); diff --git a/docs/scripts/prettify/lang-css.js b/docs/scripts/prettify/lang-css.js index 041e1f5..1918eab 100644 --- a/docs/scripts/prettify/lang-css.js +++ b/docs/scripts/prettify/lang-css.js @@ -1,2 +1,36 @@ -PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\f\r ]+/,null," \t\r\n "]],[["str",/^"(?:[^\n\f\r"\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*"/,null],["str",/^'(?:[^\n\f\r'\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*'/,null],["lang-css-str",/^url\(([^"')]*)\)/i],["kwd",/^(?:url|rgb|!important|@import|@page|@media|@charset|inherit)(?=[^\w-]|$)/i,null],["lang-css-kw",/^(-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*)\s*:/i],["com",/^\/\*[^*]*\*+(?:[^*/][^*]*\*+)*\//],["com", -/^(?:<\!--|--\>)/],["lit",/^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i],["lit",/^#[\da-f]{3,6}/i],["pln",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i],["pun",/^[^\s\w"']+/]]),["css"]);PR.registerLangHandler(PR.createSimpleLexer([],[["kwd",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i]]),["css-kw"]);PR.registerLangHandler(PR.createSimpleLexer([],[["str",/^[^"')]+/]]),["css-str"]); +PR.registerLangHandler( + PR.createSimpleLexer( + [["pln", /^[\t\n\f\r ]+/, null, " \t\r\n "]], + [ + ["str", /^"(?:[^\n\f\r"\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*"/, null], + ["str", /^'(?:[^\n\f\r'\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*'/, null], + ["lang-css-str", /^url\(([^"')]*)\)/i], + [ + "kwd", + /^(?:url|rgb|!important|@import|@page|@media|@charset|inherit)(?=[^\w-]|$)/i, + null, + ], + [ + "lang-css-kw", + /^(-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*)\s*:/i, + ], + ["com", /^\/\*[^*]*\*+(?:[^*/][^*]*\*+)*\//], + ["com", /^(?:<\!--|--\>)/], + ["lit", /^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i], + ["lit", /^#[\da-f]{3,6}/i], + ["pln", /^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i], + ["pun", /^[^\s\w"']+/], + ], + ), + ["css"], +); +PR.registerLangHandler( + PR.createSimpleLexer( + [], + [["kwd", /^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i]], + ), + ["css-kw"], +); +PR.registerLangHandler(PR.createSimpleLexer([], [["str", /^[^"')]+/]]), [ + "css-str", +]); diff --git a/docs/scripts/prettify/prettify.js b/docs/scripts/prettify/prettify.js index eef5ad7..d08f9d8 100644 --- a/docs/scripts/prettify/prettify.js +++ b/docs/scripts/prettify/prettify.js @@ -1,28 +1,740 @@ -var q=null;window.PR_SHOULD_USE_CONTINUATION=!0; -(function(){function L(a){function m(a){var f=a.charCodeAt(0);if(f!==92)return f;var b=a.charAt(1);return(f=r[b])?f:"0"<=b&&b<="7"?parseInt(a.substring(1),8):b==="u"||b==="x"?parseInt(a.substring(2),16):a.charCodeAt(1)}function e(a){if(a<32)return(a<16?"\\x0":"\\x")+a.toString(16);a=String.fromCharCode(a);if(a==="\\"||a==="-"||a==="["||a==="]")a="\\"+a;return a}function h(a){for(var f=a.substring(1,a.length-1).match(/\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\[0-3][0-7]{0,2}|\\[0-7]{1,2}|\\[\S\s]|[^\\]/g),a= -[],b=[],o=f[0]==="^",c=o?1:0,i=f.length;c122||(d<65||j>90||b.push([Math.max(65,j)|32,Math.min(d,90)|32]),d<97||j>122||b.push([Math.max(97,j)&-33,Math.min(d,122)&-33]))}}b.sort(function(a,f){return a[0]-f[0]||f[1]-a[1]});f=[];j=[NaN,NaN];for(c=0;ci[0]&&(i[1]+1>i[0]&&b.push("-"),b.push(e(i[1])));b.push("]");return b.join("")}function y(a){for(var f=a.source.match(/\[(?:[^\\\]]|\\[\S\s])*]|\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\\d+|\\[^\dux]|\(\?[!:=]|[()^]|[^()[\\^]+/g),b=f.length,d=[],c=0,i=0;c=2&&a==="["?f[c]=h(j):a!=="\\"&&(f[c]=j.replace(/[A-Za-z]/g,function(a){a=a.charCodeAt(0);return"["+String.fromCharCode(a&-33,a|32)+"]"}));return f.join("")}for(var t=0,s=!1,l=!1,p=0,d=a.length;p=5&&"lang-"===b.substring(0,5))&&!(o&&typeof o[1]==="string"))c=!1,b="src";c||(r[f]=b)}i=d;d+=f.length;if(c){c=o[1];var j=f.indexOf(c),k=j+c.length;o[2]&&(k=f.length-o[2].length,j=k-c.length);b=b.substring(5);B(l+i,f.substring(0,j),e,p);B(l+i+j,c,C(b,c),p);B(l+i+k,f.substring(k),e,p)}else p.push(l+i,b)}a.e=p}var h={},y;(function(){for(var e=a.concat(m), -l=[],p={},d=0,g=e.length;d=0;)h[n.charAt(k)]=r;r=r[1];n=""+r;p.hasOwnProperty(n)||(l.push(r),p[n]=q)}l.push(/[\S\s]/);y=L(l)})();var t=m.length;return e}function u(a){var m=[],e=[];a.tripleQuotedStrings?m.push(["str",/^(?:'''(?:[^'\\]|\\[\S\s]|''?(?=[^']))*(?:'''|$)|"""(?:[^"\\]|\\[\S\s]|""?(?=[^"]))*(?:"""|$)|'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$))/,q,"'\""]):a.multiLineStrings?m.push(["str",/^(?:'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$)|`(?:[^\\`]|\\[\S\s])*(?:`|$))/, -q,"'\"`"]):m.push(["str",/^(?:'(?:[^\n\r'\\]|\\.)*(?:'|$)|"(?:[^\n\r"\\]|\\.)*(?:"|$))/,q,"\"'"]);a.verbatimStrings&&e.push(["str",/^@"(?:[^"]|"")*(?:"|$)/,q]);var h=a.hashComments;h&&(a.cStyleComments?(h>1?m.push(["com",/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,q,"#"]):m.push(["com",/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\n\r]*)/,q,"#"]),e.push(["str",/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,q])):m.push(["com",/^#[^\n\r]*/, -q,"#"]));a.cStyleComments&&(e.push(["com",/^\/\/[^\n\r]*/,q]),e.push(["com",/^\/\*[\S\s]*?(?:\*\/|$)/,q]));a.regexLiterals&&e.push(["lang-regex",/^(?:^^\.?|[!+-]|!=|!==|#|%|%=|&|&&|&&=|&=|\(|\*|\*=|\+=|,|-=|->|\/|\/=|:|::|;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|[?@[^]|\^=|\^\^|\^\^=|{|\||\|=|\|\||\|\|=|~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\s*(\/(?=[^*/])(?:[^/[\\]|\\[\S\s]|\[(?:[^\\\]]|\\[\S\s])*(?:]|$))+\/)/]);(h=a.types)&&e.push(["typ",h]);a=(""+a.keywords).replace(/^ | $/g, -"");a.length&&e.push(["kwd",RegExp("^(?:"+a.replace(/[\s,]+/g,"|")+")\\b"),q]);m.push(["pln",/^\s+/,q," \r\n\t\xa0"]);e.push(["lit",/^@[$_a-z][\w$@]*/i,q],["typ",/^(?:[@_]?[A-Z]+[a-z][\w$@]*|\w+_t\b)/,q],["pln",/^[$_a-z][\w$@]*/i,q],["lit",/^(?:0x[\da-f]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+-]?\d+)?)[a-z]*/i,q,"0123456789"],["pln",/^\\[\S\s]?/,q],["pun",/^.[^\s\w"-$'./@\\`]*/,q]);return x(m,e)}function D(a,m){function e(a){switch(a.nodeType){case 1:if(k.test(a.className))break;if("BR"===a.nodeName)h(a), -a.parentNode&&a.parentNode.removeChild(a);else for(a=a.firstChild;a;a=a.nextSibling)e(a);break;case 3:case 4:if(p){var b=a.nodeValue,d=b.match(t);if(d){var c=b.substring(0,d.index);a.nodeValue=c;(b=b.substring(d.index+d[0].length))&&a.parentNode.insertBefore(s.createTextNode(b),a.nextSibling);h(a);c||a.parentNode.removeChild(a)}}}}function h(a){function b(a,d){var e=d?a.cloneNode(!1):a,f=a.parentNode;if(f){var f=b(f,1),g=a.nextSibling;f.appendChild(e);for(var h=g;h;h=g)g=h.nextSibling,f.appendChild(h)}return e} -for(;!a.nextSibling;)if(a=a.parentNode,!a)return;for(var a=b(a.nextSibling,0),e;(e=a.parentNode)&&e.nodeType===1;)a=e;d.push(a)}var k=/(?:^|\s)nocode(?:\s|$)/,t=/\r\n?|\n/,s=a.ownerDocument,l;a.currentStyle?l=a.currentStyle.whiteSpace:window.getComputedStyle&&(l=s.defaultView.getComputedStyle(a,q).getPropertyValue("white-space"));var p=l&&"pre"===l.substring(0,3);for(l=s.createElement("LI");a.firstChild;)l.appendChild(a.firstChild);for(var d=[l],g=0;g=0;){var h=m[e];A.hasOwnProperty(h)?window.console&&console.warn("cannot override language handler %s",h):A[h]=a}}function C(a,m){if(!a||!A.hasOwnProperty(a))a=/^\s*=o&&(h+=2);e>=c&&(a+=2)}}catch(w){"console"in window&&console.log(w&&w.stack?w.stack:w)}}var v=["break,continue,do,else,for,if,return,while"],w=[[v,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"], -"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"],F=[w,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"],G=[w,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"], -H=[G,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"],w=[w,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"],I=[v,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"], -J=[v,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"],v=[v,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"],K=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/,N=/\S/,O=u({keywords:[F,H,w,"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END"+ -I,J,v],hashComments:!0,cStyleComments:!0,multiLineStrings:!0,regexLiterals:!0}),A={};k(O,["default-code"]);k(x([],[["pln",/^[^]*(?:>|$)/],["com",/^<\!--[\S\s]*?(?:--\>|$)/],["lang-",/^<\?([\S\s]+?)(?:\?>|$)/],["lang-",/^<%([\S\s]+?)(?:%>|$)/],["pun",/^(?:<[%?]|[%?]>)/],["lang-",/^]*>([\S\s]+?)<\/xmp\b[^>]*>/i],["lang-js",/^]*>([\S\s]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\S\s]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]), -["default-markup","htm","html","mxml","xhtml","xml","xsl"]);k(x([["pln",/^\s+/,q," \t\r\n"],["atv",/^(?:"[^"]*"?|'[^']*'?)/,q,"\"'"]],[["tag",/^^<\/?[a-z](?:[\w-.:]*\w)?|\/?>$/i],["atn",/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^\s"'>]*(?:[^\s"'/>]|\/(?=\s)))/],["pun",/^[/<->]+/],["lang-js",/^on\w+\s*=\s*"([^"]+)"/i],["lang-js",/^on\w+\s*=\s*'([^']+)'/i],["lang-js",/^on\w+\s*=\s*([^\s"'>]+)/i],["lang-css",/^style\s*=\s*"([^"]+)"/i],["lang-css",/^style\s*=\s*'([^']+)'/i],["lang-css", -/^style\s*=\s*([^\s"'>]+)/i]]),["in.tag"]);k(x([],[["atv",/^[\S\s]+/]]),["uq.val"]);k(u({keywords:F,hashComments:!0,cStyleComments:!0,types:K}),["c","cc","cpp","cxx","cyc","m"]);k(u({keywords:"null,true,false"}),["json"]);k(u({keywords:H,hashComments:!0,cStyleComments:!0,verbatimStrings:!0,types:K}),["cs"]);k(u({keywords:G,cStyleComments:!0}),["java"]);k(u({keywords:v,hashComments:!0,multiLineStrings:!0}),["bsh","csh","sh"]);k(u({keywords:I,hashComments:!0,multiLineStrings:!0,tripleQuotedStrings:!0}), -["cv","py"]);k(u({keywords:"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["perl","pl","pm"]);k(u({keywords:J,hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["rb"]);k(u({keywords:w,cStyleComments:!0,regexLiterals:!0}),["js"]);k(u({keywords:"all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes", -hashComments:3,cStyleComments:!0,multilineStrings:!0,tripleQuotedStrings:!0,regexLiterals:!0}),["coffee"]);k(x([],[["str",/^[\S\s]+/]]),["regex"]);window.prettyPrintOne=function(a,m,e){var h=document.createElement("PRE");h.innerHTML=a;e&&D(h,e);E({g:m,i:e,h:h});return h.innerHTML};window.prettyPrint=function(a){function m(){for(var e=window.PR_SHOULD_USE_CONTINUATION?l.now()+250:Infinity;p=0){var k=k.match(g),f,b;if(b= -!k){b=n;for(var o=void 0,c=b.firstChild;c;c=c.nextSibling)var i=c.nodeType,o=i===1?o?b:c:i===3?N.test(c.nodeValue)?b:o:o;b=(f=o===b?void 0:o)&&"CODE"===f.tagName}b&&(k=f.className.match(g));k&&(k=k[1]);b=!1;for(o=n.parentNode;o;o=o.parentNode)if((o.tagName==="pre"||o.tagName==="code"||o.tagName==="xmp")&&o.className&&o.className.indexOf("prettyprint")>=0){b=!0;break}b||((b=(b=n.className.match(/\blinenums\b(?::(\d+))?/))?b[1]&&b[1].length?+b[1]:!0:!1)&&D(n,b),d={g:k,h:n,i:b},E(d))}}p 122 || + (d < 65 || + j > 90 || + b.push([Math.max(65, j) | 32, Math.min(d, 90) | 32]), + d < 97 || + j > 122 || + b.push([Math.max(97, j) & -33, Math.min(d, 122) & -33])); + } + } + b.sort(function (a, f) { + return a[0] - f[0] || f[1] - a[1]; + }); + f = []; + j = [NaN, NaN]; + for (c = 0; c < b.length; ++c) + (i = b[c]), + i[0] <= j[1] + 1 ? (j[1] = Math.max(j[1], i[1])) : f.push((j = i)); + b = ["["]; + o && b.push("^"); + b.push.apply(b, a); + for (c = 0; c < f.length; ++c) + (i = f[c]), + b.push(e(i[0])), + i[1] > i[0] && (i[1] + 1 > i[0] && b.push("-"), b.push(e(i[1]))); + b.push("]"); + return b.join(""); + } + function y(a) { + for ( + var f = a.source.match( + /\[(?:[^\\\]]|\\[\S\s])*]|\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\\d+|\\[^\dux]|\(\?[!:=]|[()^]|[^()[\\^]+/g, + ), + b = f.length, + d = [], + c = 0, + i = 0; + c < b; + ++c + ) { + var j = f[c]; + j === "(" + ? ++i + : "\\" === j.charAt(0) && + (j = +j.substring(1)) && + j <= i && + (d[j] = -1); + } + for (c = 1; c < d.length; ++c) -1 === d[c] && (d[c] = ++t); + for (i = c = 0; c < b; ++c) + (j = f[c]), + j === "(" + ? (++i, d[i] === void 0 && (f[c] = "(?:")) + : "\\" === j.charAt(0) && + (j = +j.substring(1)) && + j <= i && + (f[c] = "\\" + d[i]); + for (i = c = 0; c < b; ++c) + "^" === f[c] && "^" !== f[c + 1] && (f[c] = ""); + if (a.ignoreCase && s) + for (c = 0; c < b; ++c) + (j = f[c]), + (a = j.charAt(0)), + j.length >= 2 && a === "[" + ? (f[c] = h(j)) + : a !== "\\" && + (f[c] = j.replace(/[A-Za-z]/g, function (a) { + a = a.charCodeAt(0); + return "[" + String.fromCharCode(a & -33, a | 32) + "]"; + })); + return f.join(""); + } + for (var t = 0, s = !1, l = !1, p = 0, d = a.length; p < d; ++p) { + var g = a[p]; + if (g.ignoreCase) l = !0; + else if ( + /[a-z]/i.test( + g.source.replace(/\\u[\da-f]{4}|\\x[\da-f]{2}|\\[^UXux]/gi, ""), + ) + ) { + s = !0; + l = !1; + break; + } + } + for ( + var r = { b: 8, t: 9, n: 10, v: 11, f: 12, r: 13 }, + n = [], + p = 0, + d = a.length; + p < d; + ++p + ) { + g = a[p]; + if (g.global || g.multiline) throw Error("" + g); + n.push("(?:" + y(g) + ")"); + } + return RegExp(n.join("|"), l ? "gi" : "g"); + } + function M(a) { + function m(a) { + switch (a.nodeType) { + case 1: + if (e.test(a.className)) break; + for (var g = a.firstChild; g; g = g.nextSibling) m(g); + g = a.nodeName; + if ("BR" === g || "LI" === g) + (h[s] = "\n"), (t[s << 1] = y++), (t[(s++ << 1) | 1] = a); + break; + case 3: + case 4: + (g = a.nodeValue), + g.length && + ((g = p + ? g.replace(/\r\n?/g, "\n") + : g.replace(/[\t\n\r ]+/g, " ")), + (h[s] = g), + (t[s << 1] = y), + (y += g.length), + (t[(s++ << 1) | 1] = a)); + } + } + var e = /(?:^|\s)nocode(?:\s|$)/, + h = [], + y = 0, + t = [], + s = 0, + l; + a.currentStyle + ? (l = a.currentStyle.whiteSpace) + : window.getComputedStyle && + (l = document.defaultView + .getComputedStyle(a, q) + .getPropertyValue("white-space")); + var p = l && "pre" === l.substring(0, 3); + m(a); + return { a: h.join("").replace(/\n$/, ""), c: t }; + } + function B(a, m, e, h) { + m && ((a = { a: m, d: a }), e(a), h.push.apply(h, a.e)); + } + function x(a, m) { + function e(a) { + for ( + var l = a.d, + p = [l, "pln"], + d = 0, + g = a.a.match(y) || [], + r = {}, + n = 0, + z = g.length; + n < z; + ++n + ) { + var f = g[n], + b = r[f], + o = void 0, + c; + if (typeof b === "string") c = !1; + else { + var i = h[f.charAt(0)]; + if (i) (o = f.match(i[1])), (b = i[0]); + else { + for (c = 0; c < t; ++c) + if (((i = m[c]), (o = f.match(i[1])))) { + b = i[0]; + break; + } + o || (b = "pln"); + } + if ( + (c = b.length >= 5 && "lang-" === b.substring(0, 5)) && + !(o && typeof o[1] === "string") + ) + (c = !1), (b = "src"); + c || (r[f] = b); + } + i = d; + d += f.length; + if (c) { + c = o[1]; + var j = f.indexOf(c), + k = j + c.length; + o[2] && ((k = f.length - o[2].length), (j = k - c.length)); + b = b.substring(5); + B(l + i, f.substring(0, j), e, p); + B(l + i + j, c, C(b, c), p); + B(l + i + k, f.substring(k), e, p); + } else p.push(l + i, b); + } + a.e = p; + } + var h = {}, + y; + (function () { + for ( + var e = a.concat(m), l = [], p = {}, d = 0, g = e.length; + d < g; + ++d + ) { + var r = e[d], + n = r[3]; + if (n) for (var k = n.length; --k >= 0; ) h[n.charAt(k)] = r; + r = r[1]; + n = "" + r; + p.hasOwnProperty(n) || (l.push(r), (p[n] = q)); + } + l.push(/[\S\s]/); + y = L(l); + })(); + var t = m.length; + return e; + } + function u(a) { + var m = [], + e = []; + a.tripleQuotedStrings + ? m.push([ + "str", + /^(?:'''(?:[^'\\]|\\[\S\s]|''?(?=[^']))*(?:'''|$)|"""(?:[^"\\]|\\[\S\s]|""?(?=[^"]))*(?:"""|$)|'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$))/, + q, + "'\"", + ]) + : a.multiLineStrings + ? m.push([ + "str", + /^(?:'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$)|`(?:[^\\`]|\\[\S\s])*(?:`|$))/, + q, + "'\"`", + ]) + : m.push([ + "str", + /^(?:'(?:[^\n\r'\\]|\\.)*(?:'|$)|"(?:[^\n\r"\\]|\\.)*(?:"|$))/, + q, + "\"'", + ]); + a.verbatimStrings && e.push(["str", /^@"(?:[^"]|"")*(?:"|$)/, q]); + var h = a.hashComments; + h && + (a.cStyleComments + ? (h > 1 + ? m.push(["com", /^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/, q, "#"]) + : m.push([ + "com", + /^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\n\r]*)/, + q, + "#", + ]), + e.push([ + "str", + /^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/, + q, + ])) + : m.push(["com", /^#[^\n\r]*/, q, "#"])); + a.cStyleComments && + (e.push(["com", /^\/\/[^\n\r]*/, q]), + e.push(["com", /^\/\*[\S\s]*?(?:\*\/|$)/, q])); + a.regexLiterals && + e.push([ + "lang-regex", + /^(?:^^\.?|[!+-]|!=|!==|#|%|%=|&|&&|&&=|&=|\(|\*|\*=|\+=|,|-=|->|\/|\/=|:|::|;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|[?@[^]|\^=|\^\^|\^\^=|{|\||\|=|\|\||\|\|=|~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\s*(\/(?=[^*/])(?:[^/[\\]|\\[\S\s]|\[(?:[^\\\]]|\\[\S\s])*(?:]|$))+\/)/, + ]); + (h = a.types) && e.push(["typ", h]); + a = ("" + a.keywords).replace(/^ | $/g, ""); + a.length && + e.push(["kwd", RegExp("^(?:" + a.replace(/[\s,]+/g, "|") + ")\\b"), q]); + m.push(["pln", /^\s+/, q, " \r\n\t\xa0"]); + e.push( + ["lit", /^@[$_a-z][\w$@]*/i, q], + ["typ", /^(?:[@_]?[A-Z]+[a-z][\w$@]*|\w+_t\b)/, q], + ["pln", /^[$_a-z][\w$@]*/i, q], + [ + "lit", + /^(?:0x[\da-f]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+-]?\d+)?)[a-z]*/i, + q, + "0123456789", + ], + ["pln", /^\\[\S\s]?/, q], + ["pun", /^.[^\s\w"-$'./@\\`]*/, q], + ); + return x(m, e); + } + function D(a, m) { + function e(a) { + switch (a.nodeType) { + case 1: + if (k.test(a.className)) break; + if ("BR" === a.nodeName) + h(a), a.parentNode && a.parentNode.removeChild(a); + else for (a = a.firstChild; a; a = a.nextSibling) e(a); + break; + case 3: + case 4: + if (p) { + var b = a.nodeValue, + d = b.match(t); + if (d) { + var c = b.substring(0, d.index); + a.nodeValue = c; + (b = b.substring(d.index + d[0].length)) && + a.parentNode.insertBefore(s.createTextNode(b), a.nextSibling); + h(a); + c || a.parentNode.removeChild(a); + } + } + } + } + function h(a) { + function b(a, d) { + var e = d ? a.cloneNode(!1) : a, + f = a.parentNode; + if (f) { + var f = b(f, 1), + g = a.nextSibling; + f.appendChild(e); + for (var h = g; h; h = g) (g = h.nextSibling), f.appendChild(h); + } + return e; + } + for (; !a.nextSibling; ) if (((a = a.parentNode), !a)) return; + for ( + var a = b(a.nextSibling, 0), e; + (e = a.parentNode) && e.nodeType === 1; + + ) + a = e; + d.push(a); + } + var k = /(?:^|\s)nocode(?:\s|$)/, + t = /\r\n?|\n/, + s = a.ownerDocument, + l; + a.currentStyle + ? (l = a.currentStyle.whiteSpace) + : window.getComputedStyle && + (l = s.defaultView + .getComputedStyle(a, q) + .getPropertyValue("white-space")); + var p = l && "pre" === l.substring(0, 3); + for (l = s.createElement("LI"); a.firstChild; ) l.appendChild(a.firstChild); + for (var d = [l], g = 0; g < d.length; ++g) e(d[g]); + m === (m | 0) && d[0].setAttribute("value", m); + var r = s.createElement("OL"); + r.className = "linenums"; + for (var n = Math.max(0, (m - 1) | 0) || 0, g = 0, z = d.length; g < z; ++g) + (l = d[g]), + (l.className = "L" + ((g + n) % 10)), + l.firstChild || l.appendChild(s.createTextNode("\xa0")), + r.appendChild(l); + a.appendChild(r); + } + function k(a, m) { + for (var e = m.length; --e >= 0; ) { + var h = m[e]; + A.hasOwnProperty(h) + ? window.console && + console.warn("cannot override language handler %s", h) + : (A[h] = a); + } + } + function C(a, m) { + if (!a || !A.hasOwnProperty(a)) + a = /^\s*= o && (h += 2); + e >= c && (a += 2); + } + } catch (w) { + "console" in window && console.log(w && w.stack ? w.stack : w); + } + } + var v = ["break,continue,do,else,for,if,return,while"], + w = [ + [ + v, + "auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile", + ], + "catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof", + ], + F = [ + w, + "alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where", + ], + G = [ + w, + "abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient", + ], + H = [ + G, + "as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var", + ], + w = [ + w, + "debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN", + ], + I = [ + v, + "and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None", + ], + J = [ + v, + "alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END", + ], + v = [v, "case,done,elif,esac,eval,fi,function,in,local,set,then,until"], + K = + /^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/, + N = /\S/, + O = u({ + keywords: [ + F, + H, + w, + "caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END" + + I, + J, + v, + ], + hashComments: !0, + cStyleComments: !0, + multiLineStrings: !0, + regexLiterals: !0, + }), + A = {}; + k(O, ["default-code"]); + k( + x( + [], + [ + ["pln", /^[^]*(?:>|$)/], + ["com", /^<\!--[\S\s]*?(?:--\>|$)/], + ["lang-", /^<\?([\S\s]+?)(?:\?>|$)/], + ["lang-", /^<%([\S\s]+?)(?:%>|$)/], + ["pun", /^(?:<[%?]|[%?]>)/], + ["lang-", /^]*>([\S\s]+?)<\/xmp\b[^>]*>/i], + ["lang-js", /^]*>([\S\s]*?)(<\/script\b[^>]*>)/i], + ["lang-css", /^]*>([\S\s]*?)(<\/style\b[^>]*>)/i], + ["lang-in.tag", /^(<\/?[a-z][^<>]*>)/i], + ], + ), + ["default-markup", "htm", "html", "mxml", "xhtml", "xml", "xsl"], + ); + k( + x( + [ + ["pln", /^\s+/, q, " \t\r\n"], + ["atv", /^(?:"[^"]*"?|'[^']*'?)/, q, "\"'"], + ], + [ + ["tag", /^^<\/?[a-z](?:[\w-.:]*\w)?|\/?>$/i], + ["atn", /^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i], + ["lang-uq.val", /^=\s*([^\s"'>]*(?:[^\s"'/>]|\/(?=\s)))/], + ["pun", /^[/<->]+/], + ["lang-js", /^on\w+\s*=\s*"([^"]+)"/i], + ["lang-js", /^on\w+\s*=\s*'([^']+)'/i], + ["lang-js", /^on\w+\s*=\s*([^\s"'>]+)/i], + ["lang-css", /^style\s*=\s*"([^"]+)"/i], + ["lang-css", /^style\s*=\s*'([^']+)'/i], + ["lang-css", /^style\s*=\s*([^\s"'>]+)/i], + ], + ), + ["in.tag"], + ); + k(x([], [["atv", /^[\S\s]+/]]), ["uq.val"]); + k(u({ keywords: F, hashComments: !0, cStyleComments: !0, types: K }), [ + "c", + "cc", + "cpp", + "cxx", + "cyc", + "m", + ]); + k(u({ keywords: "null,true,false" }), ["json"]); + k( + u({ + keywords: H, + hashComments: !0, + cStyleComments: !0, + verbatimStrings: !0, + types: K, + }), + ["cs"], + ); + k(u({ keywords: G, cStyleComments: !0 }), ["java"]); + k(u({ keywords: v, hashComments: !0, multiLineStrings: !0 }), [ + "bsh", + "csh", + "sh", + ]); + k( + u({ + keywords: I, + hashComments: !0, + multiLineStrings: !0, + tripleQuotedStrings: !0, + }), + ["cv", "py"], + ); + k( + u({ + keywords: + "caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END", + hashComments: !0, + multiLineStrings: !0, + regexLiterals: !0, + }), + ["perl", "pl", "pm"], + ); + k( + u({ + keywords: J, + hashComments: !0, + multiLineStrings: !0, + regexLiterals: !0, + }), + ["rb"], + ); + k(u({ keywords: w, cStyleComments: !0, regexLiterals: !0 }), ["js"]); + k( + u({ + keywords: + "all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes", + hashComments: 3, + cStyleComments: !0, + multilineStrings: !0, + tripleQuotedStrings: !0, + regexLiterals: !0, + }), + ["coffee"], + ); + k(x([], [["str", /^[\S\s]+/]]), ["regex"]); + window.prettyPrintOne = function (a, m, e) { + var h = document.createElement("PRE"); + h.innerHTML = a; + e && D(h, e); + E({ g: m, i: e, h: h }); + return h.innerHTML; + }; + window.prettyPrint = function (a) { + function m() { + for ( + var e = window.PR_SHOULD_USE_CONTINUATION ? l.now() + 250 : Infinity; + p < h.length && l.now() < e; + p++ + ) { + var n = h[p], + k = n.className; + if (k.indexOf("prettyprint") >= 0) { + var k = k.match(g), + f, + b; + if ((b = !k)) { + b = n; + for (var o = void 0, c = b.firstChild; c; c = c.nextSibling) + var i = c.nodeType, + o = + i === 1 + ? o + ? b + : c + : i === 3 + ? N.test(c.nodeValue) + ? b + : o + : o; + b = (f = o === b ? void 0 : o) && "CODE" === f.tagName; + } + b && (k = f.className.match(g)); + k && (k = k[1]); + b = !1; + for (o = n.parentNode; o; o = o.parentNode) + if ( + (o.tagName === "pre" || + o.tagName === "code" || + o.tagName === "xmp") && + o.className && + o.className.indexOf("prettyprint") >= 0 + ) { + b = !0; + break; + } + b || + ((b = (b = n.className.match(/\blinenums\b(?::(\d+))?/)) + ? b[1] && b[1].length + ? +b[1] + : !0 + : !1) && D(n, b), + (d = { g: k, h: n, i: b }), + E(d)); + } + } + p < h.length ? setTimeout(m, 250) : a && a(); + } + for ( + var e = [ + document.getElementsByTagName("pre"), + document.getElementsByTagName("code"), + document.getElementsByTagName("xmp"), + ], + h = [], + k = 0; + k < e.length; + ++k + ) + for (var t = 0, s = e[k].length; t < s; ++t) h.push(e[k][t]); + var e = q, + l = Date; + l.now || + (l = { + now: function () { + return +new Date(); + }, + }); + var p = 0, + d, + g = /\blang(?:uage)?-([\w.]+)(?!\S)/; + m(); + }; + window.PR = { + createSimpleLexer: x, + registerLangHandler: k, + sourceDecorator: u, + PR_ATTRIB_NAME: "atn", + PR_ATTRIB_VALUE: "atv", + PR_COMMENT: "com", + PR_DECLARATION: "dec", + PR_KEYWORD: "kwd", + PR_LITERAL: "lit", + PR_NOCODE: "nocode", + PR_PLAIN: "pln", + PR_PUNCTUATION: "pun", + PR_SOURCE: "src", + PR_STRING: "str", + PR_TAG: "tag", + PR_TYPE: "typ", + }; +})(); diff --git a/docs/styles/jsdoc-default.css b/docs/styles/jsdoc-default.css index c14e3b9..7432b3b 100644 --- a/docs/styles/jsdoc-default.css +++ b/docs/styles/jsdoc-default.css @@ -1,10 +1,11 @@ @import url(https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,400i,500i,500,600,600i|Roboto); * { - box-sizing: border-box + box-sizing: border-box; } -html, body { +html, +body { height: 100%; width: 100%; } @@ -14,7 +15,7 @@ body { background-color: white; margin: 0 auto; padding: 0; - font-family: 'Source Sans Pro', Helvetica, sans-serif; + font-family: "Source Sans Pro", Helvetica, sans-serif; font-size: 16px; line-height: 160%; } @@ -26,18 +27,31 @@ a:active { } a:hover { - text-decoration: underline + text-decoration: underline; } -p, ul, ol, blockquote { +p, +ul, +ol, +blockquote { margin-bottom: 1em; } -h1, h2, h3, h4, h5, h6 { - font-family: 'Roboto', sans-serif; +h1, +h2, +h3, +h4, +h5, +h6 { + font-family: "Roboto", sans-serif; } -h1, h2, h3, h4, h5, h6 { +h1, +h2, +h3, +h4, +h5, +h6 { color: #000; font-weight: 400; margin: 0; @@ -46,14 +60,21 @@ h1, h2, h3, h4, h5, h6 { h1 { font-weight: 300; font-size: 48px; - margin: 1em 0 .5em; + margin: 1em 0 0.5em; } -h1.page-title {margin-bottom: 10px;font-size: 34px;font-weight: 300;border-bottom: solid 2px #ddd;padding: .5em 0 .5em;margin-top: 0;} +h1.page-title { + margin-bottom: 10px; + font-size: 34px; + font-weight: 300; + border-bottom: solid 2px #ddd; + padding: 0.5em 0 0.5em; + margin-top: 0; +} h2 { font-size: 32px; - margin: 1.2em 0 .8em; + margin: 1.2em 0 0.8em; font-weight: bold; } @@ -62,7 +83,7 @@ h3 { /* margin-bottom: 16px; */ /* font-weight: bold; */ padding: 0; - margin: 1em 0 .6em; + margin: 1em 0 0.6em; font-size: 28px; /* border-bottom: 1px solid #eee; */ /* padding-bottom: 15px; */ @@ -70,13 +91,14 @@ h3 { h4 { font-size: 18px; - margin: 1em 0 .2em; + margin: 1em 0 0.2em; color: #4d4e53; /* border-bottom: 1px solid #eee; */ padding-bottom: 8px; } -h5, .container-overview .subsection-title { +h5, +.container-overview .subsection-title { font-size: 120%; /* letter-spacing: -0.01em; */ margin: 20px 0 5px; @@ -89,8 +111,11 @@ h6 { font-style: italic; } -tt, code, kbd, samp { - font-family: Consolas, Monaco, 'Andale Mono', monospace; +tt, +code, +kbd, +samp { + font-family: Consolas, Monaco, "Andale Mono", monospace; background: #f4f4f4; padding: 1px 5px; border-radius: 5px; @@ -113,7 +138,7 @@ blockquote { } .class-description:empty { - margin: 0 + margin: 0; } /** Container **/ @@ -125,7 +150,7 @@ blockquote { } header { - display: block + display: block; } section { @@ -135,7 +160,7 @@ section { } .variation { - display: none + display: none; } .signature-attributes { @@ -189,7 +214,8 @@ section { max-width: 100%; } -.readme ul, .readme ol { +.readme ul, +.readme ol { padding-left: 2em; } @@ -263,26 +289,25 @@ nav li { } .type-function { - background: #B3E5FC; - color: #0288D1; + background: #b3e5fc; + color: #0288d1; } .type-class { - background: #D1C4E9; - color: #4527A0; + background: #d1c4e9; + color: #4527a0; } .type-member { - background: #C8E6C9; - color: #388E3C; + background: #c8e6c9; + color: #388e3c; } .type-module { - background: #E1BEE7; - color: #7B1FA2; + background: #e1bee7; + color: #7b1fa2; } - /** Footer **/ footer { color: hsl(0, 0%, 28%); @@ -295,69 +320,70 @@ footer { } .ancestors { - color: #999 + color: #999; } .ancestors a { - color: #999 !important; - text-decoration: none; + color: #999 !important; + text-decoration: none; } .clear { - clear: both + clear: both; } .important { font-weight: bold; - color: #950B02; + color: #950b02; } .yes-def { - text-indent: -1000px + text-indent: -1000px; } .type-signature { - color: #aaa + color: #aaa; } -.name, .signature { - font-family: Consolas, Monaco, 'Andale Mono', monospace +.name, +.signature { + font-family: Consolas, Monaco, "Andale Mono", monospace; } .details { margin-top: 14px; - border-left: 2px solid #DDD; + border-left: 2px solid #ddd; line-height: 30px; } .details dt { - width: 120px; - float: left; - padding-left: 10px; + width: 120px; + float: left; + padding-left: 10px; } .details dd { - margin-left: 70px + margin-left: 70px; } .details ul { - margin: 0 + margin: 0; } .details ul { - list-style-type: none + list-style-type: none; } .details li { - margin-left: 30px + margin-left: 30px; } .details pre.prettyprint { - margin: 0 + margin: 0; } .details .object-value { - padding-top: 0 + padding-top: 0; } .description { @@ -380,7 +406,7 @@ footer { } .prettyprint.source { - width: inherit + width: inherit; } .prettyprint code { @@ -388,27 +414,27 @@ footer { line-height: 18px; display: block; background-color: #fff; - color: #4D4E53; + color: #4d4e53; } .prettyprint code:empty:before { - content: ''; + content: ""; } .prettyprint > code { - padding: 15px + padding: 15px; } .prettyprint .linenums code { - padding: 0 15px + padding: 0 15px; } .prettyprint .linenums li:first-of-type code { - padding-top: 15px + padding-top: 15px; } .prettyprint code span.line { - display: inline-block + display: inline-block; } .prettyprint.linenums { @@ -420,71 +446,82 @@ footer { } .prettyprint.linenums ol { - padding-left: 0 + padding-left: 0; } .prettyprint.linenums li { - border-left: 3px #ddd solid + border-left: 3px #ddd solid; } -.prettyprint.linenums li.selected, .prettyprint.linenums li.selected * { - background-color: lightyellow +.prettyprint.linenums li.selected, +.prettyprint.linenums li.selected * { + background-color: lightyellow; } .prettyprint.linenums li * { - -webkit-user-select: text; - -moz-user-select: text; - -ms-user-select: text; - user-select: text; + -webkit-user-select: text; + -moz-user-select: text; + -ms-user-select: text; + user-select: text; } -.params, .props { +.params, +.props { border-spacing: 0; border: 1px solid #ddd; border-collapse: collapse; border-radius: 3px; - box-shadow: 0 1px 3px rgba(0,0,0,0.1); + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); width: 100%; font-size: 14px; /* margin-left: 15px; */ } -.params .name, .props .name, .name code { - color: #4D4E53; - font-family: Consolas, Monaco, 'Andale Mono', monospace; - font-size: 100%; +.params .name, +.props .name, +.name code { + color: #4d4e53; + font-family: Consolas, Monaco, "Andale Mono", monospace; + font-size: 100%; } -.params td, .params th, .props td, .props th { - margin: 0px; - text-align: left; - vertical-align: top; - padding: 10px; - display: table-cell; +.params td, +.params th, +.props td, +.props th { + margin: 0px; + text-align: left; + vertical-align: top; + padding: 10px; + display: table-cell; } .params td { - border-top: 1px solid #eee + border-top: 1px solid #eee; } -.params thead tr, .props thead tr { - background-color: #fff; - font-weight: bold; +.params thead tr, +.props thead tr { + background-color: #fff; + font-weight: bold; } -.params .params thead tr, .props .props thead tr { - background-color: #fff; - font-weight: bold; +.params .params thead tr, +.props .props thead tr { + background-color: #fff; + font-weight: bold; } -.params td.description > p:first-child, .props td.description > p:first-child { - margin-top: 0; - padding-top: 0; +.params td.description > p:first-child, +.props td.description > p:first-child { + margin-top: 0; + padding-top: 0; } -.params td.description > p:last-child, .props td.description > p:last-child { - margin-bottom: 0; - padding-bottom: 0; +.params td.description > p:last-child, +.props td.description > p:last-child { + margin-bottom: 0; + padding-bottom: 0; } dl.param-type { @@ -494,12 +531,13 @@ dl.param-type { font-size: 16px; } -.param-type dt, .param-type dd { - display: inline-block +.param-type dt, +.param-type dd { + display: inline-block; } .param-type dd { - font-family: Consolas, Monaco, 'Andale Mono', monospace; + font-family: Consolas, Monaco, "Andale Mono", monospace; display: inline-block; padding: 0; margin: 0; @@ -507,7 +545,7 @@ dl.param-type { } .disabled { - color: #454545 + color: #454545; } /* navicon button */ @@ -518,38 +556,41 @@ dl.param-type { transition: 0.25s; cursor: pointer; user-select: none; - opacity: .8; + opacity: 0.8; } -.navicon-button .navicon:before, .navicon-button .navicon:after { +.navicon-button .navicon:before, +.navicon-button .navicon:after { transition: 0.25s; } .navicon-button:hover { transition: 0.5s; opacity: 1; } -.navicon-button:hover .navicon:before, .navicon-button:hover .navicon:after { +.navicon-button:hover .navicon:before, +.navicon-button:hover .navicon:after { transition: 0.25s; } .navicon-button:hover .navicon:before { - top: .825rem; + top: 0.825rem; } .navicon-button:hover .navicon:after { - top: -.825rem; + top: -0.825rem; } /* navicon */ .navicon { position: relative; width: 2.5em; - height: .3125rem; + height: 0.3125rem; background: #000; transition: 0.3s; border-radius: 2.5rem; } -.navicon:before, .navicon:after { +.navicon:before, +.navicon:after { display: block; content: ""; - height: .3125rem; + height: 0.3125rem; width: 2.5rem; background: #000; position: absolute; @@ -558,10 +599,10 @@ dl.param-type { border-radius: 1rem; } .navicon:before { - top: .625rem; + top: 0.625rem; } .navicon:after { - top: -.625rem; + top: -0.625rem; } /* open */ @@ -589,13 +630,13 @@ dl.param-type { .nav-trigger:checked + label.plus .navicon:before, .nav-trigger:checked + label.x .navicon:before { transform: rotate(-45deg); - background: #FFF; + background: #fff; } .nav-trigger:checked + label.plus .navicon:after, .nav-trigger:checked + label.x .navicon:after { transform: rotate(45deg); - background: #FFF; + background: #fff; } .nav-trigger:checked + label.plus { @@ -641,7 +682,7 @@ dl.param-type { } nav { - background: #FFF; + background: #fff; width: 250px; height: 100%; position: fixed; diff --git a/docs/styles/prettify-tomorrow.css b/docs/styles/prettify-tomorrow.css index 81e74d1..921a383 100644 --- a/docs/styles/prettify-tomorrow.css +++ b/docs/styles/prettify-tomorrow.css @@ -4,96 +4,124 @@ /* SPAN elements with the classes below are added by prettyprint. */ /* plain text */ .pln { - color: #4d4d4c; } + color: #4d4d4c; +} @media screen { /* string content */ .str { - color: hsl(104, 100%, 24%); } + color: hsl(104, 100%, 24%); + } /* a keyword */ .kwd { - color: hsl(240, 100%, 50%); } + color: hsl(240, 100%, 50%); + } /* a comment */ .com { - color: hsl(0, 0%, 60%); } + color: hsl(0, 0%, 60%); + } /* a type name */ .typ { - color: hsl(240, 100%, 32%); } + color: hsl(240, 100%, 32%); + } /* a literal value */ .lit { - color: hsl(240, 100%, 40%); } + color: hsl(240, 100%, 40%); + } /* punctuation */ .pun { - color: #000000; } + color: #000000; + } /* lisp open bracket */ .opn { - color: #000000; } + color: #000000; + } /* lisp close bracket */ .clo { - color: #000000; } + color: #000000; + } /* a markup tag name */ .tag { - color: #c82829; } + color: #c82829; + } /* a markup attribute name */ .atn { - color: #f5871f; } + color: #f5871f; + } /* a markup attribute value */ .atv { - color: #3e999f; } + color: #3e999f; + } /* a declaration */ .dec { - color: #f5871f; } + color: #f5871f; + } /* a variable name */ .var { - color: #c82829; } + color: #c82829; + } /* a function name */ .fun { - color: #4271ae; } } + color: #4271ae; + } +} /* Use higher contrast and text-weight for printable form. */ @media print, projection { .str { - color: #060; } + color: #060; + } .kwd { color: #006; - font-weight: bold; } + font-weight: bold; + } .com { color: #600; - font-style: italic; } + font-style: italic; + } .typ { color: #404; - font-weight: bold; } + font-weight: bold; + } .lit { - color: #044; } + color: #044; + } - .pun, .opn, .clo { - color: #440; } + .pun, + .opn, + .clo { + color: #440; + } .tag { color: #006; - font-weight: bold; } + font-weight: bold; + } .atn { - color: #404; } + color: #404; + } .atv { - color: #060; } } + color: #060; + } +} /* Style */ /* pre.prettyprint { @@ -108,7 +136,8 @@ pre.prettyprint { /* Specify class=linenums on a pre to get line numbering */ ol.linenums { margin-top: 0; - margin-bottom: 0; } + margin-bottom: 0; +} /* IE indents via margin-left */ li.L0, @@ -121,7 +150,8 @@ li.L6, li.L7, li.L8, li.L9 { - /* */ } + /* */ +} /* Alternate shading for lines */ li.L1, @@ -129,4 +159,5 @@ li.L3, li.L5, li.L7, li.L9 { - /* */ } + /* */ +} diff --git a/docs/tax-Remittance.js.html b/docs/tax-Remittance.js.html index b492455..0cbadba 100644 --- a/docs/tax-Remittance.js.html +++ b/docs/tax-Remittance.js.html @@ -1,8 +1,8 @@ - + - - - + + + tax-Remittance.js - Documentation @@ -10,36 +10,111 @@ - - - - - - - - - - - - - -
- -

tax-Remittance.js

- - - - - - - -
+ + + + + + + + + + + + +
+

tax-Remittance.js

+ +
-
import axios from "axios";
+          
import axios from "axios";
 import {
   generateOAuthToken,
   encryptSecurityCredential,
@@ -67,7 +142,7 @@ 

tax-Remittance.js

* @param {number} options.partyB The account to which money will be credited. * @param {number} options.accountReference The payment registration number (PRN) issued by KRA * @param {string} options.QueueTimeOutURL URL to notify in case of a request timeout before processing. - * @param {string} options.resultUrl URL to send transaction results after processing. + * @param {string} options.resultURL URL to send transaction results after processing. * @param {boolean} [options.proErrorLogging] Logs out advanced error details - good for debugging * @returns {Promise<Object>} remittanceResponse and (optional) conditionalCallbackData. */ @@ -79,7 +154,7 @@

tax-Remittance.js

accountReference, QueueTimeOutURL, remarks, - resultUrl, + resultURL, proErrorLogging = false, }) { if (!callbackHandlerInitialized) { @@ -88,7 +163,7 @@

tax-Remittance.js

); } try { - validateUrl(resultUrl, "resultUrl"); + validateUrl(resultURL, "resultURL"); validateUrl(QueueTimeOutURL, "timeoutUrl"); const { accessToken, baseURL } = await generateOAuthToken(); const req = axios.create({ @@ -117,7 +192,7 @@

tax-Remittance.js

AccountReference: accountReference, Remarks: remarks, QueueTimeOutURL: QueueTimeOutURL, - ResultURL: resultUrl, + ResultURL: resultURL, }); conditionalCallbackData = await callbackTrigger(callbackHandlerInitialized); return { remittanceResponse: responseBody.data, conditionalCallbackData }; @@ -139,7 +214,7 @@

tax-Remittance.js

partyB, accountReference, QueueTimeOutURL, - resultUrl, + resultURL, remarks }, }, @@ -158,20 +233,20 @@

tax-Remittance.js

export { taxRemittance, handleTaxRemittanceCallbacks };
-
- - - - -
- -
- -
- Generated by JSDoc 4.0.4 on Thu Feb 27 2025 21:37:15 GMT+0300 (East Africa Time) using the Minami theme. -
- - - - +
+
+ +
+ +
+ Generated by JSDoc 4.0.4 on + Fri Feb 28 2025 10:29:32 GMT+0300 (East Africa Time) using the Minami + theme. +
+ + + + diff --git a/docs/transaction-Status.js.html b/docs/transaction-Status.js.html index eeca164..ba1a03b 100644 --- a/docs/transaction-Status.js.html +++ b/docs/transaction-Status.js.html @@ -1,8 +1,8 @@ - + - - - + + + transaction-Status.js - Documentation @@ -10,36 +10,111 @@ - - - - - + + + + + + + - - + - + - +
+

transaction-Status.js

-
- -

transaction-Status.js

- - - - - - - -
+
-
import axios from "axios";
+          
import axios from "axios";
 import {
   callbackTrigger,
   encryptSecurityCredential,
@@ -67,8 +142,8 @@ 

transaction-Status.js

* @param {string} options.remarks Information to be associated with the transaction. * @param {string} options.occasion Information to be associated with the transaction. * @param {string} options.OriginatorConversationID This is a globally unique identifier for the transaction request returned by the API proxy upon successful submission. - * @param {string} options.QueueTimeOutUrl URL for storing information about timeout transactions. - * @param {string} options.resultUrl The path that stores information of a transaction. + * @param {string} options.QueueTimeOutURL URL for storing information about timeout transactions. + * @param {string} options.resultURL The path that stores information of a transaction. * @param {boolean} [options.proErrorLogging] Logs out advanced error details - good for debugging * @returns {Promise<Object>} transactionStatusResponse and (optional) conditionalCallbackData. */ @@ -76,11 +151,11 @@

transaction-Status.js

transactionId, partyA, identifierType, - QueueTimeOutUrl, + QueueTimeOutURL, OriginatorConversationID, remarks, occasion, - resultUrl, + resultURL, initiator, proErrorLogging = false, }) { @@ -119,8 +194,8 @@

transaction-Status.js

PartyA: partyA, OriginatorConversationID, IdentifierType: identifierType, - ResultURL: resultUrl, - QueueTimeOutURL: QueueTimeOutUrl, + ResultURL: resultURL, + QueueTimeOutURL: QueueTimeOutURL, Remarks: remarks, Occasion: occasion, }); @@ -141,8 +216,8 @@

transaction-Status.js

transactionId, partyA, identifierType, - QueueTimeOutUrl, - resultUrl, + QueueTimeOutURL, + resultURL, initiator, remarks, occasion @@ -163,20 +238,20 @@

transaction-Status.js

export { transactionStatus, handleTransactStatusCallbacks };
-
- - - - -
+ +
-
+
-
- Generated by JSDoc 4.0.4 on Thu Feb 27 2025 21:37:15 GMT+0300 (East Africa Time) using the Minami theme. -
+
+ Generated by JSDoc 4.0.4 on + Fri Feb 28 2025 10:29:32 GMT+0300 (East Africa Time) using the Minami + theme. +
- - - + + + diff --git a/jsdoc.json b/jsdoc.json index 911f7e4..9ddfd1e 100644 --- a/jsdoc.json +++ b/jsdoc.json @@ -8,15 +8,13 @@ "allowUnknownTags": true, "dictionaries": ["jsdoc"] }, - "plugins": [ - "plugins/markdown" - ], + "plugins": ["plugins/markdown"], "opts": { - "destination": "./docs/", - "encoding": "utf8", - "private": true, - "recurse": true, - "template": "./node_modules/minami" + "destination": "./docs/", + "encoding": "utf8", + "private": true, + "recurse": true, + "template": "./node_modules/minami" }, "templates": { "cleverLinks": false, diff --git a/lib/core/JSDOC.md b/lib/core/JSDOC.md index 593331e..d7a03bd 100644 --- a/lib/core/JSDOC.md +++ b/lib/core/JSDOC.md @@ -1,4 +1,5 @@ # MPESA-NODE LIBRARY + ### JSDocs guide ## About diff --git a/lib/core/apis/b2c-Request.js b/lib/core/apis/b2c-Request.js index ed3dede..1592e1d 100644 --- a/lib/core/apis/b2c-Request.js +++ b/lib/core/apis/b2c-Request.js @@ -7,8 +7,9 @@ import { handleCallbacks, logErrorDetails, originatorID, - throwErrorMessages, validateFormatPhone, - validateUrl + throwErrorMessages, + validateFormatPhone, + validateUrl, } from "../utils/helpers.js"; // Globals @@ -58,7 +59,7 @@ async function b2cRequest({ `Invalid commandId provided. Must be one of: ${validCommandIds.join(", ")}`, ); } - const _msisdn = validateFormatPhone(partyB) + const _msisdn = validateFormatPhone(partyB); try { validateUrl(resultURL, "resultURL"); validateUrl(QueueTimeOutURL, "QueueTimeOutURL"); @@ -84,12 +85,12 @@ async function b2cRequest({ Remarks: remarks, QueueTimeOutURL: QueueTimeOutURL, ResultURL: resultURL, - Occasion: occasion + Occasion: occasion, }); conditionalCallbackData = await callbackTrigger(callbackHandlerInitialized); - return { b2cRequestResponse: responseBody.data, conditionalCallbackData}; + return { b2cRequestResponse: responseBody.data, conditionalCallbackData }; } catch (error) { if (proErrorLogging) { console.info( @@ -110,7 +111,7 @@ async function b2cRequest({ commandId, initiatorName, occasion, - remarks + remarks, }, }, "B2C transaction error details:", diff --git a/lib/core/apis/b2c-Topup.js b/lib/core/apis/b2c-Topup.js index 000e03f..80f931c 100644 --- a/lib/core/apis/b2c-Topup.js +++ b/lib/core/apis/b2c-Topup.js @@ -105,7 +105,7 @@ async function b2cTopUp({ requester, QueueTimeOutURL, resultURL, - remarks + remarks, }, }, "B2C account top-up error details:", diff --git a/lib/core/apis/balance-Query.js b/lib/core/apis/balance-Query.js index d11ec6a..48df31b 100644 --- a/lib/core/apis/balance-Query.js +++ b/lib/core/apis/balance-Query.js @@ -40,7 +40,7 @@ async function balanceQuery({ }) { if (!callbackHandlerInitialized) { console.info( - "\x1b[42m\x1b[30m\x1b[1m The default balance query callback handler ('handleBalanceQueryCallbacks') was not called. Ignore if handling manually.\x1b[0m" + "\x1b[42m\x1b[30m\x1b[1m The default balance query callback handler ('handleBalanceQueryCallbacks') was not called. Ignore if handling manually.\x1b[0m", ); } /** @@ -96,7 +96,14 @@ async function balanceQuery({ { apiEndpoint: "/mpesa/accountbalance/v1/query", method: "POST", - payload: { partyA, identifierType, QueueTimeOutURL, resultURL, initiator, remarks }, + payload: { + partyA, + identifierType, + QueueTimeOutURL, + resultURL, + initiator, + remarks, + }, }, "Balance query error details:", ); diff --git a/lib/core/apis/business-Paybill.js b/lib/core/apis/business-Paybill.js index e63e940..2ad097a 100644 --- a/lib/core/apis/business-Paybill.js +++ b/lib/core/apis/business-Paybill.js @@ -107,7 +107,7 @@ async function businessPaybill({ requester, QueueTimeOutURL, resultURL, - remarks + remarks, }, }, "Business Pay Bill transaction error details:", diff --git a/lib/core/apis/c2b-Simulate.js b/lib/core/apis/c2b-Simulate.js index 8fdf1dd..1786b3f 100644 --- a/lib/core/apis/c2b-Simulate.js +++ b/lib/core/apis/c2b-Simulate.js @@ -1,11 +1,11 @@ import { - generateOAuthToken, logErrorDetails, + generateOAuthToken, + logErrorDetails, throwErrorMessages, - validateFormatPhone + validateFormatPhone, } from "../utils/helpers.js"; import axios from "axios"; - /** * @name c2bSimulate * @description This function simulates a C2B (Customer to Business) transaction by initiating a payment from an MSISDN to a business shortcode. diff --git a/lib/core/apis/mpesa-Simulate.js b/lib/core/apis/mpesa-Simulate.js index 733811d..a22d72b 100644 --- a/lib/core/apis/mpesa-Simulate.js +++ b/lib/core/apis/mpesa-Simulate.js @@ -7,7 +7,7 @@ import { logErrorDetails, throwErrorMessages, validateFormatPhone, - validateUrl + validateUrl, } from "../utils/helpers.js"; // Globals @@ -32,19 +32,19 @@ let conditionalCallbackData = {}; * @returns {Promise} mpesaSimulateResponse and (optional) conditionalCallbackData. */ async function mpesaSimulate({ - partyA, - phoneNumber, - amount, - callbackURL, - accountRef, - partyB, - transactionType, + partyA, + phoneNumber, + amount, + callbackURL, + accountRef, + partyB, + transactionType, transactionDesc, - proErrorLogging = false - }) { + proErrorLogging = false, +}) { if (!callbackHandlerInitialized) { console.info( - "\x1b[42m\x1b[30m\x1b[1m The default callback handler ('handleMpesaSimulateCallbacks') was not called. Ignore if handling manually.\x1b[0m" + "\x1b[42m\x1b[30m\x1b[1m The default callback handler ('handleMpesaSimulateCallbacks') was not called. Ignore if handling manually.\x1b[0m", ); } try { @@ -55,9 +55,8 @@ async function mpesaSimulate({ baseURL, headers: { Authorization: `Bearer ${accessToken}`, - "Content-Type": "application/json" - } - + "Content-Type": "application/json", + }, }); const responseBody = await req.post("/mpesa/stkpush/v1/processrequest", { BusinessShortCode: partyB, @@ -70,18 +69,21 @@ async function mpesaSimulate({ CallBackURL: callbackURL, AccountReference: accountRef, TransactionDesc: transactionDesc, - TransactionType: transactionType + TransactionType: transactionType, }); - conditionalCallbackData = await callbackTrigger(callbackHandlerInitialized, true); + conditionalCallbackData = await callbackTrigger( + callbackHandlerInitialized, + true, + ); return { mpesaSimulateResponse: responseBody.data, - conditionalCallbackData + conditionalCallbackData, }; } catch (error) { if (proErrorLogging) { console.info( "\x1b[35m%s\x1b[0m", - "Advanced error logging for mpesaSimulate has been initialized" + "Advanced error logging for mpesaSimulate has been initialized", ); logErrorDetails( error, @@ -95,10 +97,10 @@ async function mpesaSimulate({ callbackURL, accountRef, partyB, - transactionDesc - } + transactionDesc, + }, }, - "Mpesa Simulate transaction error details:" + "Mpesa Simulate transaction error details:", ); } throw throwErrorMessages(error); diff --git a/lib/core/apis/reversals.js b/lib/core/apis/reversals.js index 6170b88..17591f7 100644 --- a/lib/core/apis/reversals.js +++ b/lib/core/apis/reversals.js @@ -42,7 +42,8 @@ async function reversals({ proErrorLogging = false, }) { if (!callbackHandlerInitialized) { - console.info("\x1b[42m\x1b[30m\x1b[1m The default callback handler ('handleReversalCallbacks') was not called. Ignore if handling manually.\x1b[0m", + console.info( + "\x1b[42m\x1b[30m\x1b[1m The default callback handler ('handleReversalCallbacks') was not called. Ignore if handling manually.\x1b[0m", ); } try { @@ -97,7 +98,7 @@ async function reversals({ receiverParty, initiator, remarks, - occasion + occasion, }, }, "Transaction reversal error details:", diff --git a/lib/core/apis/tax-Remittance.js b/lib/core/apis/tax-Remittance.js index c8488fd..7981309 100644 --- a/lib/core/apis/tax-Remittance.js +++ b/lib/core/apis/tax-Remittance.js @@ -99,7 +99,7 @@ async function taxRemittance({ accountReference, QueueTimeOutURL, resultURL, - remarks + remarks, }, }, "Tax remittance error details:", diff --git a/lib/core/apis/transaction-Status.js b/lib/core/apis/transaction-Status.js index d4acc69..a412efa 100644 --- a/lib/core/apis/transaction-Status.js +++ b/lib/core/apis/transaction-Status.js @@ -84,7 +84,10 @@ async function transactionStatus({ Occasion: occasion, }); conditionalCallbackData = await callbackTrigger(callbackHandlerInitialized); - return { transactStatusResponse: responseBody.data, conditionalCallbackData }; + return { + transactStatusResponse: responseBody.data, + conditionalCallbackData, + }; } catch (error) { if (proErrorLogging) { console.info( @@ -104,7 +107,7 @@ async function transactionStatus({ resultURL, initiator, remarks, - occasion + occasion, }, }, "Transaction status error details:", diff --git a/lib/core/utils/configs.js b/lib/core/utils/configs.js index e187de8..41b42a9 100644 --- a/lib/core/utils/configs.js +++ b/lib/core/utils/configs.js @@ -65,16 +65,16 @@ const validateGlobalConfigs = () => { "passKey", "certPath", "environment", - "baseUrl" + "baseUrl", ]; const missingConfigs = requiredConfigs.filter( - (config) => !globalConfigs[config] + (config) => !globalConfigs[config], ); if (missingConfigs.length > 0) { throw new Error( - `Missing required environment variables: ${missingConfigs.join(", ")}` + `Missing required environment variables: ${missingConfigs.join(", ")}`, ); } }; diff --git a/lib/core/utils/helpers.js b/lib/core/utils/helpers.js index aed463b..b776625 100644 --- a/lib/core/utils/helpers.js +++ b/lib/core/utils/helpers.js @@ -24,7 +24,7 @@ export function originatorID() { export async function generateOAuthToken() { const { consumerKey, consumerSecret, baseUrl } = globalConfigs; const auth = Buffer.from(`${consumerKey}:${consumerSecret}`).toString( - "base64" + "base64", ); try { const response = await axios.get( @@ -32,9 +32,9 @@ export async function generateOAuthToken() { { headers: { Authorization: `Basic ${auth}`, - "Content-Type": "application/json" - } - } + "Content-Type": "application/json", + }, + }, ); return { accessToken: response.data.access_token, baseURL: baseUrl }; } catch (error) { @@ -55,7 +55,7 @@ export function generateMpesaCredentials(shortCode) { .replace(/[^0-9]/g, "") .slice(0, -3); const password = Buffer.from(`${shortCode}${passKey}${timeStamp}`).toString( - "base64" + "base64", ); return { password, timeStamp }; } @@ -75,16 +75,16 @@ export function encryptSecurityCredential() { const encrypted = crypto.publicEncrypt( { key: privateKey, - padding: crypto.constants.RSA_PKCS1_PADDING + padding: crypto.constants.RSA_PKCS1_PADDING, }, - bufferToEncrypt + bufferToEncrypt, ); return encrypted.toString("base64"); } catch (error) { console.error("Failed to encrypt security credential:", error.message); throw new Error( - "Encryption of security credential failed. Please check the certificate path and credential." + "Encryption of security credential failed. Please check the certificate path and credential.", ); } } @@ -127,7 +127,7 @@ export function throwErrorMessages(error) { console.error("M-Pesa API Error:", { status: error.response.status, data: error.response.data, - headers: error.response.headers + headers: error.response.headers, }); // Handle specific error responses from M-Pesa API @@ -135,7 +135,7 @@ export function throwErrorMessages(error) { throw new Error("Invalid request: Please check the parameters provided."); } else if (error.response.status === 401) { throw new Error( - "Unauthorized: Invalid access token. Please verify your credentials." + "Unauthorized: Invalid access token. Please verify your credentials.", ); } else if (error.response.status >= 500) { throw new Error("M-Pesa server error: Please try again later."); @@ -143,7 +143,7 @@ export function throwErrorMessages(error) { } else if (error.request) { console.error("Network Error:", error.message); throw new Error( - "Network error: Unable to reach M-Pesa API. Please check your connection." + "Network error: Unable to reach M-Pesa API. Please check your connection.", ); } else { console.error("Unexpected Error:", error.message); @@ -165,10 +165,10 @@ export function logErrorDetails(error, context, apiTypeMsg) { ...(error.response && { status: error.response.status, data: error.response.data, - headers: error.response.headers + headers: error.response.headers, }), ...(error.request && { request: error.request }), - context + context, }); } @@ -198,7 +198,7 @@ export function validateFormatPhone(number) { let phoneStr = String(number).trim().replace(/\D/g, ""); if (phoneStr.length !== 10 || !phoneStr.startsWith("0")) { throw new Error( - `Invalid phone number: must be exactly 10 digits and start with '0'}` + `Invalid phone number: must be exactly 10 digits and start with '0'}`, ); } return Number(`254${phoneStr.substring(1)}`); @@ -236,11 +236,10 @@ function flattenObject(obj, prefix = "") { * @param {string} endpoint - Api endpoint name */ export async function handleCallbacks(appInstance, endpoint) { - try { if (!appInstance || typeof appInstance.post !== "function") { throw new Error( - "handleBalanceQueryCallbacks requires a valid Express app instance." + "handleBalanceQueryCallbacks requires a valid Express app instance.", ); } @@ -299,9 +298,8 @@ export async function handleCallbacks(appInstance, endpoint) { }); console.info( "\x1b[32m\x1b[1m%s\x1b[0m", - `Callback handler for ${endpoint} has been initialized.` + `Callback handler for ${endpoint} has been initialized.`, ); - } catch (error) { throw new Error(`Callback Error: ${error}`); } @@ -311,7 +309,10 @@ export async function callbackTrigger(state, specialCase = false) { if (state) { return await new Promise((resolve) => { const timeout = setTimeout(() => { - console.warn("\x1b[43m\x1b[1m\x1b[30m%s\x1b[0m", "No callback received within the time limit."); + console.warn( + "\x1b[43m\x1b[1m\x1b[30m%s\x1b[0m", + "No callback received within the time limit.", + ); resolve({ type: "timeout", data: {} }); }, 15000); @@ -334,5 +335,3 @@ export async function callbackTrigger(state, specialCase = false) { }); } } - - diff --git a/lib/tests/apis/dummy.spec.js b/lib/tests/apis/dummy.spec.js index 36c93d7..a944b65 100644 --- a/lib/tests/apis/dummy.spec.js +++ b/lib/tests/apis/dummy.spec.js @@ -25,11 +25,15 @@ describe("B2C Payment API - Mocked Test", function () { }); it("Should return a mocked B2C payment response", function (done) { - mpesa.b2cRequest(createOptionsForB2c("https://mock-callback.url")) + mpesa + .b2cRequest(createOptionsForB2c("https://mock-callback.url")) .then((responseBody) => { expect(responseBody).to.be.an("object"); expect(responseBody.b2cRequestResponse.ResponseCode).to.equal("0"); - console.log("MOCKED RESPONSE BODY:", JSON.stringify(responseBody, null, 2)); + console.log( + "MOCKED RESPONSE BODY:", + JSON.stringify(responseBody, null, 2), + ); done(); }) .catch(done); diff --git a/lib/tests/apis/main/b2c-Request.spec.js b/lib/tests/apis/main/b2c-Request.spec.js index c47c2e0..e67f567 100644 --- a/lib/tests/apis/main/b2c-Request.spec.js +++ b/lib/tests/apis/main/b2c-Request.spec.js @@ -25,12 +25,19 @@ describe("B2C Payment API with OAuth", function () { b2cRequest(createOptionsForB2c(urlToUse, "0741585228")), ]) .then(([responseBody1, responseBody2, responseBody3]) => { - [responseBody1, responseBody2, responseBody3].forEach((responseBody, index) => { - expect(responseBody).to.be.an("object"); - expect(responseBody?.b2cRequestResponse.ResponseCode).to.be.equal("0"); - console.log("\x1b[35m\x1b[4m\x1b[1m%s\x1b[0m", `Test ${index + 1} passed successfully`); - // console.log(`RESPONSE BODY ${index + 1}:`, JSON.stringify(responseBody, null, 2)); - }); + [responseBody1, responseBody2, responseBody3].forEach( + (responseBody, index) => { + expect(responseBody).to.be.an("object"); + expect(responseBody?.b2cRequestResponse.ResponseCode).to.be.equal( + "0", + ); + console.log( + "\x1b[35m\x1b[4m\x1b[1m%s\x1b[0m", + `Test ${index + 1} passed successfully`, + ); + // console.log(`RESPONSE BODY ${index + 1}:`, JSON.stringify(responseBody, null, 2)); + }, + ); done(); }) .catch(done); diff --git a/lib/tests/apis/main/b2c-Topup.spec.js b/lib/tests/apis/main/b2c-Topup.spec.js index 98d81ca..cf252dd 100644 --- a/lib/tests/apis/main/b2c-Topup.spec.js +++ b/lib/tests/apis/main/b2c-Topup.spec.js @@ -17,13 +17,21 @@ describe("B2B Account Top-Up API with OAuth", function () { }); it("Should simulate a B2C account top-up and receive result or timeout callback", function (done) { - b2cTopUp(createOptionsForB2cAccTopUp(NGROK_URL === "" ? "https://mock.url" : NGROK_URL)) + b2cTopUp( + createOptionsForB2cAccTopUp( + NGROK_URL === "" ? "https://mock.url" : NGROK_URL, + ), + ) .then((responseBody) => { expect(responseBody).to.be.an("object"); expect(responseBody?.b2cTopUpResponse.ResponseCode).to.be.equal("0"); - console.log("\x1b[35m\x1b[4m\x1b[1m%s\x1b[0m", `Test passed successfully`); + console.log( + "\x1b[35m\x1b[4m\x1b[1m%s\x1b[0m", + `Test passed successfully`, + ); // console.log("RESPONSE BODY:", JSON.stringify(responseBody, null, 2)); - done() - }).catch(done); + done(); + }) + .catch(done); }); }); diff --git a/lib/tests/apis/main/balance-Query.spec.js b/lib/tests/apis/main/balance-Query.spec.js index 5e03752..833a1e9 100644 --- a/lib/tests/apis/main/balance-Query.spec.js +++ b/lib/tests/apis/main/balance-Query.spec.js @@ -17,11 +17,20 @@ describe("Account Balance API with OAuth", function () { }); it("Should fetch account balance and receive result or timeout callback", function (done) { - balanceQuery(createOptionsForBalance(NGROK_URL === "" ? "https://mock.url" : NGROK_URL)) + balanceQuery( + createOptionsForBalance( + NGROK_URL === "" ? "https://mock.url" : NGROK_URL, + ), + ) .then((responseBody) => { expect(responseBody).to.be.an("object"); - expect(responseBody?.balanceQueryResponse.ResponseCode).to.be.equal("0"); - console.log("\x1b[35m\x1b[4m\x1b[1m%s\x1b[0m", `Test passed successfully`); + expect(responseBody?.balanceQueryResponse.ResponseCode).to.be.equal( + "0", + ); + console.log( + "\x1b[35m\x1b[4m\x1b[1m%s\x1b[0m", + `Test passed successfully`, + ); // console.log("RESPONSE BODY:", JSON.stringify(responseBody, null, 2)); done(); }) diff --git a/lib/tests/apis/main/business-Paybill.spec.js b/lib/tests/apis/main/business-Paybill.spec.js index 9fbdfd7..1422716 100644 --- a/lib/tests/apis/main/business-Paybill.spec.js +++ b/lib/tests/apis/main/business-Paybill.spec.js @@ -17,11 +17,20 @@ describe("B2B Paybill API with OAuth", function () { }); it("Should simulate a B2B paybill transaction and receive a response", function (done) { - businessPaybill(createOptionsForB2bPaybill(NGROK_URL === "" ? "https://mock.url" : NGROK_URL)) + businessPaybill( + createOptionsForB2bPaybill( + NGROK_URL === "" ? "https://mock.url" : NGROK_URL, + ), + ) .then((responseBody) => { expect(responseBody).to.be.an("object"); - expect(responseBody?.businessPaybillResponse.ResponseCode).to.be.equal("0"); - console.log("\x1b[35m\x1b[4m\x1b[1m%s\x1b[0m", `Test passed successfully`); + expect(responseBody?.businessPaybillResponse.ResponseCode).to.be.equal( + "1005", + ); + console.log( + "\x1b[35m\x1b[4m\x1b[1m%s\x1b[0m", + `Test passed successfully`, + ); /*console.log("RESPONSE BODY:", JSON.stringify(responseBody, null, 2));*/ done(); }) diff --git a/lib/tests/apis/main/c2b-Register.spec.js b/lib/tests/apis/main/c2b-Register.spec.js deleted file mode 100644 index e75833d..0000000 --- a/lib/tests/apis/main/c2b-Register.spec.js +++ /dev/null @@ -1,30 +0,0 @@ -import { expect } from "chai"; -import { setupNgrokServer } from "../utils/server.js"; -import { mpesa } from "../../../../index.js"; -import { createOptionsForC2bRegister } from "../utils/options.js"; - -describe("C2B Register URL API with OAuth", function() { - this.timeout(15000); - let NGROK_URL, teardown; - const { c2bRegister } = mpesa; - - before(async function() { - ({ NGROK_URL, teardown } = await setupNgrokServer("c2bRegister", true)); - }); - - after(async function() { - await teardown(); - }); - - it("Should register C2B validation and confirmation URLs and confirm success", function(done) { - c2bRegister(createOptionsForC2bRegister(NGROK_URL === "" ? "https://mock.url" : NGROK_URL)) - .then((responseBody) => { - expect(responseBody).to.be.an("object"); - expect(responseBody?.c2bRegisterResponse.ResponseCode).to.be.equal("00000000"); - console.log("\x1b[35m\x1b[4m\x1b[1m%s\x1b[0m", `Test passed successfully`); - /*console.log("RESPONSE BODY:", JSON.stringify(responseBody, null, 2));*/ - done(); - }) - .catch(done); - }); -}); diff --git a/lib/tests/apis/main/c2b-Simulate.spec.js b/lib/tests/apis/main/c2b-Simulate.spec.js index ca06b2f..6dbd4cf 100644 --- a/lib/tests/apis/main/c2b-Simulate.spec.js +++ b/lib/tests/apis/main/c2b-Simulate.spec.js @@ -2,15 +2,18 @@ import { expect } from "chai"; import { mpesa } from "../../../../index.js"; import { createOptionsForC2bSimulate } from "../utils/options.js"; -describe("C2B Simulate API with OAuth", function() { +describe("C2B Simulate API with OAuth", function () { this.timeout(28000); const { c2bSimulate } = mpesa; - it("Should simulate a C2B transaction", function(done) { + it("Should simulate a C2B transaction", function (done) { c2bSimulate(createOptionsForC2bSimulate()) .then((responseBody) => { expect(responseBody).to.be.an("object"); expect(responseBody?.c2bSimulateResponse.ResponseCode).to.be.equal("0"); - console.log("\x1b[35m\x1b[4m\x1b[1m%s\x1b[0m", `Test passed successfully`); + console.log( + "\x1b[35m\x1b[4m\x1b[1m%s\x1b[0m", + `Test passed successfully`, + ); // console.log("RESPONSE BODY:", JSON.stringify(responseBody, null, 2)); done(); }) diff --git a/lib/tests/apis/main/mpesa-Express.spec.js b/lib/tests/apis/main/mpesa-Express.spec.js new file mode 100644 index 0000000..292241c --- /dev/null +++ b/lib/tests/apis/main/mpesa-Express.spec.js @@ -0,0 +1,69 @@ +import { expect } from "chai"; +import { mpesa } from "../../../../index.js"; +import { + createOptionsForMpesaQuery, + createOptionsForMpesaSimulate, +} from "../utils/options.js"; +import { setupNgrokServer } from "../utils/server.js"; + +describe("M-pesa express (simulate) and Query", function () { + this.timeout(15000); + let NGROK_URL, teardown; + const { mpesaSimulate, mpesaQuery } = mpesa; + + before(async function () { + ({ NGROK_URL, teardown } = await setupNgrokServer("mpesaSimulate", true)); + }); + + after(async function () { + await teardown(); + }); + + it("Should initiate an M-Pesa payment then pass CheckoutRequestID to M-Pesa Query", function (done) { + mpesaSimulate( + createOptionsForMpesaSimulate( + NGROK_URL === "" ? "https://mock.url" : NGROK_URL, + ), + ) + .then((simulateResponse) => { + expect(simulateResponse).to.be.an("object"); + expect(simulateResponse?.mpesaSimulateResponse.ResponseCode).to.equal( + "0", + ); + + console.log( + "\x1b[35m\x1b[4m\x1b[1m%s\x1b[0m", + "Test for M-Pesa simulate passed successfully", + ); + // console.log("Simulate Response:", JSON.stringify(simulateResponse, null, 2)); + + return new Promise((resolve) => { + console.log( + "\x1b[45m\x1b[1m%s\x1b[0m", + " Waiting for 6 seconds before the request is transacted... ", + ); + setTimeout(() => resolve(simulateResponse), 6000); + }); + }) + .then((simulateResponse) => { + return mpesaQuery( + createOptionsForMpesaQuery( + simulateResponse.mpesaSimulateResponse.CheckoutRequestID, + ), + ); + }) + .then((queryResponse) => { + expect(queryResponse).to.be.an("object"); + expect(queryResponse?.mpesaQueryResponse.ResponseCode).to.equal("0"); + + console.log( + "\x1b[35m\x1b[4m\x1b[1m%s\x1b[0m", + "Test for M-Pesa query passed successfully", + ); + // console.log("Query Response:", JSON.stringify(queryResponse, null, 2)); + + done(); + }) + .catch(done); + }); +}); diff --git a/lib/tests/apis/main/mpesa-Query.spec.js b/lib/tests/apis/main/mpesa-Query.spec.js deleted file mode 100644 index 64ddb40..0000000 --- a/lib/tests/apis/main/mpesa-Query.spec.js +++ /dev/null @@ -1,21 +0,0 @@ -import { expect } from "chai"; -import { mpesa } from "../../../../index.js"; -import { createOptionsForMpesaQuery } from "../utils/options.js"; - -describe("Lipa Na M-Pesa Query API", function() { - this.timeout(24000); - const { mpesaQuery } = mpesa; - it("Should initiate Lipa Na M-Pesa payment query and return a response", function(done) { - mpesaQuery( - createOptionsForMpesaQuery("ws_CO_25022025092305896110081288") - ) - .then((responseBody) => { - expect(responseBody).to.be.an("object"); - expect(responseBody?.mpesaQueryResponse.ResponseCode).to.be.equal("0"); - console.log("\x1b[35m\x1b[4m\x1b[1m%s\x1b[0m", `Test passed successfully`); - // console.log("RESPONSE BODY:", JSON.stringify(responseBody, null, 2)); - done(); - }) - .catch(done); - }); -}); diff --git a/lib/tests/apis/main/mpesa-Simulate.spec.js b/lib/tests/apis/main/mpesa-Simulate.spec.js deleted file mode 100644 index 36910a1..0000000 --- a/lib/tests/apis/main/mpesa-Simulate.spec.js +++ /dev/null @@ -1,30 +0,0 @@ -import { expect } from "chai"; -import { mpesa } from "../../../../index.js"; -import { createOptionsForMpesaSimulate } from "../utils/options.js"; -import { setupNgrokServer } from "../utils/server.js"; - -describe("Lipa Na M-Pesa C2B API with OAuth", function() { - this.timeout(45000); - let NGROK_URL, teardown; - const { mpesaSimulate } = mpesa; - - before(async function() { - ({ NGROK_URL, teardown } = await setupNgrokServer("mpesaSimulate", true)); - }); - - after(async function() { - await teardown(); - }); - - it("Should initiate Lipa Na M-Pesa payment and return a response", function(done) { - mpesaSimulate(createOptionsForMpesaSimulate(NGROK_URL === "" ? "https://mock.url" : NGROK_URL)) - .then((responseBody) => { - expect(responseBody).to.be.an("object"); - expect(responseBody?.mpesaSimulateResponse.ResponseCode).to.be.equal("0"); - console.log("\x1b[35m\x1b[4m\x1b[1m%s\x1b[0m", `Test passed successfully`); - // console.log("RESPONSE BODY:", JSON.stringify(responseBody, null, 2)); - done(); - }) - .catch(done); - }); -}); diff --git a/lib/tests/apis/main/qr-Generate.spec.js b/lib/tests/apis/main/qr-Generate.spec.js index f298b9e..f3a0e61 100644 --- a/lib/tests/apis/main/qr-Generate.spec.js +++ b/lib/tests/apis/main/qr-Generate.spec.js @@ -10,7 +10,10 @@ describe("Generate Dynamic QR Code API", function () { generateQrCode(createOptionsForQrCode()) .then((responseBody) => { expect(responseBody).to.be.an("object"); - console.log("\x1b[35m\x1b[4m\x1b[1m%s\x1b[0m", `Test passed successfully`); + console.log( + "\x1b[35m\x1b[4m\x1b[1m%s\x1b[0m", + `Test passed successfully`, + ); // console.log("RESPONSE BODY:", JSON.stringify(responseBody, null, 2)); done(); }) diff --git a/lib/tests/apis/main/reversals.spec.js b/lib/tests/apis/main/reversals.spec.js index 15c9029..ead6bb7 100644 --- a/lib/tests/apis/main/reversals.spec.js +++ b/lib/tests/apis/main/reversals.spec.js @@ -3,25 +3,33 @@ import { setupNgrokServer } from "../utils/server.js"; import { createOptionsForReversals } from "../utils/options.js"; import { mpesa } from "../../../../index.js"; -describe("Reversal API Test", function() { +describe("Reversal API Test", function () { this.timeout(28000); let NGROK_URL, teardown; const { reversals } = mpesa; - before(async function() { + before(async function () { ({ NGROK_URL, teardown } = await setupNgrokServer("reversals", true)); }); - after(async function() { + after(async function () { await teardown(); }); - it("Should initiate a reversal and return a response", function(done) { - reversals(createOptionsForReversals(NGROK_URL === "" ? "https://mock.url" : NGROK_URL, "OEI2AK4Q16")) + it("Should initiate a reversal and return a response", function (done) { + reversals( + createOptionsForReversals( + NGROK_URL === "" ? "https://mock.url" : NGROK_URL, + "OEI2AK4Q16", + ), + ) .then((responseBody) => { expect(responseBody).to.be.an("object"); expect(responseBody?.reversalsResponse.ResponseCode).to.be.equal("0"); - console.log("\x1b[35m\x1b[4m\x1b[1m%s\x1b[0m", `Test passed successfully`); + console.log( + "\x1b[35m\x1b[4m\x1b[1m%s\x1b[0m", + `Test passed successfully`, + ); // console.log("RESPONSE BODY:", JSON.stringify(responseBody, null, 2)); done(); }) diff --git a/lib/tests/apis/main/tax-Remittance.spec.js b/lib/tests/apis/main/tax-Remittance.spec.js deleted file mode 100644 index d04bfa5..0000000 --- a/lib/tests/apis/main/tax-Remittance.spec.js +++ /dev/null @@ -1,31 +0,0 @@ -import { setupNgrokServer } from "../utils/server.js"; -import { mpesa } from "../../../../index.js"; -import { createOptionsForTaxRemittance } from "../utils/options.js"; -import { expect } from "chai"; - -describe("Tax remittance API with OAuth ", function () { - this.timeout(15000); - let NGROK_URL, teardown; - const { taxRemittance } = mpesa; - before(async function () { - ({ NGROK_URL, teardown } = await setupNgrokServer("taxRemittance", true)); - }); - after(async function () { - await teardown(); - }); - - it("Should simulate tax remittance to KRA and receive a response body and result body", function (done) { - taxRemittance(createOptionsForTaxRemittance(NGROK_URL)) - .then((responseBody) => { - expect(responseBody).to.be.an("object"); - expect(responseBody?.remittanceResponse.ResponseCode).to.be.equal("0"); - console.log( - "\x1b[35m\x1b[4m\x1b[1m%s\x1b[0m", - `Test passed successfully`, - ); - // console.log("RESPONSE BODY", JSON.stringify(responseBody, null, 2)); - done(); - }) - .catch(done); - }); -}); diff --git a/lib/tests/apis/main/transaction-status.spec.js b/lib/tests/apis/main/transaction-status.spec.js index da0531a..f8a97f9 100644 --- a/lib/tests/apis/main/transaction-status.spec.js +++ b/lib/tests/apis/main/transaction-status.spec.js @@ -3,25 +3,37 @@ import { setupNgrokServer } from "../utils/server.js"; import { mpesa } from "../../../../index.js"; import { createOptionsForTransactionStatus } from "../utils/options.js"; -describe("Transaction Status API", function() { +describe("Transaction Status API", function () { this.timeout(24000); let NGROK_URL, teardown; const { transactionStatus } = mpesa; - before(async function() { - ({ NGROK_URL, teardown } = await setupNgrokServer("transactionStatus", true)); + before(async function () { + ({ NGROK_URL, teardown } = await setupNgrokServer( + "transactionStatus", + true, + )); }); - after(async function() { + after(async function () { await teardown(); }); - it("Should retrieve transaction status and return a response", function(done) { - transactionStatus(createOptionsForTransactionStatus(NGROK_URL === "" ? "https://mock.url" : NGROK_URL)) + it("Should retrieve transaction status and return a response", function (done) { + transactionStatus( + createOptionsForTransactionStatus( + NGROK_URL === "" ? "https://mock.url" : NGROK_URL, + ), + ) .then((responseBody) => { expect(responseBody).to.be.an("object"); - expect(responseBody?.transactStatusResponse.ResponseCode).to.be.equal("0"); - console.log("\x1b[35m\x1b[4m\x1b[1m%s\x1b[0m", `Test passed successfully`); + expect(responseBody?.transactStatusResponse.ResponseCode).to.be.equal( + "0", + ); + console.log( + "\x1b[35m\x1b[4m\x1b[1m%s\x1b[0m", + `Test passed successfully`, + ); // console.log("RESPONSE BODY:", JSON.stringify(responseBody, null, 2)); done(); }) diff --git a/lib/tests/apis/utils/options.js b/lib/tests/apis/utils/options.js index 44ad960..9e97779 100644 --- a/lib/tests/apis/utils/options.js +++ b/lib/tests/apis/utils/options.js @@ -3,7 +3,7 @@ import { CommandIDs, IdentifierTypes, responseTypes, - trxCodeTypes + trxCodeTypes, } from "../../../core/utils/constants.js"; export function createOptionsForQrCode() { @@ -13,14 +13,14 @@ export function createOptionsForQrCode() { amount: 2000, trxCode: trxCodeTypes.BUY_GOODS, cpi: "174379", - size: "300" + size: "300", }; } export function createOptionsForMpesaQuery(passRequestIdHere) { return { businessShortCode: parseInt(configs.businessShortCode), - checkoutRequestId: `${passRequestIdHere}` + checkoutRequestId: `${passRequestIdHere}`, }; } @@ -34,7 +34,7 @@ export function createOptionsForB2c(NGROK_URL, msisdn) { QueueTimeOutURL: `${NGROK_URL}/b2c/queue`, resultURL: `${NGROK_URL}/b2c/result`, occasion: "TEST", - remarks: "TEST" + remarks: "TEST", }; } @@ -43,7 +43,7 @@ export function createOptionsForC2bSimulate() { shortCode: parseInt(configs.partyA), msisdn: configs.msisdn, amount: parseInt("100"), - billRefNumber: "invoice008" + billRefNumber: "invoice008", }; } @@ -54,7 +54,7 @@ export function createOptionsForBalance(NGROK_URL) { initiator: configs.initiatorName, QueueTimeOutURL: `${NGROK_URL}/accountbalance/queuetimeouturl`, resultURL: `${NGROK_URL}/accountbalance/result`, - remarks: "TEST" + remarks: "TEST", }; } @@ -64,10 +64,10 @@ export function createOptionsForMpesaSimulate(NGROK_URL) { partyA: configs.msisdn, phoneNumber: configs.msisdn, transactionType: "CustomerPayBillOnline", - transactionDesc:"TEST", + transactionDesc: "TEST", amount: 1, callbackURL: `${NGROK_URL}/path/result`, - accountRef: configs.accountRef + accountRef: configs.accountRef, }; } @@ -80,7 +80,7 @@ export function createOptionsForReversals(NGROK_URL, transId) { QueueTimeOutURL: `${NGROK_URL}/Reversal/queuetimeouturl`, resultURL: `${NGROK_URL}/Reversal/result`, remarks: "TEST", - occasion: "TEST" + occasion: "TEST", }; } @@ -94,7 +94,7 @@ export function createOptionsForTransactionStatus(NGROK_URL) { resultURL: `${NGROK_URL}/TransactionStatus/result/`, QueueTimeOutURL: `${NGROK_URL}/TransactionStatus/queue/`, remarks: "TEST", - occasion: "TEST" + occasion: "TEST", }; } @@ -112,15 +112,6 @@ export function createOptionsForB2cAccTopUp(NGROK_URL) { }; } -export function createOptionsForC2bRegister(NGROK_URL) { - return { - confirmationURL: `${NGROK_URL}/confirmation/result`, - validationURL: `${NGROK_URL}/validation/result`, - shortCode: parseInt(configs.businessShortCode), - responseType: responseTypes.CANCELLED - }; -} - export function createOptionsForB2bPaybill(NGROK_URL) { return { initiator: configs.initiatorName, @@ -131,19 +122,6 @@ export function createOptionsForB2bPaybill(NGROK_URL) { requester: parseInt(configs.requester), //optional resultURL: `${NGROK_URL}/path/result`, QueueTimeOutURL: `${NGROK_URL}/path/queue`, - remarks: "TEST" - }; -} - -export function createOptionsForTaxRemittance(NGROK_URL) { - return { - accountReference: parseInt(configs.accountRef, 10), - partyA: parseInt(configs.partyA, 10), - partyB: parseInt(configs.partyB, 10), - initiator: configs.initiatorName, - amount: parseInt("100", 10), - resultURL: `${NGROK_URL}/remittax/result`, - QueueTimeOutURL: `${NGROK_URL}/remittax/queue`, - remarks: "TEST" + remarks: "TEST", }; } diff --git a/lib/tests/apis/utils/server.js b/lib/tests/apis/utils/server.js index 4ef1b68..35cab43 100644 --- a/lib/tests/apis/utils/server.js +++ b/lib/tests/apis/utils/server.js @@ -9,10 +9,13 @@ app.use(express.json()); async function setupNgrokServer(name, disabled = true) { if (disabled) { - console.info("\x1b[43m\x1b[30m\x1b[1m%s\x1b[0m", " Ngrok server will not be initiated " ); + console.info( + "\x1b[43m\x1b[30m\x1b[1m%s\x1b[0m", + " Ngrok server will not be initiated ", + ); return { - NGROK_URL: "", teardown: async () => { - } + NGROK_URL: "", + teardown: async () => {}, }; } // Create and start the HTTP server on a random available port @@ -38,12 +41,13 @@ async function setupNgrokServer(name, disabled = true) { } catch (err) { console.error( "\x1b[31m\x1b[1m%s\x1b[0m", - "Error while closing server:", err + "Error while closing server:", + err, ); } finally { setTimeout(() => { - process.exit(1) - }, 2000) + process.exit(1); + }, 2000); } } From bc3532e5d97923bddd042e3b5863b6f733c19580 Mon Sep 17 00:00:00 2001 From: Samm Date: Wed, 5 Mar 2025 07:36:01 +0300 Subject: [PATCH 19/23] Updates package Json, with the actual license name. I have made keyword addition. This will ensure that developers searching for different variations of M-Pesa integrations, SDKs, and modules will find your library more easily --- package.json | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 685e732..df1a334 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "mpesa-node", "version": "0.1.3", - "description": "Node M-Pesa Library", + "description": "A Node.js library that integrates Daraja-2.0 APIs", "main": "./index.js", "repository": { "type": "git", @@ -47,9 +47,26 @@ }, "keywords": [ "mpesa", - "mpesa-node", - "mpesa-api" + "mpesa node js", + "mpesa api", + "daraja api", + "mpesa integration", + "mpesa sdk", + "mpesa node library", + "mpesa node module", + "safaricom mpesa", + "mpesa payment gateway" ], "author": "Geoffrey Mureithi", - "license": "MIT" + "license": "Apache-2.0", + "contributors": [ + { + "name": "Samm waturu", + "email": "muiruri.samwel@outlook.com" + }, + { + "name": "Dgatere", + "email": "" + } + ] } From 5daf4899f6d6a78aa5938b3c847f85b485fddc6c Mon Sep 17 00:00:00 2001 From: Samm Date: Wed, 5 Mar 2025 07:38:54 +0300 Subject: [PATCH 20/23] Enforced description to be consistent with README.md title --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index df1a334..6c8e4fb 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "mpesa-node", "version": "0.1.3", - "description": "A Node.js library that integrates Daraja-2.0 APIs", + "description": "M-Pesa Library for Node.js using REST API", "main": "./index.js", "repository": { "type": "git", From 03e4611e19e0b88ce476e844537977be0ba0b6fe Mon Sep 17 00:00:00 2001 From: Samm Date: Wed, 5 Mar 2025 07:41:45 +0300 Subject: [PATCH 21/23] Removed a redundant library, and added a types field in the package.json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6c8e4fb..54fc812 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,7 @@ "version": "0.1.3", "description": "M-Pesa Library for Node.js using REST API", "main": "./index.js", + "types": "./index.d.ts", "repository": { "type": "git", "url": "https://github.com/safaricom/mpesa-node-library" @@ -19,7 +20,6 @@ "axios": "^1.5.0", "dotenv": "^16.4.7", "express": "^4.21.2", - "globals": "^15.11.0", "ngrok": "^5.0.0-beta.2", "uuid": "^11.0.5" }, From e6af0602242248f48942cb1e695c0eeee4de9687 Mon Sep 17 00:00:00 2001 From: Samm Date: Wed, 5 Mar 2025 08:15:10 +0300 Subject: [PATCH 22/23] Disabled M-pesa query due to the server processing delay issues of a transaction. Added multiline comments in each test file, describing what the test does --- lib/tests/apis/main/b2c-Request.spec.js | 9 +++++ lib/tests/apis/main/b2c-Topup.spec.js | 9 +++++ lib/tests/apis/main/balance-Query.spec.js | 9 +++++ lib/tests/apis/main/business-Paybill.spec.js | 10 +++++ lib/tests/apis/main/c2b-Simulate.spec.js | 9 +++++ lib/tests/apis/main/mpesa-Express.spec.js | 40 +++++++++++-------- lib/tests/apis/main/qr-Generate.spec.js | 9 +++++ lib/tests/apis/main/reversals.spec.js | 10 +++++ .../apis/main/transaction-status.spec.js | 9 +++++ 9 files changed, 98 insertions(+), 16 deletions(-) diff --git a/lib/tests/apis/main/b2c-Request.spec.js b/lib/tests/apis/main/b2c-Request.spec.js index e67f567..65ac182 100644 --- a/lib/tests/apis/main/b2c-Request.spec.js +++ b/lib/tests/apis/main/b2c-Request.spec.js @@ -2,6 +2,15 @@ import { expect } from "chai"; import { mpesa } from "../../../../index.js"; import { setupNgrokServer } from "../utils/server.js"; import { createOptionsForB2c } from "../utils/options.js"; +/** + * Tests the B2C Payment API with OAuth by simulating multiple transactions in parallel. + * + * - Sets up a Ngrok server for callback handling. + * - Sends three B2C payment requests concurrently, mimicking bulk payments. + * - Verifies each response to ensure a successful transaction. + * - Logs test success for each payment. + * - Handles both valid responses and timeout scenarios. + */ describe("B2C Payment API with OAuth", function () { this.timeout(24000); diff --git a/lib/tests/apis/main/b2c-Topup.spec.js b/lib/tests/apis/main/b2c-Topup.spec.js index cf252dd..7bb2524 100644 --- a/lib/tests/apis/main/b2c-Topup.spec.js +++ b/lib/tests/apis/main/b2c-Topup.spec.js @@ -2,6 +2,15 @@ import { expect } from "chai"; import { mpesa } from "../../../../index.js"; import { setupNgrokServer } from "../utils/server.js"; import { createOptionsForB2cAccTopUp } from "../utils/options.js"; +/** + * Tests the B2B Account Top-Up API with OAuth by simulating a single transaction. + * + * - Sets up a Ngrok server for callback handling. + * - Sends a B2C top-up request to a business account. + * - Verifies the response to ensure a successful transaction. + * - Logs test success upon receiving a valid response. + * - Handles both valid responses and timeout scenarios. + */ describe("B2B Account Top-Up API with OAuth", function () { this.timeout(24000); diff --git a/lib/tests/apis/main/balance-Query.spec.js b/lib/tests/apis/main/balance-Query.spec.js index 833a1e9..278f25e 100644 --- a/lib/tests/apis/main/balance-Query.spec.js +++ b/lib/tests/apis/main/balance-Query.spec.js @@ -2,6 +2,15 @@ import { expect } from "chai"; import { setupNgrokServer } from "../utils/server.js"; import { mpesa } from "../../../../index.js"; import { createOptionsForBalance } from "../utils/options.js"; +/** + * Tests the Account Balance API with OAuth by simulating a balance query. + * + * - Sets up a Ngrok server for handling API callbacks. + * - Sends a request to fetch the account balance. + * - Verifies the response to confirm a successful balance retrieval. + * - Logs test success upon receiving a valid response. + * - Handles both valid responses and timeout scenarios. + */ describe("Account Balance API with OAuth", function () { this.timeout(24000); diff --git a/lib/tests/apis/main/business-Paybill.spec.js b/lib/tests/apis/main/business-Paybill.spec.js index 1422716..1481116 100644 --- a/lib/tests/apis/main/business-Paybill.spec.js +++ b/lib/tests/apis/main/business-Paybill.spec.js @@ -3,6 +3,16 @@ import { setupNgrokServer } from "../utils/server.js"; import { mpesa } from "../../../../index.js"; import { createOptionsForB2bPaybill } from "../utils/options.js"; +/** + * Tests the Account Balance API with OAuth by simulating a balance query. + * + * - Sets up a Ngrok server for handling API callbacks. + * - Sends a request to fetch the account balance. + * - Verifies the response to confirm a successful balance retrieval. + * - Logs test success upon receiving a valid response. + * - Handles both valid responses and timeout scenarios. + */ + describe("B2B Paybill API with OAuth", function () { this.timeout(24000); let NGROK_URL, teardown; diff --git a/lib/tests/apis/main/c2b-Simulate.spec.js b/lib/tests/apis/main/c2b-Simulate.spec.js index 6dbd4cf..363bd51 100644 --- a/lib/tests/apis/main/c2b-Simulate.spec.js +++ b/lib/tests/apis/main/c2b-Simulate.spec.js @@ -2,6 +2,15 @@ import { expect } from "chai"; import { mpesa } from "../../../../index.js"; import { createOptionsForC2bSimulate } from "../utils/options.js"; +/** + * Tests the C2B Simulate API with OAuth by initiating a customer-to-business transaction. + * + * - Sends a request to simulate a C2B payment. + * - Validates the response to ensure the transaction was processed successfully. + * - Logs test success upon receiving a valid response. + * - Handles both successful responses and possible timeout scenarios. + */ + describe("C2B Simulate API with OAuth", function () { this.timeout(28000); const { c2bSimulate } = mpesa; diff --git a/lib/tests/apis/main/mpesa-Express.spec.js b/lib/tests/apis/main/mpesa-Express.spec.js index 292241c..7ab7f72 100644 --- a/lib/tests/apis/main/mpesa-Express.spec.js +++ b/lib/tests/apis/main/mpesa-Express.spec.js @@ -2,50 +2,58 @@ import { expect } from "chai"; import { mpesa } from "../../../../index.js"; import { createOptionsForMpesaQuery, - createOptionsForMpesaSimulate, + createOptionsForMpesaSimulate } from "../utils/options.js"; import { setupNgrokServer } from "../utils/server.js"; +/** + * Initiates an M-Pesa Express (STK Push) simulation and then queries the status + * using the CheckoutRequestID. + * + * (Optional but recommended) - If you can manually provide the CheckoutRequestID, + * it's preferable. This setup automatically retrieves and passes it, but server + * delays can cause test failures. + * + * To retrieve the CheckoutRequestID, uncomment the console.log statement below. + */ -describe("M-pesa express (simulate) and Query", function () { +describe("M-pesa express (simulate) and Query", function() { this.timeout(15000); let NGROK_URL, teardown; const { mpesaSimulate, mpesaQuery } = mpesa; - before(async function () { + before(async function() { ({ NGROK_URL, teardown } = await setupNgrokServer("mpesaSimulate", true)); }); - after(async function () { + after(async function() { await teardown(); }); - - it("Should initiate an M-Pesa payment then pass CheckoutRequestID to M-Pesa Query", function (done) { + it("Should initiate an M-Pesa payment then pass CheckoutRequestID to M-Pesa Query", function(done) { mpesaSimulate( createOptionsForMpesaSimulate( - NGROK_URL === "" ? "https://mock.url" : NGROK_URL, - ), + NGROK_URL === "" ? "https://mock.url" : NGROK_URL + ) ) .then((simulateResponse) => { expect(simulateResponse).to.be.an("object"); expect(simulateResponse?.mpesaSimulateResponse.ResponseCode).to.equal( - "0", + "0" ); - console.log( "\x1b[35m\x1b[4m\x1b[1m%s\x1b[0m", - "Test for M-Pesa simulate passed successfully", + "Test for M-Pesa simulate passed successfully" ); // console.log("Simulate Response:", JSON.stringify(simulateResponse, null, 2)); - return new Promise((resolve) => { + /*return new Promise((resolve) => { console.log( "\x1b[45m\x1b[1m%s\x1b[0m", - " Waiting for 6 seconds before the request is transacted... ", + " Waiting for 6 seconds before the request is transacted... " ); setTimeout(() => resolve(simulateResponse), 6000); - }); + });*/ }) - .then((simulateResponse) => { + /*.then((simulateResponse) => { return mpesaQuery( createOptionsForMpesaQuery( simulateResponse.mpesaSimulateResponse.CheckoutRequestID, @@ -63,7 +71,7 @@ describe("M-pesa express (simulate) and Query", function () { // console.log("Query Response:", JSON.stringify(queryResponse, null, 2)); done(); - }) + })*/ .catch(done); }); }); diff --git a/lib/tests/apis/main/qr-Generate.spec.js b/lib/tests/apis/main/qr-Generate.spec.js index f3a0e61..7e034c4 100644 --- a/lib/tests/apis/main/qr-Generate.spec.js +++ b/lib/tests/apis/main/qr-Generate.spec.js @@ -2,6 +2,15 @@ import { expect } from "chai"; import { mpesa } from "../../../../index.js"; import { createOptionsForQrCode } from "../utils/options.js"; +/** + * Tests the Generate Dynamic QR Code API. + * + * - Sends a request to generate a QR code dynamically. + * - Validates that the response is an object. + * - Logs success if the QR code generation is successful. + * - Handles potential errors or timeout scenarios. + */ + describe("Generate Dynamic QR Code API", function () { this.timeout(24000); const { generateQrCode } = mpesa; diff --git a/lib/tests/apis/main/reversals.spec.js b/lib/tests/apis/main/reversals.spec.js index ead6bb7..3d8309a 100644 --- a/lib/tests/apis/main/reversals.spec.js +++ b/lib/tests/apis/main/reversals.spec.js @@ -3,6 +3,16 @@ import { setupNgrokServer } from "../utils/server.js"; import { createOptionsForReversals } from "../utils/options.js"; import { mpesa } from "../../../../index.js"; +/** + * Tests the Reversal API. + * + * - Initiates a reversal request using a provided transaction ID. + * - Verifies that the response is an object. + * - Checks if the response code indicates a successful reversal. + * - Logs success if the test passes. + * - Handles potential errors or timeout scenarios. + */ + describe("Reversal API Test", function () { this.timeout(28000); let NGROK_URL, teardown; diff --git a/lib/tests/apis/main/transaction-status.spec.js b/lib/tests/apis/main/transaction-status.spec.js index f8a97f9..f46cf2e 100644 --- a/lib/tests/apis/main/transaction-status.spec.js +++ b/lib/tests/apis/main/transaction-status.spec.js @@ -2,6 +2,15 @@ import { expect } from "chai"; import { setupNgrokServer } from "../utils/server.js"; import { mpesa } from "../../../../index.js"; import { createOptionsForTransactionStatus } from "../utils/options.js"; +/** + * Tests the Transaction Status API. + * + * - Sends a request to retrieve the status of a transaction. + * - Verifies that the response is a valid object. + * - Ensures the response code indicates a successful transaction status check. + * - Logs success if the test passes. + * - Handles potential errors or timeout scenarios. + */ describe("Transaction Status API", function () { this.timeout(24000); From 03011158727bcbbcfcb8aaea4047818b37a1c214 Mon Sep 17 00:00:00 2001 From: Samm Date: Wed, 5 Mar 2025 08:48:31 +0300 Subject: [PATCH 23/23] Added done() to properly terminate the test --- lib/tests/apis/main/mpesa-Express.spec.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/tests/apis/main/mpesa-Express.spec.js b/lib/tests/apis/main/mpesa-Express.spec.js index 7ab7f72..e90044d 100644 --- a/lib/tests/apis/main/mpesa-Express.spec.js +++ b/lib/tests/apis/main/mpesa-Express.spec.js @@ -43,6 +43,8 @@ describe("M-pesa express (simulate) and Query", function() { "\x1b[35m\x1b[4m\x1b[1m%s\x1b[0m", "Test for M-Pesa simulate passed successfully" ); + + done() // console.log("Simulate Response:", JSON.stringify(simulateResponse, null, 2)); /*return new Promise((resolve) => {

)CdQ3F!eKyF|9D2V!F-rhUpJ8J+l7g0OBBVM#THTI&O8)>EGR%u6Gd9D9pm5!S}%&6DxW}^f|7=Y zPoO3(pTZY#?(7(|!5}5Nn!D%DotZmlW)?smSMcEE<^aT$6gw#LlwubPI9BYTffL0! zyu-EPCnz{Y#ZR&1d{F!hr_NW!&#~mXis$jseXDo@U)-kR7sMBeUt-T&RQw9By@BF9 z3f?cpmw4m-R{RHncaC**(V--ipJ<~6LkW2fi6RVfh%vcYt9@z>&M0LBSf-Q|Et8wU zCt43_*JB)mHR71wb`K@~5Cizwp{`A2uuJ^_Bcl3k{7ree$8&@l?;^2nagS+NqCDBfkB?pJws=PbK~+A7|2 z{gCDJKI-i%m4LD$n{WIwWR|c+NRy`C1#)1sSBI7FiH6z-QkhY&Q_|%I3exQ zQ`X1M?cZH4^M&BSyr;2z$+^SZUMA*0001Z+HKHROw(}?!13=vX`$@Br+fGR zZ%e`5O6%Txi$Yrz0gF{}p>fY>OnlS0Uevf}oDXW;D{d2gcE<2)oFcV80@g$H)63L{HN*d{8kVzKVW(;E)$9N_%kx5Ku3R9WJbY?JW^G#k0Wdx>E$NBBVtKRLiL?sA*s%w`TdsNz1=+~FRNdB8&+@iBD0 zXFTC4C-8-Cwv(4U=LLQ~^Oa4^rG|OTr5?ItoaPMYxxh`%a*kVU z;HYGAjq6;IY{`*awo0DlOMw(hkrYdb(O28l;MYvSx*ChcQW4f^QL5UdE3HbqvbxB$pfSg`>Cj#;?~00;nMAg}==M6d%RaIhCe zARtS)01i=0um)3FSgr#ump{<1pq_<0a34Kp8x=7I1^|9 literal 0 HcmV?d00001 diff --git a/docs/fonts/OpenSans-Regular-webfont.eot b/docs/fonts/OpenSans-Regular-webfont.eot new file mode 100644 index 0000000000000000000000000000000000000000..6bbc3cf58cb011a6b4bf3cb1612ce212608f7274 GIT binary patch literal 19836 zcmZsgRZtvUw51zpym5DThsL#WcXxNU5Zv8egL^}8cXxMp4*>!Rfh5d-=k3gW1;PMQVF3RzW%ci{fFmPHfCS@z{{K`l z41n@~^u3v|;D7Xg7dAi*;0~|>xc(Q?0$BW~UjGHq0h<3YJAeWd?h+ZWM9EYu5@Hs0EOnnkAtTzP9coXJALmS|h&nzJd% z7?C@cPUEGrLHk-#NysfAePe#dP9_6D5VGbo4fVVs0)83}G7LoWV`e*{V_8RPK>Iqw z*X0)8;uQ6FzC+dip(fgJU!9*!>pW6;pdJ$jHReX|0V)o@BosG=sN|PYN^-JAOY{e4 z&QjmR91WNK#}_%Ei?QhW{ab*7Eg=}E)Ft4XeyVhoR4<|byJf1$4VGsxP`9bNBp-((Wawhx zlK;u}?+b5Ii!k>ELIS zPOH%u!jQg8T>Z_#S%<^^|CcOH?XN>$IX|aEQjBic^$pg1`=0Y3Q(mv* ztDZ~~0GdAF>L|BQmHQ*s3r;T~(0;3p;I?%VHpGPt-kXLE3iel2aEIYw5<*Tu6)mB2Zdp4#k4Oz!8SUkT&;Qte`Iq~*4U zD>qT9mSnB=3s~xUgo_vYp#API=~%dKiKqTMXWvn)p~21nSE!cT5SsJTu)R?b1p!+K z!OU2E?^HE49L>c*z)KLpsv9>&-7AKaYlMAztV}6vISI-rtA6=8k`=+S>+C0X22_El zG+i&#b34h$o{gdGZ$>$81)ovjw6Nn76?gBhm&(oX%Gl7C`RDCRpH0f?NEokA^!>;1 z%KC0rbxWq(b)XGCuDPUgvx=VFeE!Yhn7tF%LI~H+p>549%5AqnPWWvF870oRi}Ig6 zBdaI{Fa=dRbLL@+G zt@VO%=$Om*EulLy$6I72!E$J{;p zONB3HLoKgq^6jJF(Q`)L`!cZ+Rr3W%j$jUFFQ>qTy9U3hZ4h|+TM+XM0=d);0+WP* zH3@dm#w7zwp0FtidDmt@7NF1}mU4P$EY|Wkj4mH3R0-KSyk}mz4A4$XnVzGU1ny;{ zr9K{Wq#=h@cd(g4{+b*Qi^ZU3gD1uJhMpP)`|4#)S7%CUD1V?qjVHn4L!j5zA}ut& zDHYpt7rryJOpQZQcQ??@EKS$QO8W$u#LG?i4dgC}^LsmrmVoh-0>Cp<6C#oePz@ic znc{A(*xo*}Gg=DUR{sWZO2O!S=0$cJl7by8{!t-+*TZ&T9bbJ7wa2)MA?uM1^}3pD z!Mnm7PnG9ji{zTSNtd|?oe?d4$WpWLW4dMJVHy7D6t6X`N}z*zqg8B$JmXh6AP)aX zx4a+uFaSa*g>S$NC3TbnlQ^&r0ToUZAvLgxBh<1THf>}}Ts{7zD84WCblCDox?M#`(f%UZNrShhw|$nZN-MhhQP+c9hQHAgGJ_IV1b6^2F=- z?fhtv>A1W^6@54mjz5;7t*eptF`~4*cKXD!5$8W)UW}qW-In5GvPn;l{`(-SB7%7zGad2Yj6(!|Yd(VI^ zC&ZiZE>|fAm1H4v7inHh0gbSXh9;d3^mP3F9aj*xVgTHvzV&rhAm#ZR@sy6HY+57} zeQrb@_!T>7O|l5W&I8EJk4PD+eu7{9fix|s50>4l<-?he4QGVD*`Wl}V0uT=;4nY9 zEm;IJTr)#{>0^c~9uJ7iFJp7d=}N}i50uIDTAPbS1r`Kew4)^8WcXFFN4I32xs6b< zM&&#yNQ)TAU!+&2w1Dp$`K)N4lwMf`e_{ncP9W&odNN_CQ>@#pvQ|mh$&8I{E#bl> zB{VRuj9O6?c8!sDjhgs5*MQE6OxJ83X+X`AI_G)kQew9Ci-&)8eq=7sNlRp^bIxEQ zg|HclB2$$1v8c0Wisk@^O2sd2(kXv7=Ek#Wb8SVE1(H9H$$OHV^iX=5ZwM=Pu02e89|at zbFfF)-U0D3q8L$vmV7d@9I_-tBZ=NZjrKjDDP1X`vP+F--+M2*vuCD^TJ&x$t+uqT z{gy!y{@6Tm=L znG~jgC)-NfHfDLrDM=uoHZM=BNVmK{Pe(M(RjT8*-;1b0XSnNA4?|eUJqsD)D)@}; z{CpywKAqMb9wZ(6Y~4v3R-)tP9!E5UYUGBA5QC#xIu11gw%N*a*Q8(2M!m|E=H27^ zZXFt9A*oM7qF3D|Vt(Kk3UuS_L?(%S$5+s_seNGFSQN>aT|4Kk!7e7pa-zOiWG5|c z9*LIZxA-x!0O~*=M&|Ask{QPsIKK+<*}x{ZpPV@RFv0}Cxy!_fQ5O%boHd;%F?A!I zO5Q3|OR+`Cag+~w)1E`G!l8k?0rG9pOi!bU>Nj4|dc0g^TCPr_d(JY#_j4NZwiEyY zad+EiOP~qG{re_HT!Tu0b}9m&-+EnjeHax=I0qqe8wB6WTvwsvvc>M%#>dW980a;2 zMVnq%$yM7!W$r6;h2PBNLB!~Rfh|Z-k(5|?RbP-d8v>mau#JQf#7N;F!=a*C;qCy? z-m2K+j18jpX{S=OH5CGrQ#tkR&98;#oJ5MO+Z2@HIhCZe9J-ooRY{5V4N2VqE#2+mpdE}`C!1{}3U?V2V*Cw6Z>cq&a?X6gN(o2l1eaxDB zZp*{cNN;-(ALedD2XqzE89oT3lwo4=3mXEO*jLdO;tIv_q~k}02M&l{usI;}&@iUz zS};fwOPs4NxW-!BNaCWH?9w7-4k@XNVd5jN*`mdTZQRL6xF(d~cf{E$>60g9qm~}Y zo7$|>Jg_GaK?QkIjVIX6JktAcoEf>akVgU zWSWB@uUgK$ipXjs88B*f2>-^rktwrEXY&}L*onyN5S?Zl2}fWO%usD4O$9u{&mgWL zP>D}i8zKqYtdn#5(zA?O9K6f7SI0}a;RPGsZ{G)MVvdyUK55Gb7vW-S)bR572CP?b za}s;<5HMCsc1n&o(w~fCN%MLk+{Yo2x*$8G91S&vvII6dWWkg-7FUf&Y? z9a_&9hO?#ZUpRyL_MID@2}}j)E_FG>pa1$+&PWrcPSnWvfu}#_QPg_Nx=~*Hnc^a>lUicEr6y*?-!uaoR-ZkCvaM>bWQNB8YB&B0oyeY2FKgtn%Mx|B|zGtOO1xCMaIm9^>Fp z|1Zg8OMJ9}eN{aF3gzDii(~7!d|(Za0-`;2k%0_;ZYFVCxV_h^Z`S-Qr|J?3@e{Bp zWBK#47K$Yk)?@m$)2Q@24WltBwoOG0=` z@y25+2eUMkxw{C4muMZPmuIalcyZHmwYd1)B_%v}UX70wk|SH>5SVaaxUD;o@Dhcd zh|FNgT%rNB>;WzIlk_BtC5QT>=H@A3%zvd6fyU|_QtC%GbeFenirHKlnE+3UCz2cS zk;eR6X486;dzQQ*fR3!(Nh;MRJ{bSHddVHbMq`(MVV%4ojZ;9K@Btr1 zb&lxztBj%mYk@aVL;7;(v{QVF7HXojz~*}pj2?DmX~(V(#+08OeJ zhm=J|GYGwXImQ+yP_H8Y7I^9%H3M=rIWD285Gfd_$Fs6g-&4TN%3y&_2;W0Zgk}?w za_=6sPZ)r-$*f_hY`k@=Ayu>ng@d#DTXZXv@7tq;l^n^-4L&Y(M|&?5enQ=r16|$p<#N$V zGU`*|0teb@D;665)nY&vB9MAqupeY5=L?@rVjLSO~G+B!0t zm${EyNFQnV=DmK*%;_DrL%M2Do309pBq|<}a$zU42h~&usMl~SBu?9&+rk_=74cQT zNV8{uni!(;sxMT=@Aj)b(6z9^hi-WTF2)J4%-4c^LK$#bcfOaKYdpP^kf|JyHNn}I z5x>SC_yMRhQ`0u`nPp~B=t>&gGk;%$c%N8k@8N%$iD@4a!%(|(C9~zX_v_sTox}sT2FIn(x96wW|MzH>Z{$K+l@aG}8 z6emVN+jssSjniGZmXNPZFtVI4TBfB)_LyEv6_EK6Ls^Fiq+Is{ZZ3K>b*7~W21#}9 zJnFv%kbM7`$-~!N(d}_e)dO(jo(KsJlKze{>Xl({HqB9Y4T;k2@Z>};t`hD1DmDC! z3T6A<3lKNJL{T;eovS}lZp@1AxubzxSE+UuV$d|QW#k!x;H}TvqxXL&KD1M^9Q%He z6ZgH$h5>Azg;)s2sFnX@8vfu^vG+65Lhfb}t)iMB+XuUzefy&Htz(>7Lm<1?o=E{4 zqX&6#ZqO$13oQZbYjF#N)sLcNDrR67tPVY12MNsIb{<<)r!`6RZ2W|!Z8tCieo|33 zi1qv~T-j_0iW0s!NG^i0x2yQ%t)MVp0}bG#2ekg%oXooKzG6ut zec^f);@(EShH;OOYpZ+dLn(GM@`1x8GOmIsf>Ma+_7 zGmm|(C0ZbVC5ewJ(d<6^76s=Pz$)?c)GW8lu@oqkY47A!;P*8s!q3_RE%j0npP+Fi zu15RnsE2SDZd<6n|Z1F%S ze?Hl_XAf<7|COS&hj$ffTe!u49A?doGv1Qrv;5%FrxC63;QH~{jnKtZjdEq~bVAjk z+9pg(>Q_D_BW6l_iw#1?r({A3oHB#c`u8GgZzDjH&jN1LCDR(}O~bL7ZZaj_`a)0Z zyV74I4-+j}<)#Cw#d}|WCHz84q-zbWV3fxsgQ3-cIV+>z#|FW%gLQ`rjv^+yZBXnU z)2Z74=G=FolM7RW3~PCvffhenR+hPrb>;7UpH7&~(`n(UeY&4nhcKZf+Q-p-Sb5|W z(>ycw=5m7Xyi{jwK5kQwOn$R*i!~L$RiL*hmj-gNBcCplXlk^3GsdUpQF<4IheJE@ z6TYI7vr#FNf-2tM5XjcD1QJ|#h$`lmCfpYVv?XNN%Ag(67E}~t<9|!V2#vZY*UALQ zWf;z|hzP1gj#Gyqjx}lKNP=h`o}{4*_)*CJ6waG(g)uqPjRabn8aMcq)?kdhD}>jsQ)C=kk5O*e zqvnQ#3|V4k1?inmPEB69MjrLUifnrLxp;6N%`+ZG-U(r^b`fphQXkyna z9$|Nt1-^D-q!*mN=E`_fr}nlVBUpuy8#$EcZs`D3kdW&3pr=0@4xC$G!+A9Z$ z@~9vnLRWykpS9^XMK&gn8tg!~7SQw=zdw;&ibQ}lo~#6WDfy5}AvE1wm8`77Bd+2c znGRGYpWKaPL~I;BQ&0}i)Mq){(}mCj39Yq+668S}qY$+%F1f?km~mJ%t?)HdhOEy$ zEB;>Cw?uBDq~}m*pcX@m!-kBc3xG1Yblce0N~^Dsp&%D{gPqSJ1+JkL{j)|u!%%yI zyr4k{xTA(cxIXf7&ckTQ16STp7Auz16ZHhvTH1xuK<>&M6O$qc%Ua>sgtDU!3ogas zWKpyQjywXw46+(qb%#lbpo=HIb}zCyOEV9ro8Uc#&H`(_9dZZa>(9rDO{X@pjj>?E1r%zqv_Nw7(|wg1nvD(eI}a zY1qR9g@+Tu$aVk>BqD=82o9lKelCRU)1mT96r*K~aBAOT23E}m8|YE!iWo@QM-ybs z@F&)c^c=1|!lO(lxXWt>qjMKCBNmhCR90j{Ijn=a0Y==3q@HnkFWP|}RcKbu61sAT zSIyEPfbM(RQVdo{!;gtBqeBkuv1tY~mrafxO+6^1)tH}voDB3ec!O=8(f{WQQPMJCxpXPS8bZJa4`LieuX~<<&FA=Cv{tCj< zD$Z2nXKYL*Z$77+;s9oF>i!O{+YaWV98uiL2g}$o{5d4N$`#zCLDQwcH|vs`wuI%E zeVPG1Smv-FdsGelNDPio#3^|~^)+HEW!_Lr!%HjL4}Wc+X4bz=J1%IKw&JwPqaODS zW^a}yt9ma_{h|vz`P@x!X}~;k6^7%k*#SYUKDj>i{Fl?W!=GAz^cI~)g1x4wJT86U zhO1OlAuaEWU3SDlR5J7M&e$aveB3~3%_d1Pl8AG(0g7mzf;ET%w+!Hp-TB}Guz1Y; zs4|*{y3Vsu9k?G;k;EHhreUIm<&l*Y=cQr`n?mA!xqLv_9>S>W@M!6)lRwc%l6{h!X@Zkfgu|qQQ z+~C`oDuTrdU)GT6T(dU$@O*X_7_NZSznB1@R(6s9)#bz`v`Jg2HOeM2)Y&29nH?H# zO!q~3Xj>}Y@F~kpaOPal+thT*YnCc04F%vd8K3CasF+=6eUFOU)GS7I49y(_G`&?( zT;2F?ddsl9Vd=i&gqdsf{WUN666Ly#?~TzY^$YU8d!!a%kNK4{;co5&7)a1%Yy0sm zA1SQBBKQgVLb@FdK8T}kVX}$*D(N=6K;PuI3@4mr=?VRS^$id;{JdIjKf3i0BE4$8 z^8!hVXBGT3F@7)ob;`%gI3I|aM^plWDM8!kboqBkU9l|5UIKXz?}IJ8jV?0!grb9} zQpH1fO^jbE=C2Jwxev7>wvCrp%C4=D&RDyto{Rsp(S2qyiyPqLvO9OuKKIv8i+Lam+9p&%+e#Pbb=LzUxuIB!;j2{cG(cs)7 zhD1-Qu6E$hq+L;Op*5POg13v@0Ek7$S=7_Q862gfOMUUscusILHDiP`U8SCJFY-&& z1>2-~{pT;Ca6ZsqeKI!>KtHm;HZ!f}l?Sq?X@2J}MbH1;smyYrEfg|0@2W`>V~o0F0l^%&kdWZ~4K?%Uv*Dbu$zR`!b*8my%6Y0EgdQd5 zjL>9Il8==%v?Mq^5q}*h=S-CQAb4Z4AxJEg%TK3>5PfCt44^X_tsc}yMW0Gb8g)F6 zuKV1BG z44?MR&tCORGEDPd9u3%!pUH+k7Qdg%jfGo$fQCf9{Mi=hIlik4;-SbPF%&1MXXC*K z{{ZE;eC!sYX^5L3F&syX#A(C)fe(eFISkfnTbLOwn-rb%v9}{=sbnV)=_+T6rfFGqip&Olf^X*+h^QNzs++ zsUhH#Q>+R1b;3vo^Z#kWNo*q6%udadA`ObceTs0Nf2L(&~%b@ zD+GjFLBG^nzw|dWw#C@~CjSwU(#%(YwFDp^pQ3tk4Mn$bBB7iTE!f)1B{ABa*+Ru) zALtkYCrp-z!(q!?SJ#<6uVCD1@`1+owfdYPZ-juqT9_(d2K> z{N{ghL8o>L+HrJ0T*wl5fM-+G;N-Qnb?|x#8(Dc>*$Z#g3vQ;ANxQaqRz2MCy{~)~ z)|b_KGbvL`NA1;G2I3QLgoSL>G}%Oj+OabYLtSYI*p1oM0D3#Ui$6 z*TZ`~@i|09b}S$NKk>B9SQsjrmKNd*4O`s?s*mG!Rwc-}_?sQ~n8&c^Sqaax&IlIi zZ6#?2&VPc4I?LHPD95g=VCcux`gb3wV6CdC_^>FSj`%j?gkd-uQjxhnO5{(+D*o2h z$~e>%7HF64j^-=MX%1a{ZgCg4#+S~GnCHYXPEB@u&ldQ`=uxN-K;9%pF41{3lug@$ zBSSYIM=yqx+1_~zxTr;$u<(LSvmC5j#Wd+j0yOej4*%;i*U0z?D{KCF$Nc-#?TK12 zCtW}zVeA_}Ol<4PV+m>EGYx6!TKPkC!LuXd2`7q3iHhVq<=;KfqepXY9HwCqO77(w ztIn0I0N>LUq>&V3P434=KxCzKZh=K}&-~u3SGn%u?{%^Dp%ugUW=sQ6>`$29n{cu$ z8Xvck)%Q1e64!y^_tp$Po($sW;#3bj2K7;lOkUgre>Tghd5B&;2NA`zQHd%;W!HWVzVsU;+MYZ zHnqjEh^?^kBj)pnY;&z(lyl~07`ui^`4!h`Yxb?w>w-Cx20edCO=hwy9djmvD%sWVyX61$w|{i$FMd&*g~WP$9wecvWj^S>=v zCKg}2RJh=D*bnaUd1UtrjCuoIYpFCWYrC-0@Q3TlT!*q29A~2D z0g>md0zY#a(tp$-D^@(+u#+G+!7#x9qqEUxuzn!r-F)gpl0p=9WD}rVQW$ZUqfxec zVA7~)d#It@fdKJ8uP2eQA)%C;sxhM+nsTlPR=}$`D!T!Lv3CXGDn$z7_yr2Dqds-D z>|H2vETd_aHZ-NMGfe;Zl44P0)LZQ22@U1fYtczXxvDw*s~vKnZD?O@4@1Wx@@Z;G zk|N(~>A_~RNNEF1zYvxBw1#_rsd$@}_PpU^crJavbR0^oS(+XVZz_?=z6Rr|p1g?Y zQ}eggc-P*Hv3NeidGUPm)yCgrZv=PRlnBX+Q7n^2ss2qsF`49#K8-A_`-2RA`SEQS z!nemcRZ^POWXUg?DN_a=v^F%0d5E#GsRfBDn+O|lfI@$(P}eZMF$*f*tT0<8Y<8(g zQvb?$wI$TVT2J|~L>BFa*-(HRLhs~}FJArfyf9nSaEZ?e6__}qGUkbS7&pn0kk%Uz zS1LDEo^Dg+Q-ez;8`>M`nBKnn`@Q(HG;S9fyw|)uGwd6q2kvH&Ul~!8thbw25xVCu zGIi2nm8!b;H7Culw$Ok^HKP-wOk%2{DY zrb_)8fwpOpug>lk^ga5sB@e!=)FEq}P#l$t{SKVfk=%=As~IMMrDQ%$<2{NrXioS6 zjsEkXBcjHFqH~5ZZ#W~}SLxM}#2M}UmBfnOpo}xNF%6qUWf;2=|8V`K|4Lb;Ei+G1 zeCebkc>IrkI;=V;)#smOY<>!S(+!*%XVbFum}eDD#D&(fMQBnaQ!f^>DFy;I+O*s? z@+u<$dsDa2_#LU z{qy5c{l|nMiiJ=ZY-jqgXoJEbH6wPiM7C!JDYZtf8>d_;)#tDE%Wt(rH#LKl3tj&- z#48J}(`^)L6$D7t$aDS$XeNjBGk7%Dl)uT0>nM=poNHl7tu{4PAS;)wl0LnrvrhlT zsr|c7sQW!-z|1@7Z#?yl`()}3ZaJDj$r;GI5v!ozObBx_oG|Px)T6HxXt&S~vLx>O z6*u1;KKA0HGVvp=3_6~%!bq4x!w_OvVogh^5h_11Mo~ALs5mCL?5K}uKP1CT^_mWd zP>n8oUhG+rr#2>Qlke*IL1W@v+s^TMAjE2-teBxi{?t;F`C2zlO!lbUqL9q@Sqr2@ z-hdeTmsVfS89pJx;@@X7Ff2gy8d|98GIoayOZ!jMTvFr#8y%TU$p!6dPOUw^3BKf; zNRVp&3i<&Yw?0E;W#NcdGkRuw!CnqBK1M6jy4CJ}9Hhrryj*rx5-J@|2#p$CYvJl~4#@6J#)A9>%21M8jw2(!mP{<`B z>|DLI;D_>!&*N;J3lB@xSbEctr@8*)#v-Ye;->qHf|dm@SxZocRz97*;CD1HG0#O! zq`&B|jUP)dI9SxPjPIy3mD2C}BTUJGzS|xSM5BzorObpy{XB5-`h>1C>3ZRM zq;6I&0IGYFK_7bU$!9*U4Jg0VqCyr*8 zev)G4YN%31p%e@bWBNK;Q@S&)dO(CGe{(Z!54mO3Gz-9DA&=YtS>q@)zz&Vo3}oik za4OM07mgHN0kw3ks5_A z5KzxPkfE|DRX6u-j1ULvnTvb+8e^ZIJu1ZL<_*AUf*Xr5lciMmG&{)GmAuIzD zMcuE9i}a?%wwH5#}tG22`{LcP7T0g@cPHh%BU ze4!X~%TrBBO81OEuz+l>gzIn6uXb2=`tsHouH#tjt7^+nAOGayB93fpu{;E^$T%Ti z<2I)Q<&RAi3vXyxhT5FqqfFEhXrFej+*E#L-zgQ|fqLIo^=1IkWhTA%f4*XT>8uLP zL}D9e8Rr%JDK_7{GFTA`hp8y!A8lUxjh;m_L9Wvd!yTK_F)hZ*KvxbPlV(3Hx+i={ zwsrdf?x#bBe~wrx;U$VU@0{qLP(I;{DBiQ@Z{j7_g1&Uzgk#Sj#cSmLITA1a3$|Pe z#QK^%*Ft8gfJzp&YSOqvK^u_)6>GrGC?lqR5KN@v(+L>eJ14XAwNfzVGqc?fFqJavR}8I|mnUIR5Iu$?&RHeq%jR59Sf4FD3jUKeL;bMO=ckRpSTX3tb3xgf1L zw@wObtjkE@3CEJ~#4<^}D=5kqbaC)yKlEcgoDH`$p02Qy|X|75}SU1q98wx8hh3;a?U1A zSwfS5i!L(GOCy5ucZSHX<>>bEq%hl}lg?3deYRPI=Fb7qbyG#o9Vcxd)P&wUdl9~1 zc$r1ZS3m3_B~&Rc{@py{u!)F5cyGihyb|%yr=OcUmfLf(`17Nf%8^G$m}!ijXJu{$ z;s`9XR_ap3!;8lp=c#wrz(1Y9U)#Sr8iL^i7%v0LGFBcyS*fe7nvqQ?mMf^Bx<~W%VAh{G!0y))^_wVyJ8!g1T|i5q708$TSD7uN_c1|HJvM|h|6FT$+_6#lnbcl*n zo%^b*%F>B4Vak`Z>=Ck zRYj0Sr)gv(nLiV)`5xmcW=0VIOEv20sNn+UEtj>{#2ay+8GELz6G`wG1O-zkDO!$o zHB0{p15=c9^cnJ|DE7Y*y^Ak@hn zJ5lfq33a$7Fu#0B4(AphxNilM+vEe*MII^A6<-Np z&O{RZO3-PCFQ4Mr4^M!m_`W3~FwAr8mFXv6(liwOp-zm$3D?hQkV}D_j%6NMDPCswCf)pdzkB)Ud5 zRzjkpsM<7{@S!?;eyb9+@LGwM+cw zJJN1-QL><_JD6l2C3#OkWkiO)qrk3y4d1Vyu&;gY)g@;aXMbX)P;vh`bJg#I*8gucc_8^@*?L- z&xrS&qPcw%m6KRjCXk~p{moYO#anbLjCUYZMfba*&@9e=Gg$caCM%1nY`r89>{{MJ}~HyeUwhe=qC z^`fF~E9^IM?~LT<4)&XF#w)`y^F`*r7$ZlCER(3aDjvQZn!FQTt>!<h1FT%|Mbo-p{rk~uYg18>@^(G zl>gl$5~e0V`_uK>Z@%)!J?{(W{bE}#w(vlpt;Pe7$N&V3mC&MRLnpv6l-WEq6|IDD zMnK8!M?z{U#*ES)gbc_{;d;7~o~#WkHTp~yeWyIHhdwb7K0|uxv@ZrU>IHmcOV-B&o;B zhgL0V!4Y*E`w?Koa4;V%h!i@ECoi<7qGCW)q9$dWNad0|DbfWK=UMT9BVUH&Xi8TBbo=UldI!ag8npwOk4qRB!*81s#K<>;ylApOg`Kt$2iw1``Qejc52 zO<5a!n)ljYZ6h_Z{+jE5md4-T+?F~_=Mc-vWBU*Qq>+g$O}*zEc6%d6KMYZZXD+56!A+@hD0!1{$0vg{IUkdC%62agDF8{zUDR0*LHK z_S_K!k#n>KCw3X0&DV4_uglZZl+{4|^NhOav+8C#MN_!6A`xA+edK(tfhUrIM$TLf zSm~+H0LjZ)`8_-!(mwMc)he|!GS8P@Iol%_&PPiQ-pb_}H|fA5CwVD6^@K|uX<)K4O%){JmV;GXs5h%nWidwHqdR%^ny7+l#$s9Yr@3 zcA4)n5q)a1c9Igt%hkHDA{6g_L>{EREbk>);Yx$$ks%!oLya%A%71`M+)hlHOE`%^ zn<%@3V&82`-~`Z&KKvCY%P{+lLy1j+B!NSeT8f(ZT(pfSHk6b*vc##m{3xSdj*?#* z+rtG~S40-m%>udW2u45WhBY)uE-?)sDx))&!`z3$4gMZG11kzfOG0Z`{@QX((HX{g zfYLvUuefq6T+JRLv=%*jr_sW@7{;qj*&Vk!G*OgIwX!ummIx(i_T${a=9K90ghils zt480A!I$yG?Hb~$(jsyZ)0kf^N%Tr#@`A)g!we8>Ac#9Z)JM`wEZp~~EY_r?JP?oF z9baMSSAUmvSy;~7u3V6G?SK*Z)DW)I;ZF^5o9tbs;>1DF-)giJMAPOYg<6z*5&V~a zcoOXt8!Nj3O5w_a10Ctgsa|l_U9wVQ6TD~qJ_`FtX!Vc*eV8~(1M&e8*!#M22!Sn5T3=l7AildmrGBG*DNS1>1o z1d2xC>#=a5Q+~eK4{0i=<#xDPs>wXCTzXlW zMhe)YVWj*WCQ~#No6;{=9l>1)62Zi`{%2?r1W`InEo6#`^%A1B3I%y!MGi?*P!?x~ zV@FaHTuodbH<7~CR2+AK^0{VPq&Z>Lr$&drm;muZRae^;t|GY#m0l~VqXYg#7)CUB z@5W+IDgHGVdv4OGjkZy|fbF`9-*YqvC{iwxf?HjgJ1I-50$J8Vyi-91Nx0j$5lr$q zDZog0(z9u%I%B>+efGqUVk}$RZ`@zPeEkv=%19VsLONiDzJN$JZ z-7~7L-7|cA%7-P?38mi(6fs9^1djoW_mJTam1gR@^8J#i#8J$XT-P%79hx~dA<^AK z^H`29SG_*VKmqujfJj6LT;w|;`%{k~Yd0P|rwt_}Hn-9gy;@aIKR`o3+oJ}FRp_S{y-FREA93}Oi=}1=gY95r8F*D7$ z4=#bpt+K{gmp3%h@Itrvw9p6D+%dy5e#fILqV7hhHat35<4=2FUcK>NOERo0V6o$A1oNqpXZ}aE`u$Aok2H63VabKy{qT;_goHNXGVN{{8 z#DFwwM3Y^)r2fhW53*~x{JE@jZr^4hGq%P0czFsF4d7b2=ef$Q=MS#cEHExaZVT1{ z;~b)mF6Rx#pvcQ}7FX<)+pgDTP1+Qw&fCpgJnO-FTL=gF(1daD0d1Z~Gk#04vbLH^ zz-_hpE;yx12M?YPQz_0+Q53)fuQD6EzL7mMC?B2nrCYAaD#gS^z&n6YPBR94h?F2$ zNFoB2zHyA4&8O}bw}mF_D8FY;{p z4?a3hKOX;krgDl=qB*pCDWZDl*s#LmG<0qmYJ9LJUr>k^r=*E3MrA4yG%bNY{J89( zREs<``R!UOaguZsz^#yg3Rf-xa*Pb+A=o#a1|e}Vo$A9i%=$6in@fZw$q%G*{SUi- ziIT43lH@NdgO|V_Jt)~5)ThS2T?wcu6z_qU^68lK-2tV@I!UGkV`__gZd_g|bPA5? zX4JEIY!|!7GA>mag2_b*01e13Gwz!fjNygd&DL-@%z~jzXb7zR5gi#s5vquBAR~nA z0v04DL;9y}vK|I9) z_NtYfB|%`--8kce&w_WZYA>BOb$SEVd`fgmXx%PD1VCeMZq^l`ABT-Nv1S*N^Q@Dl z#zS%fICPOlTN{+gA~rkIp=<+NTtzk5%Sn&Q5#2zjeYl$Xo^*lgc1mWwG%7w=8Lz2ExCeS4I z4$9LU2vh+>1V_FJ`7ors;f8dcr4@uO3Iwl6DV+MUiQm6J6G-LyAEp`Cw?sI!-So7s?Avv4?ElGK3Cf~OiZ&9vuK z14!4qZ{GYIKf$`zo4PubByz8#IdWYY5X#kl@b7aD=PziKoe3=xSThGFYq8NY=Q&V- z1ekS7x$?MLJbh{q-6t~-r`|~ihY57I>jwbTE{fZkLD1Pp$;Piy%q<4e5DXOf1CfDP zC4X@q0MsZWVtYSsCuv}lCe1^L2U5`^>JEs8%l&R>#%AYZ$^3!bJAe&mzM~O(83cUw zBs{P|1Y$j;x)Lt^yoB-8H3u#Mr-+F%0SCj7jBY#v!jg5MUCRCb^7X1!A`E%cB$Gqy zDB@%kNYE~f3SG%1A<2!HD;r*S=|Tir89+?MSZ{=I@zGHB1easLuE=enJ4U6%&Pq(P ze=Wrt0Z|5>2RMYQ(tS#Gk+)GVaE8SL=912@3Fh&mSOX4O6Fm+nT>2j_P(G+8K(OA? zHG-)ZpGGVZ#Xn`r#yF)k?EQ5UhIokOOUc-o5YBxc|7|Rp2e05ds{^h{3Vt+O31v|344aIM zGm4inhn{nzaAmX&C9zj4frwDC0JnmrnAifY5%hH+ov4uoAWE<#NgB6_HhrX4^k#E-E#u$;&Q=9*~*koIscXwCwSM5;{j z&xWp|x)xT^*Ag-FBP-Q9so&RPT(D}sy9a^zy0DV`h`Q7hSI&+~rwa^Vv1JX@gsurR zwb&VOiTfZ7(i>DIK|o6=8w4!vrQ<2XmbJk042-8a1Aw?r=q7rqtO0?Z^)cWspr;`q zs%Vdcb&44xJo_`1723Rz__jz52hES+I)05n;ZrjqgM6zQxp?S318*1_$vk1(kZY( z^7_#DvKV$YC)APM#tvB zF)VtZ8Kx00qeET}4>_*WS$9B!3W=%#=p;|qq9rw2IF(H3PjrJ0miL_ky_=fYH<(%b zPW6H9_2)e1{HP3nKu|_SuU`5AQQyORjm6;-oj(!v^_d}k0G}*qWa?Odt9U2dGr^5P zCc&I#Wnh78c5P@H3=BIL0W2w*_VlWz#S+dyq66wXPy{&zP(Y#kl?*c&naqn0V-Im! zVct3kcqbKgw$(-mGhkw1ka_ehXtI49?zk*dqCU_~lB!Hjb1~u-X|2nJm0drBYD@m$bLwBhf|TkuZ^f zm}gFuIDo^P&Sg+U zP})x7RcPA<(y(?M)(wM7$61TK8pLHLaFcoFLG9`+s~KhSvofMWBYj^Pyg__~Gz^ zVrbS#zm;grG_HblLAo8oP9-#NZWhufM^z{3$3WUXaXp!-{3nNL4!8}cV&;ca=%d3VU1nt3Zibk$*NxWDo#&_+*|0lf5wV?=jBDrG`mXh=@QcmV1oxO$u)7p->W4y2zy>e5D@(8NHwYQnOtxt2>|}8N^y*? zLAVaH#{wjP5`|*22MN^&kfV^vT3GoBfg)2d0D~#z%a$(LVn&qQ_*P!*r8zUCG6=Xh z2)Hc<Dp_VfW;%qc9N}3_UXK>S6uMG{LPNv$U0AX?USRQuh@!*>kjltVfT(mB(+Zwq zg5odCBCXx1G$Wy-UE5Uv#?9=l*mm8)yx2Nk-|I@sJRLm%^SpL|459|Q&g?!}8M|UQ zJv+MwV>MeE*c@%Y;7T?k z97s`Mem7DIS@~7AlTK4UNweiV>x~Sb{@XV(9;ls!iLN^^iEjxhs!PZ&-&GZW195r+ zndNf~o5y&{3~)cb5$&+}@B{56aFCAkWD348T0K@~OkjRv+rdrAe<)I%BI2)PbzK|s z@lCV-d|y$1{46^TE;86z<-=ScRwp{iz6%o(UH|^74(U`A^(JYLS^Px7UNYX#$!tEE z8eLVw#5=>3-R9@LVgOe(L?0SjGzC!3xZ+r{(+i8_xgl9G<)?l|Op~UxGr}(IbPX0a z1bc~Q-CsQ$w%6=9msPWkij)lLN`s%BjKG*x$&BJ8m-_)4ksZrbC#k7mq + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/fonts/OpenSans-Regular-webfont.woff b/docs/fonts/OpenSans-Regular-webfont.woff new file mode 100644 index 0000000000000000000000000000000000000000..e231183dce4c7b452afc9e7799586fd285e146f4 GIT binary patch literal 22660 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 ML;wL%uV=LY00kR;TmS$7 literal 0 HcmV?d00001 diff --git a/docs/fonts/OpenSans-Semibold-webfont.eot b/docs/fonts/OpenSans-Semibold-webfont.eot new file mode 100644 index 0000000000000000000000000000000000000000..d8375dd0ab130207f023358d62ef6ff357108b7f GIT binary patch literal 20028 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 h1{iKYDIA5bqxNz + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/fonts/OpenSans-Semibold-webfont.ttf b/docs/fonts/OpenSans-Semibold-webfont.ttf new file mode 100644 index 0000000000000000000000000000000000000000..b3290843a7a3e621867b169c8487de9a8c7a8054 GIT binary patch literal 39476 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 F{{Vm_4C4R* literal 0 HcmV?d00001 diff --git a/docs/fonts/OpenSans-Semibold-webfont.woff b/docs/fonts/OpenSans-Semibold-webfont.woff new file mode 100644 index 0000000000000000000000000000000000000000..28d6adee03b8d2301e06680fce413eb94887b57e GIT binary patch literal 22908 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 G2><}8D24q1 literal 0 HcmV?d00001 diff --git a/docs/fonts/OpenSans-SemiboldItalic-webfont.eot b/docs/fonts/OpenSans-SemiboldItalic-webfont.eot new file mode 100644 index 0000000000000000000000000000000000000000..0ab1db22e69ec331510563590527277ba5d68cc1 GIT binary patch literal 20962 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 literal 0 HcmV?d00001 diff --git a/docs/fonts/OpenSans-SemiboldItalic-webfont.svg b/docs/fonts/OpenSans-SemiboldItalic-webfont.svg new file mode 100644 index 0000000..7166ec1 --- /dev/null +++ b/docs/fonts/OpenSans-SemiboldItalic-webfont.svg @@ -0,0 +1,1830 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/fonts/OpenSans-SemiboldItalic-webfont.ttf b/docs/fonts/OpenSans-SemiboldItalic-webfont.ttf new file mode 100644 index 0000000000000000000000000000000000000000..d2d6318f6640448517db133a40a6a5a36d36ed48 GIT binary patch literal 40252 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 Z0p^q`UCg`8t*mL+fyY_#j+!w<{{z_tFYy2X literal 0 HcmV?d00001 diff --git a/docs/fonts/OpenSans-SemiboldItalic-webfont.woff b/docs/fonts/OpenSans-SemiboldItalic-webfont.woff new file mode 100644 index 0000000000000000000000000000000000000000..d4dfca402e53e59ec985ff4c6312864da493fa83 GIT binary patch literal 23764 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 + + + + + Global - Documentation + + + + + + + + + + + +