This is too late to respond but maybe for who ever may be looking for a similar answer.
1st, you initiate the STK Push: This can be done using a submit button.
<?php
if(isset($_POST['mpesastk'])){
$app_id = mysqli_real_escape_string($conx, $_POST['app_id']);// Value to be updated in a different table during the mpesa callback url process.
$amount = '1'; //Amount to be paid
$phone = mysqli_real_escape_string($conx, $_POST['pay_phone']); //Phone Number
$config = array(
"env" => "sandbox",
"BusinessShortCode"=> "174379",
"key" => "", //Enter your consumer key here
"secret" => "", //Enter your consumer secret here
"username" => "apitest",
"TransactionType" => "CustomerPayBillOnline",
"passkey" => "bfb279f9aa9bdbcf158e97dd71a467cd2e0c893059b10f78e6b72ada1ed2c919", //Enter your passkey here
"CallBackURL" => "", //Must have SSL When using localhost, Use Ngrok to forward the response to your Localhost
"AccountReference" => "Name to appear.",
"TransactionDesc" => "Payment of X Fee for ",
);
$phone = (substr($phone, 0, 1) == "+") ? str_replace("+", "", $phone) : $phone;
$phone = (substr($phone, 0, 1) == "0") ? preg_replace("/^0/", "254", $phone) : $phone;
$phone = (substr($phone, 0, 1) == "7") ? "254{$phone}" : $phone;
$access_token = ($config['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";
//$access_token = "https://sandbox.safaricom.co.ke/oauth/v1/generate?grant_type=client_credentials";
$credentials = base64_encode($config['key'] . ':' . $config['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);
$token = isset($result->{'access_token'}) ? $result->{'access_token'} : "N/A";
$timestamp = date("YmdHis");
$password = base64_encode($config['BusinessShortCode'] . "" . $config['passkey'] ."". $timestamp);
$curl_post_data = array(
"BusinessShortCode" => $config['BusinessShortCode'],
"Password" => $password,
"Timestamp" => $timestamp,
"TransactionType" => $config['TransactionType'],
"Amount" => $amount,
"PartyA" => $phone,
"PartyB" => $config['BusinessShortCode'],
"PhoneNumber" => $phone,
"CallBackURL" => $config['CallBackURL'],
"AccountReference" => $config['AccountReference'],
"TransactionDesc" => $config['TransactionDesc'],
);
$data_string = json_encode($curl_post_data);
//$endpoint = "https://sandbox.safaricom.co.ke/mpesa/stkpush/v1/processrequest";
$endpoint = ($config['env'] == "live") ? "https://api.safaricom.co.ke/mpesa/stkpush/v1/processrequest" : "https://sandbox.safaricom.co.ke/mpesa/stkpush/v1/processrequest";
$ch = curl_init($endpoint );
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Authorization: Bearer '.$token,
'Content-Type: application/json'
]);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data_string);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$response = curl_exec($ch);
curl_close($ch);
$result = json_decode(json_encode(json_decode($response)), true);
if(!preg_match('/^[0-9]{10}+$/', $phone) && array_key_exists('errorMessage', $result)){
$errors['phone'] = $result["errorMessage"];
}
if($result['ResponseCode'] === "0"){
$MerchantRequestID = $result['MerchantRequestID'];
$CheckoutRequestID = $result['CheckoutRequestID'];
$sql = "INSERT INTO mpesastk(mpesastk_appid,mpesastk_phone,mpesastk_amount,CheckoutRequestID,MerchantRequestID)
VALUES('$app_id','$phone','$amount','$CheckoutRequestID','$MerchantRequestID')";
if ($conx->query($sql) === TRUE){
//Response to user
$err_color = "success";
$err_title = "SUCCESS!";
$err_message = '<h4><font color="#fff">Payment of X fee was sent to your phone.</font></h4>';
header("refresh:15;");
}else{
$errors['database'] = "Unable to initiate your order: ".$conx->error;;
foreach($errors as $error) {
$err_message .= $error . '<br />';
}
}
}else{
$err_color = "error";
$err_title = "ERROR!";
$err_message = '<h4><font color="#fff">Failed to send Payment Request of X fee to your phone.</font></h4>';
header("refresh:3;");
}
}
?>
Note that
$CheckoutRequestID = $result['CheckoutRequestID'];
$app_id = mysqli_real_escape_string($conx, $_POST['app_id']);
was inserted. This value will be used by CallBack URL to update the database accordingly.
Now the CallBack URL
<?php
echo '<a href="../../">Home<br /></a>';
$content = file_get_contents('php://input'); //Receives the JSON Result from safaricom
$res = json_decode($content, true); //Convert the json to an array
$dataToLog = array(
date("Y-m-d H:i:s"), //Date and time
" MerchantRequestID: ".$res['Body']['stkCallback']['MerchantRequestID'],
" CheckoutRequestID: ".$res['Body']['stkCallback']['CheckoutRequestID'],
" ResultCode: ".$res['Body']['stkCallback']['ResultCode'],
" ResultDesc: ".$res['Body']['stkCallback']['ResultDesc'],
" MpesaReceiptNumber: ".$res['Body']['stkCallback']['CallbackMetadata']['Item'][1]['Value'],
);
$data = implode(" - ", $dataToLog);
$data .= PHP_EOL;
file_put_contents('mpesastk_log', $data, FILE_APPEND); //Create a txt file and log the results to our log file
//Saves the result to the database
//Change the values accordingly to your system setup
$conn=new PDO("mysql:host=localhost;dbname=dbname","root","password");
$conn->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION);
$stmt = $conn->query("SELECT * FROM mpesastk ORDER BY mpesastk_id DESC LIMIT 1");
$stmt->execute();
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
foreach($rows as $row){
$mpesastk_id = $row['mpesastk_id'];
$app_id = $row['mpesastk_appid'];//remember this, it will be
$ResultCode = $res['Body']['stkCallback']['ResultCode'];
$ResultDesc = $res['Body']['stkCallback']['ResultDesc'];
$MpesaReceiptNumber = $res['Body']['stkCallback']['CallbackMetadata']['Item'][1]['Value'];
if($res['Body']['stkCallback']['ResultCode'] == '1032'){//if transaction canceled
$sql = $conn->query("UPDATE mpesastk SET mpesastk_status = '0',ResultCode = '$ResultCode',
ResultDesc='$ResultDesc',MpesaReceiptNumber='$MpesaReceiptNumber' WHERE mpesastk_id = $mpesastk_id");
$rs = $sql->execute();
}else{//if transaction was paid
$sql = $conn->query("UPDATE mpesastk SET mpesastk_status = '1',ResultCode = '$ResultCode',
ResultDesc='$ResultDesc',MpesaReceiptNumber='$MpesaReceiptNumber' WHERE mpesastk_id = $mpesastk_id");
$rs = $sql->execute();
//Now update a different table in the database
// Not the $app_id as set in the submit :)
$asql = $conn->query("UPDATE tblX SET tblX_status = '3' WHERE tblX_id = $app_id");
$ars = $asql->execute();
}
if($rs){
file_put_contents('error_log', "Records Inserted", FILE_APPEND);;
}else{
file_put_contents('error_log', "Failed to insert Records", FILE_APPEND);
}
}
?>
Happy Coding