# Laravel Money Laravel Money (`akaunting/laravel-money`) is a currency formatting and conversion package for Laravel that provides tools for handling monetary values in an easy, yet powerful way. It avoids the `intl` PHP extension (unlike `moneyphp`) to ensure consistent formatting results across different server environments. The package is built around two core classes — `Money` and `Currency` — and integrates tightly into the Laravel ecosystem via a service provider, Blade directives, Blade components, Eloquent casts, and validation rules. The package stores monetary amounts as integers or floats in the smallest subunit (e.g., cents for USD) and provides a rich API for arithmetic operations (add, subtract, multiply, divide, allocate), comparisons (equals, greaterThan, lessThan), and multiple formatting strategies (standard, simple, without zeroes, locale-aware). Both `Money` and `Currency` implement Laravel's `Macroable` trait, allowing the injection of custom methods at runtime. Eloquent models can cast columns directly to `Money` or `Currency` objects with zero boilerplate. --- ## Installation and Configuration Install the package via Composer and publish the config file to customize supported currencies and defaults. ```bash composer require akaunting/laravel-money php artisan vendor:publish --tag=money ``` ```php // config/money.php return [ 'defaults' => [ 'currency' => env('MONEY_DEFAULTS_CURRENCY', 'USD'), // fallback currency 'convert' => env('MONEY_DEFAULTS_CONVERT', false), // auto-convert from major to subunit ], 'currencies' => [ 'USD' => [ 'name' => 'US Dollar', 'code' => 840, 'precision' => 2, 'subunit' => 100, 'symbol' => '$', 'symbol_first' => true, 'decimal_mark' => '.', 'thousands_separator' => ',', ], // add or override currencies here... ], ]; ``` --- ## `new Money($amount, Currency $currency, bool $convert = false)` Constructs a `Money` instance. The `$amount` can be an integer, float, string (including currency-formatted strings), or a callable. When `$convert` is `true` the amount is treated as a major unit value (e.g., dollars) and is multiplied by the currency's subunit to produce the stored internal value (e.g., cents). ```php use Akaunting\Money\Currency; use Akaunting\Money\Money; // 500 cents (stored as-is, no conversion) $price = new Money(500, new Currency('USD')); echo $price; // "$5.00" // 5.00 dollars → converted to 500 cents internally $price = new Money(5.00, new Currency('USD'), true); echo $price; // "$5.00" // Parsed from a formatted string $price = new Money('$1,234.56', new Currency('USD')); echo $price->getAmount(); // 123456.0 (stored in cents, float) // Amount from a callable $price = new Money(fn() => 999, new Currency('EUR')); echo $price; // "€9.99" ``` --- ## `Money::USD($amount, bool $convert = false)` — Static Currency Factory Methods Any ISO 4217 currency code can be called as a static method on `Money` to create an instance without manually instantiating `Currency`. Over 150 currencies are supported (AED, AFN, ALL, … ZWL). ```php use Akaunting\Money\Money; echo Money::USD(500); // "$5.00" (500 cents) echo Money::USD(5.00, true); // "$5.00" (5.00 dollars → converted to 500 cents) echo Money::EUR(1000); // "€10.00" echo Money::GBP(750); // "£7.50" echo Money::JPY(500); // "¥500" (JPY has precision=0, subunit=1) echo Money::BHD(1000); // "BD1.000" (BHD has precision=3) ``` --- ## `money(mixed $amount, ?string $currency = null, ?bool $convert = null)` — Helper Function Global helper that creates a `Money` object. Falls back to `money.defaults.currency` and `money.defaults.convert` from config when parameters are omitted. ```php // Uses default currency (USD) and default convert (false) from config echo money(500); // "$5.00" // Explicit currency echo money(1000, 'EUR'); // "€10.00" // With conversion from major unit echo money(19.99, 'USD', true); // "$19.99" // In a controller public function show(Order $order) { return view('orders.show', [ 'total' => money($order->amount_cents, $order->currency), ]); } ``` --- ## `currency(?string $currency = null)` — Helper Function Global helper that creates a `Currency` object. Falls back to `money.defaults.currency` when `$currency` is `null`. ```php $usd = currency('USD'); echo $usd; // "USD (US Dollar)" echo $usd->getSymbol(); // "$" echo $usd->getPrecision(); // 2 echo $usd->getSubunit(); // 100 echo $usd->getDecimalMark(); // "." echo $usd->getThousandsSeparator(); // "," echo $usd->getPrefix(); // "$" echo $usd->getSuffix(); // "" $eur = currency('EUR'); echo $eur->getPrefix(); // "€" echo $eur->getSuffix(); // "" // Currency with suffix symbol (e.g., SEK) $sek = currency('SEK'); echo $sek->getPrefix(); // "" echo $sek->getSuffix(); // " kr" ``` --- ## `Currency::USD()` / `new Currency('USD')` — Currency Class The `Currency` class encapsulates all metadata for an ISO 4217 currency. It supports the same static call convention as `Money` for convenient instantiation. ```php use Akaunting\Money\Currency; $usd = Currency::USD(); // same as new Currency('USD') $eur = new Currency('EUR'); echo $usd->getCurrency(); // "USD" echo $usd->getName(); // "US Dollar" echo $usd->getCode(); // 840 echo $usd->getRate(); // 1.0 (default, override in config) echo $usd->isSymbolFirst(); // true echo $usd->render(); // "USD (US Dollar)" echo (string) $usd; // "USD (US Dollar)" // Equality check $usd->equals(Currency::USD()); // true $usd->equals(Currency::EUR()); // false // Serialisation $usd->toArray(); // ['USD' => ['name' => 'US Dollar', 'code' => 840, 'rate' => 1, 'precision' => 2, // 'subunit' => 100, 'symbol' => '$', 'symbol_first' => true, // 'decimal_mark' => '.', 'thousands_separator' => ',', // 'prefix' => '$', 'suffix' => '']] echo $usd->toJson(); // JSON of the above array ``` --- ## Arithmetic Operations — `add`, `subtract`, `multiply`, `divide` `Money` is **immutable by default**. Each arithmetic method returns a new `Money` instance. Call `->mutable()` to switch to in-place mutation. All methods accept an optional rounding mode constant (`ROUND_HALF_UP`, `ROUND_HALF_DOWN`, `ROUND_HALF_EVEN`, `ROUND_HALF_ODD`). ```php use Akaunting\Money\Money; $price = Money::USD(1000); // $10.00 $tax = Money::USD(80); // $0.80 $discount = Money::USD(200); // $2.00 // Immutable (default): each call returns a new instance $total = $price->add($tax)->subtract($discount); echo $total; // "$8.80" echo $price; // "$10.00" — unchanged // Mutable: mutates in place $mutablePrice = Money::USD(1000)->mutable(); $mutablePrice->add($tax); echo $mutablePrice; // "$10.80" // Multiply and divide echo Money::USD(1000)->multiply(1.15); // "$11.50" (tax inclusive) echo Money::USD(1000)->divide(4); // "$2.50" echo Money::USD(1000)->divide(3, Money::ROUND_HALF_DOWN); // custom rounding // Add/subtract raw numbers directly echo Money::USD(1000)->add(50); // "$10.50" echo Money::USD(1000)->subtract(25); // "$9.75" ``` --- ## `allocate(array $ratios): array` Distributes a monetary amount across multiple parties according to given ratios, ensuring that every cent is allocated without loss due to rounding (remainder cents are distributed one at a time starting from the first ratio). ```php use Akaunting\Money\Money; $total = Money::USD(1000); // $10.00 // Split equally three ways [$a, $b, $c] = $total->allocate([1, 1, 1]); echo $a; // "$3.34" (gets the extra cent) echo $b; // "$3.33" echo $c; // "$3.33" // Split by percentage: 70%, 20%, 10% [$majority, $minority, $small] = Money::USD(999)->allocate([70, 20, 10]); echo $majority; // "$7.00" echo $minority; // "$2.00" echo $small; // "$0.99" // Commission split: 60/40 [$merchant, $platform] = Money::USD(5000)->allocate([60, 40]); echo $merchant; // "$30.00" echo $platform; // "$20.00" ``` --- ## `convert(Currency $currency, int|float $ratio, int $roundingMode = ROUND_HALF_UP): Money` Converts a `Money` instance to a different currency by applying an exchange rate ratio. ```php use Akaunting\Money\Currency; use Akaunting\Money\Money; $usd = Money::USD(10000); // $100.00 // Convert USD → EUR at rate 0.92 $eur = $usd->convert(Currency::EUR(), 0.92); echo $eur; // "€92.00" // Convert USD → GBP at rate 0.79 $gbp = $usd->convert(Currency::GBP(), 0.79); echo $gbp; // "£79.00" // Convert with custom rounding $jpy = Money::USD(10000)->convert(Currency::JPY(), 149.50, Money::ROUND_HALF_DOWN); echo $jpy; // "¥149500" ``` --- ## Comparison Methods — `compare`, `equals`, `greaterThan`, `lessThan`, etc. All comparison methods require the two `Money` instances to share the same currency, otherwise an `InvalidArgumentException` is thrown. ```php use Akaunting\Money\Money; $price = Money::USD(1000); $discount = Money::USD(500); $same = Money::USD(1000); $price->compare($discount); // 1 (greater) $discount->compare($price); // -1 (lesser) $price->compare($same); // 0 (equal) $price->equals($same); // true $price->equals($discount); // false $price->greaterThan($discount); // true $price->greaterThanOrEqual($same); // true $price->lessThan($discount); // false $price->lessThanOrEqual($same); // true // State checks Money::USD(0)->isZero(); // true Money::USD(100)->isPositive(); // true Money::USD(-100)->isNegative(); // true // Cross-currency throws InvalidArgumentException try { Money::USD(100)->compare(Money::EUR(100)); } catch (\InvalidArgumentException $e) { echo $e->getMessage(); // 'Different currencies "USD (US Dollar)" and "EUR (Euro)"' } ``` --- ## Formatting Methods — `format`, `formatSimple`, `formatWithoutZeroes`, `formatForHumans`, `formatLocale` Multiple formatting strategies are available. `format()` and `formatSimple()` do not require the `intl` PHP extension. `formatForHumans()` and `formatLocale()` require `ext-intl`. ```php use Akaunting\Money\Money; $money = Money::USD(123456); // 123456 cents // Standard format with symbol echo $money->format(); // "$1,234.56" // Plain number only, no symbol echo $money->formatSimple(); // "1,234.56" // Omit decimal places when amount is a whole number echo Money::USD(100000)->formatWithoutZeroes(); // "$1,000" echo Money::USD(100050)->formatWithoutZeroes(); // "$1,000.50" (has cents, keeps decimals) // Human-friendly via ext-intl NumberFormatter (requires ext-intl) echo $money->formatForHumans(); // "$1,234.56" echo $money->formatForHumans('de_DE'); // "$1.234,56" // Custom NumberFormatter callback echo $money->formatForHumans(null, function (\NumberFormatter $fmt) { $fmt->setAttribute(\NumberFormatter::MIN_FRACTION_DIGITS, 4); }); // "$1,234.5600" // Locale-aware currency format via ext-intl echo $money->formatLocale('en_US'); // "$1,234.56" echo $money->formatLocale('de_DE'); // "1.234,56 $" // Get underlying numeric value (major unit float) echo $money->getValue(); // 1234.56 // Get raw internal amount (subunit integer/float) echo $money->getAmount(); // 123456 echo $money->getRoundedAmount(); // 123456 ``` --- ## Serialization — `toArray`, `toJson`, `jsonSerialize` `Money` and `Currency` both implement `Arrayable`, `Jsonable`, and `JsonSerializable`, making them transparent in API responses and JSON serialization contexts. ```php use Akaunting\Money\Money; use Akaunting\Money\Currency; $money = Money::USD(4999); print_r($money->toArray()); // ['amount' => 4999, 'value' => 49.99, 'currency' => Currency object] echo $money->toJson(); // {"amount":4999,"value":49.99,"currency":{"USD":{"name":"US Dollar","code":840,...}}} // Works naturally in Laravel API resources / response()->json() return response()->json(['price' => $money]); // auto-serialized // Currency serialization $usd = new Currency('USD'); echo $usd->toJson(); // {"USD":{"name":"US Dollar","code":840,"rate":1,"precision":2,"subunit":100, // "symbol":"$","symbol_first":true,"decimal_mark":".","thousands_separator":",", // "prefix":"$","suffix":""}} ``` --- ## Eloquent Casts — `MoneyCast` and `CurrencyCast` Cast Eloquent model attributes directly to `Money` or `Currency` objects. `MoneyCast` stores as a JSON string `{"amount":…,"currency":"…"}`. `CurrencyCast` stores as the ISO 4217 string (e.g., `"USD"`). ```php use Akaunting\Money\Money; use Akaunting\Money\Currency; use Illuminate\Database\Eloquent\Model; class Product extends Model { protected $casts = [ 'price' => Money::class, // uses MoneyCast 'currency' => Currency::class, // uses CurrencyCast ]; } // Writing $product = new Product(); $product->price = Money::USD(2999); // stored as '{"amount":2999,"currency":"USD"}' $product->currency = new Currency('EUR'); // stored as 'EUR' $product->save(); // Reading $product = Product::find(1); echo $product->price; // "$29.99" echo $product->price->getValue(); // 29.99 echo $product->currency; // "EUR (Euro)" // Using in calculations $discounted = $product->price->multiply(0.9); echo $discounted; // "$26.99" ``` --- ## Blade Directives — `@money` and `@currency` Blade directives render money and currency inline in templates, calling the global `money()` and `currency()` helpers. ```blade {{-- Basic usage --}} @money(500) {{-- $5.00 (uses default currency from config) --}} @money(500, 'USD') {{-- $5.00 --}} @money(500, 'EUR') {{-- €5.00 --}} @money(500, 'USD', true) {{-- $500.00 (convert from major unit) --}} {{-- Currency display --}} @currency('USD') {{-- USD (US Dollar) --}} @currency('GBP') {{-- GBP (Pound Sterling) --}} {{-- With a variable --}} @money($product->amount_cents, $product->currency_code) ``` --- ## Blade Components — `` and `` Blade components provide a declarative template syntax equivalent to the directives. ```blade {{-- Amount in cents (no conversion) --}} {{-- With explicit currency --}} {{-- With major-unit conversion --}} {{-- From a model attribute --}} {{-- Currency component --}} ``` --- ## Validation Rule — `CurrencyRule` and `currency_code` Validate that an input value is a valid ISO 4217 currency code defined in the config. Available both as a rule object and as a string rule registered by the service provider. ```php use Akaunting\Money\Rules\CurrencyRule; use Illuminate\Support\Facades\Validator; // As a rule object $validator = Validator::make( ['currency' => 'USD'], ['currency' => ['required', 'string', new CurrencyRule()]] ); $validator->passes(); // true $validator = Validator::make( ['currency' => 'FAKE'], ['currency' => new CurrencyRule()] ); $validator->fails(); // true $validator->errors()->first('currency'); // "The currency is invalid." // As a string rule (registered by the service provider) $request->validate([ 'currency' => 'required|string|currency_code', ]); // In a form request class UpdateSettingsRequest extends FormRequest { public function rules(): array { return [ 'default_currency' => ['required', 'string', new CurrencyRule()], 'amount' => ['required', 'numeric', 'min:0'], ]; } } ``` --- ## Macros and Mixins Both `Money` and `Currency` use Laravel's `Macroable` trait. Macros add custom methods at runtime; mixins merge entire classes of methods. ```php use Akaunting\Money\Currency; use Akaunting\Money\Money; // --- Macro: instance method --- Money::macro('absolute', fn () => $this->isPositive() ? $this : $this->multiply(-1)); $money = Money::USD(1000)->multiply(-1); // -$10.00 echo $money->absolute(); // "$10.00" // --- Macro: static method --- Money::macro('zero', fn (?string $currency = null) => new Money(0, new Currency($currency ?? 'USD'))); echo Money::zero(); // "$0.00" echo Money::zero('EUR'); // "€0.00" // --- Mixin: merge a whole class --- class MoneyHelpers { public function vatAmount(): \Closure { return fn (float $rate = 0.20) => $this->multiply($rate); } public function withVat(): \Closure { return fn (float $rate = 0.20) => $this->multiply(1 + $rate); } } Money::mixin(new MoneyHelpers()); $price = Money::USD(1000); // $10.00 echo $price->vatAmount(); // "$2.00" echo $price->withVat(); // "$12.00" // --- Currency macro --- Currency::macro('isFiat', fn () => !in_array($this->getCurrency(), ['XAU', 'XAG'])); echo Currency::USD()->isFiat() ? 'fiat' : 'commodity'; // "fiat" echo Currency::XAU()->isFiat() ? 'fiat' : 'commodity'; // "commodity" ``` --- ## Mutability — `immutable()` and `mutable()` By default `Money` is immutable: arithmetic operations return new instances. Calling `->mutable()` switches to in-place mutation for performance-sensitive code. ```php use Akaunting\Money\Money; // Immutable (default) $base = Money::USD(1000); $result = $base->add(500); echo $base; // "$10.00" — unchanged echo $result; // "$15.00" — new instance // Mutable $running = Money::USD(0)->mutable(); foreach ([100, 200, 150, 50] as $cents) { $running->add($cents); // mutates in place } echo $running; // "$5.00" (0 + 100 + 200 + 150 + 50 = 500 cents) // Switch back to immutable $snapshot = $running->immutable(); $running->add(9999); // $running changes echo $snapshot; // "$5.00" — unaffected // Check mode $running->isMutable(); // true $snapshot->isImmutable(); // true ``` --- ## Locale Support — `setLocale` / `getLocale` The service provider automatically sets the locale from Laravel's translator. You can override it statically for locale-sensitive formatting methods. ```php use Akaunting\Money\Money; Money::setLocale('de_DE'); echo Money::getLocale(); // "de_DE" $money = Money::EUR(123456); // formatForHumans respects the static locale when no locale argument is passed echo $money->formatForHumans(); // "€1.234,56" (de_DE separators) echo $money->formatForHumans('en_US'); // "$1,234.56" (explicit override) // Reset to default Money::setLocale('en_GB'); ``` --- ## `Currency::setCurrencies` / `Currency::getCurrencies` Override the full currency list at runtime (useful in tests or multi-tenant apps where each tenant has custom exchange rates). ```php use Akaunting\Money\Currency; // Fetch the active currency list $currencies = Currency::getCurrencies(); echo count($currencies); // 150+ // Override with a custom subset (e.g., for a closed-loop system) Currency::setCurrencies([ 'USD' => [ 'name' => 'US Dollar', 'code' => 840, 'precision' => 2, 'subunit' => 100, 'symbol' => '$', 'symbol_first' => true, 'decimal_mark' => '.', 'thousands_separator' => ',', 'rate' => 1.0, ], 'EUR' => [ 'name' => 'Euro', 'code' => 978, 'precision' => 2, 'subunit' => 100, 'symbol' => '€', 'symbol_first' => true, 'decimal_mark' => ',', 'thousands_separator' => '.', 'rate' => 0.92, ], ]); $eur = new Currency('EUR'); echo $eur->getRate(); // 0.92 ``` --- ## Summary Laravel Money is ideally suited for e-commerce, invoicing, accounting, and multi-currency SaaS applications built on Laravel. Its primary use cases include displaying prices and totals with correct currency symbols and separators, performing safe arithmetic on monetary values without floating-point precision loss, splitting amounts proportionally (e.g., for marketplace fee distribution or tax allocation), and converting amounts between currencies using configurable exchange rates. The `allocate` method makes it especially useful for financial distribution scenarios where every cent must be accounted for. The package integrates at every layer of a Laravel application: Eloquent casts make monetary database columns first-class PHP objects; Blade directives and components bring clean template syntax; the `CurrencyRule` validator ensures user-submitted currency codes are valid before they reach business logic; and the `Macroable` trait allows teams to extend `Money` and `Currency` with domain-specific helpers (e.g., `->withVat()`, `->toDisplayString()`) without touching the package source. Combined with immutability by default and a locale-aware formatting API, Laravel Money provides a complete, idiomatic solution for all monetary concerns in Laravel projects.