1. Go to this page and download the library: Download j-muiruri/daraja-php-sdk 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/ */
// Initiate an STK Push to a Paybill
$response = $mpesa->stk()->push(
phone: '0712345678', // or '+254712345678' or '254712345678'
amount: 1500, // KES, minimum 1
accountReference: 'INV-0042', // Max 12 chars — shown to customer
description: 'Order #42', // Max 13 chars
callbackUrl: 'https://yourapp.co.ke/mpesa/callback', // override per request
);
if ($response->isAccepted()) {
$checkoutId = $response->checkoutRequestId();
// Store $checkoutId to poll status or match against the callback
}
// Buy Goods (till number) variant
$response = $mpesa->stk()->pushBuyGoods(
phone: '0712345678',
amount: 250,
till: '123456',
callbackUrl: 'https://yourapp.co.ke/mpesa/callback',
);
// Query status (when callback isn't received)
$status = $mpesa->stk()->query($checkoutId);
echo $status->resultDescription(); // "The service request is processed successfully."
use Daraja\Enums\Frequency;
$response = $mpesa->mpesaRatiba()->createForPayBill(
standingOrderName: 'Monthly Rent - Unit 4B', // must be unique per customer
startDate: new DateTimeImmutable('2026-09-01'),
endDate: new DateTimeImmutable('2027-09-01'),
amount: 4500,
payerPhone: '0708374149',
accountReference: 'UNIT-4B',
frequency: Frequency::Monthly,
);
// Ratiba nests its status differently to every other Daraja response —
// use the service's own accessors rather than $response->isAccepted():
if ($mpesa->mpesaRatiba()->isAccepted($response)) {
// accepted for processing
}
// One-time setup
$mpesa->pullTransaction()->register(
shortCode: '600000',
nominatedNumber: '254722000000',
);
// Reconcile a window
$response = $mpesa->pullTransaction()->query(
startDate: new DateTimeImmutable('-2 days'),
endDate: new DateTimeImmutable('now'),
);
$transactions = $response->get('Transaction', []);
// Pay a supplier's paybill
$mpesa->b2b()->payBill(
receiverShortcode: '000001',
amount: 75000,
accountReference: 'SUPP-ACC-001',
remarks: 'Invoice #INV-2025-03',
);
// Pay a merchant till
$mpesa->b2b()->buyGoods('987654', 12000, 'Office supplies');
$response = $mpesa->b2bExpressCheckout()->push(
primaryShortCode: '000001', // Merchant's till (debit party)
receiverShortCode: '000002', // Your paybill (credit party)
amount: 100,
paymentRef: 'INV-0042', // Shown to the merchant in the USSD prompt
partnerName: 'Acme Traders', // Your org's friendly name, shown to the merchant
callbackUrl: 'https://yourapp.co.ke/mpesa/b2b-checkout/callback',
);
if ($response->isSuccessful()) {
// "USSD Initiated Successfully" — final result arrives at callbackUrl
}
use Daraja\Enums\IdentifierType;
$status = $mpesa->transactionStatus()->query(
transactionId: 'QHT3XXXXXXXXXXX', // M-Pesa receipt number
identifierType: IdentifierType::Shortcode,
remarks: 'Reconciliation check',
);
use Daraja\Enums\IdentifierType;
$mpesa->accountBalance()->query(
identifierType: IdentifierType::Shortcode,
remarks: 'EOD balance check',
);
// Result arrives asynchronously on your resultUrl
use Daraja\Enums\QRCodeType;
$response = $mpesa->qr()->generate(
merchantName: 'Asante Coffee',
refNo: 'INV-001',
amount: 350,
type: QRCodeType::DynamicMerchant,
size: 400,
);
// Get the Base64 PNG to embed in an <img> tag
$base64 = $mpesa->qr()->extractImage($response);
echo '<img src="data:image/png;base64,' . $base64 . '">';
// Or save to disk
$mpesa->qr()->saveImage($response, '/var/www/html/qr/payment.png');
$response = $mpesa->simSwap()->checkLastSwapDate('254722000000');
$lastSwap = $response->getString('lastSwapDate'); // e.g. "01-01-1900 00:00"
// Check a SIM's status
$response = $mpesa->iotSim()->queryLifeCycleStatus(
msisdn: '300000020000',
vpnGroup: '1-225560081663_VPN',
username: '[email protected]',
);
// Every response uses its own header/body envelope — use these helpers
// rather than Response::isAccepted():
if ($mpesa->iotSim()->isSuccessful($response)) {
$status = $response->data()['body']['status'] ?? null;
}
// Activate a SIM
$mpesa->iotSim()->activateSim('300000443539', '1-225560081663_VPN', '[email protected]');
// Suspend a subscriber
use Daraja\Enums\SimSubscriberOperation;
$mpesa->iotSim()->suspendOrResumeSubscriber(
msisdn: '300000100000',
username: '[email protected]',
vpnGroup: '1-225560081663_VPN',
product: '14205000',
operation: SimSubscriberOperation::Suspend,
);
// Send a message to a SIM
$mpesa->iotSim()->sendSingleMessage('300001172000', 'Hello device', '1-47820525000_VPN');
use Daraja\Concerns\HasSecurityCredential;
class CredentialGenerator
{
use HasSecurityCredential;
public function generate(string $password, string $certPath): string
{
return $this->generateSecurityCredential($password, $certPath);
}
}
$gen = new CredentialGenerator();
$credential = $gen->generate('MyInitiatorPassword', '/path/to/cert.cer');
// Store $credential in your .env as MPESA_SECURITY_CREDENTIAL
use Daraja\Exceptions\ApiException;
use Daraja\Exceptions\AuthenticationException;
use Daraja\Exceptions\ValidationException;
try {
$response = $mpesa->stk()->push(...);
} catch (ValidationException $e) {
// Bad parameters — check before hitting the API
foreach ($e->errors() as $field => $message) {
echo "{$field}: {$message}\n";
}
} catch (AuthenticationException $e) {
// OAuth token failure — check consumer key/secret
logger()->error('M-Pesa auth failed', ['error' => $e->getMessage()]);
} catch (ApiException $e) {
// API returned an error response
logger()->error('M-Pesa API error', [
'status' => $e->statusCode(),
'code' => $e->errorCode(),
'msg' => $e->getMessage(),
]);
}