paystack-node
Advanced tools
Comparing version 0.2.5 to 0.2.6
@@ -0,1 +1,9 @@ | ||
<a name="0.2.6"></a> | ||
# 0.2.6 (2021-02-27) | ||
### Features Added | ||
- Updated `lodash` dependency to _v4.17.21_ from _v4.17.19_ to fix ReDoS vulnerability | ||
- Beta: Added ability to mock `PayStack` instance by [@isocroft](https://github.com/isocroft) | ||
- Added _Dedicated Nuban_ API Endpoint methods by [@isocroft](https://github.com/isocroft) | ||
<a name="0.2.5"></a> | ||
@@ -2,0 +10,0 @@ # 0.2.5 (2020-07-18) |
@@ -5,3 +5,3 @@ 'use strict' | ||
PayStack.prototype.version = '0.2.4' | ||
PayStack.prototype.version = '0.2.6' | ||
@@ -8,0 +8,0 @@ const Fees = function (cap, additionalCharge, percentage, threshold) { |
{ | ||
"name": "paystack-node", | ||
"version": "0.2.5", | ||
"version": "0.2.6", | ||
"description": "A NodeJS wrapper for the Paystack API", | ||
@@ -33,3 +33,3 @@ "main": "index.js", | ||
"got": "^9.3.2", | ||
"lodash": "^4.17.19" | ||
"lodash": "^4.17.21" | ||
}, | ||
@@ -36,0 +36,0 @@ "devDependencies": { |
@@ -42,3 +42,3 @@ # Paystack | ||
const promise0 = paystack.getSettlements({ | ||
const promise_0 = paystack.getSettlements({ | ||
from:new Date("2017-02-09"), | ||
@@ -48,3 +48,3 @@ to:new Date() | ||
promise0.then(function(response){ | ||
promise_0.then(function(response){ | ||
var data = response.body.data; | ||
@@ -201,2 +201,68 @@ }).catch(function (error){ | ||
### Mocking the Instance (for Unit Tests) | ||
>Setting up mocks for testing with the paystack instance is now as easy as fliping a switch like so: | ||
```js | ||
let PayStack = require('paystack-node') | ||
let APIKEY = 'sk_live_2hWyQ6HW73jS8p1IkXmSWOlE4y9Inhgyd6g5f2R7' | ||
const environment = process.env.NODE_ENV | ||
const paystack = new PayStack(APIKEY, environment) | ||
// call the real API methods | ||
const { body } = paystack.chargeCard({ | ||
card:{ | ||
number: '5399837841116788', // mastercard | ||
cvv: '324', | ||
expiry_year: '2024', | ||
expiry_month: '08' | ||
}, | ||
email: 'me.biodunch@xyz.ng', | ||
amount: 15600000 // 156,000 Naira in kobo | ||
}) | ||
// mocking call made on the constructor | ||
// start mocking | ||
PayStack.engageMock() | ||
// call the mock API methods | ||
const { body } = await paystack.chargeBank({ | ||
bank: { | ||
code: "050", // Eco Bank | ||
account_number: "0000000000" | ||
}, | ||
email: 'me.biodunch@xyz.ng', | ||
amount: 1850000 // 18,500 Naira in kobo | ||
}) | ||
// replace mocked methods (! don't use arrow functions !) | ||
PayStack.mockMacro( | ||
'getCustomers', | ||
async function getCustomers (reqPayload = {}) { | ||
if (!(reqPayload instanceof Object)) { | ||
throw new TypeError( | ||
'Argument: [ requestParam(s) ] Should Be An Object Literal' | ||
) | ||
} | ||
// validate {reqPayload} key/value pairs | ||
if (!reqPayload.customer_id | ||
|| typeof reqPayload.customer_id !== 'string') { | ||
return new TypeError(`param: "customer_id" is not of type ${typeof reqPayload.customer_id}; please provided as needed`) | ||
} | ||
// @TODO: connect to a in-memory db (redis) for mocking purposes | ||
return { body: { statsu: true, data: reqPayload } }; | ||
}) | ||
const { body } = await paystack.getCustomers({ | ||
customer_id:'CUS_e24m6SqA6g3Jk889o21' | ||
}) | ||
// stop mocking | ||
// mocking call made on the constructor | ||
PayStack.disengageMock() | ||
``` | ||
## API Resources | ||
@@ -215,2 +281,7 @@ | ||
- paystack.listDisputes() | ||
- dedicated nuban | ||
- paystack.createDedicatedNuban() | ||
- paystack.listDedicatedNubans() | ||
- paystack.fetchDedicatedNuban() | ||
- paystack.deactivateDedicatedNuban() | ||
- invoices | ||
@@ -217,0 +288,0 @@ - paystack.createInvoice() |
@@ -21,7 +21,10 @@ 'use strict' | ||
const verifications = require('../endpoints/verifications.js') | ||
const dedicatedNuban = require('../endpoints/dedicated_nuban.js') | ||
const miscellaneous = require('../endpoints/miscellaneous.js') | ||
const settlements = require('../endpoints/settlements.js') | ||
const subscriptions = require('../endpoints/subscriptions') | ||
const subscriptions = require('../endpoints/subscriptions.js') | ||
const controlPanelForSessions = require('../endpoints/control_panel_for_sessions.js') | ||
const Mockable = require('./extension/Mockable.js') | ||
/* Any param with '$' at the end is a REQUIRED param both for request body param(s) and request route params */ | ||
@@ -44,2 +47,3 @@ const apiEndpoints = Object.assign( | ||
miscellaneous, | ||
dedicatedNuban, | ||
settlements, | ||
@@ -206,3 +210,3 @@ subscriptions, | ||
if (httpReqOptions[label][param] === null) { | ||
throw new Error(`param: "${param}" is not of type ${_type.name || _type}; please provided as needed`) | ||
throw new TypeError(`param: "${param}" is not of type ${_type.name || _type}; please provided as needed`) | ||
} | ||
@@ -223,3 +227,3 @@ } | ||
const makeMethod = function (config) { | ||
const makeMethod = function (config, methodName) { | ||
let httpConfig = { | ||
@@ -246,3 +250,5 @@ headers: { | ||
if (!(requestParams instanceof Object)) { | ||
throw new TypeError('Argument: [ requestParam(s) ] Should Be An Object Literal') | ||
throw new TypeError( | ||
'Argument: [ requestParam(s) ] Should Be An Object Literal' | ||
) | ||
} | ||
@@ -260,3 +266,5 @@ | ||
if (config.params !== null || config.route_params !== null) { | ||
throw new TypeError('Argument: [ requestParam(s) ] Not Meant To Be Empty!') | ||
throw new TypeError( | ||
'Argument: [ requestParam(s) ] Not Meant To Be Empty!' | ||
) | ||
} | ||
@@ -269,10 +277,65 @@ } | ||
let reqBody = {} | ||
for (let type in payload) { | ||
if (payload.hasOwnProperty(type)) { | ||
httpConfig[type] = (type === 'query') ? payload[type] : JSON.parse(payload[type]) | ||
reqBody = httpConfig[type] = (type === 'query') | ||
? payload[type] | ||
: JSON.parse(payload[type]) | ||
break | ||
} | ||
} | ||
let reqVerb = config.method.toLowerCase() | ||
const reqVerb = config.method.toLowerCase() | ||
const canInvokeTestingMock = ( | ||
this._mock !== null && | ||
typeof this._mock[methodName] === 'function' | ||
) | ||
if (this._mock !== null && | ||
canInvokeTestingMock) { | ||
throw new TypeError( | ||
typeof this._mock[methodName] + | ||
' is not a function' | ||
) | ||
} | ||
if (canInvokeTestingMock) { | ||
if (methodName !== 'chargeBank' && | ||
methodName !== 'chargeCard') { | ||
return this._mock[methodName]( | ||
Object.assign( | ||
httpConfig, | ||
{ 'method': config.method } | ||
)) | ||
} else if (isTypeOf(reqBody.card, Object) || | ||
isTypeOf(reqBody.bank, Object)) { | ||
/* eslint-disable camelcase */ | ||
const { cvv, expiry_month, expiry_year } = reqBody.card | ||
const { code, account_number } = reqBody.bank | ||
/* eslint-enable camelcase */ | ||
// Visa OR Verve | ||
const isTestCardPan = /^408408(4084084081|0000000409|0000005408)$/.test(reqBody.card.number) | ||
const isTestCardCVV = String(cvv) === '408' | ||
const isTestCardExpiry = String(expiry_month) === '02' && String(expiry_year) === '22' | ||
const isTestCard = (isTestCardPan && isTestCardCVV && isTestCardExpiry) | ||
// Zenith Bank OR First Bank | ||
const isTestBankCode = /^(?:057|011)$/.test(String(code)) | ||
/* eslint-disable-next-line camelcase */ | ||
const isTestBankAccount = account_number === '0000000000' | ||
const isTestBank = (isTestBankCode && isTestBankAccount) | ||
if (!isTestCard || !isTestBank) { | ||
return this._mock[methodName]( | ||
Object.assign( | ||
httpConfig, | ||
{ 'method': config.method } | ||
) | ||
) | ||
} | ||
} | ||
} | ||
return this.httpBaseClient[reqVerb](pathname, httpConfig) | ||
@@ -282,16 +345,6 @@ } | ||
class PayStack { | ||
constructor (apiKey, appEnv = 'development') { | ||
const environment = /^(?:development|local|dev)$/ | ||
this.api_base = { | ||
sandbox: 'https://api.paystack.co', | ||
live: 'https://api.paystack.co' | ||
} | ||
this.httpClientBaseOptions = { | ||
baseUrl: environment.test(appEnv) ? this.api_base.sandbox : this.api_base.live, | ||
headers: { | ||
'Authorization': `Bearer ${apiKey}` | ||
}, | ||
class PayStack extends Mockable { | ||
get httpClientBaseOptions () { | ||
return { | ||
headers: { }, | ||
hooks: { | ||
@@ -315,3 +368,3 @@ beforeResponse: [ | ||
afterResponse: [ | ||
(response, retryWithMergedOptions) => { | ||
(response) => { | ||
let errorMessage = '' | ||
@@ -334,8 +387,12 @@ switch (response.statusCode) { | ||
if (response.body && response.body.status === false) { | ||
errorMessage += '; ' + response.body.message | ||
errorMessage += '; {' + response.body.message + '}' | ||
} | ||
if (errorMessage !== '') { | ||
// console.error('PayStack Error: ', errorMessage); | ||
throw new Error(errorMessage) | ||
const error = new Error(errorMessage) | ||
if (response._isMocked) { | ||
error.response = response | ||
} | ||
error.name = 'PayStackAPIError' | ||
throw error | ||
} | ||
@@ -349,4 +406,19 @@ | ||
} | ||
} | ||
this.httpBaseClient = got.extend(this.httpClientBaseOptions) | ||
constructor (apiKey, appEnv = 'development') { | ||
super() | ||
const environment = /^(?:development|local|dev)$/ | ||
const apiBase = { | ||
sandbox: 'https://api.paystack.co', | ||
live: 'https://api.paystack.co' | ||
} | ||
const clientOptions = this.httpClientBaseOptions | ||
clientOptions.baseUrl = environment.test(appEnv) ? apiBase.sandbox : apiBase.live | ||
clientOptions.headers['Authorization'] = `Bearer ${apiKey}` | ||
this.httpBaseClient = got.extend(clientOptions) | ||
} | ||
@@ -363,6 +435,11 @@ | ||
if (apiEndpoints.hasOwnProperty(methodName)) { | ||
PayStack.prototype[methodName] = makeMethod(apiEndpoints[methodName]) | ||
PayStack.prototype[methodName] = makeMethod(apiEndpoints[methodName], methodName) | ||
} | ||
} | ||
PayStack.excludeOnMock = [ | ||
'httpClientBaseOptions', | ||
'version' | ||
] | ||
module.exports = PayStack |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
61663
27
1581
397
Updatedlodash@^4.17.21