diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..1aeb0bb --- /dev/null +++ b/.editorconfig @@ -0,0 +1,10 @@ +# http://editorconfig.org +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 2 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true \ No newline at end of file diff --git a/.prettierrc.js b/.prettierrc.js new file mode 100644 index 0000000..409c297 --- /dev/null +++ b/.prettierrc.js @@ -0,0 +1,4 @@ +module.exports = { + printWidth: 100, + singleQuote: true, +}; diff --git a/src/endpoints/account-balance.js b/src/endpoints/account-balance.js index 6dc2ff3..c8abf94 100644 --- a/src/endpoints/account-balance.js +++ b/src/endpoints/account-balance.js @@ -12,17 +12,25 @@ * @param {String} [commandId='AccountBalance'] Takes only 'AccountBalance' CommandID * @return {Promise} This returns a promise that resolves to the account balance */ -module.exports = async function accountBalance (shortCode, idType, queueUrl, resultUrl, remarks = 'Checking account balance', initiator = null, commandId = 'AccountBalance') { - const securityCredential = this.security() - const req = await this.request() +module.exports = async function accountBalance( + shortCode, + idType, + queueUrl, + resultUrl, + remarks = 'Checking account balance', + initiator = null, + commandId = 'AccountBalance' +) { + const securityCredential = this.security(); + const req = await this.request(); return req.post('/mpesa/accountbalance/v1/query', { - 'Initiator': initiator || this.configs.initiatorName, - 'SecurityCredential': securityCredential, - 'CommandID': commandId, - 'PartyA': shortCode, - 'IdentifierType': idType, - 'Remarks': remarks, - 'QueueTimeOutURL': queueUrl, - 'ResultURL': resultUrl - }) -} + Initiator: initiator || this.configs.initiatorName, + SecurityCredential: securityCredential, + CommandID: commandId, + PartyA: shortCode, + IdentifierType: idType, + Remarks: remarks, + QueueTimeOutURL: queueUrl, + ResultURL: resultUrl, + }); +}; diff --git a/src/endpoints/b2b.js b/src/endpoints/b2b.js index 866793c..3b41ade 100644 --- a/src/endpoints/b2b.js +++ b/src/endpoints/b2b.js @@ -17,21 +17,33 @@ * @param {String} [remarks='B2B Request'] Comments that are sent along with the transaction. * @return {Promise} */ -module.exports = async function (senderParty, receiverParty, amount, queueUrl, resultUrl, senderType = 4, receiverType = 4, initiator = null, commandId = 'BusinessToBusinessTransfer', accountRef = null, remarks = 'B2B Request') { - const req = await this.request() - const securityCredential = this.security() +module.exports = async function ( + senderParty, + receiverParty, + amount, + queueUrl, + resultUrl, + senderType = 4, + receiverType = 4, + initiator = null, + commandId = 'BusinessToBusinessTransfer', + accountRef = null, + remarks = 'B2B Request' +) { + const req = await this.request(); + const securityCredential = this.security(); return req.post('/mpesa/b2b/v1/paymentrequest', { - 'Initiator': initiator || this.configs.initiatorName, - 'SecurityCredential': securityCredential, - 'CommandID': commandId, - 'SenderIdentifierType': senderType, - 'RecieverIdentifierType': receiverType, - 'Amount': amount, - 'PartyA': senderParty, - 'PartyB': receiverParty, - 'AccountReference': accountRef, - 'Remarks': remarks, - 'QueueTimeOutURL': queueUrl, - 'ResultURL': resultUrl - }) -} + Initiator: initiator || this.configs.initiatorName, + SecurityCredential: securityCredential, + CommandID: commandId, + SenderIdentifierType: senderType, + RecieverIdentifierType: receiverType, + Amount: amount, + PartyA: senderParty, + PartyB: receiverParty, + AccountReference: accountRef, + Remarks: remarks, + QueueTimeOutURL: queueUrl, + ResultURL: resultUrl, + }); +}; diff --git a/src/endpoints/b2c.js b/src/endpoints/b2c.js index f42a6b5..fcf980e 100644 --- a/src/endpoints/b2c.js +++ b/src/endpoints/b2c.js @@ -15,19 +15,29 @@ * @param {string} occasion * @return {Promise} */ -module.exports = async function (senderParty, receiverParty, amount, queueUrl, resultUrl, commandId = 'BusinessPayment', initiatorName = null, remarks = 'B2C Payment', occasion) { - const securityCredential = this.security() - const req = await this.request() +module.exports = async function ( + senderParty, + receiverParty, + amount, + queueUrl, + resultUrl, + commandId = 'BusinessPayment', + initiatorName = null, + remarks = 'B2C Payment', + occasion +) { + const securityCredential = this.security(); + const req = await this.request(); return req.post('/mpesa/b2c/v1/paymentrequest', { - 'InitiatorName': initiatorName || this.configs.initiatorName, - 'SecurityCredential': securityCredential, - 'CommandID': commandId, - 'Amount': amount, - 'PartyA': senderParty, - 'PartyB': receiverParty, - 'Remarks': remarks, - 'QueueTimeOutURL': queueUrl, - 'ResultURL': resultUrl, - 'Occasion': occasion - }) -} + InitiatorName: initiatorName || this.configs.initiatorName, + SecurityCredential: securityCredential, + CommandID: commandId, + Amount: amount, + PartyA: senderParty, + PartyB: receiverParty, + Remarks: remarks, + QueueTimeOutURL: queueUrl, + ResultURL: resultUrl, + Occasion: occasion, + }); +}; diff --git a/src/endpoints/c2b-register.js b/src/endpoints/c2b-register.js index 1af3e43..a228d03 100644 --- a/src/endpoints/c2b-register.js +++ b/src/endpoints/c2b-register.js @@ -10,12 +10,17 @@ * @param {string} [responseType='Completed'] Default response type for timeout. Incase a tranaction times out, Mpesa will by default Complete or Cancel the transaction * @return {Promise} */ -module.exports = async function (confirmationUrl, validationUrl, shortCode = null, responseType = 'Completed') { - const req = await this.request() +module.exports = async function ( + confirmationUrl, + validationUrl, + shortCode = null, + responseType = 'Completed' +) { + const req = await this.request(); return req.post('/mpesa/c2b/v1/registerurl', { - 'ShortCode': shortCode || this.configs.shortCode, - 'ResponseType': responseType, - 'ConfirmationURL': confirmationUrl, - 'ValidationURL': validationUrl - }) -} + ShortCode: shortCode || this.configs.shortCode, + ResponseType: responseType, + ConfirmationURL: confirmationUrl, + ValidationURL: validationUrl, + }); +}; diff --git a/src/endpoints/c2b-simulate.js b/src/endpoints/c2b-simulate.js index 6b5bbef..1e0f514 100644 --- a/src/endpoints/c2b-simulate.js +++ b/src/endpoints/c2b-simulate.js @@ -11,13 +11,19 @@ * @param {number} [shortCode=null] Short Code receiving the amount being transacted * @return {Promise} */ -module.exports = async function (msisdn, amount, billRefNumber, commandId = 'CustomerPayBillOnline', shortCode = null) { - const req = await this.request() +module.exports = async function ( + msisdn, + amount, + billRefNumber, + commandId = 'CustomerPayBillOnline', + shortCode = null +) { + const req = await this.request(); return req.post('/mpesa/c2b/v1/simulate', { - 'ShortCode': shortCode || this.configs.shortCode, - 'CommandID': commandId, - 'Amount': amount, - 'Msisdn': msisdn, - 'BillRefNumber': billRefNumber - }) -} + ShortCode: shortCode || this.configs.shortCode, + CommandID: commandId, + Amount: amount, + Msisdn: msisdn, + BillRefNumber: billRefNumber, + }); +}; diff --git a/src/endpoints/index.js b/src/endpoints/index.js index 2f13b39..c2b3779 100644 --- a/src/endpoints/index.js +++ b/src/endpoints/index.js @@ -1,13 +1,14 @@ -const accountBalance = require('./account-balance') -const b2b = require('./b2b') -const b2c = require('./b2c') -const c2bRegister = require('./c2b-register') -const c2bSimulate = require('./c2b-simulate') -const lipaNaMpesaOnline = require('./lipa-na-mpesa-online') -const lipaNaMpesaQuery = require('./lipa-na-mpesa-query') -const oAuth = require('./oauth') -const reversal = require('./reversal') -const transactionStatus = require('./transaction-status') +/* eslint-disable @typescript-eslint/no-var-requires */ +const accountBalance = require('./account-balance'); +const b2b = require('./b2b'); +const b2c = require('./b2c'); +const c2bRegister = require('./c2b-register'); +const c2bSimulate = require('./c2b-simulate'); +const lipaNaMpesaOnline = require('./lipa-na-mpesa-online'); +const lipaNaMpesaQuery = require('./lipa-na-mpesa-query'); +const oAuth = require('./oauth'); +const reversal = require('./reversal'); +const transactionStatus = require('./transaction-status'); module.exports = { accountBalance, @@ -19,5 +20,5 @@ module.exports = { lipaNaMpesaQuery, reversal, transactionStatus, - oAuth -} + oAuth, +}; diff --git a/src/endpoints/lipa-na-mpesa-online.js b/src/endpoints/lipa-na-mpesa-online.js index 57057d2..34d06b3 100644 --- a/src/endpoints/lipa-na-mpesa-online.js +++ b/src/endpoints/lipa-na-mpesa-online.js @@ -13,23 +13,35 @@ * @param {string} [passKey=null] Lipa na mpesa passKey * @return {Promise} */ -module.exports = async function (senderMsisdn, amount, callbackUrl, accountRef, transactionDesc = 'Lipa na mpesa online', transactionType = 'CustomerPayBillOnline', shortCode = null, passKey = null) { - const _shortCode = shortCode || this.configs.lipaNaMpesaShortCode - const _passKey = passKey || this.configs.lipaNaMpesaShortPass - const timeStamp = (new Date()).toISOString().replace(/[^0-9]/g, '').slice(0, -3) - const password = Buffer.from(`${_shortCode}${_passKey}${timeStamp}`).toString('base64') - const req = await this.request() +module.exports = async function ( + senderMsisdn, + amount, + callbackUrl, + accountRef, + transactionDesc = 'Lipa na mpesa online', + transactionType = 'CustomerPayBillOnline', + shortCode = null, + passKey = null +) { + const _shortCode = shortCode || this.configs.lipaNaMpesaShortCode; + const _passKey = passKey || this.configs.lipaNaMpesaShortPass; + const timeStamp = new Date() + .toISOString() + .replace(/[^0-9]/g, '') + .slice(0, -3); + const password = Buffer.from(`${_shortCode}${_passKey}${timeStamp}`).toString('base64'); + const req = await this.request(); return req.post('/mpesa/stkpush/v1/processrequest', { - 'BusinessShortCode': _shortCode, - 'Password': password, - 'Timestamp': timeStamp, - 'TransactionType': transactionType, - 'Amount': amount, - 'PartyA': senderMsisdn, - 'PartyB': _shortCode, - 'PhoneNumber': senderMsisdn, - 'CallBackURL': callbackUrl, - 'AccountReference': accountRef, - 'TransactionDesc': transactionDesc - }) -} + BusinessShortCode: _shortCode, + Password: password, + Timestamp: timeStamp, + TransactionType: transactionType, + Amount: amount, + PartyA: senderMsisdn, + PartyB: _shortCode, + PhoneNumber: senderMsisdn, + CallBackURL: callbackUrl, + AccountReference: accountRef, + TransactionDesc: transactionDesc, + }); +}; diff --git a/src/endpoints/lipa-na-mpesa-query.js b/src/endpoints/lipa-na-mpesa-query.js index c74f174..06ac644 100644 --- a/src/endpoints/lipa-na-mpesa-query.js +++ b/src/endpoints/lipa-na-mpesa-query.js @@ -9,15 +9,18 @@ * @return {Promise} */ module.exports = async function (checkoutRequestId, shortCode = null, passKey = null) { - const _shortCode = shortCode || this.configs.lipaNaMpesaShortCode - const _passKey = passKey || this.configs.lipaNaMpesaShortPass - const timeStamp = (new Date()).toISOString().replace(/[^0-9]/g, '').slice(0, -3) - const password = Buffer.from(`${_shortCode}${_passKey}${timeStamp}`).toString('base64') - const req = await this.request() + const _shortCode = shortCode || this.configs.lipaNaMpesaShortCode; + const _passKey = passKey || this.configs.lipaNaMpesaShortPass; + const timeStamp = new Date() + .toISOString() + .replace(/[^0-9]/g, '') + .slice(0, -3); + const password = Buffer.from(`${_shortCode}${_passKey}${timeStamp}`).toString('base64'); + const req = await this.request(); return req.post('/mpesa/stkpushquery/v1/query', { - 'BusinessShortCode': _shortCode, - 'Password': password, - 'Timestamp': timeStamp, - 'CheckoutRequestID': checkoutRequestId - }) -} + BusinessShortCode: _shortCode, + Password: password, + Timestamp: timeStamp, + CheckoutRequestID: checkoutRequestId, + }); +}; diff --git a/src/endpoints/oauth.js b/src/endpoints/oauth.js index af0301b..9cf3620 100644 --- a/src/endpoints/oauth.js +++ b/src/endpoints/oauth.js @@ -1,10 +1,11 @@ -const axios = require('axios') +const axios = require('axios'); + module.exports = function (consumerKey, consumerSecret, baseURL = null) { - const auth = Buffer.from(consumerKey + ':' + consumerSecret).toString('base64') + const auth = Buffer.from(consumerKey + ':' + consumerSecret).toString('base64'); return axios.get((baseURL || this.baseURL) + '/oauth/v1/generate?grant_type=client_credentials', { headers: { - 'Authorization': 'Basic ' + auth, - 'content-type': 'application/json' - } - }) -} + Authorization: 'Basic ' + auth, + 'content-type': 'application/json', + }, + }); +}; diff --git a/src/endpoints/reversal.js b/src/endpoints/reversal.js index 4c97d8e..6b0bd8c 100644 --- a/src/endpoints/reversal.js +++ b/src/endpoints/reversal.js @@ -15,20 +15,31 @@ * @param {String} [commandId='TransactionReversal'] Takes only 'TransactionReversal' Command id * @return {Promise} */ -module.exports = async function (transactionId, amount, queueUrl, resultUrl, shortCode = null, remarks = 'Reversal', occasion = 'Reversal', initiator = null, receiverIdType = '11', commandId = 'TransactionReversal') { - const securityCredential = this.security() - const req = await this.request() +module.exports = async function ( + transactionId, + amount, + queueUrl, + resultUrl, + shortCode = null, + remarks = 'Reversal', + occasion = 'Reversal', + initiator = null, + receiverIdType = '11', + commandId = 'TransactionReversal' +) { + const securityCredential = this.security(); + const req = await this.request(); return req.post('/mpesa/reversal/v1/request', { - 'Initiator': initiator || this.configs.initiatorName, - 'SecurityCredential': securityCredential, - 'CommandID': commandId, - 'TransactionID': transactionId, - 'Amount': amount, - 'ReceiverParty': shortCode || this.configs.shortCode, - 'RecieverIdentifierType': receiverIdType, - 'ResultURL': resultUrl, - 'QueueTimeOutURL': queueUrl, - 'Remarks': remarks, - 'Occasion': occasion - }) -} + Initiator: initiator || this.configs.initiatorName, + SecurityCredential: securityCredential, + CommandID: commandId, + TransactionID: transactionId, + Amount: amount, + ReceiverParty: shortCode || this.configs.shortCode, + RecieverIdentifierType: receiverIdType, + ResultURL: resultUrl, + QueueTimeOutURL: queueUrl, + Remarks: remarks, + Occasion: occasion, + }); +}; diff --git a/src/endpoints/transaction-status.js b/src/endpoints/transaction-status.js index c4542e2..6a947c0 100644 --- a/src/endpoints/transaction-status.js +++ b/src/endpoints/transaction-status.js @@ -15,19 +15,29 @@ * @param {String} [commandId='TransactionStatusQuery'] Takes only 'TransactionStatusQuery' command id * @return {Promise} */ -module.exports = async function (transactionId, receiverParty, idType, queueUrl, resultUrl, remarks = 'TransactionReversal', occasion = 'TransactionReversal', initiator = null, commandId = 'TransactionStatusQuery') { - const securityCredential = this.security() - const req = await this.request() +module.exports = async function ( + transactionId, + receiverParty, + idType, + queueUrl, + resultUrl, + remarks = 'TransactionReversal', + occasion = 'TransactionReversal', + initiator = null, + commandId = 'TransactionStatusQuery' +) { + const securityCredential = this.security(); + const req = await this.request(); return req.post('/mpesa/transactionstatus/v1/query', { - 'Initiator': initiator || this.configs.initiatorName, - 'SecurityCredential': securityCredential, - 'CommandID': commandId, - 'TransactionID': transactionId, - 'PartyA': receiverParty, - 'IdentifierType': idType, - 'ResultURL': resultUrl, - 'QueueTimeOutURL': queueUrl, - 'Remarks': remarks, - 'Occasion': occasion - }) -} + Initiator: initiator || this.configs.initiatorName, + SecurityCredential: securityCredential, + CommandID: commandId, + TransactionID: transactionId, + PartyA: receiverParty, + IdentifierType: idType, + ResultURL: resultUrl, + QueueTimeOutURL: queueUrl, + Remarks: remarks, + Occasion: occasion, + }); +}; diff --git a/src/helpers/index.js b/src/helpers/index.js index cd36709..fd00ccb 100644 --- a/src/helpers/index.js +++ b/src/helpers/index.js @@ -1,7 +1,7 @@ -const request = require('./request') -const security = require('./security') +const request = require('./request'); +const security = require('./security'); module.exports = { request, - security -} + security, +}; diff --git a/src/helpers/request.js b/src/helpers/request.js index a9728f9..019e8fe 100644 --- a/src/helpers/request.js +++ b/src/helpers/request.js @@ -1,13 +1,14 @@ -const axios = require('axios') +const axios = require('axios'); + module.exports = async function (_baseURL = null) { - const credentials = await this.oAuth() + const credentials = await this.oAuth(); const instance = axios.create({ baseURL: _baseURL || this.baseURL, timeout: 5000, headers: { - 'Authorization': 'Bearer ' + credentials.data['access_token'], - 'Content-Type': 'application/json' - } - }) - return instance -} + Authorization: 'Bearer ' + credentials.data['access_token'], + 'Content-Type': 'application/json', + }, + }); + return instance; +}; diff --git a/src/helpers/security.js b/src/helpers/security.js index 1e9f258..052229a 100644 --- a/src/helpers/security.js +++ b/src/helpers/security.js @@ -1,15 +1,18 @@ -const fs = require('fs') -const path = require('path') -const crypto = require('crypto') +const fs = require('fs'); +const path = require('path'); +const crypto = require('crypto'); module.exports = (certPath, shortCodeSecurityCredential) => { - const bufferToEncrypt = Buffer.from(shortCodeSecurityCredential) - const data = fs.readFileSync(path.resolve(certPath)) - const privateKey = String(data) - const encrypted = crypto.publicEncrypt({ - key: privateKey, - padding: crypto.constants.RSA_PKCS1_PADDING - }, bufferToEncrypt) - const securityCredential = encrypted.toString('base64') - return securityCredential -} + const bufferToEncrypt = Buffer.from(shortCodeSecurityCredential); + const data = fs.readFileSync(path.resolve(certPath)); + const privateKey = String(data); + const encrypted = crypto.publicEncrypt( + { + key: privateKey, + padding: crypto.constants.RSA_PKCS1_PADDING, + }, + bufferToEncrypt + ); + const securityCredential = encrypted.toString('base64'); + return securityCredential; +}; diff --git a/src/m-pesa.js b/src/m-pesa.js index 36adb91..0af12a9 100644 --- a/src/m-pesa.js +++ b/src/m-pesa.js @@ -8,12 +8,9 @@ const { lipaNaMpesaQuery, oAuth, reversal, - transactionStatus -} = require('./endpoints') -const { - request, - security -} = require('./helpers') + transactionStatus, +} = require('./endpoints'); +const { request, security } = require('./helpers'); /** * Class representing the Mpesa instance @@ -24,72 +21,74 @@ class Mpesa { * @constructor * @param {Object} [config={}] The Configuration to use for mPesa */ - constructor (config = {}) { - if (!config.consumerKey) throw new Error('Consumer Key is Missing') - if (!config.consumerSecret) throw new Error('Consumer Secret is Missing') - this.configs = { ...config } - this.enviroment = config.environment === 'production' ? 'production' : 'sandbox' - this.request = request.bind(this) + constructor(config = {}) { + if (!config.consumerKey) throw new Error('Consumer Key is Missing'); + if (!config.consumerSecret) throw new Error('Consumer Secret is Missing'); + this.configs = { ...config }; + this.enviroment = config.environment === 'production' ? 'production' : 'sandbox'; + this.request = request.bind(this); this.security = () => { - return security(this.configs.certPath, this.configs.securityCredential) - } - this.baseURL = `https://${this.enviroment === 'production' ? 'api' : 'sandbox'}.safaricom.co.ke` + return security(this.configs.certPath, this.configs.securityCredential); + }; + this.baseURL = `https://${ + this.enviroment === 'production' ? 'api' : 'sandbox' + }.safaricom.co.ke`; } /** * AccountBalance via instance * @borrows AccountBalance as accountBalanceCall */ - accountBalance () { - return accountBalance.bind(this)(...arguments) + accountBalance() { + return accountBalance.bind(this)(...arguments); } /** * B2B Request via instance * @name b2bCall */ - b2b () { - return b2b.bind(this)(...arguments) + b2b() { + return b2b.bind(this)(...arguments); } /** * B2C Request * @borrows B2CRequest as b2c */ - b2c () { - return b2c.bind(this)(...arguments) + b2c() { + return b2c.bind(this)(...arguments); } - - c2bRegister () { - return c2bRegister.bind(this)(...arguments) + + c2bRegister() { + return c2bRegister.bind(this)(...arguments); } - c2bSimulate () { - if(this.enviroment === 'production'){ - throw new Error('Cannot call C2B simulate in production.') + c2bSimulate() { + if (this.enviroment === 'production') { + throw new Error('Cannot call C2B simulate in production.'); } - return c2bSimulate.bind(this)(...arguments) + return c2bSimulate.bind(this)(...arguments); } - lipaNaMpesaOnline () { - return lipaNaMpesaOnline.bind(this)(...arguments) + lipaNaMpesaOnline() { + return lipaNaMpesaOnline.bind(this)(...arguments); } - lipaNaMpesaQuery () { - return lipaNaMpesaQuery.bind(this)(...arguments) + lipaNaMpesaQuery() { + return lipaNaMpesaQuery.bind(this)(...arguments); } - oAuth () { - const { consumerKey, consumerSecret } = this.configs - return oAuth.bind(this)(consumerKey, consumerSecret) + oAuth() { + const { consumerKey, consumerSecret } = this.configs; + return oAuth.bind(this)(consumerKey, consumerSecret); } - reversal () { - return reversal.bind(this)(...arguments) + reversal() { + return reversal.bind(this)(...arguments); } - - transactionStatus () { - return transactionStatus.bind(this)(...arguments) + + transactionStatus() { + return transactionStatus.bind(this)(...arguments); } } -module.exports = Mpesa \ No newline at end of file +module.exports = Mpesa;