---
title: "Configuration — Money for Laravel | Roundly"
description: "Every config key and its default — currencies, schema precision, rounding, formatting, macros, exchange drivers, caching and the refresh schedule."
url: https://roundly-consulting.com/open-source/docs/money-for-laravel/configuration
language: en
---

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

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

# Configuration

The package works with zero configuration. The published config/money.php in full:

```php
return [
    'default_currency' => env('MONEY_DEFAULT_CURRENCY', 'EUR'),

    'currencies' => [
        'iso' => env('MONEY_ISO_CURRENCIES', true),
        'custom' => [],
        'allowed' => null,
    ],

    'schema' => [
        'currency_length' => env('MONEY_CURRENCY_LENGTH', 3),
        'precision' => env('MONEY_PRECISION', 38),
    ],

    'rounding' => env('MONEY_ROUNDING', 'half_away_from_zero'),

    'formatting' => [
        'driver' => env('MONEY_FORMATTER', 'auto'),
        'locale' => env('MONEY_LOCALE'),
        'display' => 'symbol',
        'fallback' => [
            'pattern' => '{sign}{amount} {code}',
            'decimal_separator' => '.',
            'thousands_separator' => ',',
        ],
    ],

    'macros' => [
        'collection' => true,
        'request' => true,
        'blade' => true,
        'validation' => true,
    ],

    'exchange' => [
        'default' => env('MONEY_EXCHANGE_DRIVER', 'ecb'),
        'chain' => ['database', 'ecb'],
        'pivot' => 'EUR',
        'rounding' => env('MONEY_EXCHANGE_ROUNDING', 'half_even'),
        'timezone' => env('MONEY_EXCHANGE_TIMEZONE', 'Europe/Berlin'),   // "today" for undated lookups
        'max_age_days' => env('MONEY_EXCHANGE_MAX_AGE_DAYS', 7),

        'cache' => [
            'enabled' => env('MONEY_EXCHANGE_CACHE', true),
            'store' => env('MONEY_EXCHANGE_CACHE_STORE'),
            'ttl' => env('MONEY_EXCHANGE_CACHE_TTL', 3600),
            'prefix' => 'money:exchange',
        ],

        'providers' => [
            'config' => [
                'rates' => [],   // ['EUR' => ['USD' => '1.0854']]
            ],
            'database' => [
                'table' => env('MONEY_EXCHANGE_TABLE', 'money_exchange_rates'),
                'model' => env('MONEY_EXCHANGE_MODEL', RoundlyConsulting\Money\Models\CurrencyRate::class),
            ],
            'ecb' => [
                'daily_url' => 'https://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml',
                'recent_url' => 'https://www.ecb.europa.eu/stats/eurofxref/eurofxref-hist-90d.xml',
                'history_url' => 'https://www.ecb.europa.eu/stats/eurofxref/eurofxref-hist.xml',
                'timeout' => env('MONEY_ECB_TIMEOUT', 10),
                'retries' => 2,
                'max_bytes' => 33554432,
                'cache_ttl' => 3600,
            ],
        ],

        'refresh' => [
            'schedule' => env('MONEY_EXCHANGE_SCHEDULE', false),
            'cron' => '30 16 * * 1-5',
            'timezone' => 'Europe/Berlin',
            'source' => 'ecb',
        ],
    ],
];
```

## Every key

