abutimartin / mpesa-sdk
Comprehensive PHP SDK for Safaricom M-Pesa API integration
Requires
- php: >=8.0
- ext-curl: *
- ext-json: *
- ext-openssl: *
Requires (Dev)
- mockery/mockery: ^1.4
- phpunit/phpunit: ^9.0
Suggests
- illuminate/support: Required for Laravel integration (^8.0|^9.0|^10.0)
- monolog/monolog: For advanced logging capabilities
This package is auto-updated.
Last update: 2025-08-16 18:44:43 UTC
README
A comprehensive PHP SDK for integrating with Safaricom's M-Pesa API. This SDK provides a clean, modern interface for all M-Pesa services including STK Push, B2C, B2B, C2B, Account Balance, Transaction Status, and Reversal operations.
Works with both standalone PHP projects and Laravel applications.
Features
- ✅ STK Push (Lipa Na M-Pesa Online) - Customer payments
- ✅ B2C (Business to Customer) - Send money to customers
- ✅ B2B (Business to Business) - Business payments
- ✅ C2B (Customer to Business) - Register and simulate C2B
- ✅ Account Balance - Check account balance
- ✅ Transaction Status - Query transaction status
- ✅ Reversal - Reverse transactions
- ✅ Callback Handling - Built-in callback processors
- ✅ Environment Management - Sandbox and Production support
- ✅ Comprehensive Logging - Built-in logging system
- ✅ Input Validation - Automatic parameter validation
- ✅ Error Handling - Detailed error reporting
- ✅ Framework Integration - Laravel, Symfony, CodeIgniter support
Requirements
- PHP 8.0 or higher
- cURL extension
- JSON extension
- OpenSSL extension
Installation
For All PHP Projects
composer require abutimartin/mpesa-sdk
Laravel Setup (Additional Steps)
The service provider auto-registers in Laravel 5.5+. Publish the config:
php artisan vendor:publish --tag=mpesa-config
Add to your .env
:
MPESA_ENVIRONMENT=sandbox MPESA_CONSUMER_KEY=your_consumer_key MPESA_CONSUMER_SECRET=your_consumer_secret MPESA_BUSINESS_SHORT_CODE=174379 MPESA_PASSKEY=your_passkey MPESA_INITIATOR_NAME=your_initiator MPESA_SECURITY_CREDENTIAL=your_security_credential
Quick Start
🔥 Standalone PHP Usage
<?php require_once 'vendor/autoload.php'; use MpesaSDK\MpesaSDK; // Initialize SDK $mpesa = MpesaSDK::sandbox([ 'consumer_key' => 'your_consumer_key', 'consumer_secret' => 'your_consumer_secret', 'business_short_code' => '174379', 'passkey' => 'your_passkey' ]); // STK Push $response = $mpesa->stkPush()->push( phoneNumber: '254712345678', amount: 100.00, accountReference: 'ORDER123', transactionDescription: 'Payment', callbackUrl: 'https://yourapp.com/callback' ); if ($response->isSuccessful()) { echo "Payment initiated! Checkout ID: " . $response->get('CheckoutRequestID'); }
🚀 Laravel Usage
<?php // Using Dependency Injection class PaymentController extends Controller { public function pay(Request $request, MpesaSDK $mpesa) { $response = $mpesa->stkPush()->push( phoneNumber: $request->phone, amount: $request->amount, accountReference: 'ORDER123', transactionDescription: 'Payment', callbackUrl: route('mpesa.callback') ); return response()->json($response->getData()); } } // Using Facade use MpesaSDK\Laravel\MpesaFacade as Mpesa; $response = Mpesa::requestPayment( phoneNumber: '254712345678', amount: 100.00, callbackUrl: route('mpesa.callback') );
Configuration
🔧 Standalone PHP Configuration
Option 1: Direct Configuration
// Sandbox $mpesa = MpesaSDK::sandbox([ 'consumer_key' => 'your_consumer_key', 'consumer_secret' => 'your_consumer_secret', 'business_short_code' => '174379', 'passkey' => 'your_passkey' ]); // Production $mpesa = MpesaSDK::production([ 'consumer_key' => 'your_consumer_key', 'consumer_secret' => 'your_consumer_secret', 'business_short_code' => 'your_shortcode', 'passkey' => 'your_passkey', 'initiator_name' => 'your_initiator', 'security_credential' => 'your_security_credential' ]);
Option 2: Environment Variables
Create .env
file:
MPESA_ENVIRONMENT=sandbox MPESA_CONSUMER_KEY=your_consumer_key MPESA_CONSUMER_SECRET=your_consumer_secret MPESA_BUSINESS_SHORT_CODE=174379 MPESA_PASSKEY=your_passkey
Then:
$mpesa = MpesaSDK::fromEnv();
⚙️ Laravel Configuration
Laravel automatically loads from config/mpesa.php
and .env
:
// Automatic via Service Container public function __construct(MpesaSDK $mpesa) { $this->mpesa = $mpesa; } // Or via Facade Mpesa::stkPush()->push(...);
🔍 Query Transaction Status
Standalone PHP
// STK Push Status $statusResponse = $mpesa->stkPush()->queryStatus($checkoutRequestId); // General Transaction Status $response = $mpesa->transactionStatus()->query( transactionId: 'LHG31AA5TX', queueTimeoutUrl: 'https://yourapp.com/timeout', resultUrl: 'https://yourapp.com/result' );
Laravel
// In Controller $status = $mpesa->stkPush()->queryStatus($request->checkout_id); return response()->json($status->getData());
📊 Account Balance
$response = $mpesa->accountBalance()->query( queueTimeoutUrl: 'https://yourapp.com/timeout', resultUrl: 'https://yourapp.com/result' );
🔄 Callback Handling
Standalone PHP Callback
<?php require_once 'vendor/autoload.php'; use MpesaSDK\Callbacks\STKPushCallback; use MpesaSDK\Utils\Logger; $logger = new Logger(Logger::LEVEL_INFO, 'mpesa.log'); $callback = new STKPushCallback($logger); $callback->onSuccess(function($data) { // Store successful transaction file_put_contents('transactions.log', "SUCCESS: {$data['mpesa_receipt_number']} - {$data['amount']}\n", FILE_APPEND ); }); $callback->onFailure(function($data) { // Handle failed transaction file_put_contents('transactions.log', "FAILED: {$data['result_desc']}\n", FILE_APPEND ); }); $response = $callback->process(); header('Content-Type: application/json'); echo json_encode($response);
Laravel Callback
<?php use MpesaSDK\Callbacks\STKPushCallback; use App\Models\Transaction; class MpesaController extends Controller { public function handleCallback(Request $request) { $callback = new STKPushCallback(); $callback->onSuccess(function($data) { Transaction::create([ 'checkout_request_id' => $data['checkout_request_id'], 'mpesa_receipt_number' => $data['mpesa_receipt_number'], 'amount' => $data['amount'], 'phone_number' => $data['phone_number'], 'status' => 'completed' ]); \Log::info('Payment successful', $data); }); $callback->onFailure(function($data) { Transaction::create([ 'checkout_request_id' => $data['checkout_request_id'], 'status' => 'failed', 'failure_reason' => $data['result_desc'] ]); \Log::warning('Payment failed', $data); }); $response = $callback->process(); return response()->json($response); } }
Usage Examples
📱 STK Push (Lipa Na M-Pesa)
Standalone PHP
$response = $mpesa->stkPush()->push( phoneNumber: '254712345678', amount: 100.00, accountReference: 'ORDER123', transactionDescription: 'Payment', callbackUrl: 'https://yourapp.com/callback' ); // Quick method $response = $mpesa->requestPayment( phoneNumber: '254712345678', amount: 100.00, callbackUrl: 'https://yourapp.com/callback' );
Laravel
// Controller method public function initiatePayment(Request $request, MpesaSDK $mpesa) { $response = $mpesa->stkPush()->push( phoneNumber: $request->phone, amount: $request->amount, accountReference: $request->reference, transactionDescription: 'Payment', callbackUrl: route('mpesa.callback') ); return response()->json($response->getData()); } // Using Facade $response = Mpesa::requestPayment( phoneNumber: $request->phone, amount: $request->amount, callbackUrl: route('mpesa.callback') );
💰 B2C (Send Money)
Standalone PHP
$response = $mpesa->b2c()->send( phoneNumber: '254712345678', amount: 100.00, commandId: 'BusinessPayment', remarks: 'Salary payment', queueTimeoutUrl: 'https://yourapp.com/timeout', resultUrl: 'https://yourapp.com/result' );
Laravel
$response = $mpesa->sendMoney( phoneNumber: $request->phone, amount: $request->amount, remarks: 'Payment', queueTimeoutUrl: route('mpesa.timeout'), resultUrl: route('mpesa.result') );
Logging
The SDK includes comprehensive logging:
use MpesaSDK\Utils\Logger; // Create logger $logger = new Logger( logLevel: Logger::LEVEL_INFO, logFile: 'mpesa.log', enableConsole: true ); // Use with SDK $mpesa = MpesaSDK::sandbox($config, null, $logger); // Manual logging $logger->info('Payment initiated', ['amount' => 100]); $logger->error('Payment failed', ['error' => 'Invalid phone number']);
Error Handling
use MpesaSDK\Exceptions\MpesaException; use MpesaSDK\Exceptions\ValidationException; try { $response = $mpesa->stkPush()->push( phoneNumber: '254712345678', amount: 100.00, accountReference: 'ORDER123', transactionDescription: 'Payment', callbackUrl: 'https://yourapp.com/callback' ); if (!$response->isSuccessful()) { throw new MpesaException($response->getErrorMessage()); } } catch (ValidationException $e) { echo "Validation error: " . $e->getMessage(); } catch (MpesaException $e) { echo "M-Pesa error: " . $e->getFullErrorMessage(); } catch (Exception $e) { echo "General error: " . $e->getMessage(); }
Testing
Run the test suite:
composer test
Run with coverage:
composer test-coverage
Examples
The examples/
directory contains comprehensive examples:
stkpush.php
- STK Push examplescallbacks/
- Callback handling examplessdk_integration.php
- Full integration exampleframework_callback.php
- Framework integration examples
Contributing
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature
) - Commit your changes (
git commit -m 'Add some amazing feature'
) - Push to the branch (
git push origin feature/amazing-feature
) - Open a Pull Request
License
This project is licensed under the MIT License - see the LICENSE file for details.
Support
For support, email abutimartin778@gmail.com or create an issue on GitHub.
Changelog
v1.0.0
- Initial release
- STK Push support
- B2C support
- Callback handling
- Comprehensive logging
- Framework integration examples
Author
Abuti Martin
- Email: abutimartin778@gmail.com
- GitHub: @abutimartin
Made with ❤️ for the PHP community