archtechx/money
A Laravel package for safe money handling. Its Eloquent cast, backed by brick/money, stores amounts as integers to avoid floating-point errors, with currency support, formatting helpers, and Blade components for displaying prices cleanly.
archtechx/money is a thin, Laravel-friendly wrapper around brick/money that gives you a Money value object, an Eloquent cast, and locale-aware formatting — without you hand-rolling integer-minor-unit math.
composer require archtechx/money
php artisan vendor:publish --tag=money # config/money.php
Set your app-wide defaults in config/money.php (default_currency, locale/format defaults) and put the currency in .env so staging/prod don't drift:
// config/money.php
'default_currency' => env('MONEY_CURRENCY', 'USD'),
The first use case is almost always a price column. Store money as an integer minor amount (cents) in the database and cast it on the model:
use Archtechx\Money\Casts\AsMoney;
class Product extends Model
{
protected $casts = [
'price' => AsMoney::class.':USD', // column stores 1999, you get $19.99
];
}
$product->price->format(); // "$19.99"
$product->price->getMinorAmount(); // 1999
Release note (v0.5.4): this release only adds Laravel 13 support (PR #30). There are no API changes, deprecations, or new features — it's a composer.json constraint bump. If you're on ^0.5, composer update picks it up; if you're pinned to 0.5.3 exactly, bump the pin. Nothing in your application code needs to change. On Laravel 13, confirm your PHP version matches what the framework requires before upgrading, and re-run your suite once after the jump.
Two ways in, one way out. Use Money::fromMinor() when reading from the database or an API that already speaks cents, and Money::fromDecimal() at the boundary where a human typed 19.99. Never mix the two in the same layer.
use Archtechx\Money\Money;
$price = Money::fromDecimal($request->input('price'), 'USD');
$product->price = $price; // cast writes 1999 to the column
Multi-currency rows. When currency varies per record, keep a currency column next to the amount and pass the column name to the cast so each row resolves its own currency, rather than baking a single currency into the cast string.
Arithmetic belongs in the value object. Don't reach for + on floats. Use the Money API and let brick/money handle rounding, and use allocation/splitting when dividing an amount across line items — this is where naive division produces off-by-one-cent totals.
$total = $lineA->price->plus($lineB->price);
$shares = $total->allocate([1, 1, 1]); // splits without losing cents
API output is a decision, not a default. Pick one and be consistent:
->getMinorAmount() for machine consumers (integers survive JSON and JS safely),->format() for display surfaces only.Don't let a raw Money object reach toArray()/json() and surprise you — wrap it in your API Resource and choose explicitly.
Blade. The package ships a directive, so views stay clean:
@money($product->price)
Testing. Build fixtures with Money::fromMinor() so expectations are exact, and assert on getMinorAmount() rather than formatted strings — formatting is locale-dependent and will make tests flaky across environments.
decimal(8,2) plus a cast invites double-rounding. Integer minor units in a bigInteger column is the whole point of the cast.(int) ($amount * 100) fails on values like 19.99. Always go through fromDecimal().null, not Money — guard before calling ->format() in Blade, or use ?->.USD to EUR raises an exception in brick/money. If you support multiple currencies, validate compatibility before arithmetic rather than around it.default_currency or format settings, run php artisan config:cache (and clear it) — a stale cached config is the usual cause of "it still shows the old symbol."config/money.php and in your own accessors/API resources rather than by subclassing the cast. Keep presentation logic in resources and view components; keep storage as integers.How can I help you explore Laravel packages today?