| Key | Default | Env | Purpose |
| --- | --- | --- | --- |
| `default_currency` | EUR | MONEY\_DEFAULT\_CURRENCY | Currency for Request::money() without one, the parser without a currency token (or with a shared symbol such as $ that this currency writes), and money\_amount without a parameter. |
| `currencies.iso` | true | MONEY\_ISO\_CURRENCIES | Seed the registry with the bundled ISO 4217 list (165 currencies); false = custom only. |
| `currencies.custom` | \[\] | — | Extra currencies: code => exponent (0..18), name, symbol. |
| `currencies.allowed` | null | — | Allow-list for currencies chosen by input (null = the whole registry). Must be an array of codes. Never restricts stored data. |
| `schema.currency_length` | 3 | MONEY\_CURRENCY\_LENGTH | Varchar length of currency columns (3..10) and the longest custom code. Fixed once migrated. |
| `schema.precision` | 38 | MONEY\_PRECISION | P of the decimal(P, 0) amount columns (19..65); the cast and MoneyAmount enforce it. Fixed once migrated. |
| `rounding` | half\_away\_from\_zero | MONEY\_ROUNDING | Service-level default rounding (formatter digit reduction, avgMoney). Value objects never read config. |
| `formatting.driver` | auto | MONEY\_FORMATTER | auto (intl when loaded, else decimal), intl (fails loud without the extension) or decimal. |
| `formatting.locale` | null | MONEY\_LOCALE | null follows app()->getLocale(). |
| `formatting.display` | symbol | — | Default currency display: symbol, code or none. |
| `formatting.fallback.pattern` | {sign}{amount} {code} | — | Deterministic formatter pattern; placeholders {sign} {amount} {code} {symbol}. |
| `formatting.fallback.decimal_separator` | . | — | Decimal separator — also the parser’s separator without intl. Blank = not set → '.'. |
| `formatting.fallback.thousands_separator` | , | — | Thousands separator of the formatter and parser without intl. Any string. The one setting where blank is a value: ' ' groups with a space and '' means no grouping symbol; only null takes ','. |
| `macros.collection` | true | — | Register sumMoney / minMoney / maxMoney / avgMoney. |
| `macros.request` | true | — | Register Request::money(). |
| `macros.blade` | true | — | Register @money. |
| `macros.validation` | true | — | Register the currency\_code and money\_amount string rules. |
| `exchange.default` | ecb | MONEY\_EXCHANGE\_DRIVER | Default driver: config, database, ecb, chain or your own. |
| `exchange.chain` | \['database', 'ecb'\] | — | Drivers the chain driver tries, in order — a list of driver names, never chain itself. |
| `exchange.pivot` | EUR | — | Triangulation currency for config and database. Not set (null or blank) = no pivot. |
| `exchange.rounding` | half\_even | MONEY\_EXCHANGE\_ROUNDING | Default rounding of conversions. |
| `exchange.timezone` | Europe/Berlin | MONEY\_EXCHANGE\_TIMEZONE | Timezone of “today” for undated lookups and undated manual rates, and of the returned rate dates. A requested date is always its own calendar day (Y-m-d in its own timezone). |
| `exchange.max_age_days` | 7 | MONEY\_EXCHANGE\_MAX\_AGE\_DAYS | A newest rate older than this is stale and refused. At least 0 (0 accepts only the day’s own rate). |
| `exchange.cache.enabled` | true | MONEY\_EXCHANGE\_CACHE | Cache database, ecb and custom driver lookups. |
| `exchange.cache.store` | null | MONEY\_EXCHANGE\_CACHE\_STORE | Cache store (null = default). |
| `exchange.cache.ttl` | 3600 | MONEY\_EXCHANGE\_CACHE\_TTL | Cache lifetime in seconds, at least 1 (switch caching off with cache.enabled). |
| `exchange.cache.prefix` | money:exchange | — | Cache key prefix. |
| `exchange.providers.config.rates` | \[\] | — | Static rates as decimal strings: \['EUR' => \['USD' => '1.0854'\]\]. |
| `exchange.providers.database.table` | money\_exchange\_rates | MONEY\_EXCHANGE\_TABLE | Rates table. |
| `exchange.providers.database.model` | CurrencyRate::class | MONEY\_EXCHANGE\_MODEL | Swappable rate model — CurrencyRate or a subclass; anything else throws InvalidMoneyConfiguration. |
| `exchange.providers.ecb.daily_url / recent_url / history_url` | ecb.europa.eu | — | Daily, 90-day and full-history feeds. |
| `exchange.providers.ecb.timeout` | 10 | MONEY\_ECB\_TIMEOUT | HTTP timeout in seconds, at least 1. |
| `exchange.providers.ecb.retries` | 2 | — | Retries after the first attempt, at least 0. |
| `exchange.providers.ecb.max_bytes` | 33554432 | — | Response size cap (32 MiB), at least 1. |
| `exchange.providers.ecb.cache_ttl` | 3600 | — | Parsed-feed cache in seconds, at least 1. |
| `exchange.refresh.schedule` | false | MONEY\_EXCHANGE\_SCHEDULE | Register the rate refresh schedule. |
| `exchange.refresh.cron` | 30 16 \* \* 1-5 | — | When the schedule runs (weekdays at 16:30). |
| `exchange.refresh.timezone` | Europe/Berlin | — | Timezone of the cron expression. |
| `exchange.refresh.source` | ecb | — | Source the schedule refreshes from. |

## Environment

The common knobs are env-driven, so you rarely need to publish the config at all:

```bash
MONEY_DEFAULT_CURRENCY=EUR
MONEY_ROUNDING=half_away_from_zero
MONEY_FORMATTER=auto
MONEY_EXCHANGE_DRIVER=chain
MONEY_EXCHANGE_SCHEDULE=true
MONEY_EXCHANGE_MAX_AGE_DAYS=7
```

## Macro toggles

Each developer-experience surface — collection macros, Request::money(), @money and the string validation rules — can be switched off under macros. An existing host macro with the same name always wins. The Blueprint macros have no toggle: published migrations depend on them.

## On/off switches

The on/off switches — currencies.iso, macros.\*, exchange.cache.enabled and exchange.refresh.schedule — accept true/false, 1/0, on/off or yes/no, from .env or the published file; anything else throws the package toolkit’s InvalidConfigurationException naming the key, so a typo never silently reads as the default.

## Strict settings

Every other setting is read strictly too, at the point of use. A key that is not set — absent, null or blank ('' or whitespace, such as a MONEY\_ROUNDING= line in .env) — takes its default; a present value of the wrong shape throws InvalidMoneyConfiguration naming the key, and is never cast or swapped for the default:

- Integers take an int or a canonical integer string (“30”, “-5”) within the stated range — “five”, “5.5” or “1e3” throw.
- Names — currency, driver, timezone, table, cache store and prefix, URLs, locale, pattern — must be strings.
- The rounding modes, formatting.driver and formatting.display must name one of their values.
- currencies.allowed, currencies.custom, exchange.chain and exchange.providers.config.rates must be arrays — a string allow-list such as 'EUR' throws rather than allowing every currency.
- exchange.providers.database.model must be CurrencyRate or a subclass of it — anything else throws instead of falling back to the packaged model.

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