<?php
/**
* Daraja API - STK Push (Lipa Na M-Pesa Online)
* Complete implementation with error handling
*/
class MpesaSTKPush {
private $consumerKey;
private $consumerSecret;
private $businessShortCode;
private $passkey;
private $environment; // "sandbox" or "production"
public function __construct($environment = "sandbox") {
$this->consumerKey = "YOUR_CONSUMER_KEY";
$this->consumerSecret = "YOUR_CONSUMER_SECRET";
$this->businessShortCode = "174379"; // Test shortcode
$this->passkey = "YOUR_PASSKEY";
$this->environment = $environment;
}
private function getBaseUrl() {
if ($this->environment === "production") {
return "https://api.safaricom.co.ke";
}
return "https://sandbox.safaricom.co.ke";
}
private function getAccessToken() {
$url = $this->getBaseUrl() . "/oauth/v1/generate?grant_type=client_credentials";
$credentials = base64_encode($this->consumerKey . ":" . $this->consumerSecret);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"Authorization: Basic " . $credentials
]);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode === 200) {
$data = json_decode($response, true);
return $data["access_token"] ?? null;
}
return null;
}
public function stkPush($phoneNumber, $amount, $accountReference, $transactionDesc) {
$token = $this->getAccessToken();
if (!$token) {
return ["error" => "Failed to get access token"];
}
$url = $this->getBaseUrl() . "/mpesa/stkpush/v1/processrequest";
$timestamp = date("YmdHis");
$password = base64_encode($this->businessShortCode . $this->passkey . $timestamp);
$postData = [
"BusinessShortCode" => $this->businessShortCode,
"Password" => $password,
"Timestamp" => $timestamp,
"TransactionType" => "CustomerPayBillOnline",
"Amount" => (int)$amount,
"PartyA" => $this->formatPhone($phoneNumber),
"PartyB" => $this->businessShortCode,
"PhoneNumber" => $this->formatPhone($phoneNumber),
"CallBackURL" => "https://yourdomain.com/mpesa-callback.php",
"AccountReference" => $accountReference,
"TransactionDesc" => $transactionDesc
];
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($postData));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"Authorization: Bearer " . $token,
"Content-Type: application/json"
]);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode === 200) {
return json_decode($response, true);
}
return ["error" => "STK Push failed", "response" => $response];
}
private function formatPhone($phone) {
// Convert to format 254XXXXXXXXX
$phone = preg_replace("/[^0-9]/", "", $phone);
if (substr($phone, 0, 1) === "0") {
$phone = "254" . substr($phone, 1);
}
return $phone;
}
}
// Usage example
$mpesa = new MpesaSTKPush("sandbox");
$result = $mpesa->stkPush(
"0712345678",
100,
"INV001",
"Payment for services"
);
if (isset($result["ResponseCode"]) && $result["ResponseCode"] === "0") {
echo "STK Push sent successfully. Check your phone to complete payment.";
echo "CheckoutRequestID: " . $result["CheckoutRequestID"];
} else {
echo "Error: " . ($result["error"] ?? "Unknown error");
}
?>