PHPackages                             webrek/laravel-money - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Database &amp; ORM](/categories/database)
4. /
5. webrek/laravel-money

ActiveLibrary[Database &amp; ORM](/categories/database)

webrek/laravel-money
====================

An immutable money value object for Laravel with safe arithmetic, allocation and Eloquent casting.

v1.3.0(1mo ago)00MITPHPPHP ^8.2CI passing

Since Jun 8Pushed 2w agoCompare

[ Source](https://github.com/webrek/laravel-money)[ Packagist](https://packagist.org/packages/webrek/laravel-money)[ Docs](https://github.com/webrek/laravel-money)[ RSS](/packages/webrek-laravel-money/feed)WikiDiscussions main Synced 1w ago

READMEChangelog (3)Dependencies (17)Versions (5)Used By (0)

Laravel Money
=============

[](#laravel-money)

[![Latest Version on Packagist](https://camo.githubusercontent.com/f764ee45c88ad3d54fa392dd6143894dc14a5b16109b49ab6293f16991b6551d/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f77656272656b2f6c61726176656c2d6d6f6e65792e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/webrek/laravel-money)[![Total Downloads](https://camo.githubusercontent.com/d110be6c9e05ddfeb93b7b1c615445b7ffd9fce59ce09934c5ccecbb3beb8e0a/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f77656272656b2f6c61726176656c2d6d6f6e65792e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/webrek/laravel-money)[![Tests](https://camo.githubusercontent.com/55de89591e4ec4935cecce2a12073f54789a3254a3daf737683fdc047cb52d95/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f77656272656b2f6c61726176656c2d6d6f6e65792f74657374732e796d6c3f6272616e63683d6d61696e266c6162656c3d7465737473267374796c653d666c61742d737175617265)](https://github.com/webrek/laravel-money/actions/workflows/tests.yml)[![PHP Version](https://camo.githubusercontent.com/614c8325c627c863e7127cda7159c88b9361484b2e1cd7b4f4a176f14d93d38b/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f7068702d762f77656272656b2f6c61726176656c2d6d6f6e65792e7376673f7374796c653d666c61742d737175617265)](https://php.net)[![License](https://camo.githubusercontent.com/5ce03d99eb970157c6555354e8b9f2b596c1f08bd4937ce0c86450ca1d5cf947/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f6c2f77656272656b2f6c61726176656c2d6d6f6e65792e7376673f7374796c653d666c61742d737175617265)](LICENSE)

An immutable value object for representing money in Laravel. Amounts are stored as an integer number of minor units (cents), arithmetic is exact, and rounding only happens where you explicitly ask for it.

Quick start
-----------

[](#quick-start)

```
composer require webrek/laravel-money
```

```
use Webrek\Money\Money;

$price = Money::of('19.99', 'USD');     // from major units
$tax   = $price->times('0.16');          // 16% — rounded HALF_UP to 3.20

$total = $price->plus($tax);             // USD 23.19
$total->format();                        // "USD 23.19"
$total->minorAmount;                     // 2319
```

Why not just use a decimal column and floats?
---------------------------------------------

[](#why-not-just-use-a-decimal-column-and-floats)

Because money and binary floats don't get along. `0.1 + 0.2` is not `0.3`, and a cent that drifts inside a loop turns into a reconciliation ticket. The robust approach — used by every serious payment system — is to store money as an integer count of the smallest unit (cents, fils, yen) and never let a float anywhere near it.

This package gives you exactly that as a first-class type:

- **Exact with integers.** Every amount is an `int` of minor units plus a `Currency`. Addition and subtraction cannot lose precision because there is no precision to lose.
- **Rounding is explicit.** The only operations that can produce a fraction of a cent — multiplication and division — take a `RoundingMode`, defaulting to `HALF_UP`. Nothing is rounded behind your back.
- **No money is created or destroyed when splitting.** `allocate()` and `split()`distribute down to the last minor unit.
- **Currency-aware.** Operations across currencies throw an exception instead of silently producing meaningless results, and each currency knows its own scale (USD has 2 decimals, JPY has 0, BHD has 3).

It has no dependency on `moneyphp/money` — it's a focused, Laravel-native type.

Building money
--------------

[](#building-money)

```
Money::of('10.99', 'USD');     // major units (a string is safest)
Money::of(10.99, 'USD');       // a float is accepted, rounded to the currency scale
Money::ofMinor(1099, 'USD');   // minor units directly
Money::zero('USD');

// The currency scale is respected automatically:
Money::of('1000', 'JPY')->minorAmount;   // 1000  (JPY has no minor unit)
Money::of('1.234', 'BHD')->minorAmount;  // 1234  (BHD has 3)
```

Arithmetic
----------

[](#arithmetic)

```
$a = Money::of('10.00', 'USD');
$b = Money::of('2.50', 'USD');

$a->plus($b);              // 12.50
$a->minus($b);            // 7.50
$a->times(3);             // 30.00
$a->dividedBy(3);         // 3.33  (HALF_UP)
$a->negated();            // -10.00
$a->abs();

$a->plus(Money::of('1', 'EUR'));   // throws CurrencyMismatchException
```

### Rounding modes

[](#rounding-modes)

`times()` and `dividedBy()` accept any [`RoundingMode`](src/RoundingMode.php): `UP`, `DOWN`, `CEILING`, `FLOOR`, `HALF_UP` (default), `HALF_DOWN`, `HALF_EVEN`(banker's rounding).

```
use Webrek\Money\RoundingMode;

Money::ofMinor(1099, 'USD')->times('1.5', RoundingMode::DOWN); // 16.48
Money::ofMinor(1099, 'USD')->times('1.5', RoundingMode::UP);   // 16.49
```

Currency conversion
-------------------

[](#currency-conversion)

Convert with an explicit rate (how many units of the target currency equal one unit of the source currency), with scale and rounding resolved for you:

```
Money::of('10.00', 'USD')->convertTo('EUR', '0.92');   // EUR 9.20
Money::of('10.00', 'USD')->convertTo('JPY', '150');    // JPY 1500  (0 decimals)
Money::of('1000', 'JPY')->convertTo('USD', '0.0067');  // USD 6.70
```

Or resolve the rate from an `ExchangeRateProvider`. The bundled `ArrayExchangeRateProvider` takes a map of currency =&gt; rate relative to a common base and computes the cross rates for you:

```
use Webrek\Money\ArrayExchangeRateProvider;

$rates = new ArrayExchangeRateProvider(['USD' => 1, 'EUR' => 0.92, 'MXN' => 17.5]);

Money::of('100', 'USD')->convert('EUR', $rates);   // EUR 92.00
Money::of('175', 'MXN')->convert('USD', $rates);   // USD 10.00  (cross rate)
```

Set the default provider in `config/money.php` and resolve it from the container:

```
'exchange' => ['rates' => ['USD' => 1, 'EUR' => 0.92, 'MXN' => 17.5]],
```

```
use Webrek\Money\Contracts\ExchangeRateProvider;

$eur = $price->convert('EUR', app(ExchangeRateProvider::class));
```

Plug in live rates by implementing `ExchangeRateProvider` yourself (for example, backed by an API and a cache) and binding it to the contract.

Splitting without losing cents
------------------------------

[](#splitting-without-losing-cents)

```
// Split a bill three ways — the leftover cent is distributed, nothing is lost.
$shares = Money::ofMinor(100, 'USD')->split(3);
// [USD 0.34, USD 0.33, USD 0.33]   (adds back up to exactly 1.00)

// Allocate by ratio (for example, a 70/30 revenue share):
Money::ofMinor(100, 'USD')->allocate(7, 3);
// [USD 0.70, USD 0.30]
```

The remainder is handed to the largest ratios first, so allocations are stable and fair, and `array_sum` of the parts always equals the original.

Aggregates and percentages
--------------------------

[](#aggregates-and-percentages)

```
Money::sum([$a, $b, $c]);   // total (all the same currency)
Money::min([$a, $b, $c]);
Money::max([$a, $b, $c]);

$price->percentage(16);      // 16% — for example, tax
$price->percentage('8.25');  // fractional rates are welcome
```

Sum a collection directly, optionally by key:

```
$orders->sumMoney('total');      // Money|null
collect([$a, $b])->sumMoney();   // Money|null
```

`sumMoney()` returns `null` for an empty collection; `Money::sum()` throws an exception on an empty set (there is no currency to return).

Comparison
----------

[](#comparison)

```
$a->isEqualTo($b);
$a->isGreaterThan($b);
$a->isGreaterThanOrEqualTo($b);
$a->isLessThan($b);
$a->isLessThanOrEqualTo($b);
$a->compareTo($b);        // -1 | 0 | 1
$a->isZero();
$a->isPositive();
$a->isNegative();
```

Casting with Eloquent
---------------------

[](#casting-with-eloquent)

Store the minor units in an integer column and cast it to `Money`.

**Single currency** — the column holds the minor units; the currency is fixed in the cast:

```
use Webrek\Money\Casts\MoneyCast;

protected function casts(): array
{
    return ['price' => MoneyCast::class . ':USD'];
}
```

```
$product->price = Money::of('19.99', 'USD');
$product->save();                 // stores 1999 in `price`
$product->price->format();        // "USD 19.99"
```

**Multi-currency** — add a companion string column `{column}_currency` and omit the code:

```
protected function casts(): array
{
    return ['cost' => MoneyCast::class];   // reads/writes `cost` and `cost_currency`
}
```

```
$product->cost = Money::of('15.50', 'EUR');   // stores 1550 + "EUR"
```

Assigning a currency that doesn't match a fixed-currency column throws a `CurrencyMismatchException`.

Validation
----------

[](#validation)

```
use Webrek\Money\Rules\CurrencyCode;

$request->validate([
    'currency' => ['required', new CurrencyCode],
]);
```

Formatting and serialization
----------------------------

[](#formatting-and-serialization)

```
$money = Money::ofMinor(123456, 'USD');

$money->format();             // "USD 1,234.56"   (locale-independent)
$money->formatTo('en_US');    // "$1,234.56"      (requires ext-intl)
$money->toDecimal();          // "1234.56"
(string) $money;              // "1234.56 USD"

json_encode($money);
// {"amount":"1234.56","minorAmount":123456,"currency":"USD"}
```

`formatTo()` uses the intl extension; without it, it falls back to `format()`.

Requirements
------------

[](#requirements)

ComponentVersionPHP8.2+Laravel12.x / 13.xext-intloptional, for `formatTo()`ext-bcmathoptional, for exact large-scale multiplication/divisionTesting
-------

[](#testing)

```
composer install
composer test
```

Contributing
------------

[](#contributing)

See [CONTRIBUTING.md](CONTRIBUTING.md).

Security
--------

[](#security)

Please review the [security policy](SECURITY.md) before reporting a vulnerability.

License
-------

[](#license)

The MIT License (MIT). See [LICENSE](LICENSE).

###  Health Score

39

—

LowBetter than 84% of packages

Maintenance94

Actively maintained with recent releases

Popularity0

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity49

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~3 days

Total

4

Last Release

38d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/7d8deca81629993819087597b5ad7695976b02e3d014f038e26e985f35f569de?d=identicon)[webrek](/maintainers/webrek)

---

Top Contributors

[![webrek](https://avatars.githubusercontent.com/u/5001338?v=4)](https://github.com/webrek "webrek (8 commits)")

---

Tags

currencyeloquentfinancelaravellaravel-packagemoneyphpvalue-objectlaravelmoneycurrencyValue Objecteloquentlaravel-packagedecimalfinancecastallocation

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StyleLaravel Pint

Type Coverage Yes

### Embed Badge

![Health badge](/badges/webrek-laravel-money/health.svg)

```
[![Health](https://phpackages.com/badges/webrek-laravel-money/health.svg)](https://phpackages.com/packages/webrek-laravel-money)
```

###  Alternatives

[illuminate/database

The Illuminate Database package.

2.8k54.9M12.2k](/packages/illuminate-database)[psalm/plugin-laravel

Psalm plugin for Laravel

3345.3M347](/packages/psalm-plugin-laravel)[laravel/ai

The official AI SDK for Laravel.

1.0k3.2M246](/packages/laravel-ai)[moonshine/moonshine

Laravel administration panel

1.3k253.1k86](/packages/moonshine-moonshine)[api-platform/laravel

API Platform support for Laravel

58174.6k17](/packages/api-platform-laravel)[ntanduy/cloudflare-d1-database

Cloudflare D1 database driver for Laravel — full Eloquent &amp; Query Builder support.

267.8k](/packages/ntanduy-cloudflare-d1-database)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
