PHP code example of kemboielvis / mpesa-sdk-php

1. Go to this page and download the library: Download kemboielvis/mpesa-sdk-php library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

kemboielvis / mpesa-sdk-php example snippets



Kemboielvis\MpesaSdkPhp\Mpesa;

// Option A: via constructor
$mpesa = new Mpesa('YOUR_CONSUMER_KEY', 'YOUR_CONSUMER_SECRET', 'sandbox'); // or 'live'

// Option B: via setCredentials (also allows specifying a custom token store file)
$mpesa = (new Mpesa())
    ->setCredentials('YOUR_CONSUMER_KEY', 'YOUR_CONSUMER_SECRET', 'sandbox', /* optional */ 'mpesa_api_cache.json');

// Optional: choose where to store the token cache file
// If only a filename is provided, it's stored under the system temp directory.
$mpesa->setStoreFile('mpesa_api_cache.json');

// Optional: enable debug logging (prints to PHP error_log)
$mpesa->setDebug(true);

// Example: STK Push
$response = $mpesa->setBusinessCode('YOUR_TILL_OR_SHORTCODE')
    ->setPassKey('YOUR_LNM_PASSKEY')
    ->stk()
    ->setTransactionType('CustomerPayBillOnline') // or 'CustomerBuyGoodsOnline'
    ->setAmount(100)
    ->setPhoneNumber('0712345678')                // 07..., 7..., +254... and 254... are all accepted
    ->setCallbackUrl('https://yourdomain.com/callback')
    ->setAccountReference('INV-12345')
    ->setTransactionDesc('Payment for invoice INV-12345')
    ->push()
    ->getResponse();

print_r($response);

$mpesa->setBusinessCode('STORE_NUMBER')->stk()
    ->setTransactionType('CustomerBuyGoodsOnline')
    ->setPartyB('TILL_NUMBER')
    // ...
    ->push();

$resp = $mpesa->stk()
    ->setTransactionType('CustomerPayBillOnline')
    ->setAmount(100)
    ->setPhoneNumber('254712345678')
    ->setCallbackUrl('https://yourdomain.com/callback')
    ->setAccountReference('INV-12345')
    ->setTransactionDesc('Payment for invoice')
    ->push()
    ->getResponse();

$status = $mpesa->stk()
    ->query('CHECKOUT_REQUEST_ID')
    ->getResponse();

$resp = $mpesa->customerToBusiness()
    ->setResponseType('Completed')
    ->setConfirmationUrl('https://yourdomain.com/confirmation')
    ->setValidationUrl('https://yourdomain.com/validation')
    ->registerUrl()
    ->getResponse();

$resp = $mpesa->customerToBusiness()
    ->setCommandId('CustomerPayBillOnline')
    ->setAmount(100)
    ->setPhoneNumber('254712345678')
    ->setBillRefNumber('INV-123') // for PayBill only
    ->simulate()
    ->getResponse();

$mpesa->setCertificate('/path/to/SandboxCertificate.cer'); // encrypts the password below
// or: $mpesa->setSecurityCredential('ENCRYPTED_CREDENTIAL'); // no certificate needed

$resp = $mpesa->businessToCustomer()
    ->setInitiatorName('YOUR_INITIATOR_NAME')
    ->setCommandId('SalaryPayment') // or BusinessPayment, PromotionPayment
    ->setAmount(1000)
    ->setPhoneNumber('254712345678')
    ->setRemarks('Salary payment')
    ->setOccasion('May 2023 salary')
    ->paymentRequest(
        'YOUR_INITIATOR_NAME',
        'YOUR_INITIATOR_PASSWORD',
        'SalaryPayment',
        1000,
        'YOUR_SHORTCODE',
        '254712345678',
        'Salary payment',
        'https://yourdomain.com/timeout',
        'https://yourdomain.com/result',
        'May 2023 salary'
    )
    ->getResponse();

try {
    $check = $mpesa->b2cHakikisha()
        ->setPhoneNumber('0722000000')
        ->setShortCode('123456') // defaults to setBusinessCode()
        ->lookup();

    if ($check->isFound()) {
        echo $check->getCustomerName(); // "john M****** M******"
        $check->getCustomer();          // ['firstName' => 'john', 'middleName' => 'M******', 'lastName' => 'M******']
    } else {
        echo $check->getErrorMessage();
    }
} catch (RuntimeException $e) {
    // HTTP errors, e.g. "API error (400): The customer does not exist."
}

use Kemboielvis\MpesaSdkPhp\Services\MobileNumberValidationService as Kyc;

