import { Buffer } from 'buffer' ; import { RSA_PKCS1_PADDING } from 'constants' ; import { publicEncrypt } from 'crypto' ; import { promises } from 'fs' ; import { resolve } from 'path' ; import { AccountBalanceInterface , AccountBalanceResponseInterface , AuthorizeResponseInterface , B2CInterface , B2CResponseInterface , C2BRegisterInterface , C2BRegisterResponseInterface , C2BSimulateInterface , C2BSimulateResponseInterface , CredentialsInterface , ReversalInterface , ReversalResponseInterface , StkPushInterface , StkPushResponseInterface , StkQueryInterface , StkQueryResponseInterface , TransactionStatusInterface , TransactionStatusResponseInterface , } from './models/interfaces' ; import { routes } from './routes' ; import { HttpService } from './services/http.service' ; export class Mpesa { private http : HttpService ; private environment : string ; private clientKey : string ; private clientSecret : string ; private securityCredential : string ; constructor ( { clientKey , clientSecret , securityCredential , initiatorPassword , certificatePath , } , environment ) { this . clientKey = clientKey ; this . clientSecret = clientSecret ; this . http = new HttpService ( { baseURL : environment === 'production' ? routes . production : routes . sandbox , headers : { 'Content-Type' : 'application/json' } , } ) ; if ( ! securityCredential && ! initiatorPassword ) { throw new Error ( 'You must provide either the security credential or initiator password. Both cannot be null' ) ; } if ( ! securityCredential ) { this . generateSecurityCredential ( initiatorPassword , certificatePath ) ; } else { this . securityCredential = securityCredential ; } } private async authenticate ( ) { const response = await this . http . get < AuthorizeResponseInterface > ( routes . oauth , { headers : { Authorization : 'Basic ' + Buffer . from ( this . clientKey + ':' + this . clientSecret ) . toString ( 'base64' ) , } , } ) ; return response . data . access_token ; } private async generateSecurityCredential ( password , certificatePath ) { let certificate ; if ( certificatePath != null ) { const certificateBuffer = await promises . readFile ( certificatePath ) ; certificate = String ( certificateBuffer ) ; } else { const certificateBuffer = await promises . readFile ( resolve ( __dirname , this . environment === 'production' ? 'keys/production-cert.cer' : 'keys/sandbox-cert.cer' ) ) ; certificate = String ( certificateBuffer ) ; } this . securityCredential = publicEncrypt ( { key : certificate , padding : RSA_PKCS1_PADDING , } , Buffer . from ( password ) ) . toString ( 'base64' ) ; } name description see https param { C2BRegisterInterface } data param { string } data . ValidationURLValidation param { string } data . ConfirmationURL param { string } data . ResponseType param { string } data . ShortCode returns { Promise } public async c2bRegister ( { ShortCode , ResponseType , ConfirmationURL , ValidationURL , } ) { const token = await this . authenticate ( ) ; const response = await this . http . post < C2BRegisterResponseInterface > ( routes . c2bregister , { ShortCode , ResponseType , ConfirmationURL , ValidationURL } , { headers : { Authorization : 'Bearer ' + token , } , } ) ; return response . data ; } name description see https param { C2BSimulateInterface } data param { string } data . CommandID param { number } data . Amount param { string } data . Msisdn param { any } data . BillRefNumber param { string } data . ShortCode returns { Promise } public async c2bSimulate ( { ShortCode , CommandID , Amount , Msisdn , BillRefNumber , } ) { const token = await this . authenticate ( ) ; const response = await this . http . post < C2BSimulateResponseInterface > ( routes . c2bsimulate , { ShortCode , CommandID , Amount , Msisdn , BillRefNumber : BillRefNumber ?? 'account' , } , { headers : { Authorization : 'Bearer ' + token , } , } ) ; return response . data ; } name description see https param { TransactionStatusInterface } data param { string } data . Initiator param { string } data . SecurityCredential param { string } data . CommandID param { string } data . TransactionID param { string } data . PartyA param { any | number } data . IdentifierType param { string } data . ResultURL param { string } data . QueueTimeOutURL param { string } data . Remarks param { string } data . Occasion returns { Promise } public async transactionStatus ( { Initiator , CommandID , TransactionID , PartyA , IdentifierType , ResultURL , QueueTimeOutURL , Remarks , Occasion , } ) { const token = await this . authenticate ( ) ; const response = await this . http . post < TransactionStatusResponseInterface > ( routes . transactionstatus , { Initiator , SecurityCredential : this . securityCredential , CommandID : CommandID ?? 'TransactionStatusQuery' , TransactionID , PartyA , IdentifierType , ResultURL , QueueTimeOutURL , Remarks : Remarks ?? 'Transaction Status' , Occasion : Occasion ?? 'TransactionStatus' , } , { headers : { Authorization : 'Bearer ' + token , } , } ) ; return response . data ; } name description see https param { B2CInterface } data param { string } data . InitiatorName param { string } data . CommandID param { number } data . Amount param { string } data . PartyA param { string } data . PartyB param { string } data . Remarks param { string } data . QueueTimeOutURL param { string } data . ResultURL param { string } data . Occasion returns { Promise } public async b2c ( { Initiator , CommandID , Amount , PartyA , PartyB , Remarks , QueueTimeOutURL , ResultURL , Occasion , } ) { const token = await this . authenticate ( ) ; const response = await this . http . post < B2CResponseInterface > ( routes . b2c , { InitiatorName : Initiator , SecurityCredential : this . securityCredential , CommandID : CommandID , Amount : Amount , PartyA : PartyA , PartyB : PartyB , Remarks : Remarks ?? 'account' , QueueTimeOutURL : QueueTimeOutURL , ResultURL : ResultURL , Occasion : Occasion ?? 'account' , } , { headers : { Authorization : 'Bearer ' + token , } , } ) ; return response . data ; } name description see https param { StkPushInterface } data param { string } data . BusinessShortCode param { number } data . Amount param { string } data . PartyA param { string } data . PartyB param { string } data . PhoneNumber param { string } data . CallBackURL param { string } data . AccountReference param { string } data . TransactionDesc param { any } data . passKey returns { Promise } public async lipaNaMpesaOnline ( { BusinessShortCode , passKey , TransactionDesc , TransactionType , PartyA , PartyB , Amount , AccountReference , CallBackURL , PhoneNumber , } ) { const Timestamp = new Date ( ) . toISOString ( ) . replace ( /[^0-9]/g , '' ) . slice ( 0 , - 3 ) ; const Password = Buffer . from ( BusinessShortCode + passKey + Timestamp ) . toString ( 'base64' ) ; const token = await this . authenticate ( ) ; const response = await this . http . post < StkPushResponseInterface > ( routes . stkpush , { BusinessShortCode , Password , Timestamp , TransactionType : TransactionType ?? 'CustomerPayBillOnline ' , Amount , PartyA , PartyB , PhoneNumber , CallBackURL , AccountReference , TransactionDesc : TransactionDesc ?? 'Lipa Na Mpesa Online' , } , { headers : { Authorization : 'Bearer ' + token , } , } ) ; return response . data ; } name description see https param { StkQueryInterface } data param { string } data . BusinessShortCode param { number } data . CheckoutRequestID param { any } data . passKey returns { Promise } public async lipaNaMpesaQuery ( { BusinessShortCode , passKey , CheckoutRequestID , } ) { const Timestamp = new Date ( ) . toISOString ( ) . replace ( /[^0-9]/g , '' ) . slice ( 0 , - 3 ) ; const Password = Buffer . from ( BusinessShortCode + passKey + Timestamp ) . toString ( 'base64' ) ; const token = await this . authenticate ( ) ; const response = await this . http . post < StkQueryResponseInterface > ( routes . stkquery , { BusinessShortCode , Password , Timestamp , CheckoutRequestID , } , { headers : { Authorization : 'Bearer ' + token , } , } ) ; return response . data ; } name description see https param { ReversalInterface } data param { string } data . Initiator param { string } data . TransactionID param { string } data . CommandID param { string } data . QueueTimeOutURL param { string } data . ReceiverParty param { number } data . RecieverIdentifierType param { string } data . ResultURL param { string } data . Remarks param { string } data . Occasion returns { Promise } public async reversal ( { Initiator , CommandID , TransactionID , Amount , ReceiverParty , RecieverIdentifierType , ResultURL , QueueTimeOutURL , Remarks , Occasion , } ) { const token = await this . authenticate ( ) ; const response = await this . http . post < ReversalResponseInterface > ( routes . reversal , { Initiator , SecurityCredential : this . securityCredential , CommandID : CommandID ?? 'TransactionReversal' , TransactionID , Amount , ReceiverParty , RecieverIdentifierType : RecieverIdentifierType ?? '4' , ResultURL , QueueTimeOutURL , Remarks : Remarks ?? 'Transaction Reversal' , Occasion : Occasion ?? 'TransactionReversal' , } , { headers : { Authorization : 'Bearer ' + token , } , } ) ; return response . data ; } name description see https param { AccountBalanceInterface } data param { string } data . Initiator param { string } data . SecurityCredential param { string } data . CommandID param { string } data . PartyA param { string } data . IdentifierType param { string } data . Remarks param { string } data . QueueTimeOutURL param { string } data . ResultURL returns { Promise } public async accountBalance ( { Initiator , CommandID , PartyA , IdentifierType , Remarks , QueueTimeOutURL , ResultURL , } ) { const token = await this . authenticate ( ) ; const response = await this . http . post < AccountBalanceResponseInterface > ( routes . accountbalance , { Initiator , SecurityCredential : this . securityCredential , CommandID : CommandID ?? 'AccountBalance' , PartyA , IdentifierType : IdentifierType ?? '4' , Remarks : Remarks ?? 'Account Balance' , QueueTimeOutURL , ResultURL , } , { headers : { Authorization : 'Bearer ' + token , } , } ) ; return response . data ; } }