STK Push
STK Push is a Customer to Business (C2B) payment method that allows you to initiate a payment request to your customer's phone. The customer receives a prompt on their phone to enter their M-Pesa PIN to complete the transaction.
Basic Usage
use MesaSDK\PhpMpesa\Config;
use MesaSDK\PhpMpesa\Mpesa;
use MesaSDK\PhpMpesa\Exceptions\MpesaException;
// Configuration settings
// In production, these should be loaded from environment variables or a config file
$settings = [
'environment' => 'sandbox',
'base_url' => 'https://apisandbox.safaricom.et',
'consumer_key' => '7oJ7uWPDp3jwqBzGvxQOn5g8s5rPwJ3qfXvsxwHyAknxAAxi',
'consumer_secret' => 'zEvvR7yTpNYG1DoH31MKOYOzh0iZ9kdXAK1andjjrqXdnJMTbiUMhnnz5Qf12oNC',
'shortcode' => '1020',
'callback_url' => 'https://testt.tugza.tech/examples/STKPushCallbackExample.php'
];
try {
// 1. Initialize configuration
$config = new Config();
$config->setEnvironment($settings['environment'])
->setBaseUrl($settings['base_url'])
->setConsumerKey($settings['consumer_key'])
->setConsumerSecret($settings['consumer_secret'])
->setShortCode($settings['shortcode'])
->setVerifySSL(false); // Note: Always use true in production
// 2. Initialize M-Pesa client
$mpesa = new Mpesa($config);
// Add debug logging
error_log("Attempting authentication...");
// 3. Set up the transaction details
try {
$authResult = $mpesa->authenticate();
error_log("Authentication result: " . json_encode($authResult));
$mpesa->setPhoneNumber('251700404709') // Customer's phone number (format: 2517XXXXXXXX)
->setAmount(20.00) // Amount to be charged
->setAccountReference('INV' . time()) // Dynamic reference
->setTransactionDesc('Payment for Monthly Package') // Description shown to customer
->setCallbackUrl($settings['callback_url']);
// 4. For sandbox testing only - set test credentials
if ($config->getEnvironment() === 'sandbox') {
// Try without the test password first
$mpesa->setTestPassword('M2VkZGU2YWY1Y2RhMzIyOWRjMmFkMTRiMjdjOWIwOWUxZDFlZDZiNGQ0OGYyMDRiNjg0ZDZhNWM2NTQyNTk2ZA==');
}
error_log("Initiating STK Push...");
// 5. Initiate the STK Push
$response = $mpesa->ussdPush();
error_log("STK Push Response: " . json_encode($response));
// 6. Handle the response
if ($mpesa->isSuccessful()) {
echo "✅ Transaction initiated successfully!\n\n";
echo "Transaction Details:\n";
echo "-------------------\n";
echo "🔖 Merchant Request ID: " . $mpesa->getMerchantRequestID() . "\n";
echo "🔖 Checkout Request ID: " . $mpesa->getCheckoutRequestID() . "\n\n";
// Store these IDs for later use in callback handling
// 7. Check for callback data (if synchronous)
$callbackData = $mpesa->getCallbackData();
if (!empty($callbackData)) {
echo "Callback Response:\n";
echo "----------------\n";
print_r($callbackData);
if ($mpesa->isCanceledByUser()) {
echo "❌ Transaction was canceled by the user\n";
}
echo "Result Code: " . $mpesa->getResultCode() . "\n";
echo "Result Description: " . $mpesa->getResultDesc() . "\n";
} else {
echo "ℹ️ Waiting for customer to complete the payment...\n";
echo "Check STKPushCallbackExample.php for callback handling\n";
}
} else {
echo "❌ Transaction initiation failed!\n";
echo "Error: " . $mpesa->getResultDesc() . "\n";
}
} catch (MpesaException $e) {
echo "❌ M-Pesa API Error: " . $e->getMessage() . "\n";
// Log the error for debugging
error_log("M-Pesa Error: " . $e->getMessage());
} catch (RuntimeException $e) {
echo "❌ Runtime Error: " . $e->getMessage() . "\n";
error_log("Runtime Error: " . $e->getMessage());
} catch (Exception $e) {
echo "❌ Unexpected Error: " . $e->getMessage() . "\n";
error_log("Unexpected Error: " . $e->getMessage());
}
} catch (MpesaException $e) {
echo "❌ M-Pesa API Error: " . $e->getMessage() . "\n";
// Log the error for debugging
error_log("M-Pesa Error: " . $e->getMessage());
} catch (RuntimeException $e) {
echo "❌ Runtime Error: " . $e->getMessage() . "\n";
error_log("Runtime Error: " . $e->getMessage());
} catch (Exception $e) {
echo "❌ Unexpected Error: " . $e->getMessage() . "\n";
error_log("Unexpected Error: " . $e->getMessage());
}
Configuration
Before using STK Push, ensure you have configured:
- Shortcode: Your M-Pesa shortcode
- Passkey: Your M-Pesa passkey
- Callback URLs: URLs to receive transaction notifications
- Environment: Sandbox or Production
Methods
setPhoneNumber(string $phone)
Sets the customer's phone number.
$mpesa->setPhoneNumber("254712345678");
setAmount(float $amount)
Sets the transaction amount.
$mpesa->setAmount(100.00);
setCallbackUrl(string $url)
Sets the callback URL for transaction notifications.
$mpesa->setCallbackUrl("https://your-domain.com/callback");
setAccountReference(string $reference)
Sets the account reference for the transaction.
$mpesa->setAccountReference("Order #123");
setTransactionDesc(string $desc)
Sets the transaction description.
$mpesa->setTransactionDesc("Payment for Order #123");
send()
Initiates the STK Push request.
$result = $mpesa->send();
Response Handling
The STK Push response includes:
CheckoutRequestID
: Unique identifier for the checkout requestMerchantRequestID
: Unique identifier for the merchant requestResponseCode
: Response code from M-PesaResponseDescription
: Description of the responseCustomerMessage
: Message to display to the customer
if ($result->isSuccessful()) {
echo "Checkout Request ID: " . $result->getCheckoutRequestID();
echo "Merchant Request ID: " . $result->getMerchantRequestID();
echo "Customer Message: " . $result->getCustomerMessage();
} else {
echo "Error: " . $result->getErrorMessage();
}
Checking Transaction Status
After initiating an STK Push, you can check the transaction status:
$status = $mpesa->checkTransactionStatus($result->getCheckoutRequestID());
if ($status->isSuccessful()) {
echo "Transaction Status: " . $status->getTransactionStatus();
}
Best Practices
-
Error Handling
- Always check for successful responses
- Implement proper error handling
- Log failed transactions
-
Callback Processing
- Process callbacks asynchronously
- Implement retry mechanisms
- Validate callback data
-
Security
- Use HTTPS for callback URLs
- Validate phone numbers
- Implement proper authentication
-
Testing
- Test in sandbox environment first
- Use test phone numbers
- Verify callback handling
Example Implementation
Here's a complete example of initiating an STK Push:
<?php
/**
* M-Pesa STK Push Example
*
* This example demonstrates how to initiate an STK Push request
* to prompt a customer to make a payment through the M-Pesa menu on their phone.
*
* Prerequisites:
* - Valid M-Pesa API credentials (Consumer Key and Secret)
* - A registered M-Pesa shortcode
* - SSL enabled callback URL
*/
require_once __DIR__ . '/../vendor/autoload.php';
use MesaSDK\PhpMpesa\Config;
use MesaSDK\PhpMpesa\Mpesa;
use MesaSDK\PhpMpesa\Exceptions\MpesaException;
// Configuration settings
// In production, these should be loaded from environment variables or a config file
$settings = [
'environment' => 'sandbox',
'base_url' => 'https://apisandbox.safaricom.et',
'consumer_key' => '7oJ7uWPDp3jwqBzGvxQOn5g8s5rPwJ3qfXvsxwHyAknxAAxi',
'consumer_secret' => 'zEvvR7yTpNYG1DoH31MKOYOzh0iZ9kdXAK1andjjrqXdnJMTbiUMhnnz5Qf12oNC',
'shortcode' => '1020',
'callback_url' => 'https://testt.tugza.tech/examples/STKPushCallbackExample.php'
];
try {
// 1. Initialize configuration
$config = new Config();
$config->setEnvironment($settings['environment'])
->setBaseUrl($settings['base_url'])
->setConsumerKey($settings['consumer_key'])
->setConsumerSecret($settings['consumer_secret'])
->setShortCode($settings['shortcode'])
->setVerifySSL(false); // Note: Always use true in production
// 2. Initialize M-Pesa client
$mpesa = new Mpesa($config);
// Add debug logging
error_log("Attempting authentication...");
// 3. Set up the transaction details
try {
$authResult = $mpesa->authenticate();
error_log("Authentication result: " . json_encode($authResult));
$mpesa->setPhoneNumber('251700404709') // Customer's phone number (format: 2517XXXXXXXX)
->setAmount(20.00) // Amount to be charged
->setAccountReference('INV' . time()) // Dynamic reference
->setTransactionDesc('Payment for Monthly Package') // Description shown to customer
->setCallbackUrl($settings['callback_url']);
// 4. For sandbox testing only - set test credentials
if ($config->getEnvironment() === 'sandbox') {
// Try without the test password first
$mpesa->setTestPassword('M2VkZGU2YWY1Y2RhMzIyOWRjMmFkMTRiMjdjOWIwOWUxZDFlZDZiNGQ0OGYyMDRiNjg0ZDZhNWM2NTQyNTk2ZA==');
}
error_log("Initiating STK Push...");
// 5. Initiate the STK Push
$response = $mpesa->ussdPush();
error_log("STK Push Response: " . json_encode($response));
// 6. Handle the response
if ($mpesa->isSuccessful()) {
echo "✅ Transaction initiated successfully!\n\n";
echo "Transaction Details:\n";
echo "-------------------\n";
echo "🔖 Merchant Request ID: " . $mpesa->getMerchantRequestID() . "\n";
echo "🔖 Checkout Request ID: " . $mpesa->getCheckoutRequestID() . "\n\n";
// Store these IDs for later use in callback handling
// 7. Check for callback data (if synchronous)
$callbackData = $mpesa->getCallbackData();
if (!empty($callbackData)) {
echo "Callback Response:\n";
echo "----------------\n";
print_r($callbackData);
if ($mpesa->isCanceledByUser()) {
echo "❌ Transaction was canceled by the user\n";
}
echo "Result Code: " . $mpesa->getResultCode() . "\n";
echo "Result Description: " . $mpesa->getResultDesc() . "\n";
} else {
echo "ℹ️ Waiting for customer to complete the payment...\n";
echo "Check STKPushCallbackExample.php for callback handling\n";
}
} else {
echo "❌ Transaction initiation failed!\n";
echo "Error: " . $mpesa->getResultDesc() . "\n";
}
} catch (MpesaException $e) {
echo "❌ M-Pesa API Error: " . $e->getMessage() . "\n";
// Log the error for debugging
error_log("M-Pesa Error: " . $e->getMessage());
} catch (RuntimeException $e) {
echo "❌ Runtime Error: " . $e->getMessage() . "\n";
error_log("Runtime Error: " . $e->getMessage());
} catch (Exception $e) {
echo "❌ Unexpected Error: " . $e->getMessage() . "\n";
error_log("Unexpected Error: " . $e->getMessage());
}
} catch (MpesaException $e) {
echo "❌ M-Pesa API Error: " . $e->getMessage() . "\n";
// Log the error for debugging
error_log("M-Pesa Error: " . $e->getMessage());
} catch (RuntimeException $e) {
echo "❌ Runtime Error: " . $e->getMessage() . "\n";
error_log("Runtime Error: " . $e->getMessage());
} catch (Exception $e) {
echo "❌ Unexpected Error: " . $e->getMessage() . "\n";
error_log("Unexpected Error: " . $e->getMessage());
}
Callback Processing
Create an endpoint to handle STK Push callbacks:
// callback.php
$callbackData = json_decode(file_get_contents('php://input'), true);
// Process the callback
processStkPushCallback($callbackData);
// Send response
header('Content-Type: application/json');
echo json_encode([
'ResultCode' => 0,
'ResultDesc' => 'Success'
]);