$kyc = $mpesa->mobileNumberValidation()
    ->setShortCode('776700')        // defaults to setBusinessCode()
    ->setPhoneNumber('0710860780')
    ->setIdType(Kyc::ID_NATIONAL)   // ID_NATIONAL (01), ID_MILITARY (02), ID_PASSPORT (05)
    ->setIdNumber('45435345')
    ->validate();

$kyc->isMatch();     // true when responseCode is 4000 ("Details match successfully")
$kyc->getResponse(); // responseRefID, responseCode, responseMessage, status

use Kemboielvis\MpesaSdkPhp\Services\MobileDataBundlesService as Bundles;

// 1. Fetch the offers for a customer
$bundles = $mpesa->mobileDataBundles()->fetchOffers('0708374149');
foreach ($bundles->getOffers() as $offer) {
    echo $offer->offerName, ' - Ksh ', $offer->offerPrice, PHP_EOL; // "Weekly 2GB - Ksh 99"
}

// 2. Buy one (setOffer() copies offeringId, account, price, data amount and validity)
$purchase = $mpesa->mobileDataBundles()
    ->setPhoneNumber('0708374149')
    ->setOffer($bundles->getOffers()[0])
    ->setPaymentMode(Bundles::PAYMENT_MODE_AIRTIME) // or PAYMENT_MODE_MPESA
    // ->setTransactionId('...')                    // optional; generated if omitted
    ->purchase();

$purchase->isPurchaseSuccessful();
$transactionId = $purchase->getTransactionId();

// 3. Check the status later (M-Pesa purchases complete asynchronously)
$status = $mpesa->mobileDataBundles()->checkStatus($transactionId)->getResponse();
// responseId, responseDesc, responseStatus ("1000" = success), responseCreated

$pochi = $mpesa->businessToPochi()
    ->setInitiatorName('testapi')                   // needs "ORG B2C API initiator" role
    ->setSecurityCredential('ENCRYPTED_CREDENTIAL') // from the Daraja portal
    ->setPartyA('600992')                           // B2C shortcode; defaults to setBusinessCode()
    ->setPhoneNumber('0705912645')                  // Pochi wallet number
    ->setAmount(10)                                 // Ksh 10 - 250,000
    ->setRemarks('Supplier payment')                // 2 - 100 characters
    ->setOccasion('ChristmasPay')                   // optional
    ->setQueueTimeoutUrl('https://yourdomain.com/pochi/timeout')
    ->setResultUrl('https://yourdomain.com/pochi/result')
    // ->setOriginatorConversationId('...')         // optional; a UUID is generated if omitted
    ->pay();

$pochi->getResponse();
$id = $pochi->getOriginatorConversationId(); // store it to match the callback and avoid double payment

$resp = $mpesa->reversal()
    ->setInitiator('YOUR_INITIATOR_NAME')
    ->setTransactionId('YOUR_TRANSACTION_ID')
    ->setReceiverIdentifierType('11') // 1=MSISDN, 2=Till, 4=Shortcode
    ->setRemarks('Refund')
    ->setOccasion('Customer refund')
    ->reverse(
        'YOUR_INITIATOR_NAME',
        'YOUR_INITIATOR_PASSWORD',
        'Refund',
        'YOUR_SHORTCODE',
        'YOUR_TRANSACTION_ID',
        '11',
        'https://yourdomain.com/timeout',
        'https://yourdomain.com/result',
        'Customer refund'
    )
    ->getResponse();

$bill = $mpesa->businessPayBill()
    ->setInitiator('API_Username')                  // needs "Org Business Pay Bill API initiator" role
    ->setSecurityCredential('ENCRYPTED_CREDENTIAL') // from the Daraja portal
    ->setPartyA('123456')                           // your shortcode; defaults to setBusinessCode()
    ->setPartyB('000000')                           // paybill to pay
    ->setAmount(239)
    ->setAccountReference('353353')                 // account number at the paybill, max 13 chars
    ->setRequester('254700000000')                  // optional: customer you are paying for
    ->setRemarks('OK')
    ->setOccasion('Rent')                           // optional
    ->setQueueTimeoutUrl('https://yourdomain.com/b2b/timeout')
    ->setResultUrl('https://yourdomain.com/b2b/result')
    ->pay();

$bill->getResponse(); // OriginatorConversationID, ConversationID, ResponseCode, ResponseDescription

$goods = $mpesa->businessBuyGoods()
    ->setInitiator('API_Username')
    ->setSecurityCredential('ENCRYPTED_CREDENTIAL')
    ->setPartyB('000000')           // till, store number or merchant HO
    ->setAmount(239)
    ->setAccountReference('353353') // max 13 chars
    ->setRequester('254700000000')  // optional
    ->setQueueTimeoutUrl('https://yourdomain.com/b2b/businessbuygoods/queue')
    ->setResultUrl('https://yourdomain.com/b2b/businessbuygoods/result')
    ->pay();

