---
title: "Currency-denominated buckets — Credits for Laravel | Roundly"
description: "Map a bucket to an ISO or custom money currency to read its balance as Money and refuse writes in any other currency."
url: https://roundly-consulting.com/open-source/docs/credits-for-laravel/currency-buckets
language: en
---

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

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

# Currency-denominated buckets

A bucket can be denominated in a money-for-laravel currency. Its integers are then minor units of that currency — cents for EUR, whole points for a custom PTS — and it gains money-typed helpers that refuse any other currency. Map bucket names to currency codes:

```php
// config/credits.php
'currencies' => [
    'store_credit' => 'EUR',
    'points' => 'PTS',   // a custom currency — see Custom currencies
],
```

Then read and write the bucket as Money through its scope:

```php
use RoundlyConsulting\Credits\Facades\Credits;
use RoundlyConsulting\Money\Money;

$store = Credits::for($user)->bucket('store_credit');

$store->addMoney(Money::ofMajor('25.00', 'EUR'), 'Gift card');
$store->deductMoney(Money::ofMinor(1050, 'EUR'), 'Order #42');
$store->modifyMoney(Money::ofMinor(-100, 'EUR'), 'Fee');   // a signed change

$store->money();                  // Money EUR 13.50
$store->currency();               // Currency EUR — null for a plain bucket
$store->format();                 // "13.50" — read at the EUR exponent
$store->formatMoney('en');        // money's locale formatter
$store->balance();                // 1350 — the int API is unchanged

Credits::currency('store_credit');   // Currency EUR — owner-free
```

The trait has the same helpers on the model:

```php
use RoundlyConsulting\Money\Money;

$user->creditsCurrency('store_credit');   // Currency EUR   (null for a plain bucket)

$user->modifyCreditsMoney(Money::ofMajor('25.00', 'EUR'), 'gift card', bucket: 'store_credit');
$user->modifyCreditsMoney(Money::ofMinor(-1050, 'EUR'), 'order #42', bucket: 'store_credit');

$user->creditsBalanceMoney('store_credit');              // Money EUR 14.50
$user->creditsBalanceMoney('store_credit')->minor();     // "1450" — money amounts are strings
$user->creditsBalanceMoney('store_credit')->toDecimal(); // "14.50"
$user->creditsBalance(bucket: 'store_credit');           // 1450 — the int API is unchanged
$user->displayCreditsBalance('store_credit');            // "14.50" (read at the EUR exponent)
$user->formatCreditsBalance('store_credit', 'en');       // money's locale formatter (symbol + amount)
```

## The money-typed trait helpers

| Method | Returns | Throws |
| --- | --- | --- |
| `creditsCurrency(?string $bucket = null)` | ?Currency — null when the bucket is not listed (or mapped to a blank code) | UnknownCurrency (code not registered), InvalidMoneyConfiguration (malformed map) |
| `creditsBalanceMoney(?string $bucket = null, ?CarbonInterface $at = null)` | Money of the bucket currency — Money::ofMinor(creditsBalance($at, $bucket), $currency) | BucketNotDenominatedException |
| `modifyCreditsMoney(Money $amount, ?string $description = null, ?array $meta = null, bool $allowOverdraft = false, ?string $bucket = null)` | The new Credit row | BucketNotDenominatedException, CurrencyMismatch, AmountOverflow, InsufficientCreditsException |
| `formatCreditsBalance(?string $bucket = null, ?string $locale = null)` | Money::format($locale) of the balance | BucketNotDenominatedException |

## How it behaves

- One write path. addMoney(), deductMoney(), modifyMoney() and modifyCreditsMoney() check the currency, convert with Money::minorInt() and write through the integer path — the same overdraft guard, minimum balance, row lock and CreditsModified event. Every refusal happens before anything is written.
- Currency identity is code + exponent (money’s Currency::equals()): a USD amount into an EUR bucket throws CurrencyMismatch.
- displayCreditsBalance() reads a denominated bucket at its currency exponent and renders there unless scale overrides it. displayCredits() takes no bucket, so it always uses credits.scale.
- The default bucket is denominated too when credits.currencies lists its name — the helpers then work without a bucket argument.
- Plain buckets keep working side by side; the int API (creditsBalance(), modifyCredits()) is unchanged for denominated buckets.

```php
use RoundlyConsulting\Money\Exceptions\CurrencyMismatch;

try {
    $user->modifyCreditsMoney(Money::ofMajor('5.00', 'USD'), 'refund', bucket: 'store_credit');
} catch (CurrencyMismatch $e) {
    // a USD amount into an EUR bucket — nothing was written
}

$user->creditsBalanceMoney('promotional'); // throws BucketNotDenominatedException (a plain bucket)
```

## Past balances and display scale

creditsBalanceMoney() and displayCreditsBalance() accept a point in time, and the display scale and rounding overrides work from the currency exponent:

```php
$user->creditsBalanceMoney('store_credit', now()->subMonth());          // Money at a past moment
$user->displayCreditsBalance('store_credit', scale: 0);                  // "15" — rounded at the display scale
$user->displayCreditsBalance('store_credit', scale: 4);                  // "14.5000"
```

## 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/credits-for-laravel.md)
