---
title: "Money & prices — Purchases for Laravel | Roundly"
description: "Prices as arbitrary-precision money-for-laravel Money, cast over two columns and converted exactly from each store’s format."
url: https://roundly-consulting.com/open-source/docs/purchases-for-laravel/money
language: en
---

[All packages](https://roundly-consulting.com/open-source.md)

[Purchases for Laravel](https://roundly-consulting.com/open-source/docs/purchases-for-laravel.md)

# Money & prices

The price attribute on Purchase, PurchaseItem, Subscription, SubscriptionItem and PurchaseRefund is a money-for-laravel Money, cast with AsMoney over two columns: price (decimal(38,0) minor units) and price\_currency (ISO code). Amounts are arbitrary-precision strings — no 32- or 64-bit ceiling on PostgreSQL or MySQL — and currencies such as JPY and BHD use their real exponents:

```php
use RoundlyConsulting\Money\Money;

$purchase->price = Money::ofMinor(2599, 'USD');   // writes price + price_currency
$purchase->price->minor();                         // "2599" (string)
$purchase->price->currency()->code;                // "USD"
$purchase->price->toDecimal();                     // "25.99"
$purchase->price->add(Money::ofMinor(100, 'USD')); // 26.99 USD
$purchase->price->format('en_US');                 // "$25.99"

$purchase->price = null;                           // clears the amount, keeps price_currency
```

The cast is strict: assigning a raw integer throws InvalidMoneyValue (a bare number has no currency), and an amount stored with a null price\_currency throws on read. Factories and seeders pass Money::ofMinor(…).

## Store prices

ProviderResult::price() is converted from each store’s wire format without floating-point arithmetic:

| Store | Source | Conversion |
| --- | --- | --- |
| Stripe | amount, amount\_total, amount\_paid, amount\_due or amount\_refunded, plus currency | Minor units via Money::ofMinor(); ISK, UGX and MGA re-scaled per StripeAmount::SCALE\_EXCEPTIONS. |
| Apple | Signed transaction price in milli-units, plus currency | Money::ofScaled() at scale 3, rounded half away from zero, once; a refund records only the refunded share. |
| Google subscriptions | Line items’ recurringPrice (units + nanos) | Summed exactly via GoogleMoney; prepaid plans carry none. |
| Google one-time products | — | The Play Developer API carries no price, so price() is null. |

A price the package cannot convert exactly — a float or fractional Stripe amount, an unknown currency, an out-of-range value — becomes null rather than being truncated, and the rest of the result is still recorded.

## Totals in SQL

Because price is a numeric column, orderBy('price') and sum('price') work per currency. sum() returns a string on PostgreSQL and MySQL — pass it straight back into Money::ofMinor():

```php
use RoundlyConsulting\Money\Money;
use RoundlyConsulting\Purchases\Models\Purchase;

$sum = Purchase::query()
    ->where('price_currency', 'USD')
    ->sum('price');                    // a string on PostgreSQL and MySQL

$total = Money::ofMinor($sum, 'USD');
$total->format('en_US');
```

## Audit snapshots

Audit notifications store the price as Money::toArray(), with minor as a JSON string so amounts beyond int64 survive. purchases:replay rebuilds it with Money::fromArray() and skips a snapshot whose price cannot be rebuilt:

```json
{ "minor": "1999", "decimal": "19.99", "currency": "USD" }
```

## 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](https://roundly-consulting.com/support-us.md)

By donating, you agree to our [donation terms](https://roundly-consulting.com/donation-terms.md).

[Support our open source work (opens in a new tab)](https://donate.stripe.com/dRmeVe8FX5PF1Qd9pXcEw00) [Join us on Patreon (opens in a new tab)](https://www.patreon.com/cw/roundly)

## 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.

[Get a quote in 48 hours](https://roundly-consulting.com/contact.md) [Browse all packages](https://roundly-consulting.com/open-source/docs/purchases-for-laravel.md)
