Basic Usage

Basic Usage

Overview

This guide demonstrates the basic usage of the M-Pesa SDK for common operations like STK Push, B2C, and C2B transactions.

STK Push (Customer to Business)

Initiating an STK Push

use MesaSDK\PhpMpesa\Config;
use MesaSDK\PhpMpesa\Mpesa;
 
$config = new Config();
$config->setBaseUrl("https://apisandbox.safaricom.et")
    ->setConsumerKey("your_consumer_key")
    ->setConsumerSecret("your_consumer_secret")
    ->setEnvironment('sandbox');
 
$mpesa = new Mpesa($config);
 
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());
}

Handling STK Push Callback

// In your callback endpoint
$callbackData = json_decode(file_get_contents('php://input'), true);
$mpesa->processCallback($callbackData);

B2C (Business to Customer)

Initiating a B2C Payment

try {
    // 1. Authenticate with M-Pesa API
    $mpesa->authenticate();
 
    // 2. Set up B2C payment parameters
    $params = [
        'initiatorName' => 'apitest',  // Your registered initiator name
        'securityCredential' => 'lMhf0UqE4ydeEDwpUskmPgkNDZnA6NLi7z3T1TQuWCkH3/ScW8pRRnobq/AcwFvbC961+zDMgOEYGm8Oivb7L/7Y9ED3lhR7pJvnH8B1wYis5ifdeeWI6XE2NSq8X1Tc7QB9Dg8SlPEud3tgloB2DlT+JIv3ebIl/J/8ihGVrq499bt1pz/EA2nzkCtGeHRNbEDxkqkEnbioV0OM//0bv4K++XyV6jUFlIIgkDkmcK6aOU8mPBHs2um9aP+Y+nTJaa6uHDudRFg0+3G6gt1zRCPs8AYbts2IebseBGfZKv5K6Lqk9/W8657gEkrDZE8Mi78MVianqHdY/8d6D9KKhw==',
        'commandId' => 'BusinessPayment',  // Options: SalaryPayment, BusinessPayment, PromotionPayment
        'amount' => 10,
        'partyA' => '1020',  // Your shortcode
        'partyB' => '251700404709',  // Recipient phone number
        'remarks' => 'Bonus payment',  // Payment description
        'occasion' => 'StallOwner',  // Optional reference
        'queueTimeOutURL' => 'https://testt.tugza.tech/',  // Timeout callback URL
        'resultURL' => 'https://testt.tugza.tech/'  // Success callback URL
    ];
 
    // 3. Initiate the B2C payment using fluent interface
    $result = $mpesa
        ->setInitiatorName($params['initiatorName'])
        ->setSecurityCredential($params['securityCredential'])
        ->setCommandId($params['commandId'])
        ->setAmount($params['amount'])
        ->setPartyA($params['partyA'])
        ->setPartyB($params['partyB'])
        ->setRemarks($params['remarks'])
        ->setOccasion($params['occasion'])
        ->setQueueTimeOutUrl($params['queueTimeOutURL'])
        ->setResultUrl($params['resultURL'])
        ->b2c();
 
    // 4. Handle the response
    if ($result && $result->getResponseMessage()) {
        echo "✅ B2C payment initiated successfully!\n";
        echo "Response: \n";
        print_r($result->getResponseMessage());
 
        // Store these details in your database for reconciliation
        // $conversationId = $result->getConversationId();
        // $originatorConversationId = $result->getOriginatorConversationId();
    }
 
} catch (MpesaException $e) {
    // Handle M-Pesa specific errors
    echo "❌ M-Pesa Error:\n";
    echo "Message: " . $e->getMessage() . "\n";
    echo "Code: " . $e->getCode() . "\n";
 
    // Log the error for debugging
    error_log("M-Pesa B2C Error: " . $e->getMessage());
} catch (Exception $e) {
    // Handle unexpected errors
    echo "❌ Unexpected Error: " . $e->getMessage() . "\n";
    error_log("Unexpected B2C Error: " . $e->getMessage());
}

C2B (Customer to Business)

Registering URLs

