├── .gitignore ├── jest.config.js ├── src ├── index.ts ├── constants.ts ├── types │ ├── payment.ts │ └── cart.ts └── paynow.ts ├── dist ├── index.js.map ├── constants.js.map ├── types │ ├── payment.js.map │ ├── payment.js │ ├── cart.js.map │ └── cart.js ├── constants.js ├── index.js ├── paynow.js.map └── paynow.js ├── tsconfig.json ├── tests ├── payment.test.ts └── cart.test.ts ├── .github └── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md ├── .travis.yml ├── package.json └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | *.lock 3 | package-lock.json 4 | test.js -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: 'ts-jest', 3 | testEnvironment: 'node', 4 | }; -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export { default as Payment } from "./types/payment" 2 | export { CartItem, default as Cart } from "./types/cart" 3 | export { Paynow, StatusResponse, InitResponse} from './paynow'; 4 | -------------------------------------------------------------------------------- /dist/index.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,2CAAoD;AAA3C,kGAAA,OAAO,OAAW;AAC3B,qCAAwD;AAA/C,gGAAA,QAAQ,OAAA;AAAE,4FAAA,OAAO,OAAQ;AAClC,mCAAgE;AAAtD,gGAAA,MAAM,OAAA;AAAE,wGAAA,cAAc,OAAA;AAAE,sGAAA,YAAY,OAAA"} -------------------------------------------------------------------------------- /dist/constants.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":";;;AAGa,QAAA,WAAW,GAAY,IAAI,CAAC;AAK5B,QAAA,cAAc,GAAY,OAAO,CAAC;AAKlC,QAAA,wBAAwB,GAAY,wDAAwD,CAAC;AAK7F,QAAA,+BAA+B,GAAW,sDAAsD,CAAC;AAE/F,QAAA,wBAAwB,GAAG,8CAA8C,CAAC;AAC1E,QAAA,gBAAgB,GAAG,4DAA4D,CAAC"} -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "paths": { "*": ["types/*"] }, 5 | "target": "es5", 6 | "module": "commonjs", 7 | "noImplicitAny": true, 8 | "removeComments": true, 9 | "preserveConstEnums": true, 10 | "outDir": "dist/", 11 | "sourceMap": true 12 | }, 13 | "include": [ 14 | "src/*" 15 | ], 16 | "exclude": [ 17 | "node_modules", 18 | ] 19 | } -------------------------------------------------------------------------------- /dist/types/payment.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"payment.js","sourceRoot":"","sources":["../../src/types/payment.ts"],"names":[],"mappings":";;AAAA,+BAAuC;AAUvC;IACI,iBAAoB,SAAiB,EAAS,SAAiB,EAAS,KAAwB;QAAxB,sBAAA,EAAA,YAAkB,cAAI,EAAE;QAA5E,cAAS,GAAT,SAAS,CAAQ;QAAS,cAAS,GAAT,SAAS,CAAQ;QAAS,UAAK,GAAL,KAAK,CAAmB;IAAI,CAAC;IAOrG,qBAAG,GAAH,UAAI,KAAa,EAAE,MAAc,EAAE,QAAkB;QACnD,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,eAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAA;QACrD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,sBAAI,GAAJ;QACE,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;IAC9B,CAAC;IAMD,uBAAK,GAAL;QACE,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC;IAC/B,CAAC;IACH,cAAC;AAAD,CAAC,AAxBH,IAwBG"} -------------------------------------------------------------------------------- /tests/payment.test.ts: -------------------------------------------------------------------------------- 1 | import Payment from '../src/types/payment' 2 | 3 | let payment = new Payment("test-reference", "pay@abc.xyz"); 4 | 5 | test('Payment Type Instantiation', () => { 6 | expect(payment).toBeInstanceOf(Payment); 7 | }); 8 | 9 | test('Cart Instatiation on Payment Type', () =>{ 10 | expect(payment.items).toBeDefined(); 11 | }) 12 | 13 | test('If you can add items to the the cart', () => { 14 | let initialCartLength = payment.items.length(); 15 | payment.add("Test Item", 10, 1); 16 | let cartLength = payment.items.length(); 17 | expect(initialCartLength).toBeLessThan(cartLength); 18 | }) 19 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /dist/constants.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | exports.GOOGLE_QR_PREFIX = exports.INNBUCKS_DEEPLINK_PREFIX = exports.URL_INITIATE_MOBILE_TRANSACTION = exports.URL_INITIATE_TRANSACTION = exports.RESPONSE_ERROR = exports.RESPONSE_OK = void 0; 4 | exports.RESPONSE_OK = "ok"; 5 | exports.RESPONSE_ERROR = "error"; 6 | exports.URL_INITIATE_TRANSACTION = "https://www.paynow.co.zw/interface/initiatetransaction"; 7 | exports.URL_INITIATE_MOBILE_TRANSACTION = "https://www.paynow.co.zw/interface/remotetransaction"; 8 | exports.INNBUCKS_DEEPLINK_PREFIX = "schinn.wbpycode://innbucks.co.zw?pymInnCode="; 9 | exports.GOOGLE_QR_PREFIX = "https://chart.googleapis.com/chart?cht=qr&chs=200x200&chl="; 10 | //# sourceMappingURL=constants.js.map -------------------------------------------------------------------------------- /src/constants.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Success response from Paynow 3 | */ 4 | export const RESPONSE_OK : string = "ok"; 5 | 6 | /** 7 | * Error response from Paynow 8 | */ 9 | export const RESPONSE_ERROR: string = "error"; 10 | 11 | /** 12 | * API endpoint for initiating normal web-based transactions 13 | */ 14 | export const URL_INITIATE_TRANSACTION : string = "https://www.paynow.co.zw/interface/initiatetransaction"; 15 | 16 | /** 17 | * API endpoint for initiating mobile based transactions 18 | */ 19 | export const URL_INITIATE_MOBILE_TRANSACTION: string = "https://www.paynow.co.zw/interface/remotetransaction"; 20 | 21 | export const INNBUCKS_DEEPLINK_PREFIX = "schinn.wbpycode://innbucks.co.zw?pymInnCode="; 22 | export const GOOGLE_QR_PREFIX = "https://chart.googleapis.com/chart?cht=qr&chs=200x200&chl="; 23 | 24 | -------------------------------------------------------------------------------- /dist/types/payment.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | var cart_1 = require("./cart"); 4 | var Payment = (function () { 5 | function Payment(reference, authEmail, items) { 6 | if (items === void 0) { items = new cart_1.default(); } 7 | this.reference = reference; 8 | this.authEmail = authEmail; 9 | this.items = items; 10 | } 11 | Payment.prototype.add = function (title, amount, quantity) { 12 | this.items.add(new cart_1.CartItem(title, amount, quantity)); 13 | return this; 14 | }; 15 | Payment.prototype.info = function () { 16 | return this.items.summary(); 17 | }; 18 | Payment.prototype.total = function () { 19 | return this.items.getTotal(); 20 | }; 21 | return Payment; 22 | }()); 23 | exports.default = Payment; 24 | //# sourceMappingURL=payment.js.map -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - stable 4 | - '6' 5 | deploy: 6 | provider: npm 7 | email: projects@webdevworld.com 8 | api_key: 9 | secure: DWwoL8O6ZXWZjJjoDfEwsLa9f9DKTSJJkPguB99YcJXOa0tI2IRW7HpCxcPIxs6peCPcghLcVDsgoUayhxWy3DPW1sO0BIMcw08ganYlPw/Vs/p5z/Hsvt1hKVJQls3J/RuOO5R1omEeUBHklySSYuoUUb3UBiDVexjoW3VuZdozCnpzLyCws5UutAkCCFPfdP3vrnYTIXrHNKK+/azY/ZMQYWCG5UIZ9qHZRmtvjyjJZbDhT0s81b73G3bADRjzmMsyXCMSPBzMB94SVrarV/T6Hcgnw/6x7zQuXR+UzzHYTlSUZAcHjm/jUypYJfJoYLR/XgOy56bqG2wy2oo86AhcBLAjVZiyReIvARjqwTeGPv4C0ox6b4YOk9gy+5BJp1PdkIWu4Ta2gsupgc8l3aMCxncvqElO3E3zfzv8GBSQr3KeHZFyU2I+2uoL2Mfz9ADsqCLksPphyQtQF7dU6jbK6yoJ+zuDF7ywh6m8pyW9Cy0NpX5LrGq436PNmoaeQ6sCEfJXhyZ0w+FVYFlXAtHzlWY9SLr8S0IHfPVN+r3hJYy+1fHSFGX1nazfNQxawjIqLjdnGCtX+N9WK5G684+/Zux9ROlPzFl0/lrTHm0XlTeswThlz3XZlcdecyuEI/Xh/b0GaOrYjy9a6vWpvQnNniukiLoSHz6xGVxeSiI= 10 | on: 11 | tags: true 12 | repo: paynow/Paynow-NodeJS-SDK 13 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "paynow", 3 | "version": "2.2.2", 4 | "description": "Node.JS SDK for Zimbabwe's Leading Payment Gateway, Paynow", 5 | "main": "dist/index.js", 6 | "scripts": { 7 | "test": "jest", 8 | "build:tsc": "tsc --removeComments --sourceMap", 9 | "build": "npm run build:tsc" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/paynow/Paynow-NodeJS-SDK.git" 14 | }, 15 | "license": "MIT", 16 | "keywords": [ 17 | "payment", 18 | "gateway", 19 | "paynow", 20 | "sdk", 21 | "nodejs" 22 | ], 23 | "engines": { 24 | "node": ">=14.0.0" 25 | }, 26 | "dependencies": { 27 | "axios": "^1.7.9", 28 | "js-sha512": "^0.9.0" 29 | }, 30 | "devDependencies": { 31 | "@types/jest": "^29.5.14", 32 | "@types/node": "^18.0.0", 33 | "jest": "^29.7.0", 34 | "typescript": "^5.7.3" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /src/types/payment.ts: -------------------------------------------------------------------------------- 1 | import Cart, { CartItem } from './cart' 2 | 3 | //#region Payment Class 4 | /** 5 | * @param reference unique identifier for the transaction. 6 | * @param authEmail customer's email address. 7 | * @param items items inthe user's Cart 8 | 9 | */ 10 | 11 | export default class Payment { 12 | constructor( public reference: string, public authEmail: string, public items: Cart = new Cart() ) {} 13 | 14 | /** 15 | * Adds an item to the 'shopping cart' 16 | * @param title 17 | * @param amount 18 | */ 19 | add(title: string, amount: number, quantity? : number): Payment { 20 | this.items.add(new CartItem(title, amount, quantity)) 21 | return this; 22 | } 23 | 24 | info(): string { 25 | return this.items.summary(); 26 | } 27 | 28 | /** 29 | * Get the total of the items in the cart 30 | * @returns {*|number} 31 | */ 32 | total(): number { 33 | return this.items.getTotal(); 34 | } 35 | } 36 | 37 | //#endregion 38 | -------------------------------------------------------------------------------- /dist/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | exports.InitResponse = exports.StatusResponse = exports.Paynow = exports.Cart = exports.CartItem = exports.Payment = void 0; 4 | var payment_1 = require("./types/payment"); 5 | Object.defineProperty(exports, "Payment", { enumerable: true, get: function () { return payment_1.default; } }); 6 | var cart_1 = require("./types/cart"); 7 | Object.defineProperty(exports, "CartItem", { enumerable: true, get: function () { return cart_1.CartItem; } }); 8 | Object.defineProperty(exports, "Cart", { enumerable: true, get: function () { return cart_1.default; } }); 9 | var paynow_1 = require("./paynow"); 10 | Object.defineProperty(exports, "Paynow", { enumerable: true, get: function () { return paynow_1.Paynow; } }); 11 | Object.defineProperty(exports, "StatusResponse", { enumerable: true, get: function () { return paynow_1.StatusResponse; } }); 12 | Object.defineProperty(exports, "InitResponse", { enumerable: true, get: function () { return paynow_1.InitResponse; } }); 13 | //# sourceMappingURL=index.js.map -------------------------------------------------------------------------------- /dist/types/cart.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"cart.js","sourceRoot":"","sources":["../../src/types/cart.ts"],"names":[],"mappings":";;;AAQA;IACI,kBAAmB,KAAa,EAAS,MAAc,EAAS,QAAoB;QAApB,yBAAA,EAAA,YAAoB;QAAjE,UAAK,GAAL,KAAK,CAAQ;QAAS,WAAM,GAAN,MAAM,CAAQ;QAAS,aAAQ,GAAR,QAAQ,CAAY;IAAI,CAAC;IAC3F,eAAC;AAAD,CAAC,AAFH,IAEG;AAFU,4BAAQ;AAQrB;IAEI,cAAY,MAAmB;QAA/B,iBAIC;QALM,UAAK,GAAgB,EAAE,CAAC;QAE3B,IAAI,MAAM,EAAC,CAAC;YACR,MAAM,CAAC,OAAO,CAAE,UAAA,KAAK,IAAM,KAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA,CAAC,CAAC,CAAC,CAAC;QACzD,CAAC;IACL,CAAC;IACD,qBAAM,GAAN;QACK,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC;IAC9B,CAAC;IAED,kBAAG,GAAH,UAAI,IAAa;QACf,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACtB,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAA;IAC1B,CAAC;IAED,uBAAQ,GAAR;QACE,IAAI,SAAS,GAAW,CAAC,CAAC;QAC1B,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,UAAC,IAAc;YAChC,SAAS,IAAK,IAAI,CAAC,MAAM,GAAI,IAAI,CAAC,QAAQ,CAAC;QAC7C,CAAC,CAAC,CAAC;QACH,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,sBAAO,GAAP;QACE,IAAI,OAAO,GAAG,EAAE,CAAC;QAEjB,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,UAAS,IAAI,EAAE,KAAK;YACnC,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC;QAChD,CAAC,CAAC,CAAA;QAGF,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,EAAE,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QAGhD,OAAO,OAAO,CAAC;IACjB,CAAC;IAGH,WAAC;AAAD,CAAC,AAvCH,IAuCG"} -------------------------------------------------------------------------------- /tests/cart.test.ts: -------------------------------------------------------------------------------- 1 | import Cart, { CartItem } from '../src/types/cart'; 2 | 3 | test('CartItem is 1 if quantity is not added', () => { 4 | let cartItem = new CartItem("Item", 10); 5 | expect(cartItem.quantity).toBe(1); 6 | }); 7 | 8 | test('CartItem takes value given in constructor if available', () =>{ 9 | let cartItem = new CartItem("Item", 10, 5); 10 | expect(cartItem.quantity).toBe(5); 11 | }); 12 | 13 | test("Cart Instantiation", () => { 14 | let cart = new Cart(); 15 | expect(cart.items.length).toBe(0); 16 | }); 17 | 18 | test("Adding Items to Cart", () => { 19 | let cart = new Cart(); 20 | cart.add( new CartItem("Item", 10)); 21 | cart.add( new CartItem("item2", 6)); 22 | 23 | expect(cart.items.length).toBe(2); 24 | }); 25 | 26 | test(" Cart Total Calculation", () => { 27 | let cart = new Cart(); 28 | cart.add( new CartItem("Item", 10, 5)); 29 | cart.add( new CartItem("item2", 6)); 30 | 31 | expect(cart.getTotal()).toBe(56); 32 | }); 33 | 34 | test("Cart Summary is a string", () => { 35 | let cart = new Cart(); 36 | cart.add( new CartItem("Item", 10, 5)); 37 | cart.add( new CartItem("item2", 6)); 38 | 39 | expect(cart.summary()).toBeTruthy; 40 | }); 41 | -------------------------------------------------------------------------------- /src/types/cart.ts: -------------------------------------------------------------------------------- 1 | //#region CartItem Class 2 | /** 3 | * @param title the name of the cart item 4 | * 5 | * @param amount the cost of a single unit of the item 6 | * 7 | * @param quantity the number of units of the item 8 | */ 9 | export class CartItem { 10 | constructor(public title: string, public amount: number, public quantity: number = 1 ) {} 11 | } 12 | 13 | //#endregion 14 | 15 | //#region 16 | 17 | export default class Cart { 18 | public items : CartItem[] = []; 19 | constructor(_items?: CartItem[] ){ 20 | if (_items){ 21 | _items.forEach( thing => { this.items.push(thing) }); 22 | } 23 | } 24 | length() : number { 25 | return this.items.length; 26 | } 27 | 28 | add(item:CartItem){ 29 | this.items.push(item); 30 | return this.items.length 31 | } 32 | 33 | getTotal(): number { 34 | let cartTotal: number = 0; 35 | this.items.forEach((item: CartItem) => { 36 | cartTotal += item.amount * item.quantity; 37 | }); 38 | return cartTotal; 39 | } 40 | 41 | summary(): string{ 42 | let summary = ""; 43 | 44 | this.items.forEach(function(item, index) { 45 | summary = summary.concat(item.title + ", "); 46 | }) 47 | // console.log(summary, this.items); 48 | 49 | summary = summary.substr(0, summary.length - 3); 50 | 51 | 52 | return summary; 53 | } 54 | 55 | 56 | } 57 | //#endregion -------------------------------------------------------------------------------- /dist/types/cart.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | exports.CartItem = void 0; 4 | var CartItem = (function () { 5 | function CartItem(title, amount, quantity) { 6 | if (quantity === void 0) { quantity = 1; } 7 | this.title = title; 8 | this.amount = amount; 9 | this.quantity = quantity; 10 | } 11 | return CartItem; 12 | }()); 13 | exports.CartItem = CartItem; 14 | var Cart = (function () { 15 | function Cart(_items) { 16 | var _this = this; 17 | this.items = []; 18 | if (_items) { 19 | _items.forEach(function (thing) { _this.items.push(thing); }); 20 | } 21 | } 22 | Cart.prototype.length = function () { 23 | return this.items.length; 24 | }; 25 | Cart.prototype.add = function (item) { 26 | this.items.push(item); 27 | return this.items.length; 28 | }; 29 | Cart.prototype.getTotal = function () { 30 | var cartTotal = 0; 31 | this.items.forEach(function (item) { 32 | cartTotal += item.amount * item.quantity; 33 | }); 34 | return cartTotal; 35 | }; 36 | Cart.prototype.summary = function () { 37 | var summary = ""; 38 | this.items.forEach(function (item, index) { 39 | summary = summary.concat(item.title + ", "); 40 | }); 41 | summary = summary.substr(0, summary.length - 3); 42 | return summary; 43 | }; 44 | return Cart; 45 | }()); 46 | exports.default = Cart; 47 | //# sourceMappingURL=cart.js.map -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.com/paynow/Paynow-NodeJS-SDK.svg?branch=master)](https://travis-ci.com/paynow/Paynow-NodeJS-SDK) 2 | 3 | 4 | # Node.JS SDK for Paynow Zimbabwe's API 5 | 6 | ## Sign in to Paynow and get integration details 7 | 8 | > Before you can start making requests to Paynow's API, you need to get an integration ID and integration Key from Paynow. 9 | See Documentation [Generating Integration Key and Viewing integration ID](https://developers.paynow.co.zw/docs/integration_generation.html) 10 | 11 | ## Documentation 12 | 13 | See the [Paynow QuickStart](https://developers.paynow.co.zw/docs/quickstart.html). 14 | 15 | ## Prerequisites 16 | 17 | This library has a set of prerequisites that must be met for it to work 18 | 19 | 1. Node version 0.6.0 and above 20 | 1. NPM (node's package manager, used to install the node library) 21 | 22 | ## Installation 23 | 24 | Install the library using NPM or yarn 25 | 26 | ```sh 27 | $ npm install --save paynow 28 | ``` 29 | ```sh 30 | $ yarn add paynow 31 | ``` 32 | 33 | ## Usage example 34 | 35 | ### Importing library 36 | 37 | ```javascript 38 | const { Paynow } = require("paynow"); 39 | ``` 40 | 41 | Create an instance of the Paynow class optionally setting the result and return url(s) 42 | 43 | ```javascript 44 | let paynow = new Paynow("INTEGRATION_ID", "INTEGRATION_KEY"); 45 | 46 | paynow.resultUrl = "http://example.com/gateways/paynow/update"; 47 | paynow.returnUrl = "http://example.com/return?gateway=paynow"; 48 | 49 | /* The return url can be set at later stages. 50 | You might want to do this if you want to pass data to the return url (like the reference of the transaction) */ 51 | ``` 52 | The Integration ID and Key can be optionally loaded from `PAYNOW_INTEGRATION_ID` and `PAYNOW_INTEGRATION_KEY` environment variables (respectively). An instance of the Paynow class can then be created using the following: 53 | 54 | ```javascript 55 | let paynow = new Paynow(); 56 | ``` 57 | 58 | Create a new payment passing in the reference for that payment (e.g invoice id, or anything that you can use to identify the transaction. 59 | 60 | ```javascript 61 | let payment = paynow.createPayment("Invoice 35"); 62 | ``` 63 | 64 | You can then start adding items to the payment 65 | 66 | ```javascript 67 | // Passing in the name of the item and the price of the item 68 | payment.add("Bananas", 2.5); 69 | payment.add("Apples", 3.4); 70 | ``` 71 | 72 | Once you're done building up your cart and you're finally ready to send your payment to Paynow, you can use the `send` method in the `paynow` object. 73 | 74 | ```javascript 75 | // Save the response from paynow in a variable 76 | paynow.send(payment); 77 | ``` 78 | 79 | The send method will return a `Promise`, the InitResponse object being the response from Paynow and it will contain some useful information like whether the request was successful or not. If it was, for example, it contains the url to redirect the user so they can make the payment. You can view the full list of data contained in the response in our wiki 80 | 81 | If request was successful, you should consider saving the poll url sent from Paynow in your database 82 | 83 | ```javascript 84 | paynow.send(payment).then(response => { 85 | // Check if request was successful 86 | if (response.success) { 87 | // Get the link to redirect the user to, then use it as you see fit 88 | let link = response.redirectUrl; 89 | } 90 | }); 91 | ``` 92 | 93 | > Mobile Transactions 94 | 95 | If you want to send an express (mobile) checkout request instead, the only thing that differs is the last step. You make a call to the `sendMobile` in the `paynow` object 96 | instead of the `send` method. 97 | 98 | The `sendMobile` method unlike the `send` method takes in two additional arguments i.e The phone number to send the payment request to and the mobile money method to use for the request. **Note that currently only Ecocash and OneMoney are supported** 99 | 100 | ```javascript 101 | paynow.sendMobile(payment, '0777000000', 'ecocash').then(response => { 102 | // Handle response 103 | }); 104 | ``` 105 | 106 | The response object is almost identical to the one you get if you send a normal request. With a few differences, firstly, you don't get a url to redirect to. Instead you instructions (which ideally should be shown to the user instructing them how to make payment on their mobile phone) 107 | 108 | ```javascript 109 | paynow.sendMobile( 110 | 111 | // The payment to send to Paynow 112 | payment, 113 | 114 | // The phone number making payment 115 | '0777000000', 116 | 117 | // The mobile money method to use. 118 | 'ecocash' 119 | 120 | ).then(function(response) { 121 | if(response.success) { 122 | // These are the instructions to show the user. 123 | // Instruction for how the user can make payment 124 | let instructions = response.instructions // Get Payment instructions for the selected mobile money method 125 | 126 | // Get poll url for the transaction. This is the url used to check the status of the transaction. 127 | // You might want to save this, we recommend you do it 128 | let pollUrl = response.pollUrl; 129 | 130 | console.log(instructions) 131 | 132 | } else { 133 | console.log(response.error) 134 | } 135 | }).catch(ex => { 136 | // Ahhhhhhhhhhhhhhh 137 | // *freak out* 138 | console.log('Your application has broken an axle', ex) 139 | }); 140 | ``` 141 | 142 | # Checking transaction status 143 | 144 | The SDK exposes a handy method that you can use to check the status of a transaction. Once you have instantiated the Paynow class. 145 | 146 | ```javascript 147 | // Check the status of the transaction with the specified pollUrl 148 | // Now you see why you need to save that url ;-) 149 | let status = paynow.pollTransaction(pollUrl); 150 | 151 | if (status.paid()) { 152 | // Yay! Transaction was paid for 153 | } else { 154 | console.log("Why you no pay?"); 155 | } 156 | ``` 157 | 158 | ## Full Usage Example 159 | 160 | ```javascript 161 | // Require in the Paynow class 162 | const { Paynow } = require("paynow"); 163 | 164 | // Create instance of Paynow class 165 | let paynow = new Paynow("INTEGRATION_ID", "INTEGRATION_KEY"); 166 | 167 | // Set return and result urls 168 | paynow.resultUrl = "http://example.com/gateways/paynow/update"; 169 | paynow.returnUrl = "http://example.com/return?gateway=paynow"; 170 | 171 | // Create a new payment 172 | let payment = paynow.createPayment("Invoice 35"); 173 | 174 | // Add items to the payment list passing in the name of the item and it's price 175 | payment.add("Bananas", 2.5); 176 | payment.add("Apples", 3.4); 177 | 178 | // Send off the payment to Paynow 179 | paynow.send(payment).then( (response) => { 180 | 181 | // Check if request was successful 182 | if(response.success) { 183 | // Get the link to redirect the user to, then use it as you see fit 184 | let link = response.redirectUrl; 185 | 186 | // Save poll url, maybe (recommended)? 187 | let pollUrl = response.pollUrl; 188 | } 189 | 190 | }); 191 | ``` 192 | 193 | 194 | ## Development 195 | 196 | Fork this repository and clone to local machine 197 | -------------------------------------------------------------------------------- /dist/paynow.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"paynow.js","sourceRoot":"","sources":["../src/paynow.ts"],"names":[],"mappings":"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"} -------------------------------------------------------------------------------- /dist/paynow.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 3 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 4 | return new (P || (P = Promise))(function (resolve, reject) { 5 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 6 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 7 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 8 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 9 | }); 10 | }; 11 | var __generator = (this && this.__generator) || function (thisArg, body) { 12 | var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype); 13 | return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; 14 | function verb(n) { return function (v) { return step([n, v]); }; } 15 | function step(op) { 16 | if (f) throw new TypeError("Generator is already executing."); 17 | while (g && (g = 0, op[0] && (_ = 0)), _) try { 18 | if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; 19 | if (y = 0, t) op = [op[0] & 2, t.value]; 20 | switch (op[0]) { 21 | case 0: case 1: t = op; break; 22 | case 4: _.label++; return { value: op[1], done: false }; 23 | case 5: _.label++; y = op[1]; op = [0]; continue; 24 | case 7: op = _.ops.pop(); _.trys.pop(); continue; 25 | default: 26 | if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } 27 | if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } 28 | if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } 29 | if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } 30 | if (t[2]) _.ops.pop(); 31 | _.trys.pop(); continue; 32 | } 33 | op = body.call(thisArg, _); 34 | } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } 35 | if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; 36 | } 37 | }; 38 | Object.defineProperty(exports, "__esModule", { value: true }); 39 | exports.Paynow = exports.InitResponse = exports.StatusResponse = void 0; 40 | var payment_1 = require("./types/payment"); 41 | var constants_1 = require("./constants"); 42 | var axios_1 = require("axios"); 43 | var StatusResponse = (function () { 44 | function StatusResponse(data) { 45 | if (data.status.toLowerCase() === constants_1.RESPONSE_ERROR) { 46 | this.error = data.error; 47 | } 48 | else { 49 | this.reference = data.reference; 50 | this.amount = data.amount; 51 | this.paynowReference = data.paynowreference; 52 | this.pollUrl = data.pollurl; 53 | this.status = data.status; 54 | } 55 | } 56 | return StatusResponse; 57 | }()); 58 | exports.StatusResponse = StatusResponse; 59 | var InitResponse = (function () { 60 | function InitResponse(data) { 61 | this.status = data.status.toLowerCase(); 62 | this.success = this.status === constants_1.RESPONSE_OK; 63 | this.hasRedirect = typeof data.browserurl !== "undefined"; 64 | this.isInnbucks = false; 65 | if (!this.success) { 66 | this.error = data.error; 67 | } 68 | else { 69 | this.pollUrl = data.pollurl; 70 | if (this.hasRedirect) { 71 | this.redirectUrl = data.browserurl; 72 | } 73 | if (typeof data.instructions !== "undefined") { 74 | this.instructions = data.instructions; 75 | } 76 | if (typeof data.authorizationcode !== "undefined") { 77 | this.isInnbucks = true; 78 | this.innbucks_info = []; 79 | this.innbucks_info.push({ 80 | authorizationcode: data.authorizationcode, 81 | deep_link_url: constants_1.INNBUCKS_DEEPLINK_PREFIX + data.authorizationcode, 82 | qr_code: constants_1.GOOGLE_QR_PREFIX + data.authorizationcode, 83 | expires_at: data.authorizationexpires, 84 | }); 85 | } 86 | } 87 | } 88 | return InitResponse; 89 | }()); 90 | exports.InitResponse = InitResponse; 91 | var Paynow = (function () { 92 | function Paynow(integrationId, integrationKey, resultUrl, returnUrl) { 93 | this.integrationId = integrationId; 94 | this.integrationKey = integrationKey; 95 | this.resultUrl = resultUrl; 96 | this.returnUrl = returnUrl; 97 | } 98 | Paynow.prototype.send = function (payment) { 99 | return this.init(payment); 100 | }; 101 | Paynow.prototype.sendMobile = function (payment, phone, method) { 102 | return this.initMobile(payment, phone, method); 103 | }; 104 | Paynow.prototype.createPayment = function (reference, authEmail) { 105 | return new payment_1.default(reference, authEmail); 106 | }; 107 | Paynow.prototype.fail = function (message) { 108 | throw new Error(message); 109 | }; 110 | Paynow.prototype.init = function (payment) { 111 | var _this = this; 112 | this.validate(payment); 113 | var data = this.build(payment); 114 | return (0, axios_1.default)({ 115 | method: "POST", 116 | url: constants_1.URL_INITIATE_TRANSACTION, 117 | data: data, 118 | headers: { 119 | 'Content-Type': 'application/x-www-form-urlencoded', 120 | }, 121 | }).then(function (response) { 122 | return _this.parse(response.data); 123 | }) 124 | .catch(function (err) { 125 | console.log("An error occured while initiating transaction", err); 126 | }); 127 | }; 128 | Paynow.prototype.initMobile = function (payment, phone, method) { 129 | return __awaiter(this, void 0, void 0, function () { 130 | var data, response, err_1; 131 | return __generator(this, function (_a) { 132 | switch (_a.label) { 133 | case 0: 134 | this.validate(payment); 135 | if (!this.isValidEmail(payment.authEmail)) 136 | this.fail("Invalid email. Please ensure that you pass a valid email address when initiating a mobile payment"); 137 | data = this.buildMobile(payment, phone, method); 138 | _a.label = 1; 139 | case 1: 140 | _a.trys.push([1, 3, , 4]); 141 | return [4, (0, axios_1.default)({ 142 | method: "POST", 143 | url: constants_1.URL_INITIATE_MOBILE_TRANSACTION, 144 | data: data, 145 | headers: { 146 | 'Content-Type': 'application/x-www-form-urlencoded', 147 | }, 148 | })]; 149 | case 2: 150 | response = _a.sent(); 151 | return [2, this.parse(response.data)]; 152 | case 3: 153 | err_1 = _a.sent(); 154 | console.log("An error occured while initiating transaction", err_1); 155 | return [3, 4]; 156 | case 4: return [2]; 157 | } 158 | }); 159 | }); 160 | }; 161 | Paynow.prototype.isValidEmail = function (emailAddress) { 162 | if (!emailAddress || emailAddress.length === 0) 163 | return false; 164 | return /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/.test(emailAddress); 165 | }; 166 | Paynow.prototype.parse = function (response) { 167 | if (typeof response === "undefined") { 168 | return null; 169 | } 170 | if (response) { 171 | var parsedResponseURL = this.parseQuery(response); 172 | if (parsedResponseURL.status.toString().toLowerCase() !== constants_1.RESPONSE_ERROR && 173 | !this.verifyHash(parsedResponseURL)) { 174 | throw new Error("Hashes do not match!"); 175 | } 176 | return new InitResponse(parsedResponseURL); 177 | } 178 | else { 179 | throw new Error("An unknown error occurred"); 180 | } 181 | }; 182 | Paynow.prototype.generateHash = function (values, integrationKey) { 183 | var sha512 = require("js-sha512").sha512; 184 | var string = ""; 185 | for (var _i = 0, _a = Object.keys(values); _i < _a.length; _i++) { 186 | var key = _a[_i]; 187 | if (key !== "hash") { 188 | string += values[key]; 189 | } 190 | } 191 | string += integrationKey.toLowerCase(); 192 | return sha512(string).toUpperCase(); 193 | }; 194 | Paynow.prototype.verifyHash = function (values) { 195 | if (typeof values["hash"] === "undefined") { 196 | return false; 197 | } 198 | else { 199 | return values["hash"] === this.generateHash(values, this.integrationKey); 200 | } 201 | }; 202 | Paynow.prototype.urlEncode = function (url) { 203 | return encodeURI(url); 204 | }; 205 | Paynow.prototype.urlDecode = function (url) { 206 | return decodeURIComponent((url + "") 207 | .replace(/%(?![\da-f]{2})/gi, function () { 208 | return "%25"; 209 | }) 210 | .replace(/\+/g, "%20")); 211 | }; 212 | Paynow.prototype.parseQuery = function (queryString) { 213 | var query = {}; 214 | var pairs = (queryString[0] === "?" ? queryString.substr(1) : queryString).split("&"); 215 | for (var i = 0; i < pairs.length; i++) { 216 | var pair = pairs[i].split("="); 217 | query[this.urlDecode(pair[0])] = this.urlDecode(pair[1] || ""); 218 | } 219 | return query; 220 | }; 221 | Paynow.prototype.build = function (payment) { 222 | var data = { 223 | resulturl: this.resultUrl, 224 | returnurl: this.returnUrl, 225 | reference: payment.reference, 226 | amount: payment.total().toString(), 227 | id: this.integrationId, 228 | additionalinfo: payment.info(), 229 | authemail: typeof payment.authEmail === "undefined" ? "" : payment.authEmail, 230 | status: "Message", 231 | }; 232 | for (var _i = 0, _a = Object.keys(data); _i < _a.length; _i++) { 233 | var key = _a[_i]; 234 | if (key === "hash") 235 | continue; 236 | data[key] = this.urlEncode(data[key]); 237 | } 238 | data["hash"] = this.generateHash(data, this.integrationKey); 239 | return data; 240 | }; 241 | Paynow.prototype.buildMobile = function (payment, phone, method) { 242 | var data = { 243 | resulturl: this.resultUrl, 244 | returnurl: this.returnUrl, 245 | reference: payment.reference, 246 | amount: payment.total().toString(), 247 | id: this.integrationId, 248 | additionalinfo: payment.info(), 249 | authemail: payment.authEmail, 250 | phone: phone, 251 | method: method, 252 | status: "Message", 253 | }; 254 | for (var _i = 0, _a = Object.keys(data); _i < _a.length; _i++) { 255 | var key = _a[_i]; 256 | if (key === "hash") 257 | continue; 258 | data[key] = this.urlEncode(data[key]); 259 | } 260 | data["hash"] = this.generateHash(data, this.integrationKey); 261 | return data; 262 | }; 263 | Paynow.prototype.pollTransaction = function (url) { 264 | return __awaiter(this, void 0, void 0, function () { 265 | var response; 266 | return __generator(this, function (_a) { 267 | switch (_a.label) { 268 | case 0: return [4, (0, axios_1.default)({ 269 | method: "POST", 270 | url: url, 271 | data: null, 272 | })]; 273 | case 1: 274 | response = _a.sent(); 275 | return [2, this.parse(response.data)]; 276 | } 277 | }); 278 | }); 279 | }; 280 | Paynow.prototype.parseStatusUpdate = function (response) { 281 | if (response.length > 0) { 282 | response = this.parseQuery(response); 283 | if (!this.verifyHash(response)) { 284 | throw new Error("Hashes do not match!"); 285 | } 286 | return new StatusResponse(response); 287 | } 288 | else { 289 | throw new Error("An unknown error occurred"); 290 | } 291 | }; 292 | Paynow.prototype.validate = function (payment) { 293 | if (payment.items.length() <= 0) { 294 | this.fail("You need to have at least one item in cart"); 295 | } 296 | if (payment.total() <= 0) { 297 | this.fail("The total should be greater than zero"); 298 | } 299 | }; 300 | return Paynow; 301 | }()); 302 | exports.Paynow = Paynow; 303 | //# sourceMappingURL=paynow.js.map -------------------------------------------------------------------------------- /src/paynow.ts: -------------------------------------------------------------------------------- 1 | import Payment from "./types/payment"; 2 | import { 3 | URL_INITIATE_MOBILE_TRANSACTION, 4 | URL_INITIATE_TRANSACTION, 5 | RESPONSE_ERROR, 6 | RESPONSE_OK, 7 | GOOGLE_QR_PREFIX, 8 | INNBUCKS_DEEPLINK_PREFIX, 9 | } from "./constants"; 10 | import axios, {AxiosResponse} from "axios"; 11 | //#region StatusResponse Class 12 | /** 13 | * 14 | * @property {String} reference - merchant transaction reference . 15 | * @property {String} amount - original amount for the transaction. 16 | * @property {String} paynowReference - the Paynow transaction reference. 17 | * @property {String} pollUrl - the URL on Paynow the merchant can poll to confirm the transaction’s status. 18 | * @property {String} status - transaction status returned from paynow. 19 | * @property {String} error - error message sent from Paynow (if any). 20 | * 21 | * @param data data from the status response 22 | */ 23 | 24 | export class StatusResponse { 25 | reference: String; 26 | amount: String; 27 | paynowReference: String; 28 | pollUrl: String; 29 | status: String; 30 | error: String; 31 | 32 | constructor(data: any) { 33 | if (data.status.toLowerCase() === RESPONSE_ERROR) { 34 | this.error = data.error; 35 | } else { 36 | this.reference = data.reference; 37 | this.amount = data.amount; 38 | this.paynowReference = data.paynowreference; 39 | this.pollUrl = data.pollurl; 40 | this.status = data.status; 41 | } 42 | } 43 | } 44 | //#endregion 45 | 46 | //#region InitResponse Class 47 | /** 48 | * 49 | * @property {boolean} success - indicates if initiate request was successful or not. 50 | * @property {boolean} hasRedirect - indicates if the response has a URL to redirect to. 51 | * @property {String} redirectUrl - the URL the user should be redirected to so they can make a payment. 52 | * @property {String} error - error message sent from Paynow (if any). 53 | * @property {String} pollUrl - pollUrl sent from Paynow that can be used to check transaction status. 54 | * @property {String} instructions - instructions for USSD push for customers to dial incase of mobile money payments. 55 | * @property {String} status - status from Paynow. 56 | * 57 | * @param data - data from the Response. 58 | * 59 | */ 60 | 61 | export class InitResponse { 62 | success: boolean; 63 | hasRedirect: boolean; 64 | redirectUrl: String; 65 | error: String; 66 | pollUrl: String; 67 | instructions: String; 68 | status: String; 69 | innbucks_info: Array; 70 | isInnbucks: boolean; 71 | 72 | constructor(data: any) { 73 | this.status = data.status.toLowerCase(); 74 | this.success = this.status === RESPONSE_OK; 75 | this.hasRedirect = typeof data.browserurl !== "undefined"; 76 | this.isInnbucks = false; 77 | if (!this.success) { 78 | this.error = data.error; 79 | } else { 80 | this.pollUrl = data.pollurl; 81 | 82 | if (this.hasRedirect) { 83 | this.redirectUrl = data.browserurl; 84 | } 85 | 86 | if (typeof data.instructions !== "undefined") { 87 | this.instructions = data.instructions; 88 | } 89 | 90 | if (typeof data.authorizationcode !== "undefined") { 91 | this.isInnbucks = true; 92 | this.innbucks_info = []; 93 | this.innbucks_info.push({ 94 | authorizationcode: data.authorizationcode, 95 | deep_link_url : INNBUCKS_DEEPLINK_PREFIX + data.authorizationcode, 96 | qr_code: GOOGLE_QR_PREFIX + data.authorizationcode, 97 | expires_at: data.authorizationexpires, 98 | }); 99 | } 100 | } 101 | } 102 | } 103 | //#endregion 104 | 105 | /** 106 | * Paynow Class 107 | * 108 | * @param integrationId {String} Merchant's integration id 109 | * @param integrationKey {String} Merchant's integration key 110 | * @param resultUrl {String} Url where where transaction status will be sent 111 | * @param returnUrl {String} Url to redirect the user after payment 112 | **/ 113 | 114 | export class Paynow { 115 | constructor( 116 | public integrationId: string, 117 | public integrationKey: string, 118 | public resultUrl: string, 119 | public returnUrl: string 120 | ) {} 121 | 122 | /** 123 | * Send a payment to paynow 124 | * @param payment 125 | */ 126 | send(payment: Payment) { 127 | return this.init(payment); 128 | } 129 | 130 | /** 131 | * Send a mobile money payment to paynow 132 | * @param payment 133 | */ 134 | sendMobile(payment: Payment, phone: string, method: string) { 135 | return this.initMobile(payment, phone, method); 136 | } 137 | 138 | /** 139 | * Create a new Paynow payment 140 | * @param {String} reference This is the unique reference of the transaction 141 | * @param {String} authEmail This is the email address of the person making payment. Required for mobile transactions 142 | * @returns {Payment} 143 | */ 144 | createPayment(reference: string, authEmail: string): Payment { 145 | return new Payment(reference, authEmail); 146 | } 147 | 148 | /** 149 | * Throw an exception with the given message 150 | * @param message* 151 | * @returns void 152 | */ 153 | fail(message: string): Error { 154 | throw new Error(message); 155 | } 156 | 157 | /** 158 | * Initialize a new transaction with PayNow 159 | * @param payment 160 | * @returns {PromiseLike | Promise} 161 | */ 162 | init(payment: Payment) { 163 | this.validate(payment); 164 | let data = this.build(payment); 165 | return axios({ 166 | method: "POST", 167 | url: URL_INITIATE_TRANSACTION, 168 | data: data, 169 | headers: { 170 | 'Content-Type': 'application/x-www-form-urlencoded', 171 | }, 172 | }).then((response) => { 173 | return this.parse(response.data); 174 | }) 175 | .catch(function (err) { 176 | console.log("An error occured while initiating transaction", err); 177 | }); 178 | } 179 | 180 | /** 181 | * Initialize a new mobile transaction with PayNow 182 | * @param {Payment} payment 183 | * @param phone - The phone number to be used for the payment 184 | * @param method - The express checkout method. 185 | * @returns {PromiseLike | Promise} the response from the initiation of the transaction 186 | */ 187 | async initMobile(payment: Payment, phone: string, method: string): Promise | Promise> { 188 | this.validate(payment); 189 | 190 | if (!this.isValidEmail(payment.authEmail)) 191 | this.fail("Invalid email. Please ensure that you pass a valid email address when initiating a mobile payment"); 192 | 193 | let data = this.buildMobile(payment, phone, method); 194 | 195 | try { 196 | const response = await axios({ 197 | method: "POST", 198 | url: URL_INITIATE_MOBILE_TRANSACTION, 199 | data: data, 200 | headers: { 201 | 'Content-Type': 'application/x-www-form-urlencoded', 202 | }, 203 | }); 204 | return this.parse(response.data); 205 | } catch (err) { 206 | console.log("An error occured while initiating transaction", err); 207 | } 208 | } 209 | 210 | /** 211 | * Validates whether an email address is valid or not 212 | * 213 | * @param {string} emailAddress The email address to validate 214 | * 215 | * @returns {boolean} A value indicating an email is valid or not 216 | */ 217 | isValidEmail(emailAddress: string) { 218 | if (!emailAddress || emailAddress.length === 0) return false; 219 | 220 | return /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/.test(emailAddress); 221 | } 222 | 223 | /** 224 | * Parses the response from Paynow 225 | * @param response 226 | * @returns {InitResponse} 227 | */ 228 | parse(response: Response) { 229 | if (typeof response === "undefined") { 230 | return null; 231 | } 232 | if (response) { 233 | let parsedResponseURL = this.parseQuery(response as unknown as string); 234 | 235 | if ( 236 | parsedResponseURL.status.toString().toLowerCase() !== RESPONSE_ERROR && 237 | !this.verifyHash(parsedResponseURL) 238 | ) { 239 | throw new Error("Hashes do not match!"); 240 | } 241 | 242 | return new InitResponse(parsedResponseURL); 243 | } else { 244 | throw new Error("An unknown error occurred"); 245 | } 246 | } 247 | 248 | /** 249 | * Creates a SHA512 hash of the transactions 250 | * @param values 251 | * @param integrationKey 252 | * @returns {string} 253 | */ 254 | generateHash(values: { [key: string]: string }, integrationKey: String) { 255 | let sha512 = require("js-sha512").sha512; 256 | let string: string = ""; 257 | 258 | for (const key of Object.keys(values)) { 259 | if (key !== "hash") { 260 | string += values[key]; 261 | } 262 | } 263 | 264 | string += integrationKey.toLowerCase(); 265 | 266 | return sha512(string).toUpperCase(); 267 | } 268 | 269 | /** 270 | * Verify hashes at all interactions with server 271 | * @param {*} values 272 | */ 273 | verifyHash(values: { [key: string]: string }) { 274 | if (typeof values["hash"] === "undefined") { 275 | return false; 276 | } else { 277 | return values["hash"] === this.generateHash(values, this.integrationKey); 278 | } 279 | } 280 | 281 | /** 282 | * URL encodes the given string 283 | * @param str {String} 284 | * @returns {String} 285 | */ 286 | urlEncode(url: string) { 287 | return encodeURI(url); 288 | } 289 | 290 | /** 291 | * URL decodes the given string 292 | * @param str {String} 293 | * @returns {String} 294 | */ 295 | urlDecode(url: string) { 296 | return decodeURIComponent( 297 | (url + "") 298 | .replace(/%(?![\da-f]{2})/gi, function () { 299 | return "%25"; 300 | }) 301 | .replace(/\+/g, "%20") 302 | ); 303 | } 304 | 305 | /** 306 | * Parse responses from Paynow 307 | * @param queryString 308 | */ 309 | parseQuery(queryString: string) { 310 | let query: { [key: string]: string } = {}; 311 | let pairs = ( 312 | queryString[0] === "?" ? queryString.substr(1) : queryString 313 | ).split("&"); 314 | for (let i = 0; i < pairs.length; i++) { 315 | let pair = pairs[i].split("="); 316 | query[this.urlDecode(pair[0])] = this.urlDecode(pair[1] || ""); 317 | } 318 | 319 | // if(!this.verifyHash(query)) 320 | // throw new Error("Hash mismatch"); 321 | return query; 322 | } 323 | 324 | /** 325 | * Build up a payment into the format required by Paynow 326 | * @param payment 327 | * @returns {{resulturl: String, returnurl: String, reference: String, amount: number, id: String, additionalinfo: String, authemail: String, status: String}} 328 | */ 329 | build(payment: Payment) { 330 | let data: { [key: string]: string } = { 331 | resulturl: this.resultUrl, 332 | returnurl: this.returnUrl, 333 | reference: payment.reference, 334 | amount: payment.total().toString(), 335 | id: this.integrationId, 336 | additionalinfo: payment.info(), 337 | authemail: 338 | typeof payment.authEmail === "undefined" ? "" : payment.authEmail, 339 | status: "Message", 340 | }; 341 | 342 | for (const key of Object.keys(data)) { 343 | if (key === "hash") continue; 344 | 345 | data[key] = this.urlEncode(data[key]); 346 | } 347 | 348 | data["hash"] = this.generateHash(data, this.integrationKey); 349 | 350 | return data; 351 | } 352 | 353 | /** 354 | * Build up a mobile payment into the format required by Paynow 355 | * @param payment 356 | * @returns {{resulturl: String, returnurl: String, reference: String, amount: number, id: String, additionalinfo: String, authemail: String, status: String}} 357 | */ 358 | buildMobile( 359 | payment: Payment, 360 | phone: string, 361 | method: string 362 | ): Error | { [key: string]: string } { 363 | let data: { [key: string]: string } = { 364 | resulturl: this.resultUrl, 365 | returnurl: this.returnUrl, 366 | reference: payment.reference, 367 | amount: payment.total().toString(), 368 | id: this.integrationId, 369 | additionalinfo: payment.info(), 370 | authemail: payment.authEmail, 371 | phone: phone, 372 | method: method, 373 | status: "Message", 374 | }; 375 | 376 | for (const key of Object.keys(data)) { 377 | if (key === "hash") continue; 378 | 379 | data[key] = this.urlEncode(data[key]); 380 | } 381 | 382 | data["hash"] = this.generateHash(data, this.integrationKey); 383 | 384 | return data; 385 | } 386 | 387 | /** 388 | * Check the status of a transaction 389 | * @param url 390 | * @returns {PromiseLike | Promise} 391 | */ 392 | public async pollTransaction(url: string): Promise | Promise> { 393 | let response = await axios({ 394 | method: "POST", 395 | url: url, 396 | data: null, 397 | }); 398 | return this.parse(response.data); 399 | } 400 | 401 | /** 402 | * Parses the response from Paynow 403 | * @param response 404 | * @returns {StatusResponse} 405 | */ 406 | parseStatusUpdate(response: any): StatusResponse { 407 | if (response.length > 0) { 408 | response = this.parseQuery(response); 409 | 410 | if (!this.verifyHash(response)) { 411 | throw new Error("Hashes do not match!"); 412 | } 413 | 414 | return new StatusResponse(response); 415 | } else { 416 | throw new Error("An unknown error occurred"); 417 | } 418 | } 419 | 420 | /** 421 | * Validates an outgoing request before sending it to Paynow (data sanity checks) 422 | * @param payment 423 | */ 424 | validate(payment: Payment) { 425 | if (payment.items.length() <= 0) { 426 | this.fail("You need to have at least one item in cart"); 427 | } 428 | 429 | if (payment.total() <= 0) { 430 | this.fail("The total should be greater than zero"); 431 | } 432 | } 433 | } 434 | --------------------------------------------------------------------------------