---
title: "The Exchange & Currencies facades — Money for Laravel | Roundly"
description: "Exchange converts, looks up rates, manages the stored rates table and fakes itself; Currencies reads and extends the currency registry."
url: https://roundly-consulting.com/open-source/docs/money-for-laravel/facade
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)

# The Exchange & Currencies facades

Money and Currency are plain value objects you create directly (see The Money object). The services around them sit behind two facades: Exchange for conversion and the stored rates table, Currencies for the currency registry.

## Exchange

```php
use RoundlyConsulting\Money\Currency;
use RoundlyConsulting\Money\Facades\Exchange;

Exchange::convert($money, 'USD');                          // Money — default driver, latest rate
Exchange::convert($money, 'USD', now()->subDay());         // historical
Exchange::convertWithRate($money, 'CZK');                  // Conversion: original, converted, rate
Exchange::rate(Currency::of('EUR'), Currency::of('USD'));  // ExchangeRate
Exchange::driver('database')->rate(Currency::of('EUR'), Currency::of('USD'));
Exchange::getDefaultDriver();                              // "ecb"
Exchange::extend('fixer', fn ($app) => new FixerProvider(/* ... */));
Exchange::source('ecb')->fetch($from, $to);                // the raw, un-cached source
```

| Exchange:: | Returns / does |
| --- | --- |
| `rate($from, $to, ?$on)` | An ExchangeRate from the default driver (null = the latest). |
| `convert($money, $to, ?$on, ?$rounding)` | The converted Money, rounded once (money.exchange.rounding). |
| `convertWithRate($money, $to, ?$on, ?$rounding)` | A Conversion: original, converted and the rate used. |
| `driver(?$name) / provider(?$name)` | A cached driver — config, database, ecb, chain or your own. |
| `getDefaultDriver()` | money.exchange.default, else ecb. |
| `source($name)` | The raw, un-cached driver the refresh pipeline fetches from. |
| `extend($driver, Closure $callback)` | Register a host driver — cached automatically. |
| `forgetDrivers()` | Drop the resolved (cached) driver instances. |
| `rates()` | The RateStore that manages the stored rates table. |
| `fake($rates = [], ?$pivot)` | Swap in static rates for tests — see Testing. |

## Exchange::rates() — the stored rates table

rates() manages the money\_exchange\_rates table. Every write goes through one path, and a manual row is never overwritten by a refresh:

```php
use RoundlyConsulting\Money\Enums\EcbFeed;
use RoundlyConsulting\Money\Facades\Exchange;

Exchange::rates()->refresh();                                        // RefreshResult: fetch the ECB daily feed now
Exchange::rates()->refresh(source: 'ecb', feed: EcbFeed::Recent, from: $from, to: $to);
Exchange::rates()->refreshLater(feed: EcbFeed::History);             // queue it (unique per source + feed)
Exchange::rates()->manual('EUR', 'CZK', '25.10', $date);             // a manual rate, today if undated
Exchange::rates()->store(...$rates);                                 // ExchangeRate ...$rates
Exchange::rates()->prune(before: now()->subYear(), includeManual: false, pretend: true); // int
```

| Exchange::rates()-> | Does | Returns |
| --- | --- | --- |
| `refresh($source = 'ecb', $feed = EcbFeed::Daily, ?$from, ?$to)` | Fetch a source into the table now; fires ExchangeRatesRefreshed, or ExchangeRatesRefreshFailed and rethrows. | `RefreshResult` |
| `refreshLater(same arguments)` | Queue the same refresh — unique per source and feed for ten minutes. | `void` |
| `store(ExchangeRate ...$rates)` | Upsert rates; a stored manual row is only ever overwritten by another manual rate. | `RefreshResult` |
| `manual($from, $to, $rate, ?$on)` | One manual rate (1 from = rate × to), effective today in money.exchange.timezone unless dated. | `RefreshResult` |
| `prune($before, $includeManual = false, $pretend = false)` | Delete — or with pretend, count — rows effective before a date; manual rows stay unless included. | `int` |

The money:rates:refresh and money:rates:prune commands call the same methods, so everything they do is recorded under Exchange::fake() too.

## Currencies

Currencies proxies the currency registry singleton — lookups, registration and listings (details under Currencies):

| Currencies:: | Returns / does |
| --- | --- |
| `get($code)` | The Currency — throws UnknownCurrency for an unknown code. |
| `find($code)` | The Currency, or null. |
| `has($code)` | Whether the code is registered. |
| `findByNumericCode($numeric)` | Look up by ISO numeric code (978 or "978"), or null. |
| `register(Currency $currency, bool $replace = false)` | Add a custom currency — in a service provider’s boot(). |
| `all() / iso() / custom()` | list<Currency> — everything, the bundled ISO list, or your own. |

Currencies has no fake: the registry is in-memory and deterministic. Exchange::fake() is covered under Testing.

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