NewWe open-sourced 50+ Laravel packages
Custom AI apps, agents and automation — Roundly ConsultingRoundly
All packages
Money for Laravel

Validation, requests & collections

Validate money input with rule objects or string rules:

use RoundlyConsulting\Money\Rules\CurrencyCode;
use RoundlyConsulting\Money\Rules\MoneyAmount;

$request->validate([
    'price'    => ['required', MoneyAmount::in('EUR')->positive()->max('99999.99')],
    'fee'      => [MoneyAmount::inCurrencyFrom('currency')->allowNegative()],
    'amount'   => [MoneyAmount::in('EUR')->localized('sk')],      // accepts "1 234,50"
    'currency' => ['required', (new CurrencyCode)->isoOnly()],
    'payout'   => ['required', new CurrencyCode(['EUR', 'CZK'])],
    'legacy'   => 'money_amount:EUR',
    'code'     => 'currency_code:EUR,CZK',
]);
  • MoneyAmount::in($currency) or MoneyAmount::inCurrencyFrom($field) — a fixed currency, or one taken from a sibling input (held to currencies.allowed).
  • min() / max() — ints and decimal strings are major units; a bound finer than the currency rounds inwards.
  • positive() requires more than zero; allowNegative() lifts the default non-negative check.
  • localized(?$locale) — accept localized input such as “1 234,50” through the parser.
  • new CurrencyCode(?$allowed) — a registered code, limited to $allowed or currencies.allowed; ->isoOnly() refuses custom currencies.
  • String rules: currency_code, currency_code:EUR,CZK and money_amount:EUR (the default currency without a parameter).

Messages live under money::validation.* — amount, precision, too_large, negative, positive, min, max and currency — in English and Slovak. Publish money-translations to edit them.

Reading money from a request

$price = $request->money('price', 'EUR');              // JSON 1.1 → 1.10 EUR, not 1.11
$fee   = $request->money('fee', currencyKey: 'currency'); // currency from another input
$tip   = $request->money('tip');                       // money.default_currency; null when blank
  • Missing or blank input returns null.
  • An int is whole major units; a JSON float becomes its shortest round-trip decimal, so 1.1 is 110 cents, not 111.
  • The currency comes from the argument, then the input at currencyKey, then money.default_currency.
  • Send amounts beyond 2⁵³ as JSON strings, and validate with MoneyAmount first.

Collections

$order->items->sumMoney(
    fn ($item) => $item->price->multiply($item->quantity),
    currencyIfEmpty: 'EUR',
);

$prices->minMoney();              // the items are Money
$products->maxMoney('price');     // a key or property path
$products->avgMoney('price');     // rounds once with money.rounding

sumMoney, minMoney, maxMoney and avgMoney work on Collection and LazyCollection (Eloquent collections inherit them). The value is null (the items are Money), a key or property path, or a callback. Empty collections follow Money::sum/min/max/average.

API resources

use RoundlyConsulting\Money\Http\Resources\MoneyResource;

return ['price' => MoneyResource::from($product->price)];
// {"minor":"1050","decimal":"10.50","currency":"USD","exponent":2,"formatted":"$10.50"}

MoneyResource::from() returns null for null. formatted uses the bound formatter in the app locale and is presentation only — clients should compute with minor.

Show your open-source love

This package is free and MIT-licensed. If it saves you time, a one-off donation or a Patreon membership keeps it maintained, tested and documented.

More ways to support, including crypto

By donating, you agree to our donation terms.

Want this built into your product?

We integrate our packages into custom Laravel and AI builds. Tell us what you're working on and we'll reply within 48 hours.