Skip to content

M-Pesa Express (STK Push)

M-Pesa Express, commonly called STK Push, sends a payment prompt to a customer’s phone. They confirm with their M-Pesa PIN and the money lands in your short code.

POST mpesa/stkpush/v1/processrequest
use Starnerz\LaravelDaraja\Facades\Daraja;
$response = Daraja::stk()->push(
phone: '0712345678',
amount: 1500,
accountReference: 'INV-001',
description: 'Invoice 001',
);
$response->accepted(); // true — the prompt was sent
$response->checkoutRequestId; // keep this; it identifies the attempt
$response->merchantRequestId;
$response->customerMessage;

Any Kenyan format works for phone0712345678, +254712345678, 254712345678, even with spaces or dashes. Invalid numbers raise a DarajaException before a request is sent.

Field Limit
accountReference 12 characters — shown to the customer in the prompt
description 13 characters, defaults to the account reference
amount KES 1 to 250,000, rounded to whole shillings

For a till, the short code and the credit party are different numbers: BusinessShortCode is your HO or store number, PartyB is the till.

use Starnerz\LaravelDaraja\Enums\TransactionType;
Daraja::stk()->push(
phone: '0712345678',
amount: 500,
accountReference: 'ORDER-9',
type: TransactionType::BuyGoods,
partyB: '5678901', // the till number
);

Omit partyB and it defaults to the short code, which is correct for Pay Bill and wrong for Buy Goods.

POST mpesa/stkpushquery/v1/query
$status = Daraja::stk()->query($response->checkoutRequestId);
$status->paid(); // ResultCode 0
$status->cancelledByUser(); // ResultCode 1032
$status->resultDescription;

Use this as a fallback when a callback never arrives, not as a polling loop.

use Starnerz\LaravelDaraja\Events\StkCallbackReceived;
class RecordPayment
{
public function handle(StkCallbackReceived $event): void
{
$callback = $event->callback;
if (! $callback->successful()) {
return;
}
Order::where('checkout_request_id', $callback->checkoutRequestId)
->update([
'mpesa_receipt' => $callback->receipt(),
'amount_paid' => $callback->amount(),
'paid_at' => now(),
]);
}
}

CallbackMetadata is absent entirely when the payment fails, so amount() and receipt() return empty values rather than throwing.

On success it is a list of {Name, Value} items — but not every item has a Value. Sandbox returns {"Name": "Balance"} with the key omitted altogether, so code doing $item['Value'] on each entry raises an undefined-key error. The package reads them through ResultParameters, which returns null for a missing value and leaves the surrounding items intact.

Result code Meaning
0 Paid
1032 Cancelled by the user, or the prompt timed out
1037 Prompt never reached the handset
1031 User did not enter a PIN in time
2001 Wrong M-Pesa PIN
1001 Another session is already open for that number

Unlike B2C, M-Pesa Express transactions can be reversed through the Reversal API.