| --- | ||
| name: Bug report | ||
| about: Create a report to help us improve | ||
| title: '' | ||
| labels: '' | ||
| assignees: '' | ||
| --- | ||
| **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. |
| --- | ||
| name: Feature request | ||
| about: Suggest an idea for this project | ||
| title: '' | ||
| labels: '' | ||
| assignees: '' | ||
| --- | ||
| **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. |
| <?xml version="1.0" encoding="UTF-8"?> | ||
| <project version="4"> | ||
| <component name="ProjectModuleManager"> | ||
| <modules> | ||
| <module fileurl="file://$PROJECT_DIR$/.idea/Paynow-NodeJS-SDK.iml" filepath="$PROJECT_DIR$/.idea/Paynow-NodeJS-SDK.iml" /> | ||
| </modules> | ||
| </component> | ||
| </project> |
Sorry, the diff of this file is not supported yet
| <?xml version="1.0" encoding="UTF-8"?> | ||
| <project version="4"> | ||
| <component name="VcsDirectoryMappings"> | ||
| <mapping directory="" vcs="Git" /> | ||
| </component> | ||
| </project> |
+12
| language: node_js | ||
| node_js: | ||
| - stable | ||
| - '6' | ||
| deploy: | ||
| provider: npm | ||
| email: projects@webdevworld.com | ||
| api_key: | ||
| secure: DWwoL8O6ZXWZjJjoDfEwsLa9f9DKTSJJkPguB99YcJXOa0tI2IRW7HpCxcPIxs6peCPcghLcVDsgoUayhxWy3DPW1sO0BIMcw08ganYlPw/Vs/p5z/Hsvt1hKVJQls3J/RuOO5R1omEeUBHklySSYuoUUb3UBiDVexjoW3VuZdozCnpzLyCws5UutAkCCFPfdP3vrnYTIXrHNKK+/azY/ZMQYWCG5UIZ9qHZRmtvjyjJZbDhT0s81b73G3bADRjzmMsyXCMSPBzMB94SVrarV/T6Hcgnw/6x7zQuXR+UzzHYTlSUZAcHjm/jUypYJfJoYLR/XgOy56bqG2wy2oo86AhcBLAjVZiyReIvARjqwTeGPv4C0ox6b4YOk9gy+5BJp1PdkIWu4Ta2gsupgc8l3aMCxncvqElO3E3zfzv8GBSQr3KeHZFyU2I+2uoL2Mfz9ADsqCLksPphyQtQF7dU6jbK6yoJ+zuDF7ywh6m8pyW9Cy0NpX5LrGq436PNmoaeQ6sCEfJXhyZ0w+FVYFlXAtHzlWY9SLr8S0IHfPVN+r3hJYy+1fHSFGX1nazfNQxawjIqLjdnGCtX+N9WK5G684+/Zux9ROlPzFl0/lrTHm0XlTeswThlz3XZlcdecyuEI/Xh/b0GaOrYjy9a6vWpvQnNniukiLoSHz6xGVxeSiI= | ||
| on: | ||
| tags: true | ||
| repo: paynow/Paynow-NodeJS-SDK |
| module.exports = { | ||
| preset: 'ts-jest', | ||
| testEnvironment: 'node', | ||
| }; |
| /** | ||
| * Success response from Paynow | ||
| */ | ||
| export const RESPONSE_OK : string = "ok"; | ||
| /** | ||
| * Error response from Paynow | ||
| */ | ||
| export const RESPONSE_ERROR: string = "error"; | ||
| /** | ||
| * API endpoint for initiating normal web-based transactions | ||
| */ | ||
| export const URL_INITIATE_TRANSACTION : string = "https://www.paynow.co.zw/interface/initiatetransaction"; | ||
| /** | ||
| * API endpoint for initiating mobile based transactions | ||
| */ | ||
| export const URL_INITIATE_MOBILE_TRANSACTION: string = "https://www.paynow.co.zw/interface/remotetransaction"; | ||
| export const INNBUCKS_DEEPLINK_PREFIX = "schinn.wbpycode://innbucks.co.zw?pymInnCode="; | ||
| export const GOOGLE_QR_PREFIX = "https://chart.googleapis.com/chart?cht=qr&chs=200x200&chl="; | ||
| export { default as Payment } from "./types/payment" | ||
| export { CartItem, default as Cart } from "./types/cart" | ||
| export { Paynow, StatusResponse, InitResponse} from './paynow'; |
+433
| import Payment from "./types/payment"; | ||
| import { | ||
| URL_INITIATE_MOBILE_TRANSACTION, | ||
| URL_INITIATE_TRANSACTION, | ||
| RESPONSE_ERROR, | ||
| RESPONSE_OK, | ||
| GOOGLE_QR_PREFIX, | ||
| INNBUCKS_DEEPLINK_PREFIX, | ||
| } from "./constants"; | ||
| import axios, {AxiosResponse} from "axios"; | ||
| //#region StatusResponse Class | ||
| /** | ||
| * | ||
| * @property {String} reference - merchant transaction reference . | ||
| * @property {String} amount - original amount for the transaction. | ||
| * @property {String} paynowReference - the Paynow transaction reference. | ||
| * @property {String} pollUrl - the URL on Paynow the merchant can poll to confirm the transaction’s status. | ||
| * @property {String} status - transaction status returned from paynow. | ||
| * @property {String} error - error message sent from Paynow (if any). | ||
| * | ||
| * @param data data from the status response | ||
| */ | ||
| export class StatusResponse { | ||
| reference: String; | ||
| amount: String; | ||
| paynowReference: String; | ||
| pollUrl: String; | ||
| status: String; | ||
| error: String; | ||
| constructor(data: any) { | ||
| if (data.status.toLowerCase() === RESPONSE_ERROR) { | ||
| this.error = data.error; | ||
| } else { | ||
| this.reference = data.reference; | ||
| this.amount = data.amount; | ||
| this.paynowReference = data.paynowreference; | ||
| this.pollUrl = data.pollurl; | ||
| this.status = data.status; | ||
| } | ||
| } | ||
| } | ||
| //#endregion | ||
| //#region InitResponse Class | ||
| /** | ||
| * | ||
| * @property {boolean} success - indicates if initiate request was successful or not. | ||
| * @property {boolean} hasRedirect - indicates if the response has a URL to redirect to. | ||
| * @property {String} redirectUrl - the URL the user should be redirected to so they can make a payment. | ||
| * @property {String} error - error message sent from Paynow (if any). | ||
| * @property {String} pollUrl - pollUrl sent from Paynow that can be used to check transaction status. | ||
| * @property {String} instructions - instructions for USSD push for customers to dial incase of mobile money payments. | ||
| * @property {String} status - status from Paynow. | ||
| * | ||
| * @param data - data from the Response. | ||
| * | ||
| */ | ||
| export class InitResponse { | ||
| success: boolean; | ||
| hasRedirect: boolean; | ||
| redirectUrl: String; | ||
| error: String; | ||
| pollUrl: String; | ||
| instructions: String; | ||
| status: String; | ||
| innbucks_info: Array<any>; | ||
| isInnbucks: boolean; | ||
| constructor(data: any) { | ||
| this.status = data.status.toLowerCase(); | ||
| this.success = this.status === RESPONSE_OK; | ||
| this.hasRedirect = typeof data.browserurl !== "undefined"; | ||
| this.isInnbucks = false; | ||
| if (!this.success) { | ||
| this.error = data.error; | ||
| } else { | ||
| this.pollUrl = data.pollurl; | ||
| if (this.hasRedirect) { | ||
| this.redirectUrl = data.browserurl; | ||
| } | ||
| if (typeof data.instructions !== "undefined") { | ||
| this.instructions = data.instructions; | ||
| } | ||
| if (typeof data.authorizationcode !== "undefined") { | ||
| this.isInnbucks = true; | ||
| this.innbucks_info = []; | ||
| this.innbucks_info.push({ | ||
| authorizationcode: data.authorizationcode, | ||
| deep_link_url : INNBUCKS_DEEPLINK_PREFIX + data.authorizationcode, | ||
| qr_code: GOOGLE_QR_PREFIX + data.authorizationcode, | ||
| expires_at: data.authorizationexpires, | ||
| }); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| //#endregion | ||
| /** | ||
| * Paynow Class | ||
| * | ||
| * @param integrationId {String} Merchant's integration id | ||
| * @param integrationKey {String} Merchant's integration key | ||
| * @param resultUrl {String} Url where where transaction status will be sent | ||
| * @param returnUrl {String} Url to redirect the user after payment | ||
| **/ | ||
| export class Paynow { | ||
| constructor( | ||
| public integrationId: string, | ||
| public integrationKey: string, | ||
| public resultUrl: string, | ||
| public returnUrl: string | ||
| ) {} | ||
| /** | ||
| * Send a payment to paynow | ||
| * @param payment | ||
| */ | ||
| send(payment: Payment) { | ||
| return this.init(payment); | ||
| } | ||
| /** | ||
| * Send a mobile money payment to paynow | ||
| * @param payment | ||
| */ | ||
| sendMobile(payment: Payment, phone: string, method: string) { | ||
| return this.initMobile(payment, phone, method); | ||
| } | ||
| /** | ||
| * Create a new Paynow payment | ||
| * @param {String} reference This is the unique reference of the transaction | ||
| * @param {String} authEmail This is the email address of the person making payment. Required for mobile transactions | ||
| * @returns {Payment} | ||
| */ | ||
| createPayment(reference: string, authEmail: string): Payment { | ||
| return new Payment(reference, authEmail); | ||
| } | ||
| /** | ||
| * Throw an exception with the given message | ||
| * @param message* | ||
| * @returns void | ||
| */ | ||
| fail(message: string): Error { | ||
| throw new Error(message); | ||
| } | ||
| /** | ||
| * Initialize a new transaction with PayNow | ||
| * @param payment | ||
| * @returns {PromiseLike<InitResponse> | Promise<InitResponse>} | ||
| */ | ||
| init(payment: Payment) { | ||
| this.validate(payment); | ||
| let data = this.build(payment); | ||
| return axios({ | ||
| method: "POST", | ||
| url: URL_INITIATE_TRANSACTION, | ||
| data: data, | ||
| headers: { | ||
| 'Content-Type': 'application/x-www-form-urlencoded', | ||
| }, | ||
| }).then((response) => { | ||
| return this.parse(response.data); | ||
| }) | ||
| .catch(function (err) { | ||
| console.log("An error occured while initiating transaction", err); | ||
| }); | ||
| } | ||
| /** | ||
| * Initialize a new mobile transaction with PayNow | ||
| * @param {Payment} payment | ||
| * @param phone - The phone number to be used for the payment | ||
| * @param method - The express checkout method. | ||
| * @returns {PromiseLike<InitResponse> | Promise<InitResponse>} the response from the initiation of the transaction | ||
| */ | ||
| async initMobile(payment: Payment, phone: string, method: string): Promise<PromiseLike<InitResponse> | Promise<InitResponse>> { | ||
| this.validate(payment); | ||
| if (!this.isValidEmail(payment.authEmail)) | ||
| this.fail("Invalid email. Please ensure that you pass a valid email address when initiating a mobile payment"); | ||
| let data = this.buildMobile(payment, phone, method); | ||
| try { | ||
| const response = await axios({ | ||
| method: "POST", | ||
| url: URL_INITIATE_MOBILE_TRANSACTION, | ||
| data: data, | ||
| headers: { | ||
| 'Content-Type': 'application/x-www-form-urlencoded', | ||
| }, | ||
| }); | ||
| return this.parse(response.data); | ||
| } catch (err) { | ||
| console.log("An error occured while initiating transaction", err); | ||
| } | ||
| } | ||
| /** | ||
| * Validates whether an email address is valid or not | ||
| * | ||
| * @param {string} emailAddress The email address to validate | ||
| * | ||
| * @returns {boolean} A value indicating an email is valid or not | ||
| */ | ||
| isValidEmail(emailAddress: string) { | ||
| if (!emailAddress || emailAddress.length === 0) return false; | ||
| return /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/.test(emailAddress); | ||
| } | ||
| /** | ||
| * Parses the response from Paynow | ||
| * @param response | ||
| * @returns {InitResponse} | ||
| */ | ||
| parse(response: Response) { | ||
| if (typeof response === "undefined") { | ||
| return null; | ||
| } | ||
| if (response) { | ||
| let parsedResponseURL = this.parseQuery(response as unknown as string); | ||
| if ( | ||
| parsedResponseURL.status.toString().toLowerCase() !== RESPONSE_ERROR && | ||
| !this.verifyHash(parsedResponseURL) | ||
| ) { | ||
| throw new Error("Hashes do not match!"); | ||
| } | ||
| return new InitResponse(parsedResponseURL); | ||
| } else { | ||
| throw new Error("An unknown error occurred"); | ||
| } | ||
| } | ||
| /** | ||
| * Creates a SHA512 hash of the transactions | ||
| * @param values | ||
| * @param integrationKey | ||
| * @returns {string} | ||
| */ | ||
| generateHash(values: { [key: string]: string }, integrationKey: String) { | ||
| let sha512 = require("js-sha512").sha512; | ||
| let string: string = ""; | ||
| for (const key of Object.keys(values)) { | ||
| if (key !== "hash") { | ||
| string += values[key]; | ||
| } | ||
| } | ||
| string += integrationKey.toLowerCase(); | ||
| return sha512(string).toUpperCase(); | ||
| } | ||
| /** | ||
| * Verify hashes at all interactions with server | ||
| * @param {*} values | ||
| */ | ||
| verifyHash(values: { [key: string]: string }) { | ||
| if (typeof values["hash"] === "undefined") { | ||
| return false; | ||
| } else { | ||
| return values["hash"] === this.generateHash(values, this.integrationKey); | ||
| } | ||
| } | ||
| /** | ||
| * URL encodes the given string | ||
| * @param str {String} | ||
| * @returns {String} | ||
| */ | ||
| urlEncode(url: string) { | ||
| return encodeURI(url); | ||
| } | ||
| /** | ||
| * URL decodes the given string | ||
| * @param str {String} | ||
| * @returns {String} | ||
| */ | ||
| urlDecode(url: string) { | ||
| return decodeURIComponent( | ||
| (url + "") | ||
| .replace(/%(?![\da-f]{2})/gi, function () { | ||
| return "%25"; | ||
| }) | ||
| .replace(/\+/g, "%20") | ||
| ); | ||
| } | ||
| /** | ||
| * Parse responses from Paynow | ||
| * @param queryString | ||
| */ | ||
| parseQuery(queryString: string) { | ||
| let query: { [key: string]: string } = {}; | ||
| let pairs = ( | ||
| queryString[0] === "?" ? queryString.substr(1) : queryString | ||
| ).split("&"); | ||
| for (let i = 0; i < pairs.length; i++) { | ||
| let pair = pairs[i].split("="); | ||
| query[this.urlDecode(pair[0])] = this.urlDecode(pair[1] || ""); | ||
| } | ||
| // if(!this.verifyHash(query)) | ||
| // throw new Error("Hash mismatch"); | ||
| return query; | ||
| } | ||
| /** | ||
| * Build up a payment into the format required by Paynow | ||
| * @param payment | ||
| * @returns {{resulturl: String, returnurl: String, reference: String, amount: number, id: String, additionalinfo: String, authemail: String, status: String}} | ||
| */ | ||
| build(payment: Payment) { | ||
| let data: { [key: string]: string } = { | ||
| resulturl: this.resultUrl, | ||
| returnurl: this.returnUrl, | ||
| reference: payment.reference, | ||
| amount: payment.total().toString(), | ||
| id: this.integrationId, | ||
| additionalinfo: payment.info(), | ||
| authemail: | ||
| typeof payment.authEmail === "undefined" ? "" : payment.authEmail, | ||
| status: "Message", | ||
| }; | ||
| for (const key of Object.keys(data)) { | ||
| if (key === "hash") continue; | ||
| data[key] = this.urlEncode(data[key]); | ||
| } | ||
| data["hash"] = this.generateHash(data, this.integrationKey); | ||
| return data; | ||
| } | ||
| /** | ||
| * Build up a mobile payment into the format required by Paynow | ||
| * @param payment | ||
| * @returns {{resulturl: String, returnurl: String, reference: String, amount: number, id: String, additionalinfo: String, authemail: String, status: String}} | ||
| */ | ||
| buildMobile( | ||
| payment: Payment, | ||
| phone: string, | ||
| method: string | ||
| ): Error | { [key: string]: string } { | ||
| let data: { [key: string]: string } = { | ||
| resulturl: this.resultUrl, | ||
| returnurl: this.returnUrl, | ||
| reference: payment.reference, | ||
| amount: payment.total().toString(), | ||
| id: this.integrationId, | ||
| additionalinfo: payment.info(), | ||
| authemail: payment.authEmail, | ||
| phone: phone, | ||
| method: method, | ||
| status: "Message", | ||
| }; | ||
| for (const key of Object.keys(data)) { | ||
| if (key === "hash") continue; | ||
| data[key] = this.urlEncode(data[key]); | ||
| } | ||
| data["hash"] = this.generateHash(data, this.integrationKey); | ||
| return data; | ||
| } | ||
| /** | ||
| * Check the status of a transaction | ||
| * @param url | ||
| * @returns {PromiseLike<InitResponse> | Promise<InitResponse>} | ||
| */ | ||
| public async pollTransaction(url: string): Promise<PromiseLike<InitResponse> | Promise<InitResponse>> { | ||
| let response = await axios({ | ||
| method: "POST", | ||
| url: url, | ||
| data: null, | ||
| }); | ||
| return this.parse(response.data); | ||
| } | ||
| /** | ||
| * Parses the response from Paynow | ||
| * @param response | ||
| * @returns {StatusResponse} | ||
| */ | ||
| parseStatusUpdate(response: any): StatusResponse { | ||
| if (response.length > 0) { | ||
| response = this.parseQuery(response); | ||
| if (!this.verifyHash(response)) { | ||
| throw new Error("Hashes do not match!"); | ||
| } | ||
| return new StatusResponse(response); | ||
| } else { | ||
| throw new Error("An unknown error occurred"); | ||
| } | ||
| } | ||
| /** | ||
| * Validates an outgoing request before sending it to Paynow (data sanity checks) | ||
| * @param payment | ||
| */ | ||
| validate(payment: Payment) { | ||
| if (payment.items.length() <= 0) { | ||
| this.fail("You need to have at least one item in cart"); | ||
| } | ||
| if (payment.total() <= 0) { | ||
| this.fail("The total should be greater than zero"); | ||
| } | ||
| } | ||
| } |
| //#region CartItem Class | ||
| /** | ||
| * @param title the name of the cart item | ||
| * | ||
| * @param amount the cost of a single unit of the item | ||
| * | ||
| * @param quantity the number of units of the item | ||
| */ | ||
| export class CartItem { | ||
| constructor(public title: string, public amount: number, public quantity: number = 1 ) {} | ||
| } | ||
| //#endregion | ||
| //#region | ||
| export default class Cart { | ||
| public items : CartItem[] = []; | ||
| constructor(_items?: CartItem[] ){ | ||
| if (_items){ | ||
| _items.forEach( thing => { this.items.push(thing) }); | ||
| } | ||
| } | ||
| length() : number { | ||
| return this.items.length; | ||
| } | ||
| add(item:CartItem){ | ||
| this.items.push(item); | ||
| return this.items.length | ||
| } | ||
| getTotal(): number { | ||
| let cartTotal: number = 0; | ||
| this.items.forEach((item: CartItem) => { | ||
| cartTotal += item.amount * item.quantity; | ||
| }); | ||
| return cartTotal; | ||
| } | ||
| summary(): string{ | ||
| let summary = ""; | ||
| this.items.forEach(function(item, index) { | ||
| summary = summary.concat(item.title + ", "); | ||
| }) | ||
| // console.log(summary, this.items); | ||
| summary = summary.substr(0, summary.length - 3); | ||
| return summary; | ||
| } | ||
| } | ||
| //#endregion |
| import Cart, { CartItem } from './cart' | ||
| //#region Payment Class | ||
| /** | ||
| * @param reference unique identifier for the transaction. | ||
| * @param authEmail customer's email address. | ||
| * @param items items inthe user's Cart | ||
| */ | ||
| export default class Payment { | ||
| constructor( public reference: string, public authEmail: string, public items: Cart = new Cart() ) {} | ||
| /** | ||
| * Adds an item to the 'shopping cart' | ||
| * @param title | ||
| * @param amount | ||
| */ | ||
| add(title: string, amount: number, quantity? : number): Payment { | ||
| this.items.add(new CartItem(title, amount, quantity)) | ||
| return this; | ||
| } | ||
| info(): string { | ||
| return this.items.summary(); | ||
| } | ||
| /** | ||
| * Get the total of the items in the cart | ||
| * @returns {*|number} | ||
| */ | ||
| total(): number { | ||
| return this.items.getTotal(); | ||
| } | ||
| } | ||
| //#endregion |
| import Cart, { CartItem } from '../src/types/cart'; | ||
| test('CartItem is 1 if quantity is not added', () => { | ||
| let cartItem = new CartItem("Item", 10); | ||
| expect(cartItem.quantity).toBe(1); | ||
| }); | ||
| test('CartItem takes value given in constructor if available', () =>{ | ||
| let cartItem = new CartItem("Item", 10, 5); | ||
| expect(cartItem.quantity).toBe(5); | ||
| }); | ||
| test("Cart Instantiation", () => { | ||
| let cart = new Cart(); | ||
| expect(cart.items.length).toBe(0); | ||
| }); | ||
| test("Adding Items to Cart", () => { | ||
| let cart = new Cart(); | ||
| cart.add( new CartItem("Item", 10)); | ||
| cart.add( new CartItem("item2", 6)); | ||
| expect(cart.items.length).toBe(2); | ||
| }); | ||
| test(" Cart Total Calculation", () => { | ||
| let cart = new Cart(); | ||
| cart.add( new CartItem("Item", 10, 5)); | ||
| cart.add( new CartItem("item2", 6)); | ||
| expect(cart.getTotal()).toBe(56); | ||
| }); | ||
| test("Cart Summary is a string", () => { | ||
| let cart = new Cart(); | ||
| cart.add( new CartItem("Item", 10, 5)); | ||
| cart.add( new CartItem("item2", 6)); | ||
| expect(cart.summary()).toBeTruthy; | ||
| }); |
| import Payment from '../src/types/payment' | ||
| let payment = new Payment("test-reference", "pay@abc.xyz"); | ||
| test('Payment Type Instantiation', () => { | ||
| expect(payment).toBeInstanceOf(Payment); | ||
| }); | ||
| test('Cart Instatiation on Payment Type', () =>{ | ||
| expect(payment.items).toBeDefined(); | ||
| }) | ||
| test('If you can add items to the the cart', () => { | ||
| let initialCartLength = payment.items.length(); | ||
| payment.add("Test Item", 10, 1); | ||
| let cartLength = payment.items.length(); | ||
| expect(initialCartLength).toBeLessThan(cartLength); | ||
| }) |
| { | ||
| "compilerOptions": { | ||
| "baseUrl": ".", | ||
| "paths": { "*": ["types/*"] }, | ||
| "target": "es5", | ||
| "module": "commonjs", | ||
| "noImplicitAny": true, | ||
| "removeComments": true, | ||
| "preserveConstEnums": true, | ||
| "outDir": "dist/", | ||
| "sourceMap": true | ||
| }, | ||
| "include": [ | ||
| "src/*" | ||
| ], | ||
| "exclude": [ | ||
| "node_modules", | ||
| ] | ||
| } |
+3
-7
| { | ||
| "name": "paynow", | ||
| "version": "2.2.1", | ||
| "version": "2.2.2", | ||
| "description": "Node.JS SDK for Zimbabwe's Leading Payment Gateway, Paynow", | ||
| "main": "dist/index.js", | ||
| "types": "dist/index.d.ts", | ||
| "scripts": { | ||
| "test": "jest", | ||
| "build:tsc": "tsc", | ||
| "build:tsc": "tsc --removeComments --sourceMap", | ||
| "build": "npm run build:tsc" | ||
@@ -36,6 +35,3 @@ }, | ||
| "typescript": "^5.7.3" | ||
| }, | ||
| "files": [ | ||
| "dist/**/*" | ||
| ] | ||
| } | ||
| } |
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
55320
59.68%27
125%937
139.03%0
-100%