omidgfx / laravel-open-wallet
Database-portable wallet ledger for Laravel applications
Requires
- php: ^8.2
- illuminate/database: ^10.0|^11.0|^12.0|^13.0
- illuminate/support: ^10.0|^11.0|^12.0|^13.0
Requires (Dev)
None
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-10-28 08:35:00 UTC
README
A standalone wallet ledger for Laravel applications. It provides wallet ownership, signed immutable ledger entries, balance snapshots, atomic credits/debits, and double-entry transfers. It contains no application-specific workflows or user/profile assumptions.
Requirements and database support
Requires PHP 8.2+ and Laravel 10–13. Storage uses Laravel Eloquent, the Schema builder, and transactions. The supplied migration avoids database-specific SQL, database-generated UUID functions, and cross-table foreign keys. It is designed for Laravel SQL connections such as SQLite, MySQL/MariaDB, PostgreSQL, and SQL Server, subject to the capabilities of each server and Laravel driver. MongoDB and other non-SQL stores need a storage adapter and are not claimed as supported by this SQL migration.
Money is stored as signed 64-bit integer minor units (for example, cents). Pass integers only. Debits are negative ledger rows; credits are positive. Transfers create a matching pair, each with the post-operation balance.
Install
composer require open-wallet/laravel-wallet php artisan vendor:publish --tag=open-wallet-config php artisan vendor:publish --tag=open-wallet-migrations php artisan migrate
The migration can instead be loaded automatically by the provider. Publish it when you need to customize its table/column schema before migrating.
Use
use App\Models\User; use OpenWallet\WalletManager; $user = User::findOrFail(42); $wallets = app(WalletManager::class); $wallet = $wallets->createWallet($user, 'cash'); $wallets->credit($wallet, 5_000, 'deposit', metadata: ['source' => 'gateway']); $wallets->debit($wallet, 1_200, 'purchase'); $wallets->transfer($wallet, $merchantWallet, 800, 'purchase', subject: $order); $entries = $wallets->entries($wallet, perPage: 50);
Configure allowed wallet types in wallet_types. entry_types is optional; when non-empty, it restricts accepted entry labels. allow_negative_balance_for contains wallet type names that may go below zero. Every other type rejects overdrafts inside the transaction.
For a non-Eloquent holder, use createWallet(HolderClass::class, $holderId, 'cash'). Eloquent morph maps are respected through getMorphClass().
Configuration
config/open-wallet.php controls:
- Database connection (
connection; null uses Laravel's default connection). - Table names and every non-primary wallet/entry column name.
- Allowed wallet and entry type labels.
- Wallet types permitted to have negative balances.
- Transaction deadlock retry count (
lock_retries). - Morph key string size (default 64), UUID generator, and maximum page size.
Keep database column names and table names valid for the selected database. If you change the config after publishing migrations, update the migration to match before running it. The id primary keys remain conventional auto-incrementing integers.
Consistency and concurrency
Every balance mutation runs in a transaction and locks wallet rows in ascending primary-key order for transfers. Laravel/database engines differ in how they implement row locks; production correctness under concurrent writers depends on using a transactional engine that honors SELECT ... FOR UPDATE (or an equivalent supported by Laravel). SQLite is useful for development but does not provide the same row-level locking guarantees as PostgreSQL, MySQL/InnoDB, or SQL Server. Use one configured connection for both wallet tables so balance updates and ledger rows commit atomically.
The ledger is append-only by package convention. Application database credentials should prevent updates/deletes to ledger rows if tamper resistance is required. Reconcile cached balances against entry sums periodically in applications with strict accounting requirements.
API
WalletManager exposes createWallet, walletFor, credit, debit, transfer, and entries. Wallet records and ledger entries are available as OpenWallet\Models\Wallet and OpenWallet\Models\WalletEntry. Invalid amounts and overdrafts throw package exceptions under OpenWallet\Exceptions.