$topUp = $mpesa->b2cAccountTopUp()
    ->setInitiator('testapi')                       // needs "Org Business Pay to Bulk API initiator" role
    ->setSecurityCredential('ENCRYPTED_CREDENTIAL') // from the Daraja portal
    ->setPartyA('600979')                           // your shortcode; defaults to setBusinessCode()
    ->setPartyB('600000')                           // B2C shortcode to load
    ->setAmount(239)
    ->setAccountReference('353353')
    ->setRequester('254708374149')                  // optional
    ->setRemarks('Top up')
    ->setQueueTimeoutUrl('https://yourdomain.com/topup/timeout')
    ->setResultUrl('https://yourdomain.com/topup/result')
    ->topUp();

$topUp->getResponse(); // OriginatorConversationID, ConversationID, ResponseCode, ResponseDescription

$balance = $mpesa->setBusinessCode('600000')   // PartyA: your shortcode
    ->accountBalance()
    ->setInitiator('testapiuser')
    ->setSecurityCredential('ENCRYPTED_CREDENTIAL') // from the Daraja portal
    ->setIdentifierType('4')                        // optional, 4 = shortcode (default)
    ->setRemarks('Balance check')
    ->setQueueTimeoutUrl('https://yourdomain.com/timeout')
    ->setResultUrl('https://yourdomain.com/balance/result')
    ->accountBalance();

$balance->getResponse(); // OriginatorConversationID, ConversationID, ResponseCode, ResponseDescription

use Kemboielvis\MpesaSdkPhp\Services\AccountBalanceService;

$balances = AccountBalanceService::parseBalances(file_get_contents('php://input'));
// ['Working Account' => ['currency' => 'KES', 'current' => 700000.0, 'available' => 700000.0,
//                        'reserved' => 0.0, 'uncleared' => 0.0], 'Utility Account' => [...], ...]

// One-time registration (1000 = registered, 1001 = already registered)
$mpesa->pullTransactions()
    ->setShortCode('600000')          // defaults to setBusinessCode()
    ->setNominatedNumber('0722000000') // number in the shortcode KYC details
    ->setCallbackUrl('https://yourdomain.com/pull/callback')
    ->register()
    ->getResponse();

// Query (1000 = transactions found, 1001 = none in the period)
$pull = $mpesa->pullTransactions()
    ->setShortCode('600000')
    ->setStartDate(new DateTime('-2 hours'))  // or '2020-08-04 08:36:00'
    ->setEndDate(new DateTime())
    ->setOffset(0)                             // row to start from, for paging
    ->query();

foreach ($pull->getTransactions() as $trx) {
    echo $trx->transactionId, ' ', $trx->amount, ' ', $trx->billreference, PHP_EOL;
}

$tax = $mpesa->taxRemittance()
    ->setInitiator('TaxPayer')
    ->setSecurityCredential('ENCRYPTED_CREDENTIAL') // from the Daraja portal
    ->setPartyA('888880')                           // your shortcode; defaults to setBusinessCode()
    ->setAmount(239)
    ->setAccountReference('PRN1234XN')              // payment registration number from KRA
    ->setRemarks('VAT for March')
    ->setQueueTimeoutUrl('https://yourdomain.com/b2b/remittax/queue')
    ->setResultUrl('https://yourdomain.com/b2b/remittax/result')
    ->remit();

$tax->getResponse(); // OriginatorConversationID, ConversationID, ResponseCode, ResponseDescription

$b2b = $mpesa->b2bExpressCheckout()
    ->setPrimaryShortCode('000001')   // merchant till paying (debit party)
    ->setReceiverShortCode('000002')  // your paybill (defaults to setBusinessCode())
    ->setAmount(100)
    ->setPaymentRef('INV-123')        // shown to the merchant in the prompt
    ->setCallbackUrl('https://yourdomain.com/b2b/result')
    ->setPartnerName('Your Business') // your name as the merchant knows it
    // ->setRequestRefId('...')       // optional; a UUID is generated if omitted
    ->push();

$ack = $b2b->getResponse();          // e.g. { "code": "0", "status": "USSD Initiated Successfully" }
$requestId = $b2b->getRequestRefId(); // matches `requestId` in the callback

$qr = $mpesa->dynamicQr()
    ->setMerchantName('TEST SUPERMARKET')
    ->setRefNo('INV-123')
    ->setAmount(100)
    ->setTrxCode('BG')   // BG=Buy Goods, WA=Agent withdraw, PB=Paybill, SM=Send Money, SB=Send to Business
    ->setCpi('373132')   // till/paybill/phone; defaults to setBusinessCode()
    ->setSize(300)       // optional, pixels (default 300)
    ->generate();

