Introduction
In today’s online business world, especially here in Kenya, convenience is everything. And when it comes to mobile money, MPESA remains king. But it has evolved far beyond just sending and receiving cash. With e-commerce booming (and yes, thanks COVID pushed us all to work from home), businesses are moving online and customers expect fast, mobile-friendly payments.
That’s where the STK-Push API from Safaricom’s Daraja API portal comes in. This guide will walk you — step-by-step — through integrating MPESA STK-Push into your PHP website. Buckle up
Types of MPESA Integration Services
Before we dive into code, let’s understand the different integration types:
- Business-to-Customer (B2C) – your business pays customers (refunds, bonuses).
- Business-to-Business (B2B) – your business pays another business (suppliers, vendors).
- Customer-to-Business (C2B) – customers pay your business (what you’ll be doing).
- Lipa na MPESA Online (STK-Push) – you initiate a payment on behalf of the customer; the customer confirms via MPESA prompt.
What is STK Push?
STK stands for Sim Tool Kit. With STK-Push, after you initiate a payment from your website, the customer receives a prompt on their phone to enter their MPESA PIN and confirm the payment. Once they do, both parties (you and the customer) get confirmation messages. That real-time magic is what turns browsers into cash registers.
Step 1: Sign up for Safaricom Daraja Developer Account
- Visit the official Daraja portal and register for an account.
- Once logged in, add a new App: select Lipa na MPESA Sandbox and MPESA Sandbox, then create. You’ll get your Consumer Key, Consumer Secret, and Passkey. Keep them secret.
Step 2: Set Up Your Code Structure
Inside your PHP website (e.g., in a folder called mpesa/) set up:
mpesa/
checkout.php ← the user payment form
express-stk.php ← initiates STK Push
callback.php ← receives MPESA result from Safaricom
status.php ← optional: checks transaction status
transaction_log ← plain text log file (optional)
These file names mirror many reference guides.
Step 3: Create the Checkout Page (checkout.php)
This page shows the form where the customer enters their phone number (and you might show the amount). Example skeleton:
<?php include('express-stk.php'); ?>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Pay with MPESA</title>
</head>
<body>
<h1>Pay KES 100.00</h1>
<form action="<?php echo $_SERVER['PHP_SELF']; ?>" method="POST">
<label for="phone_number">Phone Number</label>
<input type="text" id="phone_number" name="phone_number" placeholder="07XXXXXXXX" maxlength="10" required>
<input type="hidden" name="orderNo" value="#ORD-12345">
<button type="submit">Confirm and Pay</button>
</form>
</body>
</html>
When the form posts, it triggers express-stk.php.
Step 4: Initiate STK Push (express-stk.php)
Here is a bare-bones version of initiating STK Push. Make sure you replace the config values with your own.
<?php
session_start();
$errors = array();
$errmsg = '';
$config = [
"env" => "sandbox", // or "live"
"BusinessShortCode"=> "174379", // your PayBill or Till number
"key" => "YOUR_CONSUMER_KEY",
"secret" => "YOUR_CONSUMER_SECRET",
"passkey" => "YOUR_PASSKEY",
"TransactionType" => "CustomerPayBillOnline",
"CallBackURL" => "https://yourdomain.co.ke/mpesa/callback.php",
"AccountReference" => "Invoice1234",
"TransactionDesc" => "Payment for Order #ORD-12345",
];
if (isset($_POST['phone_number'])) {
$phone = $_POST['phone_number'];
$orderNo = $_POST['orderNo'];
$amount = 100; // for example
// Normalize phone number to 254 format
if (substr($phone, 0, 1) === "0") {
$phone = "254" . substr($phone, 1);
} elseif (substr($phone, 0, "+")) {
$phone = substr($phone, 1);
}
// Obtain Access Token
$url = ($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";
$credentials = base64_encode($config['key'] . ':' . $config['secret']);
$curl = curl_init($url);
curl_setopt($curl, CURLOPT_HTTPHEADER, ["Authorization: Basic $credentials"]);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($curl);
curl_close($curl);
$result = json_decode($response);
$token = isset($result->access_token) ? $result->access_token : "";
// Prepare STK push request
$timestamp = date("YmdHis");
$password = base64_encode($config['BusinessShortCode'] . $config['passkey'] . $timestamp);
$postData = [
"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'],
];
$endPoint = ($config['env'] === "live")
? "https://api.safaricom.co.ke/mpesa/stkpush/v1/processrequest"
: "https://sandbox.safaricom.co.ke/mpesa/stkpush/v1/processrequest";
$curl = curl_init($endPoint);
curl_setopt($curl, CURLOPT_HTTPHEADER, [
"Authorization: Bearer $token",
"Content-Type: application/json"
]);
curl_setopt($curl, CURLOPT_POST, true);
curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($postData));
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($curl);
curl_close($curl);
$result = json_decode($response, true);
if ($result['ResponseCode'] === "0") {
$_SESSION["MerchantRequestID"] = $result['MerchantRequestID'] ?? '';
$_SESSION["CheckoutRequestID"] = $result['CheckoutRequestID'] ?? '';
$_SESSION["phone"] = $phone;
$_SESSION["orderNo"] = $orderNo;
header('Location: confirm-payment.php');
exit;
} else {
$errmsg = $result['errorMessage'] ?? 'Unknown error';
}
}
?>
Notes:
- Use the sandbox URL and credentials while testing.
- For live transactions, ensure you are approved and using correct live values.
- Log responses for audit and debugging.
Step 5: Handle the Callback (callback.php)
Safaricom will send a JSON payload to your CallBackURL. This is how you receive status (success/failure/cancel). Example:
<?php
$content = file_get_contents('php://input');
$data = json_decode($content, true);
$merchantRequestID = $data['Body']['stkCallback']['MerchantRequestID'] ?? '';
$checkoutRequestID = $data['Body']['stkCallback']['CheckoutRequestID'] ?? '';
$resultCode = $data['Body']['stkCallback']['ResultCode'] ?? '';
$resultDesc = $data['Body']['stkCallback']['ResultDesc'] ?? '';
$logLine = date("Y-m-d H:i:s") .
" | MerchantRequestID: {$merchantRequestID}" .
" | CheckoutRequestID: {$checkoutRequestID}" .
" | ResultCode: {$resultCode}" .
" | ResultDesc: {$resultDesc}" . "\n";
file_put_contents("transaction_log", $logLine, FILE_APPEND);
// Update database based on resultCode...
// If resultCode = 0 → SUCCESS, else FAILED / CANCELLED.
?>
Important:
- Make the callback URL publicly accessible (not localhost) unless you use tunneling (e.g., ngrok) for testing.
- Validate and sanitize everything.
- Use HTTPS.
Step 6: Optional – Check Transaction Status (status.php)
If you want to query the status later, you can use the Transaction Status API endpoint. Example:
<?php
// Similar token generation as above...
$transactionID = $_GET['transactionID'] ?? '';
$postData = [
"Initiator" => "testapi",
"SecurityCredential" => "ENCODED_INITIATOR_PASSWORD",
"CommandID" => "TransactionStatusQuery",
"TransactionID" => $transactionID,
"PartyA" => "SHORTCODE",
"IdentifierType" => "4",
"ResultURL" => "https://yourdomain.co.ke/mpesa/status-result.php",
"QueueTimeOutURL" => "https://yourdomain.co.ke/mpesa/timeout.php",
"Remarks" => "Transaction Status Query",
"Occasion" => "StatusCheck"
];
// Then curl POST to:
// https://sandbox.safaricom.co.ke/mpesa/transactionstatus/v1/query
// or live equivalent.
?>
This is more advanced and optional for basic STK Push.
Step 7: Going Live
After sandbox testing is successful, you’ll need to:
- Submit your app for live approval in Daraja portal.
- Switch env to "live", use live Consumer Key, Secret, Passkey; switch endpoints to the live API URLs.
- Use your real PayBill/Till number.
- Ensure SSL is active and your callback URL is reachable.
- Perform a few test transactions in the live environment.
Troubleshooting & Tips
- Ensure your phone number is in the format 2547XXXXXXXX.
- Ensure your server times (timestamp) are correct. STK Push fails if the timestamp is wrong.
- Monitor error messages — the API often returns clear errorMessage.
- Use logs (transaction_log) and database entries for audit trail.
- Keep your credentials (Consumer Key, Secret, Passkey) safe — don’t expose them in public repos.
- Use HTTPS for any callback or endpoint handling payment data.
- Make sure your server firewall allows HTTP POST from Safaricom IPs.
- If using localhost, use tunneling tools like ngrok to test callback.
Conclusion
Congratulations — you’ve integrated MPESA STK-Push into your PHP website. Your customers can now pay via MPESA, browse your site on any device, and complete checkout in seconds.
By adding this payment method, you increase trust, reduce cart abandonment, and open up your business to Kenya’s mobile-first market.
No comments yet. Be the first to comment!