UNPKG

daraja-sdk

Version:

sdk for the developers integrating with safaricom's daraja api

1,245 lines (1,229 loc) 42.1 kB
const AuthClient=require('./AuthClient'); let {isValidPayload,validateSchema}=require('./utils'); class DarajaSDK { /** * Initializes the DarajaSDK * * The initial configurations * * { * * consumerKey:{ required:true, type:'string', }, consumerSecret:{ required:true, type:'string', }, passKey:{ type:'string', //The passkey is required in the configuration for the stk push service. }, shortCode:{ type:'number' }, mode:{ type:'string', allowed:['sandbox','live'], default:'sandbox' } * } */ constructor(config) { if(!config.consumerKey){ throw new Error(`A DarajaSDK instance should have a 'consumer key' for the purpose of authentication and authorization.`); } if(!config.consumerSecret){ throw new Error(`A DarajaSDK instance should at have a 'consumer secret' for the purpose of authentication and authorization.`); } let nodeVersion=process.version const versionParts = nodeVersion.substring(1).split('.').map(Number); let requiredVersion=versionParts[0]; if(requiredVersion<19){ throw new Error(`The nodejs version ${nodeVersion} may be incompatible or fail to work please upgrade the version to at least version 19.0.0`); } /** * The initial configurations * * { * * consumerKey:{ required:true, type:'string', * }, consumerSecret:{ required:true, type:'string', }, passKey:{ type:'string', }, shortCode:{ type:'number' }, mode:{ type:'string', allowed:['sandbox','live'], default:'sandbox' } * } */ this.config={ ConsumerKey:config.consumerKey, ConsumerSecret:config.consumerSecret, PassKey:config.PassKey, ShortCode:config.ShortCode, mode:config.mode } this.authClient = new AuthClient(this.config); } /** * Fetches the access token * Automatically refreshes the token if needed. * @returns {Promise<Object>} - An object containing the Authorization header. */ async _getAuthorizedHeaders() { try{ const token = await this.authClient.getAccessToken(); return { 'Authorization': `Bearer ${token}`,'Content-Type':'Application/json'}; }catch(error){ throw error; } } /** * To send a payment prompt to your customer's M-PESA registered phone number requesting them to authorize and complete payment. * @param {Object} [payload] The payload should have the following schema * @PayloadSchema * BusinessShortCode:{ * required:true, type:'number', }, Password:{ * type:'string', }, Timestamp:{ * type:'timestamp', }, TransactionType:{ * required:true, type:'string', }, Amount:{ * type:'number', required:true, }, PartyA:{ * type:'number', required:true, }, PartyB:{ * type:'number', required:true, }, PhoneNumber:{ * type:'number', required:true, }, CallBackURL:{ * type:'url', required:true, }, AccountReference:{ * type:'string', required:true, maxLength:12 }, TransactionDesc:{ * type:'string', required:true, maxLength:13 } * @returns {Object} * @throws {Error} */ async stkPush(payload){ try{ const {stkPush}=require('./resources/StkPush'); const {StkPushSchema}=require("./resources/schema/StkPushSchema"); isValidPayload(payload); validateSchema(payload,StkPushSchema,this.config); const headers = await this._getAuthorizedHeaders(); return stkPush(payload,this.config,headers); }catch(error){ throw error; } } /** * To initiate USSD Push to till enabling their fellow merchants to pay from their own till numbers to the vendor's paybill. * @param {Object} [payload] The payload should have the following schema * @PayloadSchema * { * * primaryShortCode:{ type:'number', required:true, }, receiverShortCode:{ type:'number', required:true, }, amount:{ type:'number', required:true, }, paymentRef:{ type:'string', required:true, }, callbackUrl:{ type:'url', required:true, }, partnerName:{ type:'string', required:true }, RequestRefId:{ type:'string', required:true }, * } * @returns {Object} * @throws {Error} */ async b2bExpressCheckout(payload){ const {b2bExpressCheckout}=require('./resources/b2bExpressCheckout'); const {B2BExpressCheckoutSchema}=require("./resources/schema/B2BExpressCheckout"); isValidPayload(payload); validateSchema(payload,B2BExpressCheckoutSchema,this.config); const headers = await this._getAuthorizedHeaders(); return b2bExpressCheckout(payload,this.config,headers); } /** * To make payments from Business to Customers(Pay outs).Used in several scenarios by businesses that require to either make Salary Payments,Cashback Payments,Promotional Payments,Winnings,Financial Institutions * @param {Object} [payload] The payload should have the following schema * @PayloadSchema * { * * OriginatorConversationID:{ type:'string', required:true }, InitiatorName:{ type:'string', required:true }, SecurityCredential:{ type:'string', required:true }, CommandID:{ type:'string', required:true, allowed:['SalaryPayment','BusinessPayment','PromotionPayment'] }, Amount:{ type:'number', required:true, }, PartyA:{ type:'number', required:true, }, PartyB:{ type:'number', required:true, }, Remarks:{ type:'string', required:true, maxLength:100 }, QueueTimeOutURL:{ type:'url', required:true, }, ResultURL:{ type:'url', required:true }, occasion:{ type:'string', required:true, maxLength:100 } } * @returns {Object} * @throws {Error} */ async b2cPaymentRequest(payload){ try{ const {b2cPaymentrequest}=require('./resources/b2c'); const {B2CPaymentRequsetSchema}=require("./resources/schema/B2CPaymentRequestSchema"); isValidPayload(payload); validateSchema(payload,B2CPaymentRequsetSchema,this.config); const headers = await this._getAuthorizedHeaders(); return b2cPaymentrequest(payload,this.config,headers); }catch(error){ throw error; } } /** * To opt you as a biller to safaricom's bill manager features.Once intergrated to the service and send arequset with a success response,your shortcode is whitelisted and you are able to intergrate with the remaining bill manager services. * @param {Object} [payload] The payload should have the following schema * @PayloadSchema * { * * shortcode:{ type:'number', required:true, }, email:{ type:'string', required:true, }, officialContact:{ type:'string', required:true, }, sendReminders:{ type:'number', required:true, allowed:[0,1] }, logo:{ type:'url', required:true }, callBackURL:{ type:'url', required:true, } * } * @returns {Object} * @throws {Error} */ async billManagerOptIn(payload){ try{ const {billManagerOptIn}=require('./resources/billManager'); const {BillManagerOptInSchema}=require("./resources/schema/BillManagerSchema"); isValidPayload(payload); validateSchema(payload,BillManagerOptInSchema,this.config); const headers = await this._getAuthorizedHeaders(); return billManagerOptIn(payload,this.config,headers); }catch(error){ throw error; } } /** * To send out customizedindividual e-invoices.Customers receive this notification(s) via an SMS to the Safaricom Phone number specified while creating the invoice. * @param {Object} [payload] The payload should have the following schema * @PayloadSchema * { * * externalReference:{ type:'string', required:true, }, billedFullName:{ type:'string', required:true }, billedPhoneNumber:{ type:'string', required:true }, billedPeriod:{ type:'string', required:true, }, invoiceName:{ type:'string', required:true }, dueDate:{ type:'string', required:true, }, accountReference:{ type:'string', required:true }, amount:{ type:'number', required:true, }, invoiceItems:{ type:'array', children:{ name:{ type:'string', required:true }, amount:{ type:'number', required:true } } } * } * @returns {Object} * @throws {Error} */ async billManagerSingleInvoicing(payload){ try{ const {billManagerSingleInvoicing}=require('./resources/billManager'); const {BillManagerSingleInvoicingSchema}=require("./resources/schema/BillManagerSchema"); isValidPayload(payload); validateSchema(payload,BillManagerSingleInvoicingSchema,this.config); const headers = await this._getAuthorizedHeaders(); return billManagerSingleInvoicing(payload,this.config,headers); }catch(error){ throw error; } } /** * To update opt-in details * @param {Object} [payload] The payload should have the following schema * @PayloadSchema * { * * shortcode:{ type:'number', required:true, }, email:{ type:'string', required:true, }, officialContact:{ type:'string', required:true, }, sendReminders:{ type:'string', required:true, allowed:['0','1'] }, logo:{ type:'url', }, callbackUrl:{ type:'url', required:true, } * } * } * @returns {Object} * @throws {Error} */ async billManagerUpdateOptIn(payload){ try{ const {billManagerUpdateOptIn}=require('./resources/billManager'); const {BillManagerUpdateOptInSchema}=require("./resources/schema/BillManagerSchema"); isValidPayload(payload) validateSchema(payload,BillManagerUpdateOptInSchema,this.config); const headers = await this._getAuthorizedHeaders(); return billManagerUpdateOptIn(payload,this.config,headers); }catch(error){ throw error; } } /** * To recall a sent invoice.The invoice will cease to exist and cannot be used as a reference to a payment. * @param {Object} [payload] The payload should have the following schema * @payloadSchema * { * * externalReference:{ type:'string', required:true } * } * @returns {Object} * @throws {Error} */ async billManagerCancelSingleInvoice(payload){ try{ const {billManagerCancelSingleInvoice}=require('./resources/billManager'); const {BillManagerCancelSingleInvoiceSchema}=require("./resources/schema/BillManagerSchema"); isValidPayload(payload); validateSchema(payload,BillManagerCancelSingleInvoiceSchema,this.config); const headers = await this._getAuthorizedHeaders(); return billManagerCancelSingleInvoice(payload,this.config,headers); }catch(error){ throw error; } } /** * To send receipts to payments made to your paybill. * @param {Object} [payload] The payload should have the following schema * @PayloadSchema * { * * transactionId:{ type:'string', required:true, }, paidAmount:{ type:'number', required:true, }, msisdn:{ type:'number', required:true, }, dateCreated:{ type:'string', required:true, }, accountReference:{ type:'string', required:true }, shortCode:{ type:'number', required:true, } * } * @returns {Object} * @throws {Error} */ async billManagerReconcoilliation(payload){ try{ const {billManagerReconciliation}=require('./resources/billManager'); const {BillManagerReconsiliationSchema}=require('./resources/schema/BillManagerSchema'); isValidPayload(payload); validateSchema(payload,BillManagerReconsiliationSchema,this.config); const headers = await this._getAuthorizedHeaders(); return billManagerReconciliation(payload,this.config,headers); }catch(error){ throw error; } } /** * To pay for goods and services directly from your business account to a till number,merchant store number or Merchant HO.You can also use this to pay merchant on behalf of a consumer/requestor. * @param {Object} [payload] The payload should have the following schema * @PayloadSchema * { * * CommandID:{ type:'string', constant:'BusinessBuyGoods' }, Initiator:{ type:'string', required:true, }, SecurityCredential:{ type:'string', required:true }, PartyA:{ type:'number', required:true, }, SenderIdentifierType:{ type:'number', constant:4 }, PartyB:{ type:'number', required:true, }, RecieverIdentifierType:{ type:'number', constant:4 }, Requester:{ type:'number', }, Amount:{ type:'number', required:true, }, AccountReference:{ type:'string', required:true, maxLength:13 }, Remarks:{ type:'string', required:true, maxLength:100 }, QueueTimeOutURL:{ type:'url', required:true, }, ResultURL:{ type:'url', required:true, }, Occassion:{ type:'string', required:true, maxLength:100 } * } * @returns {Object} * @throws {Error} */ async businessBuyGoods(payload){ try{ const {businessBuyGoods}=require('./resources/businessBuyGoods'); const {BusinessBuyGoodsSchema}=require("./resources/schema/BusinessBuyGoodsSchema"); isValidPayload(payload) validateSchema(payload,BusinessBuyGoodsSchema,this.config); const headers = await this._getAuthorizedHeaders(); return businessBuyGoods(payload,this.config,headers); }catch(error){ throw error; } } /** * To pay bills directly from your business account to a paybill number or paybill store. * @param {Object} [payload] The payload should have the following schema * @PayloadSchema * { * * CommandID:{ type:'string', constant:'BusinessPayBill' }, Initiator:{ type:'string', required:true, }, SecurityCredential:{ type:'string', required:true }, PartyA:{ type:'number', required:true, }, SenderIdentifierType:{ type:'number', constant:4 }, PartyB:{ type:'number', required:true, }, RecieverIdentifierType:{ type:'number', constant:4 }, Requester:{ type:'number', }, Amount:{ type:'number', required:true, }, AccountReference:{ type:'string', required:true, maxLength:13 }, Remarks:{ type:'string', required:true, maxLength:100 }, QueueTimeOutURL:{ type:'url', required:true, }, ResultURL:{ type:'url', required:true, }, Occassion:{ type:'string', required:true, maxLength:100 } * } * @returns {Object} * @throws {Error} */ async businessPayBill(payload){ try{ const {businessPayBill}=require('./resources/businessPayBill'); const {BusinessPayBillSchema}=require('./resources/schema/BusinessPayBillSchema'); isValidPayload(payload) validateSchema(payload,BusinessPayBillSchema,this.config); const headers = await this._getAuthorizedHeaders(); return businessPayBill(payload,this.config,headers); }catch(error){ throw error; } } /** * To register callback URLs for C2B APIs via which you shall receive notifications for payments to your paybill/till number * @param {Object} [payload] The payload should have the following schema * @PayloadSchema * * ShortCode:{ * type:'number', required:true }, ResponseType:{ * type:'string', required:true, allowed:['Completed','Cancelled'] }, ConfirmationURL:{ * type:'url', required:true, }, ValidationURL:{ * type:'url', required:true, } * @returns {Object} * @throws {Error} */ async c2bRegisterURL(payload){ try{ const {c2bRegisterURL}=require('./resources/c2bRegisterURL'); const {C2BRegisterURLSchema}=require('./resources/schema/C2BRegisterURLSchema'); isValidPayload(payload); validateSchema(payload,C2BRegisterURLSchema,this.config); const headers = await this._getAuthorizedHeaders(); return c2bRegisterURL(payload,this.config,headers); }catch(error){ throw error; } } /** * To generate a Dynamic QR code which enables Safaricom M-PESA customers who have My Safaricom App or M-PESA app,to capture till number and amount then authorize to pay for goods and services at select LIPA NA M-PESA(LNM) merchant outlets * @param {Object} [payload] The payload should have the following schema * @PayloadSchema * { * * MerchantName:{ * type:'string', required:true, }, RefNo:{ type:'string', required:true }, Amount:{ type:'number', required:true, }, TrxCode:{ type:'string', required:true, allowed:['BG','WA','PB','SM','SB'] }, CPI:{ required:true, type:'string', }, Size:{ required:true, type:'string', } } * @returns {object} * @throws {Error} */ async dynamicQr(payload){ try{ const {dynamicQr}=require('./resources/dynamicQr'); const {DynamicQrCodeSchema}=require('./resources/schema/DynamicQrcodeSchema'); isValidPayload(payload); validateSchema(payload,DynamicQrCodeSchema,this.config); const headers = await this._getAuthorizedHeaders(); return dynamicQr(payload,this.config,headers); }catch(error){ throw error; } } /** * For businesss who wish to integrate with standing orders for the automation of recurring revenue collection. * @param {Object} [payload] The payload should have the following schema * @PayloadSchema * { * * StandingOrderName:{ type:'string', required:true }, StartDate:{ type:'string', required:true, }, EndDate:{ type:'string', required:true, }, BusinessShortCode:{ type:'string', required:true, }, TransactionType:{ type:'string', required:true, allowed:['Standing Order Customer Pay Bill','Standing Order Customer Buy Goods'] }, Amount:{ type:'string', required:true, }, PartyA:{ type:'string', required:true, }, CallBackURL:{ type:'url', required:true, }, AccountReference:{ type:'string', required:true, maxLength:12 }, TransactionDesc:{ type:'string', required:true, maxLength:13 }, Frequency:{ type:'string', required:true, allowed:['1','2','3','4','5','6','7','8'] }, ReceiverPartyIdentifierType:{ type:'string', required:true, allowed:['2','4'] } * } * @returns {Object} * @throws {Error} */ async ratiba(payload){ try{ const {ratiba}=require('./resources/mpesaRatiba'); const {MPesaRatibaSchema}=require('./resources/schema/MpesaRatibaSchema'); isValidPayload(payload); validateSchema(payload,MPesaRatibaSchema,this.config); const headers = await this._getAuthorizedHeaders(); return ratiba(payload,this.config,headers); }catch(error){ throw error; } } /** * Reverses a C2B M-Pesa transaction. * @param {Object} [payload] The payload should have the following schema * @PayloadSchema * { * CommandID:{ type:'string', constant:'TransactionReversal' }, ReceiverParty:{ type:'number', required:true, }, ReceiverIdentifierType:{ type:'string', required:true, }, Remarks:{ type:'string', required:true, }, Initiator:{ type:'string', required:true }, SecurityCredential:{ type:'string', required:true }, QueueTimeOutURL:{ type:'url', required:true }, ResultURL:{ type:'url', required:true }, TransactionID:{ type:'string', required:true, }, Amount:{ type:'number', required:true, }, Occassion:{ type:'string', required:true, maxLength:100 } * } * @returns {Object} * @throws {Error} */ async reverse(payload){ try { const {reverse}=require('./resources/reversals'); const {ReversalsSchema}=require('./resources/schema/ReversalsSchema'); isValidPayload(payload); validateSchema(payload,ReversalsSchema,this.config); const headers = await this._getAuthorizedHeaders(); return reverse(payload,this.config,headers); } catch (error) { throw error; } } /** * Enables businesses to remit tax to Kenya Revenue Authority.To use this, prior integration is required with KRA for tax declaration,payment registration number(PRN) generation and exchange of other tax related information. * @param {Object} [payload] The payload should have the following schema * @PayloadSchema * { * CommandID:{ type:'string', constant:'PayTaxToKRA', }, Initiator:{ type:'string', required:true, }, SecurityCredential:{ type:'string', required:true }, PartyA:{ type:'number', required:true, }, SenderIdentifierType:{ type:'string', constant:'4' }, PartyB:{ type:'number', constant:572572 }, RecieverIdentifierType:{ type:'string', constant:'4' }, Amount:{ type:'number', required:true, }, AccountReference:{ type:'number', required:true, }, Remarks:{ type:'string', required:true, maxLength:100 }, QueueTimeOutURL:{ type:'url', required:true }, ResultURL:{ type:'url', required:true } * } * @returns {Object} * @throws {Error} */ async taxRemittance(payload){ try { const {taxRemittance}=require('./resources/taxRemittance'); const {TaxRemittanceSchema}=require('./resources/schema/TaxRemmitanceSchema'); isValidPayload(payload); validateSchema(payload,TaxRemittanceSchema,this.config); const headers = await this._getAuthorizedHeaders(); return taxRemittance(payload,this.config,headers); } catch (error) { throw error; } } /** * To check status of a transaction * @param {Object} [payload] The payload should have the following schema * @PayloadSchema * { * * CommandID:{ type:'string', required:true, constant:'TransactionStatusQuery' }, PartyA:{ type:'number', required:true, }, IdentifierType:{ type:'number', required:true, allowed:[1,2,4] }, Remarks:{ type:'string', required:true, maxLength:100 }, Initiator:{ type:'string', required:true }, SecurityCredential:{ type:'string', required:true }, QueueTimeOutURL:{ type:'url', required:true }, TransactionID:{ type:'string', required:true }, ResultURL:{ type:'url', required:true }, Occassion:{ type:'string', required:true, maxLength:100 }, OriginatorConversationID:{ type:'string', required:true } * } * @returns {Object} * @throws {Error} */ async transactionStatus(payload){ try{ const {transactionStatus}=require('./resources/transactionStatus'); const {TransactionStatusSchema}=require('./resources/schema/TransactionStatusSchema'); isValidPayload(payload); validateSchema(payload,TransactionStatusSchema,this.config); const headers = await this._getAuthorizedHeaders(); return transactionStatus(payload,this.config,headers); }catch(error){ throw error; } } /** * To check the status of a Lipa Na M-PESA Online Payment * @param {object} [payload] The payload should have the following schema * @PayloadSchema * { * * BusinessShortCode:{ required:true, type:'number', }, Password:{ type:'string', }, Timestamp:{ type:'timestamp', }, CheckoutRequestID:{ type:'string', required:true } * * } * @returns {Object} * @throws {Error} */ async expressQuery(payload){ try{ const {expressQuery}=require('./resources/expressQuery'); const {ExpressQuerySchema}=require('./resources/schema/ExpressQuerySchema'); isValidPayload(payload); validateSchema(payload,ExpressQuerySchema,this.config); const headers=await this._getAuthorizedHeaders(); return expressQuery(payload,this.config,headers); }catch(error){ throw error; } } /** * To request account balance of a short code. * @param {Object} [payload] The payload should have the following schema * @PayloadSchemma * { * * CommandID:{ type:'string', constant:'TransactionStatusQuery' }, PartyA:{ type:'number', required:true, }, IdentifierType:{ type:'number', required:true, allowed:[2,4] }, Remarks:{ type:'string', required:true, maxLength:100 }, Initiator:{ type:'string', required:true }, SecurityCredential:{ type:'string', required:true }, QueueTimeOutURL:{ type:'url', required:true }, ResultURL:{ type:'url', required:true } } * @returns {Object} * @throws {Error} */ async accountBalance(payload){ try{ const {accountBalance}=require('./resources/accountBalance'); const {AccountBalanceSchema}=require('./resources/schema/AccountBalanceSchema'); isValidPayload(payload); validateSchema(payload,AccountBalanceSchema,this.config); const headers=await this._getAuthorizedHeaders(); return accountBalance(payload,this.config,headers); }catch(error){ throw error; } } /** * To load funds to a B2C shortcode directly for disbursment.Move money from your MMF/wrking account to the recipient's utility account. * @param {Object} [payload] The payload should have the following schema * @returns {Object} * @PayloadSchema * { * * CommandID:{ type:'string', constant:'BusinessPayToBulk' }, Initiator:{ type:'str', required:true }, SecurityCredential:{ type:'string', required:true }, PartyA:{ type:'number', required:true, }, SenderIdentifierType:{ type:'number', constant:4 }, PartyB:{ type:'number', required:true, }, RecieverIdentifierType:{ type:'number', constant:4 }, Requester:{ type:'number', }, Amount:{ type:'number', required:true, }, Remarks:{ type:'string', required:true, maxLength:100 }, QueueTimeOutURL:{ type:'url', required:true, }, ResultURL:{ type:'url', required:true } * } * @throws {Error} */ async b2cAccountTopUp(payload){ try{ const {b2cAccountTopUp}=require('./resources/b2bAccountTopup'); const {B2CAccountTopUpSchema}=require('./resources/schema/B2CAccountTopup'); isValidPayload(payload); validateSchema(payload,B2CAccountTopUpSchema,this.config); const headers=await this._getAuthorizedHeaders(); return b2cAccountTopUp(payload,this.config,headers); }catch(error){ throw error; } } /** * Generates a timestamp that is in the form YYYYMMddHHmmss * @returns {Timestamp} */ async genarateTimestamp(){ const {getTimestamp}=require('./utils'); return getTimestamp(); } /** * Generates the password by concatenating the shortcde,passkey and timestamp the encoding it in base64 format * @param {number} BusinessShortCode The business shortcode that is associated with the business * @param {string} Passkey The pass key offerd via email for production but copy the provided one in sandbox * @param {timestamp} timestamp This timestamp should ne in the format YYYYMMddHHmmss * @returns {string} */ async generatePassword(BusinessShortCode,Passkey,timestamp){ const {generatePassword}=require('./utils'); return generatePassword(BusinessShortCode,Passkey,timestamp); } } module.exports= DarajaSDK;