Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 55 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,16 @@ var paystack = require('paystack')('secret_key');
The resource methods accepts are promisified, but can receive optional callback as the last argument.

```js
// First Option
// paystack.{resource}.{method}
// First Option (with callback)
// paystack.{resource}.{method}(callback)
paystack.customer.list(function(error, body) {
console.log(error);
console.log(body);
});
```
```js
// Second Option
// paystack.{resource}
// Second Option (as promise)
// paystack.{resource}.{method}.then().catch()
paystack.customer.list()
.then(function(body) {
console.log(body);
Expand All @@ -40,7 +40,28 @@ paystack.customer.list()



For resource methods that use POST or PUT, the JSON body can be passed as the first argument.
For GET endpoints with url path parameters (e.g. https://api.paystack.co/plan/{id_or_plan_code}), pass path parameter as string or number
Separate path parameter values by comma if more than 1 path parameter and place them in order as they appear in the url path.

```js
paystack.plan.get(90)
.then(function(error, body) {
console.log(error);
console.log(body);
});
```

For GET endpoints with query string parameters (e.g. https://api.paystack.co/bank/resolve?account_number=0022728151&bank_code=063), pass paramaters as object.

```js
paystack.bank.resolve_account_number({account_number: '0022778151', bank_code: '063'})
.then(function(error, body) {
console.log(error);
console.log(body);
});
```

For POST or PUT endpoints, the JSON body should be passed as the first argument.

```js
paystack.plan.create({
Expand All @@ -54,25 +75,18 @@ paystack.plan.create({
});
```

For GET, you can pass the required ID as string and optional parameters as an optional object argument.

```js
paystack.plan.get(90)
.then(function(error, body) {
console.log(error);
console.log(body);
});
```
For POST or PUT endpoints, if the endpoint also has path parameters (e.g. https://api.paystack.co/customer/{id_or_customer_code}), pass the path parameters as explained above, before passing the JSON body object.

```js
paystack.transactions.list({perPage: 20})
.then(function(error, body) {
console.log(error);
console.log(body);
});
var customer_id = 100;
paystack.customer.update(customer_id, {last_name: 'Kehers'})
.then(function(error, body) {
console.log(error);
console.log(body);
});
```

### Resources
### Resources and Methods

- customer
- create
Expand Down Expand Up @@ -108,11 +122,31 @@ paystack.transactions.list({perPage: 20})
- list
- listBanks
- update
- bank
- list
- resolveAccountNumber
- resolveBin
- Miscellanous
- list_banks
- resolve_bin


To use any endpoint, call
```js
//using callback function
paystack.{resource}.{method}(function(err, body){
console.log(error);
console.log(body);
});

//or as promise
paystack.{resource}.{method}
.then(function(body) {
console.log(body);
})
.catch(function(error) {
console.log(error);
});
```

### Contributing
- To ensure consistent code style, please follow the [editorconfig rules](http://obem.be/2015/06/01/a-quick-note-on-editorconfig.html) in .editorconfig
Expand Down
144 changes: 75 additions & 69 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ Paystack API wrapper

'use strict';

var
var
request = require('request'),
root = 'https://api.paystack.co',
baseUrl = 'https://api.paystack.co',
acceptedMethods = [ "get", "post", "put" ],
Promise = require('promise')
;

Expand All @@ -19,23 +20,25 @@ var resources = {
subscription: require('./resources/subscription'),
subaccount: require('./resources/subaccount'),
settlements: require('./resources/settlements'),
misc: require('./resources/misc')
misc: require('./resources/misc'),
bank: require('./resources/bank')
}

function Paystack(key) {
if (!(this instanceof Paystack)) {
return new Paystack(key);
}

this.key = key;
this.key = key || process.env["PAYSTACK_SECRET_KEY"];
this.importResources();
}

Paystack.prototype = {

extend: function(params) {
extend: function(endpoint) {
// This looks more sane.
var self = this;
var secretKey = this.key;

return function(){
// Convert argument to array
var args = new Array(arguments.length);
Expand All @@ -44,90 +47,88 @@ Paystack.prototype = {
args[i] = arguments[i];
}

// Check for callback & Pull it out from the array
// Check if last argument is supplied and is a valid callback function & Pull it out from the array
var callback = l > 0 && typeof args.slice(l-1)[0] === "function" ? args.splice(l-1)[0] : undefined;

var body, qs;

// quick fix - method checking
var method = params.method in {"get":'', "post":'', "put":''}
? params.method
: (function () { throw new Error("Method not Allowed! - Resource declaration error") })()
var endpoint = [root, params.endpoint].join('');
// Checking for required params;
if(params.params) {
var paramList = params.params;

// Pull body passed
var body = args.length === 2 ? args[1] : args[0];
paramList.filter(function(item, index, array) {
if(item.indexOf("*") === -1) {
// Not required
return;
}
item = item.replace("*", "");

if(!(item in body)) {
throw new Error("Required Parameters Ommited - " + item);
}
return;

});
// method checking
if (acceptedMethods.indexOf(endpoint.method) < 0) {
throw new Error("Method - " + endpoint.method + " - not Allowed! - Resource declaration error")
}

// Get arguments in endpoint e.g {id} in customer/{id} and pull
// out from array
var argsInEndpoint = endpoint.match(/{[^}]+}/g);
var method = endpoint.method;
var url = [baseUrl, endpoint.path].join('');

// First check path parameters (e.g {id} in customer/{id}) before checking post body or query string paramters
// Pull out all path parameters from url into array
var argsInEndpoint = url.match(/{[^}]+}/g);
if (argsInEndpoint) {
l = argsInEndpoint.length;

// Do we have one or more?
if (l > 0) {
// Confirm resource declaration good
if (!Array.isArray(params.args)) {
// error
throw new Error('Resource declaration error');
}

// Confirm user passed the argument to method
// and replace in endpoint

var match, index;
for (var i=0;i<l;i++) {
match = argsInEndpoint[i].replace(/\W/g, '');
index = params.args.indexOf(match);
if (index != -1) {
if (!args[index]) {
// error
throw new Error('Resource declaration error');
}

// todo: args[index] must be string or int
endpoint = endpoint.replace(new RegExp(argsInEndpoint[i]), args[index]);
args.splice(index, 1);
//get the argument name from the path defined in resource
var argumentName = argsInEndpoint[i].replace(/\W/g, '');

if (!args[i]) {
// caller did not pass in this particular argument
throw new Error('Required path parameter ommited - ' + argumentName);
}

//args[index] must be string or int
var argumentValue = args[i];
var valueType = typeof argumentValue;
if (valueType !== 'string' && valueType !== 'number') {
throw new Error('Invalid path parameter argument for ' + argumentName + '. Expected string or number. Found ' + valueType);
}

url = url.replace(new RegExp(argsInEndpoint[i]), argumentValue);
}

//we've replaced all url path parameters with values from args
//now delete all such used values from args leaving only the optional qs/body parameters as first argument (if exist) in args
args.splice(0, l);
}
}

// Add post/put/[delete?] body
if (args[0]) {
var body, qs;

// Checking for required params;
if(endpoint.params) {
var parametersList = endpoint.params;
var parametersReceived = args[0]; //should now be first argument, having removed all path arguments

parametersList.filter(function(parameterName, index, array) {
if(parameterName.indexOf("*") === -1) {
// Not required
return;
}
parameterName = parameterName.replace("*", "");

if(!(parameterName in parametersReceived)) {
throw new Error("Required parameter ommited - " + parameterName);
}
return;
});

if (method == 'post' || method == 'put') {
// Body
body = args[0];
body = parametersReceived;
}
else if (method == 'get') {
qs = args[0];
qs = parametersReceived;
}
}

// Make request
var options = {
url: endpoint,
url: url,
json: true,
method: method.toUpperCase(),
headers: {
'Authorization': ['Bearer ', self.key].join('')
'Authorization': ['Bearer ', secretKey].join('')
}
}

Expand All @@ -144,7 +145,6 @@ Paystack.prototype = {
reject(error);
}
else if(!body.status){

// Error from API??
error = body;
body = null;
Expand All @@ -169,16 +169,22 @@ Paystack.prototype = {
},

importResources: function() {
var anon;
var resourceFunction, resource;
// Looping over all resources
for (var j in resources) {
// each resource contains a collection of endpoints
for (var resourceName in resources) {
//get the resource object
resource = resources[resourceName];
// Creating a surrogate function
anon = function(){};
// Looping over the properties of each resource
for(var i in resources[j]) {
anon.prototype[i] = this.extend(resources[j][i]);
resourceFunction = function(){};

// Looping over the endpoints of each resource
// each endpoint contains information for validating and calling the endpoint
for(var endpoint in resource) {
resourceFunction.prototype[endpoint] = this.extend(resource[endpoint]);
}
Paystack.prototype[j] = new anon();

Paystack.prototype[resourceName] = new resourceFunction();
}
}
};
Expand Down
26 changes: 26 additions & 0 deletions resources/bank.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
'use strict';

var root = '/bank';

module.exports = {
/*
List supported banks
*/
list: {
method: 'get',
path: root,
params: ['perPage', 'page']
},

resolveAccountNumber: {
method: 'get',
path: [root, '/resolve'].join(''),
params: ['account_number*', 'bank_code*']
},

resolveBin: {
method: 'get',
path: '/decision/bin/{bin}'
}

}
Loading