PHPackages                             felixmuhoro/laravel-mpesa-wallet - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Payment Processing](/categories/payments)
4. /
5. felixmuhoro/laravel-mpesa-wallet

ActiveLibrary[Payment Processing](/categories/payments)

felixmuhoro/laravel-mpesa-wallet
================================

A virtual wallet system for Laravel backed by M-Pesa deposits and withdrawals.

v1.0.0(1mo ago)00MITPHPPHP ^8.1

Since Jun 4Pushed 1mo agoCompare

[ Source](https://github.com/felixmuhoro/laravel-mpesa-wallet)[ Packagist](https://packagist.org/packages/felixmuhoro/laravel-mpesa-wallet)[ Docs](https://github.com/felixmuhoro/laravel-mpesa-wallet)[ RSS](/packages/felixmuhoro-laravel-mpesa-wallet/feed)WikiDiscussions master Synced 1w ago

READMEChangelogDependencies (2)Versions (2)Used By (0)

laravel-mpesa-wallet
====================

[](#laravel-mpesa-wallet)

A production-ready virtual wallet system for Laravel, backed by M-Pesa deposits and withdrawals via [`felixmuhoro/laravel-mpesa`](https://github.com/felixmuhoro/laravel-mpesa).

Features
--------

[](#features)

- **Virtual wallet** per Eloquent owner (User, Merchant, etc.) with KES balance tracking
- **M-Pesa STK Push deposits** — registers pending transaction, credits wallet on callback
- **M-Pesa B2C withdrawals** — locks funds atomically, settles or refunds on callback
- **Wallet-to-wallet transfers** — deadlock-safe using ordered `SELECT ... FOR UPDATE`
- **Freeze / unfreeze** wallets to block all transactions
- **Locked balance** tracks in-flight funds without double-spending
- **Enum-typed** transaction status and type, immutable `Money` value object
- **REST API** with resource classes, pagination, and validation
- **Event listener** (`CreditWalletOnPayment`) auto-credits wallet on `PaymentSuccessful`
- **Zero race conditions** — all mutations use DB-level row locks inside transactions

---

Requirements
------------

[](#requirements)

DependencyVersionPHP`^8.1`Laravel`^10.0felixmuhoro/laravel-mpesa`^1.2`---

Installation
------------

[](#installation)

```
composer require felixmuhoro/laravel-mpesa-wallet
```

Publish config and migrations:

```
php artisan vendor:publish --tag=mpesa-wallet-config
php artisan vendor:publish --tag=mpesa-wallet-migrations
php artisan migrate
```

---

Setup
-----

[](#setup)

### 1. Add the trait to your User model

[](#1-add-the-trait-to-your-user-model)

```
use FelixMuhoro\MpesaWallet\Concerns\HasWallet;

class User extends Authenticatable
{
    use HasWallet;
}
```

### 2. Configure `.env`

[](#2-configure-env)

```
MPESA_WALLET_CURRENCY=KES
MPESA_WALLET_MIN_DEPOSIT=10
MPESA_WALLET_MAX_DEPOSIT=150000
MPESA_WALLET_MIN_WITHDRAWAL=10
MPESA_WALLET_MAX_WITHDRAWAL=150000
MPESA_WALLET_CALLBACK_URL=https://yourdomain.com/api/mpesa/callback
```

---

Usage
-----

[](#usage)

### Via trait shortcuts

[](#via-trait-shortcuts)

```
$user = auth()->user(); // User uses HasWallet

// Initiate M-Pesa STK push deposit (returns pending WalletTransaction)
$txn = $user->deposit(1000, '254712345678');

// Initiate B2C withdrawal (locks funds immediately)
$txn = $user->withdraw(500, '254712345678');

// Wallet-to-wallet transfer
[$debit, $credit] = $user->transferTo($anotherUser, 300);

// Check balance (spendable, excludes locked funds)
echo $user->walletBalance()->format(); // "KES 5,000.00"
echo $user->formattedWalletBalance();
```

### Via WalletManager (injected)

[](#via-walletmanager-injected)

```
use FelixMuhoro\MpesaWallet\WalletManager;

class PaymentService
{
    public function __construct(private WalletManager $wallet) {}

    public function topUp(User $user, int $amount, string $phone): void
    {
        $pendingTxn = $this->wallet->deposit($user, $amount, $phone);
        // CreditWalletOnPayment listener will settle on M-Pesa callback
    }
}
```

---

API Endpoints
-------------

[](#api-endpoints)

All routes require `auth:sanctum` by default, prefixed `api/wallet`.

MethodURIDescription`GET``/api/wallet/balance`Current balance`POST``/api/wallet/deposit`Initiate STK push`POST``/api/wallet/withdraw`Initiate B2C withdrawal`POST``/api/wallet/transfer`Wallet-to-wallet transfer`GET``/api/wallet/transactions`Paginated history`GET``/api/wallet/transactions/{uuid}`Single transaction### Deposit

[](#deposit)

```
{ "amount": 1000, "phone": "254712345678" }
```

### Withdraw

[](#withdraw)

```
{ "amount": 500, "phone": "254712345678" }
```

### Transfer

[](#transfer)

```
{ "recipient_id": 42, "amount": 300 }
```

---

How deposits work
-----------------

[](#how-deposits-work)

```
User -> POST /deposit -> WalletManager::deposit()
    creates pending WalletTransaction (balance unchanged)
    -> caller fires STK Push via felixmuhoro/laravel-mpesa
Safaricom -> POST /callback -> PaymentSuccessful event
    -> CreditWalletOnPayment listener (queued, 3 retries)
    -> WalletManager::creditWallet() — lockForUpdate -> balance++
    -> pending transaction -> completed

```

How withdrawals work
--------------------

[](#how-withdrawals-work)

```
User -> POST /withdraw -> WalletManager::withdraw()
    lockForUpdate -> locked_balance += amount
    creates pending WalletTransaction
    -> caller fires B2C via felixmuhoro/laravel-mpesa
Safaricom -> POST /callback
  Success -> settleWithdrawal(): balance -= amount, locked -= amount
  Failure -> failWithdrawal():  locked -= amount (funds refunded)

```

---

Configuration reference
-----------------------

[](#configuration-reference)

```
// config/mpesa-wallet.php

return [
    'currency'    => 'KES',
    'auto_create' => true,
    'limits' => [
        'min_deposit'    => 10,
        'max_deposit'    => 150_000,
        'min_withdrawal' => 10,
        'max_withdrawal' => 150_000,
    ],
    'routes' => [
        'enabled'    => true,
        'prefix'     => 'api/wallet',
        'middleware' => ['api', 'auth:sanctum'],
        'name'       => 'mpesa-wallet.',
    ],
    'tables' => [
        'wallets'      => 'wallets',
        'transactions' => 'wallet_transactions',
    ],
];
```

---

Testing
-------

[](#testing)

```
composer install
vendor/bin/phpunit --testdox
```

---

License
-------

[](#license)

MIT

###  Health Score

35

—

LowBetter than 77% of packages

Maintenance90

Actively maintained with recent releases

Popularity0

Limited adoption so far

Community2

Small or concentrated contributor base

Maturity42

Maturing project, gaining track record

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

50d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/f4e43995a8ec026a33d25263c30f437674d6d8481092cb3acd64e96e4ba3f4e4?d=identicon)[felixmuhoro](/maintainers/felixmuhoro)

---

Tags

laravelmpesakenyasafaricom

### Embed Badge

![Health badge](/badges/felixmuhoro-laravel-mpesa-wallet/health.svg)

```
[![Health](https://phpackages.com/badges/felixmuhoro-laravel-mpesa-wallet/health.svg)](https://phpackages.com/packages/felixmuhoro-laravel-mpesa-wallet)
```

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
