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

Filament Wallet Laravel Package

tomatophp/filament-wallet

Manage account balances and wallets in FilamentPHP with actions to charge wallets, view transactions, and track balances, built on Bavix Wallet for easy integration with any model.

View on GitHub
Deep Wiki
Context7

Getting Started

Check the branch before anything else. This package ships two live lines: the default v5 line targets Filament v5 / Laravel 12–13 with bavix/laravel-wallet 11–12, and a separate v4 branch targets Filament v4. Pin an exact version rather than a caret range — you do not want a Filament major silently arriving via composer update.

composer require tomatophp/filament-wallet
php artisan filament-wallet:install

The install command's job is essentially to get Bavix's migrations onto disk and run them. Read what it publishes before running it against anything you care about — the package itself adds no schema, so every table you get (wallets, transactions, and Bavix's supporting tables) belongs to Bavix and will be governed by Bavix's upgrade path.

Register the plugin in the panel provider — only the panel that ops staff use, not every panel in the app:

->plugin(\TomatoPHP\FilamentWallet\FilamentWalletPlugin::make())

Then make a model wallet-capable. This is the Bavix contract, not a package-specific one:

use Bavix\Wallet\Interfaces\Wallet;
use Bavix\Wallet\Traits\HasWalletFloat;

class Account extends Model implements Wallet
{
    use HasWalletFloat;
}

Finally expose it in a resource table:

use TomatoPHP\FilamentWallet\Filament\Actions\WalletAction;

$table->actions([
    WalletAction::make('wallet'),
]);

First use case: an internal ops console. Open a users/accounts list, click the wallet action, charge a balance, then go verify the resulting row in the transactions resource. Do that end-to-end on a seeded staging database before you write a line of your own code — that single loop tells you whether balance math, rounding, and transaction records behave the way your finance people expect.

Where to look first, in order: the plugin class (what resources and pages it registers), TomatoPHP\FilamentWallet\Filament\Actions\WalletAction (the only place money actually moves through this package), then Bavix's own docs. The README points you at bavix/laravel-wallet and means it — 90% of your questions will be answered there, not here.

Implementation Patterns

Treat the action as a UI shell, and own the semantics. The most common real-world pattern is to use WalletAction as a starting point and immediately wrap it:

WalletAction::make('wallet')
    ->visible(fn ($record) => auth()->user()->can('chargeWallet', $record))
    ->authorize(fn ($record) => auth()->user()->can('chargeWallet', $record))

Add a policy for the model and check it in both visible() and authorize() — visible() hides the UI, authorize() stops someone replaying the Livewire call. A table action is a Livewire request; it is not protected by the fact that you hid the button.

Do the actual money movement through Bavix when it matters. For anything beyond a manual one-off, call Bavix directly from a queued job or command rather than driving the modal:

$account->deposit(1500, ['reason' => 'invoice-4821', 'actor_id' => auth()->id()]);
$account->withdraw(500);
$account->transfer($other, 250);

This keeps bulk credits, backfills, and reversals out of the operator UI (where they'd be one lazy() round trip per record) and gives you one place to add idempotency keys and reason codes.

Wrap charges in a transaction with your own audit record. The package does not log who charged what. Before calling deposit, capture the balance; after, write an audit row with actor, before/after, reason, and the Bavix transaction id:

DB::transaction(function () use ($record, $amount) {
    $before = $record->balanceFloat;
    $tx = $record->deposit($amount, ['reason' => $this->data['reason']]);
    AuditLog::create([...]);
});

Add a duplicate-submission guard. Operators double-click. Put a unique constraint on a reference column in your own ledger table, or use a short-lived cache lock keyed by actor + record + amount, and reject the second call.

Filament Accounts integration is a two-step. Install tomatophp/filament-account, publish the model (php artisan vendor:publish --tag="filament-accounts-model"), then use that published model as your wallet-bearing model and register it with ->useAccounts(). In v5 the bridge only registers when the accounts package is actually installed — on older versions calling ->useAccounts() without the dependency is a self-inflicted failure.

Choose the trait deliberately. HasWalletFloat is what the README shows and what most people copy. If your finance team thinks in integer minor units (cents, piasters), use HasWallet instead and format on display. Mixing the two across models makes every report a rounding argument.

Reconciliation as a scheduled job. Pick a source of truth (payment provider, invoicing system) and run a nightly comparison against Bavix balances, alerting on drift. This is the thing that catches both a bug in your charge flow and a bug in WalletAction — without it you find out from a customer.

Test the money paths, not the package. The package has its own tests; yours should assert that after your action runs, balanceFloat equals what you expect and exactly one transaction row exists with the right TransactionType. Pin those assertions to the Bavix version you're on — v11→v12 introduced the TransactionType enum on transactions and changes what you read back.

Register the plugin per-panel. If you run multiple panels, register FilamentWalletPlugin only in the ops/admin one. Putting a charge button on a customer-facing panel is how you get a support incident.

Gotchas and Tips

Float precision is the trap. The README prescribes HasWalletFloat, and an early release (v1.0.6) explicitly switched integer to float. Before you ship currency, verify how amounts round-trip: charge 10.01, then 0.99, and check the stored balance is exactly 11.00 and not 10.999999. If you see drift, switch the model to integer traits and handle formatting at the edge — better to find out in the spike than in reconciliation.

Do not add ->live() to the wallet action. v4.0.0 changed ->live() to ->lazy() specifically so the modal waits for user input before recalculating. Re-adding live() reverts a real bug fix: the balance preview recalculates on every keystroke and you get stale or partial amounts. If a form field seems to lag, look for a Livewire version mismatch before you "fix" the reactivity.

The install command namespace was broken pre-5.0. If you inherit a project on the 1.x line, php artisan filament-wallet:install may not resolve. On v5 it was fixed to TomatoPHP\FilamentWallet\Console. If the command is missing, check the version before you start debugging your autoloader.

Missing Notification import was a real bug (v1.0.3). The action notifies you of the result; if you're on 1.0.0–1.0.2 and the action silently dies after a charge, that's why. Another reason to pin versions and re-test the charge flow on every bump.

No authorization ships with it. Anyone who can see the table can charge. Assume the action is unprotected until you prove otherwise, and put a policy in front of it in the same PR that adds it.

No idempotency, no limits, no reason codes. There is no documented support for amount ceilings, dual approval, or duplicate guards. If a manual charge is audit-sensitive in your org, build those before rollout, not after.

No audit trail. Bavix records transactions; it does not record who pressed the button. Your own ledger table is not optional if anyone will ever ask "who credited this account and why."

Large tables get slow without indexes. The wallets and transactions resources query Bavix's tables directly. Transactions grows without bound. Filter and index on wallet_id, created_at, and type on the underlying tables before the list becomes unusable — you cannot add these from the package, they go in your own migration.

Bulk work does not belong in the UI. lazy() means one round trip per interaction. Anything over a few dozen records goes through a job calling Bavix. This also makes it retryable, which the modal is not.

Watch the two branches and the single maintainer. Budget a few engineer-days a year to move between the v4 and v5 lines and to track Bavix majors. Because the package is thin, forking into an internal namespace is cheap and is the realistic mitigation — vendor it, and keep a documented fallback where you reimplement the action against Bavix directly if upstream stalls.

Extension points worth knowing: subclass WalletAction and override its form/behavior rather than editing vendor code; the plugin class is the place to intercept resource registration; and everything genuinely financial lives in Bavix — configure limits, overdraft, and decimal handling there. If you need a capability the package's UI doesn't expose, add it to your own layer and leave the package alone, so upgrades stay trivial.

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.
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin
skyraptor/chatter
edlugz/sasapay
abbasudo/artisan-route