try {
    // Initialize the class with your API credentials
    $mpesa = new Mpesa($config);
    $mpesa->setApiKey('7oJ7uWPDp3jwqBzGvxQOn5g8s5rPwJ3qfXvsxwHyAknxAAxi');
 
    // Manually authenticate before making any requests
 
    // Register URLs using the public method
    $response = $mpesa->setShortcode('6989')
        ->registerUrls(
            'https://your-domain.com/confirmation',  // confirmationUrl
            'https://your-domain.com/validation'     // validationUrl
        );
    // Handle the response
    if (is_array($response)) {
        echo "Success: " . ($response['ResponseDescription'] ?? 'URL registration successful') . "\n";
    } else {
        echo "Response: " . $response . "\n";
    }
} catch (MpesaException $e) {
    echo "Error: " . $e->getMessage() . "\n";
}

Simulating C2B Transaction

 
try {
    // 1. First, authenticate with M-Pesa API
    $mpesa->authenticate();
 
    // 2. Simulate a C2B payment using the fluent interface (recommended)
    /** @var C2BSimulationResponse $response */
    $response = $mpesa
        ->setC2BAmount(110.00)                // Set amount
        ->setC2BMsisdn('251745628580')       // Set customer phone number
        ->setC2BBillRefNumber('091091')      // Set bill reference number
        ->executeC2BSimulation();            // Execute the simulation
 
    // 3. Handle the response using the model methods
    if ($response->isSuccessful()) {
        echo "✅ C2B payment simulation initiated successfully!\n";
        echo "Response Code: " . $response->getResponseCode() . "\n";
        echo "Description: " . $response->getResponseDescription() . "\n";
        echo "Conversation ID: " . $response->getConversationId() . "\n";
        echo "Originator Conversation ID: " . $response->getOriginatorConversationId() . "\n";
        echo "Customer Message: " . $response->getCustomerMessage() . "\n";
        echo "Merchant Request ID: " . $response->getMerchantRequestId() . "\n";
        echo "Checkout Request ID: " . $response->getCheckoutRequestId() . "\n";
 
        // You can also get the response as an array
        $responseArray = $response->toArray();
        echo "\nFull Response Array:\n";
        print_r($responseArray);
    } else {
        echo "❌ C2B payment simulation failed!\n";
        echo "Error Code: " . $response->getResponseCode() . "\n";
        echo "Error Description: " . $response->getResponseDescription() . "\n";
    }
 
} catch (MpesaException $e) {
    echo "❌ M-Pesa Error: " . $e->getMessage() . "\n";
} catch (\Exception $e) {
    echo "❌ Error: " . $e->getMessage() . "\n";
}

Transaction Status Query

Checking Transaction Status

try {
    // 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
 
    // Initialize M-Pesa client
    $mpesa = new Mpesa($config);
 
    // Authenticate
    $mpesa->authenticate();
 
    // Query transaction status using fluent API
    /** @var TransactionStatusResponse $response */
    $response = $mpesa
        ->setStatusInitiator('apitest')
        ->setStatusSecurityCredential('lMhf0UqE4ydeEDwpUskmPgkNDZnA6NLi7z3T1TQuWCkH3/ScW8pRRnobq/AcwFvbC961+zDMgOEYGm8Oivb7L/7Y9ED3lhR7pJvnH8B1wYis5ifdeeWI6XE2NSq8X1Tc7QB9Dg8SlPEud3tgloB2DlT+JIv3ebIl/J/8ihGVrq499bt1pz/EA2nzkCtGeHRNbEDxkqkEnbioV0OM//0bv4K++XyV6jUFlIIgkDkmcK6aOU8mPBHs2um9aP+Y+nTJaa6uHDudRFg0+3G6gt1zRCPs8AYbts2IebseBGfZKv5K6Lqk9/W8657gEkrDZE8Mi78MVianqHdY/8d6D9KKhw==')
        ->setTransactionId('0')
        ->setResultUrl($settings['result_url'])
        ->setQueueTimeOutUrl($settings['timeout_url'])
        ->setRemarks('Transaction Status Query')
        ->setStatusOccasion('Query trans status')
        ->queryTransactionStatus();
 
    // Check if request was successful
    if ($response->isSuccessful()) {
        echo "Transaction status query initiated successfully\n";
 
        // Access specific transaction details using the model methods
        echo "Transaction Status: " . $response->getTransactionStatus() . "\n";
        echo "Amount: " . $response->getAmount() . "\n";
        echo "Transaction Date: " . $response->getTransactionDate() . "\n";
        echo "Phone Number: " . $response->getPhoneNumber() . "\n";
        echo "Receipt Number: " . $response->getReceiptNumber() . "\n";
        echo "Debit Party: " . $response->getDebitPartyName() . "\n";
        echo "Credit Party: " . $response->getCreditPartyName() . "\n";
 
        // Check if transaction is completed
        if ($response->isCompleted()) {
            echo "Transaction is completed\n";
        } else {
            echo "Transaction is not completed\n";
        }
 
        // Get any additional parameter
        $customParam = $response->getResultParameter('CustomKey', 'default value');
        echo "Custom Parameter: " . $customParam . "\n";
 
        // Get all data as array or JSON
        echo "All Data (JSON): " . $response->toJson() . "\n";
    } else {
        echo "Transaction status query failed\n";
        echo "Error Code: " . $response->getResultCode() . "\n";
        echo "Error Description: " . $response->getResultDesc() . "\n";
    }
 
} catch (MpesaException $e) {
    echo "M-Pesa Error: " . $e->getMessage() . "\n";
} catch (\Exception $e) {
    echo "Error: " . $e->getMessage() . "\n";
}

