PHPackages                             goldoni/laravel-virtual-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. goldoni/laravel-virtual-wallet

ActiveLibrary[Payment Processing](/categories/payments)

goldoni/laravel-virtual-wallet
==============================

Virtual multi-wallet for Laravel with entries, transfers, enums and configurable table prefix

v0.1.1(9mo ago)01MITPHPPHP ^8.2

Since Sep 10Pushed 3mo agoCompare

[ Source](https://github.com/fgoldoni/laravel-virtual-wallet)[ Packagist](https://packagist.org/packages/goldoni/laravel-virtual-wallet)[ Docs](https://github.com/fgoldoni/laravel-virtual-wallet)[ RSS](/packages/goldoni-laravel-virtual-wallet/feed)WikiDiscussions main Synced 1w ago

READMEChangelogDependenciesVersions (3)Used By (0)

🧾 Laravel Virtual Wallet
========================

[](#-laravel-virtual-wallet)

> Virtual multi-wallets for Laravel — credit, debit, transfer.
> Atomic, idempotent, configurable, and PSR-12 compliant.

---

✨ Features
----------

[](#-features)

- ✅ Multi-wallet by **`label + currency`**
- ✅ Immutable **entries log** with running balance
- ✅ **Atomic** operations with per-wallet locking
- ✅ **Idempotency** via `idempotency_key`
- ✅ **Configurable** enums, models, and table prefix
- ✅ **Fluent** API: ```
    Wallet::for($user)->label('main')->currency('EUR')->credit('100.00');
    ```

---

⚙️ Requirements
---------------

[](#️-requirements)

- PHP **8.2+**
- Laravel (provided by the host application)
- Any DB supported by Laravel that handles `decimal(20,8)`

---

🚀 Quick Start
-------------

[](#-quick-start)

1. **Install** (path repo)

```
composer require goldoni/laravel-virtual-wallet:dev-main
```

2. **Publish config**

```
php artisan vendor:publish --tag=wallet-config
```

3. **Migrate**

```
php artisan migrate
```

Default tables (with prefix `wallet_`):

- `wallet_wallets`, `wallet_entries`, `wallet_transfers`

---

🧩 Configuration
---------------

[](#-configuration)

`config/wallet.php`

```
return [
    'allow_negative' => false,
    'default_currency' => 'EUR',
    'precision' => 8,
    'table_prefix' => 'wallet_',

    'models' => [
        'wallet' => Goldoni\LaravelVirtualWallet\Models\Wallet::class,
        'entry' => Goldoni\LaravelVirtualWallet\Models\Entry::class,
        'transfer' => Goldoni\LaravelVirtualWallet\Models\Transfer::class,
    ],

    'enums' => [
        'entry_type' => Goldoni\LaravelVirtualWallet\Enums\EntryType::class,
        'entry_status' => Goldoni\LaravelVirtualWallet\Enums\EntryStatus::class,
        'transfer_status' => Goldoni\LaravelVirtualWallet\Enums\TransferStatus::class,
    ],
];
```

**Notes**

- `table_prefix`: change to avoid table name conflicts.
- `allow_negative`: allow overdraft on debit.
- `precision`: monetary scale (columns are `decimal(20,8)`).

---

🧱 Add the Trait to Your Owner
-----------------------------

[](#-add-the-trait-to-your-owner)

```
use Goldoni\LaravelVirtualWallet\Traits\HasWallets;

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

The trait adds:

- `wallets()` morphMany relation
- `wallet(string $label = 'main', ?string $currency = null)` creator/getter

---

🛠️ Usage
--------

[](#️-usage)

```
use Goldoni\LaravelVirtualWallet\Facades\Wallet;
```

### Credit

[](#credit)

```
Wallet::for($user)
    ->label('main')
    ->currency('EUR')
    ->credit('100.00', ['idempotency_key' => 'dep-100']);
```

### Debit

[](#debit)

```
Wallet::for($user)
    ->label('main')
    ->currency('EUR')
    ->debit('25.00', ['idempotency_key' => 'wd-25']);
```

### Transfer

[](#transfer)

```
Wallet::for($buyer)
    ->label('main')
    ->currency('EUR')
    ->transfer(
        toOwner: $seller,
        amount: '35.00',
        options: ['idempotency_key' => 'order-500'],
        fromLabel: 'main',
        toLabel: 'revenue'
    );
```

### History &amp; Balances

[](#history--balances)

```
$entries = Wallet::for($user)->label('main')->currency('EUR')->history(50);
$balance = Wallet::for($user)->label('main')->currency('EUR')->balance();
$wallets = Wallet::for($user)->wallets();
$totals  = Wallet::for($user)->totalBalanceByCurrency();
```

---

📚 Examples
----------

[](#-examples)

### Chain `credit`, `debit`, `transfer` (no `idempotency_key`)

[](#chain-credit-debit-transfer-no-idempotency_key)

```
Wallet::for($user)
    ->label('main')
    ->currency('EUR')
    ->credit('100.00')
    ->debit('25.00')
    ->transfer($seller, '30.00', options: [], fromLabel: 'main', toLabel: 'revenue');
```

### Multi-wallet on the same owner

[](#multi-wallet-on-the-same-owner)

```
// Fund two wallets
Wallet::for($user)->label('main')->currency('EUR')->credit('200.00');
Wallet::for($user)->label('savings')->currency('EUR')->credit('50.00');

// Move funds from main to savings
Wallet::for($user)
    ->label('main')
    ->currency('EUR')
    ->transfer($user, '25.00', options: [], fromLabel: 'main', toLabel: 'savings');

// Read balances
$mainBalance = Wallet::for($user)->label('main')->currency('EUR')->balance();
$savingsBalance = Wallet::for($user)->label('savings')->currency('EUR')->balance();
```

---

🧠 API at a Glance
-----------------

[](#-api-at-a-glance)

All amounts are **strings** (avoid float pitfalls).

```
Wallet::for(\Illuminate\Database\Eloquent\Model $owner): self
Wallet::label(string $label): self
Wallet::currency(string $currency): self

Wallet::credit(string $amount, array $options = [], ?string $label = null, ?string $currency = null): self
Wallet::debit(string $amount, array $options = [], ?string $label = null, ?string $currency = null): self
Wallet::transfer(\Illuminate\Database\Eloquent\Model $toOwner, string $amount, array $options = [], ?string $fromLabel = null, ?string $toLabel = 'main', ?string $currency = null): self

Wallet::history(int $limit = 50, ?string $label = null, ?string $currency = null): \Illuminate\Support\Collection
Wallet::historyBetween(string $from, string $to, ?string $label = null, ?string $currency = null): \Illuminate\Support\Collection
Wallet::paginateHistory(int $perPage = 50, ?string $label = null, ?string $currency = null): \Illuminate\Contracts\Pagination\LengthAwarePaginator
Wallet::cursorHistory(int $perPage = 50, ?string $label = null, ?string $currency = null): \Illuminate\Contracts\Pagination\CursorPaginator

Wallet::balance(?string $label = null, ?string $currency = null): string
Wallet::balances(): \Illuminate\Support\Collection
Wallet::totalBalanceByCurrency(): \Illuminate\Support\Collection
Wallet::wallets(): \Illuminate\Support\Collection
Wallet::ensureWallet(string $label, string $currency): \Illuminate\Database\Eloquent\Model
```

`$options` supports:

- `idempotency_key`
- `reference_type`, `reference_id`
- `meta` (array)
- optional `currency` validation override

---

🗄️ Data Model
-------------

[](#️-data-model)

TableKey ColumnsConstraints`wallet_wallets``id`, `ulid`, `owner_type`, `owner_id`, `label`, `currency`, `balance`, `meta`, timestampsUnique: `(owner_type, owner_id, label, currency)``wallet_entries``id`, `wallet_id`, `ulid`, `type`, `status`, `amount`, `balance_after`, `currency`, `reference_*`, `meta`Unique: `(wallet_id, idempotency_key)``wallet_transfers``id`, `ulid`, `from_wallet_id`, `to_wallet_id`, `amount`, `currency`, `status`, `idempotency_key`, `meta`Unique: `idempotency_key`> Table names honor your `table_prefix`.

---

🧾 Enums
-------

[](#-enums)

```
Goldoni\LaravelVirtualWallet\Enums\EntryType::CREDIT | DEBIT
Goldoni\LaravelVirtualWallet\Enums\EntryStatus::PENDING | COMPLETED | REVERSED
Goldoni\LaravelVirtualWallet\Enums\TransferStatus::PENDING | COMPLETED | FAILED
```

Swap enum classes via `config('wallet.enums.*')`.

---

🔔 Events
--------

[](#-events)

- `EntryRecorded($entry)`
- `TransferCompleted($transfer)`

Dispatched **after commit** for consistency.

---

🧯 Exceptions
------------

[](#-exceptions)

- `InvalidAmount` — non-numeric or non-positive amount
- `InsufficientFunds` — would go negative and `allow_negative` is false
- `CurrencyMismatch` — mismatched currencies
- `DuplicateOperation` — reused `idempotency_key`

---

✅ Best Practices
----------------

[](#-best-practices)

1. Use `idempotency_key` for all external or retryable flows.
2. Keep labels meaningful: `main`, `savings`, `revenue`, etc.
3. For multi-currency workflows, set `.currency('USD')` or `.currency('EUR')`.
4. Pass amounts as strings: `'100.00'`.

---

🧪 Example Controller
--------------------

[](#-example-controller)

```
use Goldoni\LaravelVirtualWallet\Facades\Wallet;

class WalletController
{
    public function deposit(Request $request)
    {
        $user = $request->user();

        Wallet::for($user)
            ->label('main')
            ->currency('EUR')
            ->credit($request->input('amount'), [
                'idempotency_key' => $request->header('Idempotency-Key')
            ]);

        return response()->noContent();
    }
}
```

---

🛠️ Custom Models
----------------

[](#️-custom-models)

Replace Eloquent classes via `config('wallet.models.*')`. Your replacements must keep compatible columns and relations:

- `Wallet`: `owner()` and `entries()` relations
- `Entry`: belongs to `Wallet` via `wallet_id`
- `Transfer`: uses `from_wallet_id` and `to_wallet_id`

---

🧹 Coding Standards
------------------

[](#-coding-standards)

- PSR-12 compliant

---

📄 License
---------

[](#-license)

MIT

```

```

###  Health Score

31

—

LowBetter than 66% of packages

Maintenance70

Regular maintenance activity

Popularity1

Limited adoption so far

Community8

Small or concentrated contributor base

Maturity39

Early-stage or recently created project

 Bus Factor1

Top contributor holds 63.6% of commits — single point of failure

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

Every ~0 days

Total

2

Last Release

271d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/23716281?v=4)[Goldoni](/maintainers/fgoldoni)[@fgoldoni](https://github.com/fgoldoni)

---

Top Contributors

[![fgoldoni](https://avatars.githubusercontent.com/u/23716281?v=4)](https://github.com/fgoldoni "fgoldoni (7 commits)")[![FouotsaTHAG](https://avatars.githubusercontent.com/u/235260992?v=4)](https://github.com/FouotsaTHAG "FouotsaTHAG (4 commits)")

---

Tags

laravelpaymentswalletledgerbalanceVirtual Walletmultiwallet

### Embed Badge

![Health badge](/badges/goldoni-laravel-virtual-wallet/health.svg)

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

###  Alternatives

[021/laravel-wallet

Reliable and flexible wallet system for Laravel

2775.8k](/packages/021-laravel-wallet)[bavix/laravel-wallet-swap

Addition to the package laravel-wallet.

2428.9k](/packages/bavix-laravel-wallet-swap)[musahmusah/laravel-multipayment-gateways

A Laravel Package that makes implementation of multiple payment Gateways endpoints and webhooks seamless

882.2k1](/packages/musahmusah-laravel-multipayment-gateways)[threesquared/laravel-paymill

Laravel wrapper for the Paymill API

121.3k](/packages/threesquared-laravel-paymill)

PHPackages © 2026

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