Looking to hire Laravel developers? Try LaraJobs

laravel-mpesa-livewire maintained by felixmuhoro

Description
Livewire M-Pesa payment components
Author
Last update
2026/06/04 17:59 (v1.0.0)
License
Downloads
0

Comments
comments powered by Disqus

laravel-mpesa-livewire

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

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