Account Balance Query

Checking Account Balance

try {
    // Initialize the CustomMpesa class with configuration
    $mpesa = new Mpesa($config);
 
    // Configure account balance specific parameters using fluent interface
    $response = $mpesa
        ->authenticate()
        ->setSecurityCredential("lMhf0UqE4ydeEDwpUskmPgkNDZnA6NLi7z3T1TQuWCkH3/ScW8pRRnobq/AcwFvbC961+zDMgOEYGm8Oivb7L/7Y9ED3lhR7pJvnH8B1wYis5ifdeeWI6XE2NSq8X1Tc7QB9Dg8SlPEud3tgloB2DlT+JIv3ebIl/J/8ihGVrq499bt1pz/EA2nzkCtGeHRNbEDxkqkEnbioV0OM//0bv4K++XyV6jUFlIIgkDkmcK6aOU8mPBHs2um9aP+Y+nTJaa6uHDudRFg0+3G6gt1zRCPs8AYbts2IebseBGfZKv5K6Lqk9/W8657gEkrDZE8Mi78MVianqHdY/8d6D9KKhw==")
        ->setAccountBalanceInitiator('apitest')
        ->setAccountBalancePartyA('1020')
        ->setAccountBalanceRemarks('Monthly balance check')
        // Optional parameters
        ->setAccountBalanceIdentifierType('4')
        ->setAccountBalanceRemarks('Monthly balance check')
        ->setQueueTimeOutUrl('https://your-domain.com/timeout')
        ->setResultUrl('https://your-domain.com/result')
        // Execute the balance check
        ->checkAccountBalance();
 
    echo "API Response:\n";
    echo json_encode($response, JSON_PRETTY_PRINT) . "\n\n";
 
    // // If this is a result callback
    // if (isset($response['Result'])) {
    //     $balanceInfo = $mpesa->parseBalanceResult($response);
    //     echo "Parsed Balance Information:\n";
    //     foreach ($balanceInfo as $account) {
    //         echo sprintf(
    //             "Account: %s\nCurrency: %s\nAmount: %s\n\n",
    //             $account['account'],
    //             $account['currency'],
    //             $account['amount']
    //         );
    //     }
    // }
} catch (\Exception $e) {
    echo "Error: " . $e->getMessage() . "\n";
}

Best Practices

  1. Error Handling

    • Always use try-catch blocks
    • Handle both M-Pesa specific and general exceptions
    • Log errors appropriately
  2. Security

    • Use environment variables for sensitive data
    • Enable SSL verification in production
    • Validate all input data
  3. Logging

    • Configure logging for debugging
    • Log all API requests and responses
    • Monitor for errors and issues
  4. Callbacks

    • Implement proper callback handling
    • Validate callback data
    • Store transaction details securely

For more detailed information about specific features, check out the API Reference section.