felixmuhoro / laravel-mpesa-livewire
Livewire M-Pesa payment components
Package info
github.com/felixmuhoro/laravel-mpesa-livewire
pkg:composer/felixmuhoro/laravel-mpesa-livewire
Requires
- php: ^8.1
- felixmuhoro/laravel-mpesa: ^1.2
- illuminate/support: ^10.0|^11.0|^12.0|^13.0
This package is auto-updated.
Last update: 2026-06-06 23:42:06 UTC
README
Livewire v3 components for M-Pesa payment flows. Drop-in checkout button, payment history, real-time status polling, and a compact wallet widget — all styled with Tailwind and wired with proper Livewire 3 patterns.
Requirements
- PHP 8.1+
- Laravel 10 / 11 / 12 / 13
- Livewire 3.x
- felixmuhoro/laravel-mpesa ^1.2
Installation
composer require felixmuhoro/laravel-mpesa-livewire
The service provider auto-registers via Laravel package discovery.
Publish the config:
php artisan vendor:publish --tag=mpesa-livewire-config
Optionally publish views to customise them:
php artisan vendor:publish --tag=mpesa-livewire-views
Components
<livewire:mpesa-stk-push-button />
Full STK push checkout flow with state machine: idle → initiating → awaiting_confirmation → success | failed. Polls the Daraja STK query endpoint every 3 seconds while waiting for the user's PIN.
{{-- Fixed amount --}} <livewire:mpesa-stk-push-button :amount="1500" reference="ORDER-{{ $order->id }}" description="Payment for order #{{ $order->id }}" /> {{-- User-entered amount --}} <livewire:mpesa-stk-push-button reference="TOP-UP" description="Wallet top-up" />
Props
| Prop | Type | Default | Description |
|---|---|---|---|
amount |
float | 0 |
Amount in KES. If 0, renders an amount input. |
phone |
string | '' |
Pre-fill phone. Falls back to authenticated user's phone column. |
reference |
string | 'Payment' |
Account reference (max 12 chars). |
description |
string | 'M-Pesa Payment' |
Transaction description (max 13 chars). |
Events dispatched
| Event | Payload | Description |
|---|---|---|
mpesa-stk-initiated |
{checkoutRequestId, phone, amount} |
STK push accepted |
mpesa-payment-success |
{checkoutRequestId, receiptNumber, amount, phone} |
Payment confirmed |
mpesa-payment-failed |
{checkoutRequestId, resultCode} |
Payment failed/cancelled |
Listening to events in parent components:
#[On('mpesa-payment-success')] public function onPaymentSuccess(array $event): void { Order::where('reference', $this->reference) ->update([ 'status' => 'paid', 'mpesa_receipt' => $event['receiptNumber'], ]); }
<livewire:mpesa-payment-history />
Paginated, filterable, sortable transaction table scoped to the authenticated user.
<livewire:mpesa-payment-history />
Filters: date range, status, amount range, free-text search (phone / receipt / reference).
URL query parameters are synced via #[Url] so filters survive page refresh.
<livewire:mpesa-payment-status />
Real-time status card. Given a checkout_request_id, polls every 3 seconds and shows animated feedback.
<livewire:mpesa-payment-status checkout-request-id="{{ $checkoutRequestId }}" />
States: pending (spinning) → completed (green checkmark) → failed/cancelled (red X).
Events dispatched
| Event | Payload |
|---|---|
mpesa-status-confirmed |
{checkoutRequestId, receiptNumber} |
Also listens to echo:payments,PaymentConfirmed for instant broadcast updates if you have Laravel Echo configured.
<livewire:mpesa-widget />
Compact collapsible widget combining a mini balance display with a quick-pay form. Ideal for sidebars or dashboards.
<livewire:mpesa-widget reference="Top Up" />
Configure how balance is resolved in config/mpesa-livewire.php:
// Use a column on the users table 'user_balance_column' => 'wallet_balance', // Or a custom callable 'balance_resolver' => fn ($user) => $user->wallet->availableBalance(),
Configuration
config/mpesa-livewire.php:
return [ 'transactions_table' => 'mpesa_transactions', // DB table 'user_phone_column' => 'phone', // Column for pre-filling phone 'user_balance_column' => 'balance', // Column for wallet balance 'balance_resolver' => null, // Custom callable (overrides column) 'scope_to_user' => true, // Scope history to auth user 'poll_max_attempts' => 20, // Polls before timeout (3s each = 60s) 'receipt_route' => 'mpesa.receipt', // Named route for receipt downloads ];
Styling
All components use Tailwind CSS utility classes. No additional CSS is required if you have Tailwind in your project. To customise, publish the views and edit them directly.
M-Pesa brand green: #00A651 / #007A3D.
Events & Broadcasting
To enable instant confirmation (instead of polling), fire a PaymentConfirmed broadcast event on your server after receiving the M-Pesa callback:
broadcast(new PaymentConfirmed($checkoutRequestId, $receiptNumber)) ->toOthers();
The PaymentStatus component listens on echo:payments,PaymentConfirmed and will stop polling immediately on receipt.
Testing
composer test
Tests use Livewire's Livewire::test() helper and mock the mpesa service binding.
License
MIT — Felix Muhoro