STK Push

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:

  1. Shortcode: Your M-Pesa shortcode
  2. Passkey: Your M-Pesa passkey
  3. Callback URLs: URLs to receive transaction notifications
  4. 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 request
  • MerchantRequestID: Unique identifier for the merchant request
  • ResponseCode: Response code from M-Pesa
  • ResponseDescription: Description of the response
  • CustomerMessage: 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

  1. Error Handling

    • Always check for successful responses
    • Implement proper error handling
    • Log failed transactions
  2. Callback Processing

    • Process callbacks asynchronously
    • Implement retry mechanisms
    • Validate callback data
  3. Security

    • Use HTTPS for callback URLs
    • Validate phone numbers
    • Implement proper authentication
  4. 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'
]);

Related Topics