tomatophp/tomato-wallet
A Laravel wallet package from TomatoPHP for managing user balances and transactions. Add credit and debit operations, track wallet history, and integrate multi-wallet support into your app with a simple, fluent API.
Architecture fit. tomato-wallet is a Laravel package in the TomatoPHP/TomatoCRM family that models account-held balances with a transaction history, attachable polymorphically to any Eloquent model via a HasWallet trait, plus payment-provider integration hooks. For a TPM the operative framing is that this is an ecosystem-shaped component: its value proposition is largely convention alignment with Filament admin scaffolding and sibling tomato-* packages. On a Laravel + TomatoPHP stack it removes the "invent a wallet schema" decision from the roadmap and supplies a working primitive for prepaid balances, refunds-as-credit, drawdown/partial payment, agency retainers, and store credit. Off that stack it imports dependencies and conventions the team would not otherwise adopt, and the build-vs-buy equation tilts toward building or selecting an ecosystem-independent ledger.
The distinction that actually governs scoping is wallet as cached balance versus wallet as ledger. The package appears to do both — a balance column plus transaction records with metadata — and the description claims correctness via atomic database transactions. That claim is the single most important thing to validate, because a credit/debit system is only as trustworthy as its concurrency semantics. Program-wise it determines whether this supports a pilot (feature validation, internal tooling, low-value floats) or a platform commitment (customer money movement). Size every part of the adoption decision to the first case unless the second is proven.
Integration feasibility. Mechanically low-friction: Composer install, vendor publish, run migrations, rely on package auto-discovery, optionally register a Filament plugin. The real effort is discovering the sanctioned domain API — which trait attaches a wallet, whether balances are stored as decimal or integer minor units, how transfers compose, what the transaction metadata contract is, and whether a service/action class is the mutation path versus calling models directly. That is a one-to-two-day spike, not a research project. The gating question for a TPM is not "will it install" but "does it survive review by the engineer who gets paged for balance drift."
Technical risk. High relative to consequence, driven almost entirely by adoption signals rather than an observed defect. One star, zero dependents, a single maintainer, last release May 2024: no third-party production usage to mine for failure patterns, no visible security review, no evidence of ongoing maintenance. For a financial-adjacent component, absence of validation is the risk — the dangerous failures are silent (lost updates on concurrent debits, non-idempotent webhook replays, partially committed transfers) and none surface in a happy-path demo. Treat maturity as "reference implementation," not "dependency," and plan from day one to vendor and own it.
Key questions to resolve before commitment.
tomato-* packages are hard transitive dependencies, and how mature are those independently? Inheriting three unmaintained packages to get one is a different decision than inheriting one.lockForUpdate, optimistic versioning, or nothing?Stack fit. Viable for Laravel 10/11 + Filament 3 applications already inside the TomatoPHP orbit, where the team has accepted platform standardization as a deliberate, recorded product decision. Poor fit for non-Filament or polyglot stacks, for teams wanting the wallet domain decoupled from admin scaffolding, and for any product where finance, audit, or a regulated partner will scrutinize the ledger design. The honest TPM framing of "adopt this package" is "adopt the TomatoPHP platform for the wallet domain" — that call should be explicit, because reversing it later costs more than reversing a library choice.
Migration path. Prefer greenfield: a new wallet feature with no legacy balances to reconcile. If existing balances must carry over, scope it as a data-engineering workstream — export historical activity, replay it through the package's ledger model, reconcile to a known-good closing balance before cutover. Do not migrate in place against a live balance table. Define a reconciliation window in which legacy and new systems are compared daily, and keep cutover reversible until that window closes clean.
Compatibility. Pin Laravel, PHP, database, and payment-SDK versions explicitly, and test on the actual target database — MySQL and Postgres diverge on JSON columns and enum handling, precisely where a wallet schema bites. Because upstream release cadence is sparse, the practical posture is to vendor or fork under an internal namespace and track upstream as a reference rather than a dependency. That converts "wait for a fix" into "we fix it on our schedule," which is the correct trade for a single-maintainer package — and it means the team owns security patching, so budget for it rather than discovering it during an incident.
Sequencing.
Maintenance. The team becomes maintainer-of-last-resort on adoption. Budget a named internal owner and a standing maintenance line covering dependency bumps, security patching, and Filament major upgrades. The fork-and-own posture is not optional overhead — it is the actual cost of the license being MIT. Track upstream as reference, not as a source of fixes.
Support. There is no vendor, SLA, or support channel. Escalation paths are the internal owner and, at best, GitHub issues on a repository with a single maintainer. Stakeholders should hear this plainly: the package accelerates time-to-learning; it does not transfer operational risk.
Scaling. Suitable for pilot and moderate volumes with straightforward credit/debit in one currency. Assumptions that break at scale: single-currency reporting, no double-entry guarantees, no reconciliation tooling against provider settlement reports, and concurrency safety that has not been load-tested. Establish volume and value ceilings for the pilot upfront — a threshold at which the ledger layer is replaced regardless of feature success — so scaling decisions are pre-committed rather than negotiated under pressure.
Failure modes. The dangerous ones are silent and financial: lost updates from concurrent debits, duplicate credits from replayed webhooks, partially committed transfers leaving balances and transactions inconsistent, rounding drift from decimal handling, and negative balances where overdraft was not intended. Operational mitigations are the same regardless of code quality — daily reconciliation against provider reports, an immutable audit trail you control, alerting on balance-sum mismatches between the transaction log and stored balances, and a kill switch that stops wallet mutations without taking the app down. Also watch for abandoned-dependency risk: a Filament major release silently breaking the admin path.
Ramp-up. Expect one to two days for the initial source-and-API spike, then roughly one engineer-week to fork, wrap in an internal interface, and write invariant tests around debit/credit and partial-failure paths. Add per-provider adapter validation time, which varies with provider documentation quality and is the least predictable line item. The ramp-up is modest; the durable cost is ongoing ownership. Frame the pitch to stakeholders as buying an option and a few weeks of learning — feature-flagged, low-value cohort, daily reconciliation, with replacement of the ledger layer sequenced in parallel if the feature proves out.
How can I help you explore Laravel packages today?