$qr->getResponse();           // ResponseCode, RequestID, ResponseDescription, QRCode
$qr->getQrCode();             // base64 PNG
echo '<img src="' . $qr->getQrCodeDataUri() . '">';
$qr->saveQrCode('/path/to/qr.png');

// Optional: see what points are worth (1 point = Ksh 0.2)
$ksh = $mpesa->lipaNaBonga()->calculatePoints(40)->getCalculatedAmount(); // 8.0

$bonga = $mpesa->lipaNaBonga()
    ->setPhoneNumber('0720776155')
    ->setAmount(50)                 // points are worked out (250) unless you call setPoints()
    ->setShortCode('888880')        // defaults to setBusinessCode()
    ->setAccountNumber('INV-123')
    // ->setConversionRate(0.2)     // default
    ->redeem();                     // customer confirms with their M-Pesa PIN

$bonga->isSuccessful();
$bonga->getCustomerMessage();

$age = $mpesa->ageOnNetwork()->check('0722000000');

if ($age->isSuccessful()) {
    $age->getRegistrationDate();      // raw value, e.g. "2019-01-12" or a message
    $age->getRegistrationDateTime();  // DateTimeImmutable, or null if it is not a date
}

$iot = $mpesa->iotSim()
    ->setVpnGroup('1-555162310488_VPN')           // your IoT account number
    ->setUsername('[email protected]'); // user registered on the account

// SIM operations
$sims = $iot->getAllSims(0, 20)->getSims();        // start index, page size
$iot->queryLifeCycleStatus('0110100606')->getBody(); // desc, status, statusCode
$info = $iot->queryCustomerInfo('0110100606')->getBody(); // offeringName, offeringId, ...
$iot->activateSim('0110100606');
$iot->renameAsset('0110100606', 'Tracker001');
$iot->suspendSim('0110100606', $info->offeringId);
$iot->resumeSim('0110100606', $info->offeringId);
$iot->getActivationTrends(new DateTime('-30 days'), new DateTime()); // or '20240221', '20240421'

// Messaging
$iot->sendMessage('0110100606', 'Test');
$messages = $iot->searchMessages('0110100606')->getMessages(); // "254" is added for you
$iot->filterMessages('02-05-2024 08:39:11', new DateTime(), '1', 1, 10)->getMessages();
$iot->getAllMessages(1, 10)->getMessages();
$iot->deleteMessage($messages[0]->id);
$iot->deleteMessageThread('0110100606');

$iot->isSuccessful(); // header.responseCode === 200 for the last call

try {
    $resp = $mpesa->stk()->push()->getResponse();
} catch (\Throwable $e) {
    error_log('M-Pesa error: ' . $e->getMessage());
}

// Option A: an encrypted credential you already have (e.g. from the Daraja portal).
// No certificate needed.
$mpesa->setSecurityCredential('ENCRYPTED_CREDENTIAL');   // for every service
$mpesa->accountBalance()->setSecurityCredential('...');  // or for one service only

// Option B: let the SDK encrypt the plain password with the certificate
// (download the sandbox or production certificate from the Daraja portal).
$mpesa->setCertificate('/path/to/ProductionCertificate.cer'); // path or PEM contents
$mpesa->accountBalance()->accountBalance('initiator', 'INITIATOR_PASSWORD', /* ... */);

$mpesa->setVerifySsl(true);  // verify certificates on token and API requests
$mpesa->setVerifySsl(false); // default

$mpesa->setTimeouts(30, 5); // request timeout, connect timeout (seconds); defaults 60 and 10

$mpesa->setStoreFile('/var/run/mpesa/token.json');
$path = $mpesa->getResolvedStoreFilePath(); // inspect where it ends up

$mpesa->setDebug(true); // lock events, cache hits/misses, and token response metadata go to error_log

$mpesa->getConfig()->setBaseUrl('http://127.0.0.1:8091');
bash
composer 
bash
cd src/Tests
MPESA_CONSUMER_KEY=... MPESA_CONSUMER_SECRET=... MPESA_PHONE=2547XXXXXXXX \
MPESA_CALLBACK_URL=https://yourdomain.com/callback php tests.php
bash
php src/Tests/token_cache_smoke.php
bash
# Start fake token server in a background shell
php -S 127.0.0.1:8091 src/Tests/fake_mpesa_server.php

# In another shell
php src/Tests/concurrency_test.php
bash
# Start fake token server on a different port
php -S 127.0.0.1:8092 src/Tests/fake_mpesa_server.php

# In another shell
php src/Tests/tamper_concurrency_test.php