diff --git a/readme.md b/readme.md index e0d5163..9aa160b 100644 --- a/readme.md +++ b/readme.md @@ -1,51 +1,55 @@ **Introduction** This package seeks to help php developers implement the various Mpesa APIs without much hustle. It is based on the REST API whose documentation is available on https://developer.safaricom.co.ke. - + **Installation using composer**
`composer require safaricom/mpesa`
- - + + **Configuration**
At your project root, create a .env file and in it set the consumer key and consumer secret as follows `MPESA_CONSUMER_KEY= [consumer key]`
`MPESA_CONSUMER_SECRET=[consumer secret]`
+ `MPESA_PASSWORD=[your mpesa password]`
+ `MPESA_PUBLICKEYPATH=[/path/to/your/public_key_file.cer]`
`MPESA_ENV=[live or sandbox]`
For Laravel users, open the Config/App.php file and add `\Safaricom\Mpesa\MpesaServiceProvider::class` under providers and ` 'Mpesa'=> \Safaricom\Mpesa\MpesaServiceProvider::class` under aliases. - + _Remember to edit the consumer_key and consumer_secret values appropriately when switching between sandbox and live_ - + **Usage** - - **Confirmation and validation urls** + + **Confirmation and validation urls** **B2C Payment Request** - + This creates transaction between an M-Pesa short code to a phone number registered on M-Pesa. - -`$mpesa= new \Safaricom\Mpesa\Mpesa();` -`$b2cTransaction=$mpesa->b2c($InitiatorName, $SecurityCredential, $CommandID, $Amount, $PartyA, $PartyB, $Remarks, $QueueTimeOutURL, $ResultURL, $Occasion);` +`$mpesa= new \Safaricom\Mpesa\Mpesa();` +Use this functional to generate the SecurityCredential if you have several different MPESA_PASSWORD and MPESA_PUBLICKEYPATH, if you only have one just declare your MPESA_PASSWORD and MPESA_PUBLICKEYPATH in your environment variables and do not pass it in subsequent functional calls +`$SecurityCredential=$mpesa->generateSecurityCredential([$Password = null] [, $PublicKey = null]);` +The $SecurityCredential is optional, it is automatically generated using your MPESA_PASSWORD and MPESA_PUBLICKEYPATH declared in your environment variables +`$b2cTransaction=$mpesa->b2c($InitiatorName [, $SecurityCredential], $CommandID, $Amount, $PartyA, $PartyB, $Remarks, $QueueTimeOutURL, $ResultURL, $Occasion);` **Account Balance Request** - + This is used to enquire the balance on an M-Pesa BuyGoods (Till Number) `$mpesa= new \Safaricom\Mpesa\Mpesa();` -`$balanceInquiry=$mpesa->accountBalance($CommandID, $Initiator, $SecurityCredential, $PartyA, $IdentifierType, $Remarks, $QueueTimeOutURL, $ResultURL);` +`$balanceInquiry=$mpesa->accountBalance($CommandID, $Initiator [, $SecurityCredential], $PartyA, $IdentifierType, $Remarks, $QueueTimeOutURL, $ResultURL);` **Transaction Status Request** -This is used to check the status of transaction. +This is used to check the status of transaction. `$mpesa= new \Safaricom\Mpesa\Mpesa();` -`$trasactionStatus=$mpesa->transactionStatus($Initiator, $SecurityCredential, $CommandID, $TransactionID, $PartyA, $IdentifierType, $ResultURL, $QueueTimeOutURL, $Remarks, $Occasion);` +`$trasactionStatus=$mpesa->transactionStatus($Initiator [, $SecurityCredential], $CommandID, $TransactionID, $PartyA, $IdentifierType, $ResultURL, $QueueTimeOutURL, $Remarks, $Occasion);` @@ -85,7 +89,7 @@ This is used to initiate online payment on behalf of a customer. **STK Push Status Query** This is used to check the status of a Lipa Na M-Pesa Online Payment. - + `$mpesa= new \Safaricom\Mpesa\Mpesa();` `$STKPushRequestStatus=$mpesa->STKPushQuery($checkoutRequestID,$businessShortCode,$password,$timestamp);` @@ -98,24 +102,21 @@ M-Pesa APIs are asynchronous. When a valid M-Pesa API request is received by the **Obtaining post data from callbacks** This is used to get post data from callback in json format. The data can be decoded and stored in a database. - + `$mpesa= new \Safaricom\Mpesa\Mpesa();` - + `$callbackData=$mpesa->getDataFromCallback();` - + **Finishing a transaction** After obtaining the Post data from the callbacks, use this at the end of your callback routes to complete the transaction - + `$mpesa= new \Safaricom\Mpesa\Mpesa();` - + `$callbackData=$mpesa->finishTransaction();` If validation fails, pass `false` to `finishTransaction()` `$mpesa= new \Safaricom\Mpesa\Mpesa();` - - `$callbackData=$mpesa->finishTransaction(false);` - - + `$callbackData=$mpesa->finishTransaction(false);` diff --git a/src/Mpesa.php b/src/Mpesa.php index aa49d0f..1e70375 100644 --- a/src/Mpesa.php +++ b/src/Mpesa.php @@ -18,15 +18,15 @@ class Mpesa { - /** + /** * Define env method similar to laravel's * * @param String $env_param | Environment Param Name - * + * * @return String | Actual Param */ - public static function env($env_param){ - + public static function env($env_param) + { $dotenv = new Dotenv(); $dotenv->load('../.env'); @@ -39,8 +39,8 @@ public static function env($env_param){ * This is used to generate tokens for the live environment * @return mixed */ - public static function generateLiveToken(){ - + public static function generateLiveToken() + { try { $consumer_key = env("MPESA_CONSUMER_KEY"); $consumer_secret = env("MPESA_CONSUMER_SECRET"); @@ -49,7 +49,7 @@ public static function generateLiveToken(){ $consumer_secret = self::env("MPESA_CONSUMER_SECRET"); } - if(!isset($consumer_key)||!isset($consumer_secret)){ + if (!isset($consumer_key)||!isset($consumer_secret)) { die("please declare the consumer key and consumer secret as defined in the documentation"); } $url = 'https://api.safaricom.co.ke/oauth/v1/generate?grant_type=client_credentials'; @@ -65,17 +65,41 @@ public static function generateLiveToken(){ $curl_response = curl_exec($curl); return json_decode($curl_response)->access_token; + } + /** + *Use this function to generate your security credential + * @param $password | Optional | Your Mpesa Password, Default to Declaration of MPESA_PASSWORD in Environment Variables + * @param $PublicKey | Optional | The path to the location of your public key file, Default to Declaration of MPESA_PUBLICKEYPATH in Environment Variables + */ + public function generateSecurityCredential($Password = null, $PublicKey = null) + { + try { + $Password = env("MPESA_PASSWORD"); + $PublicKey = env("MPESA_PUBLICKEYPATH"); + } catch (\Throwable $th) { + $Password = self::env("MPESA_PASSWORD"); + $PublicKey = self::env("MPESA_PUBLICKEYPATH"); + } - } + $fp=fopen($PublicKey, "r"); + $pub_key=fread($fp, 8192); + fclose($fp); + $PK=""; + $PK=openssl_get_publickey($pub_key); + openssl_public_encrypt($this->pass, $encrypted, $PK, OPENSSL_PKCS1_PADDING); + return $this->securityCredential = base64_encode($encrypted); + + echo json_encode($resultArray); + } /** * use this function to generate a sandbox token * @return mixed */ - public static function generateSandBoxToken(){ - + public static function generateSandBoxToken() + { try { $consumer_key = env("MPESA_CONSUMER_KEY"); $consumer_secret = env("MPESA_CONSUMER_SECRET"); @@ -84,7 +108,7 @@ public static function generateSandBoxToken(){ $consumer_secret = self::env("MPESA_CONSUMER_SECRET"); } - if(!isset($consumer_key)||!isset($consumer_secret)){ + if (!isset($consumer_key)||!isset($consumer_secret)) { die("please declare the consumer key and consumer secret as defined in the documentation"); } $url = 'https://sandbox.safaricom.co.ke/oauth/v1/generate?grant_type=client_credentials'; @@ -106,7 +130,7 @@ public static function generateSandBoxToken(){ * Use this function to initiate a reversal request * @param $CommandID | Takes only 'TransactionReversal' Command id * @param $Initiator | The name of Initiator to initiating the request - * @param $SecurityCredential | Encrypted Credential of user getting transaction amount + * @param $SecurityCredential | Optional | Encrypted password for the initiator to autheticate the transaction request, Autogenerated with Environment variables MPESA_PASSWORD & MPESA_PUBLICKEYPATH * @param $TransactionID | Unique Id received with every transaction response. * @param $Amount | Amount * @param $ReceiverParty | Organization /MSISDN sending the transaction @@ -117,21 +141,25 @@ public static function generateSandBoxToken(){ * @param $Occasion | Optional Parameter * @return mixed|string */ - public static function reversal($CommandID, $Initiator, $SecurityCredential, $TransactionID, $Amount, $ReceiverParty, $RecieverIdentifierType, $ResultURL, $QueueTimeOutURL, $Remarks, $Occasion){ - + public static function reversal($CommandID, $Initiator, $SecurityCredential = null, $TransactionID, $Amount, $ReceiverParty, $RecieverIdentifierType, $ResultURL, $QueueTimeOutURL, $Remarks, $Occasion) + { try { $environment = env("MPESA_ENV"); } catch (\Throwable $th) { $environment = self::env("MPESA_ENV"); } - if( $environment =="live"){ + if($SecurityCredential == null) { + $SecurityCredential = generateSecurityCredential(); + } + + if ($environment =="live") { $url = 'https://api.safaricom.co.ke/mpesa/reversal/v1/request'; $token=self::generateLiveToken(); - }elseif ($environment=="sandbox"){ + } elseif ($environment=="sandbox") { $url = 'https://sandbox.safaricom.co.ke/mpesa/reversal/v1/request'; $token=self::generateSandBoxToken(); - }else{ + } else { return json_encode(["Message"=>"invalid application status"]); } @@ -164,12 +192,11 @@ public static function reversal($CommandID, $Initiator, $SecurityCredential, $Tr curl_setopt($curl, CURLOPT_HEADER, false); $curl_response = curl_exec($curl); return json_decode($curl_response); - } /** * @param $InitiatorName | This is the credential/username used to authenticate the transaction request. - * @param $SecurityCredential | Encrypted password for the initiator to autheticate the transaction request + * @param $SecurityCredential | Optional | Encrypted password for the initiator to autheticate the transaction request, Autogenerated with Environment variables MPESA_PASSWORD & MPESA_PUBLICKEYPATH * @param $CommandID | Unique command for each transaction type e.g. SalaryPayment, BusinessPayment, PromotionPayment * @param $Amount | The amount being transacted * @param $PartyA | Organization’s shortcode initiating the transaction. @@ -180,21 +207,25 @@ public static function reversal($CommandID, $Initiator, $SecurityCredential, $Tr * @param $Occasion | Optional * @return string */ - public static function b2c($InitiatorName, $SecurityCredential, $CommandID, $Amount, $PartyA, $PartyB, $Remarks, $QueueTimeOutURL, $ResultURL, $Occasion){ - + public static function b2c($InitiatorName, $SecurityCredential = null, $CommandID, $Amount, $PartyA, $PartyB, $Remarks, $QueueTimeOutURL, $ResultURL, $Occasion) + { try { $environment = env("MPESA_ENV"); } catch (\Throwable $th) { $environment = self::env("MPESA_ENV"); } - if( $environment =="live"){ + if($SecurityCredential == null) { + $SecurityCredential = generateSecurityCredential(); + } + + if ($environment =="live") { $url = 'https://api.safaricom.co.ke/mpesa/b2c/v1/paymentrequest'; $token=self::generateLiveToken(); - }elseif ($environment=="sandbox"){ + } elseif ($environment=="sandbox") { $url = 'https://sandbox.safaricom.co.ke/mpesa/b2c/v1/paymentrequest'; $token=self::generateSandBoxToken(); - }else{ + } else { return json_encode(["Message"=>"invalid application status"]); } @@ -226,7 +257,6 @@ public static function b2c($InitiatorName, $SecurityCredential, $CommandID, $Amo $curl_response = curl_exec($curl); return json_encode($curl_response); - } /** * Use this function to initiate a C2B transaction @@ -237,21 +267,21 @@ public static function b2c($InitiatorName, $SecurityCredential, $CommandID, $Amo * @param $BillRefNumber | Bill Reference Number (Optional). * @return mixed|string */ - public static function c2b($ShortCode, $CommandID, $Amount, $Msisdn, $BillRefNumber ){ - + public static function c2b($ShortCode, $CommandID, $Amount, $Msisdn, $BillRefNumber) + { try { $environment = env("MPESA_ENV"); } catch (\Throwable $th) { $environment = self::env("MPESA_ENV"); } - - if( $environment =="live"){ + + if ($environment =="live") { $url = 'https://api.safaricom.co.ke/mpesa/c2b/v1/simulate'; $token=self::generateLiveToken(); - }elseif ($environment=="sandbox"){ + } elseif ($environment=="sandbox") { $url = 'https://sandbox.safaricom.co.ke/mpesa/c2b/v1/simulate'; $token=self::generateSandBoxToken(); - }else{ + } else { return json_encode(["Message"=>"invalid application status"]); } @@ -277,7 +307,6 @@ public static function c2b($ShortCode, $CommandID, $Amount, $Msisdn, $BillRef curl_setopt($curl, CURLOPT_HEADER, false); $curl_response = curl_exec($curl); return $curl_response; - } @@ -285,7 +314,7 @@ public static function c2b($ShortCode, $CommandID, $Amount, $Msisdn, $BillRef * Use this to initiate a balance inquiry request * @param $CommandID | A unique command passed to the M-Pesa system. * @param $Initiator | This is the credential/username used to authenticate the transaction request. - * @param $SecurityCredential | Encrypted password for the initiator to autheticate the transaction request + * @param $SecurityCredential | Optional | Encrypted password for the initiator to autheticate the transaction request, Autogenerated with Environment variables MPESA_PASSWORD & MPESA_PUBLICKEYPATH * @param $PartyA | Type of organization receiving the transaction * @param $IdentifierType |Type of organization receiving the transaction * @param $Remarks | Comments that are sent along with the transaction. @@ -293,21 +322,25 @@ public static function c2b($ShortCode, $CommandID, $Amount, $Msisdn, $BillRef * @param $ResultURL | The path that stores information of transaction * @return mixed|string */ - public static function accountBalance($CommandID, $Initiator, $SecurityCredential, $PartyA, $IdentifierType, $Remarks, $QueueTimeOutURL, $ResultURL){ - + public static function accountBalance($CommandID, $Initiator, $SecurityCredential = null, $PartyA, $IdentifierType, $Remarks, $QueueTimeOutURL, $ResultURL) + { try { $environment = env("MPESA_ENV"); } catch (\Throwable $th) { $environment = self::env("MPESA_ENV"); } - if( $environment =="live"){ + if($SecurityCredential == null) { + $SecurityCredential = generateSecurityCredential(); + } + + if ($environment =="live") { $url = 'https://api.safaricom.co.ke/mpesa/accountbalance/v1/query'; $token=self::generateLiveToken(); - }elseif ($environment=="sandbox"){ + } elseif ($environment=="sandbox") { $url = 'https://sandbox.safaricom.co.ke/mpesa/accountbalance/v1/query'; $token=self::generateSandBoxToken(); - }else{ + } else { return json_encode(["Message"=>"invalid application status"]); } @@ -341,7 +374,7 @@ public static function accountBalance($CommandID, $Initiator, $SecurityCredentia /** * Use this function to make a transaction status request * @param $Initiator | The name of Initiator to initiating the request. - * @param $SecurityCredential | Encrypted password for the initiator to autheticate the transaction request. + * @param $SecurityCredential | Optional | Encrypted password for the initiator to autheticate the transaction request, Autogenerated with Environment variables MPESA_PASSWORD & MPESA_PUBLICKEYPATH * @param $CommandID | Unique command for each transaction type, possible values are: TransactionStatusQuery. * @param $TransactionID | Organization Receiving the funds. * @param $PartyA | Organization/MSISDN sending the transaction @@ -352,21 +385,25 @@ public static function accountBalance($CommandID, $Initiator, $SecurityCredentia * @param $Occasion | Optional Parameter * @return mixed|string */ - public function transactionStatus($Initiator, $SecurityCredential, $CommandID, $TransactionID, $PartyA, $IdentifierType, $ResultURL, $QueueTimeOutURL, $Remarks, $Occasion){ - + public function transactionStatus($Initiator, $SecurityCredential = null, $CommandID, $TransactionID, $PartyA, $IdentifierType, $ResultURL, $QueueTimeOutURL, $Remarks, $Occasion) + { try { $environment = env("MPESA_ENV"); } catch (\Throwable $th) { $environment = self::env("MPESA_ENV"); } - - if( $environment =="live"){ + + if($SecurityCredential == null) { + $SecurityCredential = generateSecurityCredential(); + } + + if ($environment =="live") { $url = 'https://api.safaricom.co.ke/mpesa/transactionstatus/v1/query'; $token=self::generateLiveToken(); - }elseif ($environment=="sandbox"){ + } elseif ($environment=="sandbox") { $url = 'https://sandbox.safaricom.co.ke/mpesa/transactionstatus/v1/query'; $token=self::generateSandBoxToken(); - }else{ + } else { return json_encode(["Message"=>"invalid application status"]); } @@ -404,7 +441,7 @@ public function transactionStatus($Initiator, $SecurityCredential, $CommandID, $ /** * Use this function to initiate a B2B request * @param $Initiator | This is the credential/username used to authenticate the transaction request. - * @param $SecurityCredential | Encrypted password for the initiator to autheticate the transaction request. + * @param $SecurityCredential | Optional | Encrypted password for the initiator to autheticate the transaction request, Autogenerated with Environment variables MPESA_PASSWORD & MPESA_PUBLICKEYPATH. * @param $Amount | Base64 encoded string of the B2B short code and password, which is encrypted using M-Pesa public key and validates the transaction on M-Pesa Core system. * @param $PartyA | Organization’s short code initiating the transaction. * @param $PartyB | Organization’s short code receiving the funds being transacted. @@ -418,21 +455,25 @@ public function transactionStatus($Initiator, $SecurityCredential, $CommandID, $ * @return mixed|string */ - public function b2b($Initiator, $SecurityCredential, $Amount, $PartyA, $PartyB, $Remarks, $QueueTimeOutURL, $ResultURL, $AccountReference, $commandID, $SenderIdentifierType, $RecieverIdentifierType){ - + public function b2b($Initiator, $SecurityCredential = null, $Amount, $PartyA, $PartyB, $Remarks, $QueueTimeOutURL, $ResultURL, $AccountReference, $commandID, $SenderIdentifierType, $RecieverIdentifierType) + { try { $environment = env("MPESA_ENV"); } catch (\Throwable $th) { $environment = self::env("MPESA_ENV"); } - - if( $environment =="live"){ + + if($SecurityCredential == null) { + $SecurityCredential = generateSecurityCredential(); + } + + if ($environment =="live") { $url = 'https://api.safaricom.co.ke/mpesa/b2b/v1/paymentrequest'; $token=self::generateLiveToken(); - }elseif ($environment=="sandbox"){ + } elseif ($environment=="sandbox") { $url = 'https://sandbox.safaricom.co.ke/mpesa/b2b/v1/paymentrequest'; $token=self::generateSandBoxToken(); - }else{ + } else { return json_encode(["Message"=>"invalid application status"]); } $curl = curl_init(); @@ -458,7 +499,6 @@ public function b2b($Initiator, $SecurityCredential, $Amount, $PartyA, $PartyB, curl_setopt($curl, CURLOPT_POSTFIELDS, $data_string); $curl_response = curl_exec($curl); return $curl_response; - } /** @@ -476,27 +516,27 @@ public function b2b($Initiator, $SecurityCredential, $Amount, $PartyA, $PartyB, * @param $Remark | Remarks * @return mixed|string */ - public function STKPushSimulation($BusinessShortCode, $LipaNaMpesaPasskey, $TransactionType, $Amount, $PartyA, $PartyB, $PhoneNumber, $CallBackURL, $AccountReference, $TransactionDesc, $Remark){ - + public function STKPushSimulation($BusinessShortCode, $LipaNaMpesaPasskey, $TransactionType, $Amount, $PartyA, $PartyB, $PhoneNumber, $CallBackURL, $AccountReference, $TransactionDesc, $Remark) + { try { $environment = env("MPESA_ENV"); } catch (\Throwable $th) { $environment = self::env("MPESA_ENV"); } - - if( $environment =="live"){ + + if ($environment =="live") { $url = 'https://api.safaricom.co.ke/mpesa/stkpush/v1/processrequest'; $token = self::generateLiveToken(); - }elseif ($environment=="sandbox"){ + } elseif ($environment=="sandbox") { $url = 'https://sandbox.safaricom.co.ke/mpesa/stkpush/v1/processrequest'; $token = self::generateSandBoxToken(); - }else{ + } else { return json_encode(["Message"=>"invalid application status"]); } - $timestamp='20'.date( "ymdhis"); + $timestamp='20'.date("ymdhis"); $password=base64_encode($BusinessShortCode.$LipaNaMpesaPasskey.$timestamp); $curl = curl_init(); @@ -526,8 +566,6 @@ public function STKPushSimulation($BusinessShortCode, $LipaNaMpesaPasskey, $Tran curl_setopt($curl, CURLOPT_HEADER, false); $curl_response=curl_exec($curl); return $curl_response; - - } @@ -539,21 +577,21 @@ public function STKPushSimulation($BusinessShortCode, $LipaNaMpesaPasskey, $Tran * @param $timestamp | Timestamp * @return mixed|string */ - public static function STKPushQuery($environment, $checkoutRequestID, $businessShortCode, $password, $timestamp){ - + public static function STKPushQuery($environment, $checkoutRequestID, $businessShortCode, $password, $timestamp) + { try { $environment = env("MPESA_ENV"); } catch (\Throwable $th) { $environment = self::env("MPESA_ENV"); } - - if( $environment =="live"){ + + if ($environment =="live") { $url = 'https://api.safaricom.co.ke/mpesa/stkpushquery/v1/query'; $token=self::generateLiveToken(); - }elseif ($environment=="sandbox"){ + } elseif ($environment=="sandbox") { $url = 'https://sandbox.safaricom.co.ke/mpesa/stkpushquery/v1/query'; $token=self::generateSandBoxToken(); - }else{ + } else { return json_encode(["Message"=>"invalid application status"]); } @@ -608,10 +646,9 @@ public function finishTransaction($status = true) /** *Use this function to get callback data posted in callback routes */ - public function getDataFromCallback(){ + public function getDataFromCallback() + { $callbackJSONData=file_get_contents('php://input'); return $callbackJSONData; } - - }