Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Php Prices Laravel Package

whitecube/php-prices

A PHP library for working with prices and currencies: immutable Price objects, VAT rates, discounts, arithmetic, and locale-aware formatting/parsing via intl. Framework-agnostic, with optional Laravel integration.

Deep Wiki
Context7

Getting Started

whitecube/php-prices is a small, framework-agnostic value-object layer on top of brick/money for the one thing money libraries tend to leave you to build yourself: VAT (and other modifiers) on a price, and the base/VAT/total breakdown that invoices, carts and quotes need.

composer require whitecube/php-prices

It requires PHP 8.x and pulls in brick/money / brick/math. There is no service provider and no config — in a Laravel app you use it as a plain object inside your own value objects, actions and casts. If you want Eloquent casts and Blade helpers on top, pair it with whitecube/laravel-prices, but start here first so you understand the underlying object.

Read the README in this order: the Price constructor and arithmetic, then the VAT section, then modifiers. That is roughly the order you will need them.

First use case — a cart line:

use Whitecube\Price\Price;

$unit = new Price(1999, 'EUR');   // €19.99
$unit->setVat(21);                // 21% VAT

$line = $unit->multipliedBy(3);   // 3 units

(string) $line->exclVat();        // "49.59" — net
(string) $line->getVat();         // "10.41" — VAT portion
(string) $line->inclVat();        // "60.00" — gross

The single most important idea: a Price carries its VAT rate with it, so downstream code never has to ask "is this number net or gross?".

Implementation Patterns

Store and transport cents, never floats. Keep getAmount() (integer minor units) in the database and in API payloads; rebuild the Price when you hydrate. A decimal(10,2) column works too, but cents removes an entire class of rounding argument.

Treat the object as immutable. plus(), minus(), multipliedBy(), dividedBy() and the VAT methods hand you back a new instance — always reassign. This is a feature: a Price can be passed into a pricing rule, a discount calculator and a view composer without anyone mutating it behind your back.

Build a line-item DTO and price it in one place.

final class LineItem
{
    public function __construct(
        public readonly string $sku,
        public readonly int $quantity,
        public readonly Price $unitPrice,
    ) {}

    public function total(): Price
    {
        return $this->unitPrice->multipliedBy($this->quantity);
    }
}

Sum with a reduce, not with floats.

$total = $lines->reduce(
    fn (?Price $carry, LineItem $line) => $carry
        ? $carry->plus($line->total())
        : $line->total(),
    null
);

Because each line already knows its VAT rate, $total->getVat() gives you the correct aggregate tax figure without a second pass — this is exactly the code you would otherwise write by hand and get subtly wrong.

Apply the VAT rate at the boundary, once. Keep catalogue and cost data net, set setVat() when you build the sellable price (usually from a product/tax-rate model), and let everything downstream be rate-aware. Avoid calling setVat() on an already-gross price just to "convert" it — decide which side of VAT your source number sits on first, then construct accordingly.

Discounts: order matters, so make it explicit. Compute the discount from the net price, subtract it, and only then read inclVat()/getVat() so the tax figure follows the discounted amount. If your business gives discounts on the gross, do it the other way round — but write it down in a comment, because this is the line a finance person will ask about.

Money interop. getMoney() returns the underlying Brick\Money\Money and getCurrency() the currency, so you can hand off to exchange-rate or accounting libraries without re-parsing. Conversely you can construct from a Money instance when integrating with a package that already speaks brick/money.

Formatting belongs at the edge. (string) $price gives you the plain decimal amount (what you want for data-* attributes and logs); use the formatting helpers from whitecube/laravel-prices (or your own NumberFormatter wrapper) for what humans see. Keep those two paths separate — do not store a formatted string.

In Laravel: wrap it in a cast (AsPrice) or, more simply, keep Price construction inside your actions and expose int cents through API resources. In Blade, use a @price directive or a Price method rather than number_format() scattered through templates.

Gotchas and Tips

  • Integer vs. decimal amounts are read differently. new Price(1000) and new Price(10.00) are easy to confuse — the library treats an int as minor units and a float/string as a decimal amount. Pick one style in your codebase (I'd say integers everywhere) and never mix them in the same file.
  • Rounding happens somewhere; find out where before shipping. Rounding per line and then summing will not always equal rounding the total. Invoices and VAT returns expect the former in most jurisdictions, so round per line at the point of persistence and make sure your displayed total is the sum of the rounded lines, not a re-rounded aggregate.
  • Don't round mid-calculation. Chain plus/multipliedBy at full precision and round only when persisting or rendering. Premature rounding on unit prices times quantity is the classic source of one-cent drift.
  • VAT semantics are easy to invert. Confirm whether setVat() is being applied to the price as net (the usual, and the assumption in the example above) or whether you are starting from a gross figure. If you start from gross, compute the net once and construct from that, rather than trying to reverse the percentage inline.
  • Currency mismatches throw. Adding EUR to USD (or leaving the currency off one operand) will raise rather than silently coerce. That's good, but it means every price in a calculation chain needs the currency set, including intermediate ones you build from scratch.
  • dividedBy() for unit prices is a rounding trap. Splitting a €10 total across 3 units gives an infinite decimal; the result is only as precise as the underlying scale. Do the division for display, but keep the original total as the source of truth.
  • Serialization drops the VAT rate. If you json_encode a Price or cast it to string and rehydrate, you get the amount and currency back but you must re-apply setVat() yourself. Store the rate alongside the amount if it isn't derivable from the product.
  • Debugging is easiest via getMoney(). When a number looks wrong, dump (string) $price->getMoney() next to (string) $price->exclVat() and (string) $price->getVat() — it immediately shows whether the problem is the amount, the rate, or the gross/net direction.
  • Extension points are deliberately thin. The package is small (no plugin registry, no tax engine) — for tiered or destination-based tax rates, resolve the percentage in your own service and pass the number in. Don't try to make Price into a tax engine.
  • Low release cadence is a feature here. It's a stable, narrow dependency with no framework coupling; the main upgrade risk is brick/money major versions, so pin sensibly and keep a couple of tests covering VAT math, totals and rounding. Those tests are the real safety net for this package.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console