Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Core Wallet Manager Laravel Package

mukadi/core-wallet-manager

Core wallet management for Laravel apps: define wallets, track balances and transactions, and build on a reusable ledger foundation for credits, debits, and account histories. A lightweight package for payment, points, or credit systems.

View on GitHub
Deep Wiki
Context7

Getting Started

composer require mukadi/core-wallet-manager

Before writing a line of application code, spend 30 minutes reading the package rather than the README. With 2 stars and 0 dependents, there is no tutorial corpus, no Stack Overflow history, and no blog posts to fall back on — src/ and tests/ are the only real documentation.

Order of inspection:

  1. composer.json — PHP constraint, required extensions (does it need bcmath/gmp?), and whether there is an extra.laravel.providers block. A "lightweight PHP library" is usually framework-agnostic, so expect no service provider and no auto-discovery.
  2. The entry class in src/ — usually a WalletManager-style facade or a static factory. This is your public API surface; everything else is internals.
  3. Contracts/ (if present) — the interfaces tell you what is designed to be swapped and what is an extension point.
  4. Migrations / schema — the table shape (wallets, transactions/ledger entries) determines almost every design decision downstream.
  5. tests/ — the fastest way to see a working credit → debit → transfer cycle. Copy the setup block into a scratch test.

First use case to build (in a throwaway branch): a single-currency wallet for a User, credit it 100, debit it 30, read back the balance, and list transactions. That one loop exercises persistence, the manager, rounding, and the model accessors — and will surface 90% of the rough edges before you design anything on top.

Wiring into Laravel, since it likely won't be automatic:

// app/Providers/AppServiceProvider.php
$this->app->singleton(WalletManager::class, fn () => new WalletManager(/* config */));

Then copy (don't --publish if there's no publish tag) the migrations into database/migrations, and keep them in your repo under version control so a composer update can't silently change your schema.

Implementation Patterns

Wrap, don't scatter. Never call package classes from controllers or models. Create one App\Services\WalletService (or two: WalletService + TransferService) that owns the manager, the transaction boundary, and the idempotency logic. This is non-negotiable for a package this small — the wrapper is your fork-point when you inevitably need to patch behavior.

final class WalletService
{
    public function __construct(private WalletManager $manager) {}

    public function credit(Wallet $wallet, int $amountMinor, string $idempotencyKey): Transaction
    {
        return DB::transaction(function () use ($wallet, $amountMinor, $idempotencyKey) {
            $wallet = Wallet::whereKey($wallet->getKey())->lockForUpdate()->firstOrFail();
            // idempotency check, then delegate to the manager
        });
    }
}

Money is integers. Store minor units (int), format only at the edge (Blade/API resource). If the package takes floats or strings, normalize at the boundary of your WalletService and never let a float reach the library. Verify how it rounds — half-up vs. truncation is a support ticket waiting to happen.

Append-only ledger, derived balance. Treat transaction rows as immutable facts. If the package exposes an update/delete on transactions, don't use it; model corrections as reversal entries. Maintain a cached balance column only if the package does, and add a nightly reconciliation command that recomputes from the ledger and alerts on drift.

Concurrency is your problem, not the library's. Wrap every mutation in DB::transaction and take a lockForUpdate() on the wallet row before reading the balance. A "lightweight" library very often reads-then-writes without locking; if the source confirms that, the lock in your wrapper is what prevents double-spend. Add a unique index on (wallet_id, idempotency_key) at the DB level so retries from a queued job are safe.

Transfers span two wallets. Lock in a deterministic order (lowest id first) to avoid deadlocks, and require both legs to be in one DB transaction. If the package's transfer helper doesn't do this, write it yourself against the credit/debit primitives.

Integrate through events. After a successful mutation, dispatch your own domain event (WalletCredited, WalletDebited) from the wrapper — not from inside package callbacks. Everything downstream (notifications, accounting export, fraud checks) subscribes to your event, so you stay decoupled from the package's event naming.

Test against the real DB. If the package uses row locking or driver-specific SQL, in-memory SQLite will either fail or silently pass a race you actually have. Run the wallet test suite against the same engine as production (MySQL/Postgres) in CI.

Gotchas and Tips

  • Treat it as unvetted code. 2 stars, 0 dependents, score 0.04 means no community has stress-tested this. Pin an exact version ("mukadi/core-wallet-manager": "1.0.3", not ^1.0), commit your composer.lock, and read every release diff. Assume you may need to fork; your WalletService wrapper is what makes that cheap.
  • No service provider is likely. Don't wait for auto-discovery to fail mysteriously — check extra.laravel first, then bind manually.
  • Migrations may not be publishable. Look for publishes() calls; if absent, copy the migration files by hand and adjust table names/timestamps to avoid collisions with existing tables.
  • Read the balance calculation. Some small wallet libraries compute balance as sum(credits) - sum(debits) on every read (fine, but O(n) and slow past ~10k rows) and some keep a denormalized column without a lock (fast, but wrong under concurrency). Know which you have before you scale.
  • Check what happens on insufficient funds. Does it throw, return false, or allow a negative balance? Wrap it and normalize to a single InsufficientFundsException so callers don't branch on library internals.
  • Currency and precision. Confirm multi-currency support (and whether a wallet is bound to one currency) and the decimal precision assumption. Mixing currencies in a single wallet is the classic bug in packages like this.
  • Refunds and reversals. If there's no first-class reversal, don't delete or edit rows — post an offsetting entry. Your reconciliation command will thank you.
  • Debugging: enable the query log around a single credit() call and read the exact SQL. That reveals the locking behavior, the rounding, and whether there's a hidden SELECT per write. Log every ledger mutation with the idempotency key and the resulting transaction id at info level — with no community tooling, your logs are the only forensic trail.
  • Extension points: favor the package's Contracts (storage driver, transaction repository) over subclassing concrete classes. If everything is final or concrete, keep the wrapper thin and put behavior in your own layer instead of forking immediately.
  • Authorization stays in your app. Route every wallet mutation through a policy/gate ($this->authorize('debit', $wallet)); don't assume the package enforces ownership of the wallet.
  • Have an exit plan. Because the dependency risk is high, keep the WalletService interface free of package types in its signatures (accept your own DTOs and IDs, return your own TransactionResult). If you ever swap to bavix/laravel-wallet or a hand-rolled ledger, only the wrapper's internals change.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core