MediaForce is a web development Game-Changer on Clutch
July 8, 2022How to build an effective branded marketing caravan campaign
August 23, 2022If you are wondering how to accept payments to your Laravel website, you are in the right place. We have shown you how to integrate MPESA into your PHP website. If you are not using any PHP framework, see this article.
In this article, we are going to look at how to integrate MPESA STK Push payment to your Laravel Website. Laravel is an open-source PHP framework, which is robust and easy to understand (read more). The difference is not that much except that Laravel helps organize our request functions and classes into Models and Controllers.
This article will help laravel PHP developers to implement the MPESA daraja API for Lipa na MPESA STK Push without much hustle. Therefore, you will need to have prior knowledge about Laravel.
Requirements & Installation
Before we get started, ensure you have the following:
- Safaricom Daraja Account
- PHP version 7.4 and above
- Laravel version 7.4 and above
- cURL extension for PHP installed on your server
If your environment meets these requirements, let’s get started!
Firstly, let us create an MPESA STK Push Model in your Laravel Project named MpesaStkPush.php. Therefore, on your terminal type $ php artisan make:model MpesaStkPush
<?php namespace App\Repositories; use Carbon\Carbon; use function Psy\sh; class MpesaStkpush { protected $consumer_key; protected $consumer_secret; protected $passkey; protected $amount; protected $accountReference; protected $phone; protected $env; protected $short_code; protected $parent_short_code; protected $initiatorName; protected $initiatorPassword ; public function __construct(){ $this->short_code = '7854001'; $this->parent_short_code='5868111'; $this->consumer_key=" "; //Your Consumer key $this->consumer_secret=" "; //Your Secret key $this->passkey = " "; //Your Passkey $this->CallBackURL = " "; //Your callback URL $this->env = "sandbox"; //Your Environment sandbox or Live $this->initiatorName = "testapi"; //Username of your choice $this->initiatorPassword = "Safaricom978!"; //Password of your choice } /** Lipa na M-PESA password **/ public function getPassword() { $timestamp = Carbon::now()->format('YmdHms'); $password = base64_encode($this->short_code. "" . $this->passkey ."". $timestamp); return $password; } public function lipaNaMpesa($amount,$phone,$accountReference){ $this->phone = $phone; $this->amount=$amount; $this->accountReference=$accountReference; $Password = getPassword() $headers = ['Content-Type:application/json; charset=utf8']; $access_token_url = ($this->env == "live") ? "https://api.safaricom.co.ke/oauth/v1/generate?grant_type=client_credentials" : "https://sandbox.safaricom.co.ke/oauth/v1/generate?grant_type=client_credentials"; $initiate_url = ($this->env == "live") ? "https://api.safaricom.co.ke/mpesa/stkpush/v1/processrequest" : "https://sandbox.safaricom.co.ke/mpesa/stkpush/v1/processrequest"; $curl = curl_init($access_token_url); curl_setopt($curl, CURLOPT_HTTPHEADER, $headers); curl_setopt($curl, CURLOPT_RETURNTRANSFER, TRUE); curl_setopt($curl, CURLOPT_HEADER, FALSE); curl_setopt($curl, CURLOPT_USERPWD, $this->consumer_key.':'.$this->consumer_secret); $result = curl_exec($curl); $status = curl_getinfo($curl, CURLINFO_HTTP_CODE); $result = json_decode($result); $access_token = $result->access_token; curl_close($curl); # header for stk push $stkheader = ['Content-Type:application/json','Authorization:Bearer '.$access_token]; # initiating the transaction $curl = curl_init(); curl_setopt($curl, CURLOPT_URL, $initiate_url); curl_setopt($curl, CURLOPT_HTTPHEADER, $stkheader); //setting custom header $curl_post_data = array( //Fill in the request parameters with valid values 'BusinessShortCode' => $this->short_code, 'Password' => $Password, 'Timestamp' => $Timestamp, 'TransactionType' => 'CustomerBuyGoodsOnline', 'Amount' => $this->amount, 'PartyA' => $phone, 'PartyB' => $this->parent_short_code, 'PhoneNumber' => $phone, 'CallBackURL' => $this->CallBackURL, 'AccountReference' => $this->accountReference, 'TransactionDesc' => $phone." has paid ".$this->amount." to ".$this->short_code ); $data_string = json_encode($curl_post_data); curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); curl_setopt($curl, CURLOPT_POST, true); curl_setopt($curl, CURLOPT_POSTFIELDS, $data_string); $response = curl_exec($curl); return $response; } public function status($transactionCode){ $type = 4; $command = "TransactionStatusQuery"; $remarks = "Transaction Status Query"; $occasion = "Transaction Status Query"; $results_url = "https://mydomain.com/TransactionStatus/result/"; //Endpoint to receive results Body $timeout_url = "https://mydomain.com/TransactionStatus/queue/"; //Endpoint to to go to on timeout $access_token = ($this->env == "live") ? "https://api.safaricom.co.ke/oauth/v1/generate?grant_type=client_credentials" : "https://sandbox.safaricom.co.ke/oauth/v1/generate?grant_type=client_credentials"; $credentials = base64_encode($this->consumer_key . ':' . $this->consumer_secret); $ch = curl_init($access_token); curl_setopt($ch, CURLOPT_HTTPHEADER, ["Authorization: Basic " . $credentials]); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); $response = curl_exec($ch); curl_close($ch); $result = json_decode($response); //echo $result->{'access_token'}; $token = isset($result->{'access_token'}) ? $result->{'access_token'} : "N/A"; $publicKey = file_get_contents(__DIR__ . "/mpesa_public_cert.cer"); $isvalid = openssl_public_encrypt($this->initiatorPassword, $encrypted, $publicKey, OPENSSL_PKCS1_PADDING); $password = base64_encode($encrypted); //echo $token; $curl_post_data = array( "Initiator" => $this->initiatorName, "SecurityCredential" => $password, "CommandID" => $command, "TransactionID" => $transactionCode, "PartyA" => $this->short_code, "IdentifierType" => $type, "ResultURL" => $results_url, "QueueTimeOutURL" => $timeout_url, "Remarks" => $remarks, "Occasion" => $occasion, ); $data_string = json_encode($curl_post_data); //echo $data_string; $endpoint = ($env == "live") ? "https://api.safaricom.co.ke/mpesa/transactionstatus/v1/query" : "https://sandbox.safaricom.co.ke/mpesa/transactionstatus/v1/query"; $ch2 = curl_init($endpoint); curl_setopt($ch2, CURLOPT_HTTPHEADER, [ 'Authorization: Bearer '.$token, 'Content-Type: application/json' ]); curl_setopt($ch2, CURLOPT_POST, 1); curl_setopt($ch2, CURLOPT_POSTFIELDS, $data_string); curl_setopt($ch2, CURLOPT_RETURNTRANSFER, 1); $response = curl_exec($ch2); curl_close($ch2); //echo "Authorization: ". $response; $result = json_decode($response); return $result; }
Secondly, we create a controller to manage the MPESA transaction requests. On your terminal type $ php artisan make:controller TransactionController
The controller TransactionController.php, have the following code:
<?php namespace App\Http\Controllers; use App\Repositories\MpesaStkpush; use Carbon\Carbon; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Str; class TransactionController extends Controller { //Initiate STK Push public function stkPushRequest(Request $request){ $accountReference='Transaction#'.Str::random(10); $amount= $request->amount); $phone=$this->formatPhone($request->phone_number); $mpesa=new MpesaStkpush(); $stk=$mpesa->lipaNaMpesa(1,$phone,$accountReference); $invalid=json_decode($stk); if(@$invalid->errorCode){ Session::flash('mpesa-error', 'Invalid phone number!'); Session::flash('alert-class', 'alert-danger'); return back(); } return redirect('/confirm/'.encrypt($accountReference)); } public function checkTransactionStatus($transactionCode){ $mpesa=new MpesaStkpush(); $status=$mpesa->status($transactionCode); $tStatus = $status->{'ResponseCode'}; return $tStatus; } public function formatPhone($phone) { $phone = 'hfhsgdgs' . $phone; $phone = str_replace('hfhsgdgs0', '', $phone); $phone = str_replace('hfhsgdgs', '', $phone); $phone = str_replace('+', '', $phone); if (strlen($phone) == 9) { $phone = '254' . $phone; } return $phone; } }
Thirdly, after setting up the controller, we will set up the routes for making these requests. So let us go to routes/api.php
<?php use Illuminate\Http\Request; use Illuminate\Support\Facades\Route; /* |-------------------------------------------------------------------------- | API Routes |-------------------------------------------------------------------------- | | Here is where you can register API routes for your application. These | routes are loaded by the RouteServiceProvider within a group which | is assigned the "api" middleware group. Enjoy building your API! | */ Route::middleware('auth:api')->get('/user', function (Request $request) { return $request->user(); }); Route::post('v1/mfc/stk/push', 'TransactionController@stkPushRequest');
After stk push was initiated, the user is redirected to a confirmation page which should be close to something like this:
When the user clicks “Complete order”, an MPESA Transaction status query is initiated to check the status of the transaction (To see if the user actually PAID, CANCELED or if there was an ERROR).
run $ php artisan serve on your terminal to start your application if you haven’t. Use Postman service to test your request by using the URL: http://127.0.0.1:8000/api/v1/mfc/stk/push.
1 Comment
Hi
on stk push lines 124 and 126 you are using the variable $encrypted. Where has this been defined?
Billy