|
1 | | -# <img src="https://s3.amazonaws.com/swap.assets/swap_logo.png" height="30px" width="30px"/> Swap |
| 1 | +# Swap |
2 | 2 |
|
3 | 3 | [](https://github.com/florianv/swap/actions/workflows/tests.yml) |
4 | 4 | [](https://github.com/florianv/swap/actions/workflows/psalm.yml) |
5 | 5 | [](https://packagist.org/packages/florianv/swap) |
6 | 6 | [](https://packagist.org/packages/florianv/swap) |
7 | 7 |
|
8 | | -Swap allows you to retrieve currency exchange rates from various services such as **[Fixer](https://fixer.io/)**, **[Currency Data](https://currencylayer.com)** or **[Exchange Rates Data](https://exchangeratesapi.io/)** and optionally cache the results. |
9 | | -It is integrated to other libraries like [moneyphp/money](https://github.com/moneyphp/money) and provides |
10 | | -a [Symfony Bundle](https://github.com/florianv/FlorianvSwapBundle) and a [Laravel Package](https://github.com/florianv/laravel-swap). |
| 8 | +> _The easy-to-use PHP currency conversion library. Retrieve exchange rates from 30 providers, with caching and fallback. Maintained since 2014._ |
11 | 9 |
|
12 | | -## QuickStart |
| 10 | +Swap is a mature PHP **currency conversion library** for retrieving and working with exchange rates. It provides a single, easy-to-use API on top of 30 exchange rate providers, ranging from public sources (the European Central Bank, several national banks, exchangerate.host) to commercial **exchange rate APIs** that require an API key. Caching, historical rates, and a fallback chain are built in. Used in real-world PHP applications since 2014. |
| 11 | + |
| 12 | +## What is Swap? |
| 13 | + |
| 14 | +- Swap is a PHP library for currency conversion and exchange rate retrieval. |
| 15 | +- It supports 30 exchange rate providers behind a common interface. |
| 16 | +- It caches results via PSR-16 SimpleCache. |
| 17 | +- It supports historical rates. |
| 18 | +- It supports a fallback chain. When a provider errors, the next provider in the chain is tried. |
| 19 | + |
| 20 | +## When should you use Swap? |
| 21 | + |
| 22 | +- Use Swap when you need to retrieve exchange rates in a PHP application: currency conversion workflows, multi-currency pricing, invoice totals, reconciliation, or historical FX data. |
| 23 | +- Use the lower-level [Exchanger](https://github.com/florianv/exchanger) library when Swap's defaults are too opinionated and you want finer control over chain composition, caching, or HTTP plumbing. |
| 24 | + |
| 25 | +## Why not call an exchange rate API directly? |
| 26 | + |
| 27 | +You can. Here is what Swap does for you so you don't have to build it again: |
| 28 | + |
| 29 | +- **Provider abstraction.** Swap one provider for another without rewriting application code; all 30 providers expose the same interface. |
| 30 | +- **Fallback chain.** Try a primary provider first, fall back to others on error. Unsupported currency pairs are skipped silently. |
| 31 | +- **Caching.** PSR-16 SimpleCache integration with per-query TTL and per-query disable. |
| 32 | +- **Historical rates.** One method (`historical()`) regardless of the underlying endpoint shape. |
| 33 | +- **Typed result objects.** `getValue()`, `getDate()`, `getCurrencyPair()`, `getProviderName()` are uniform across providers, so no per-provider response parsing in your application code. |
| 34 | +- **HTTP plumbing.** PSR-18 / PSR-17 friendly, auto-discovered via `php-http/discovery`. Any compliant HTTP client (Symfony HTTP Client, Guzzle, etc.) works without custom wiring. |
| 35 | + |
| 36 | +## Installation |
| 37 | + |
| 38 | +Swap requires PHP 8.2 or newer. |
13 | 39 |
|
14 | 40 | ```bash |
15 | | -$ composer require php-http/curl-client nyholm/psr7 php-http/message florianv/swap |
| 41 | +composer require florianv/swap symfony/http-client nyholm/psr7 |
16 | 42 | ``` |
17 | 43 |
|
| 44 | +`symfony/http-client` is the PSR-18 HTTP client and `nyholm/psr7` provides the PSR-17 factories. Any PSR-18 / PSR-17 implementation works (see the [documentation](doc/readme.md) for alternatives such as Guzzle). |
| 45 | + |
| 46 | +## Quickstart |
| 47 | + |
18 | 48 | ```php |
19 | 49 | use Swap\Builder; |
20 | 50 |
|
21 | | -// Build Swap |
| 51 | +// Build Swap with the European Central Bank (free, no API key required). |
22 | 52 | $swap = (new Builder()) |
| 53 | + ->add('european_central_bank') |
| 54 | + ->build(); |
23 | 55 |
|
24 | | - // Use the Fixer service as first level provider |
25 | | - ->add('apilayer_fixer', ['api_key' => 'Get your key here: https://fixer.io/']) |
26 | | - |
27 | | - // Use the currencylayer service as first fallback |
28 | | - ->add('apilayer_currency_data', ['api_key' => 'Get your key here: https://currencylayer.com']) |
29 | | - |
30 | | - // Use the exchangerates service as second fallback |
31 | | - ->add('apilayer_exchange_rates_data', ['api_key' => 'Get your key here: https://exchangeratesapi.io/']) |
32 | | -->build(); |
33 | | - |
34 | | -// Get the latest EUR/USD rate |
| 56 | +// EUR → USD exchange rate |
35 | 57 | $rate = $swap->latest('EUR/USD'); |
36 | 58 |
|
37 | | -// 1.129 |
38 | | -$rate->getValue(); |
| 59 | +$rate->getValue(); // e.g. 1.0823 (a float) |
| 60 | +$rate->getDate()->format('Y-m-d'); // e.g. 2026-04-29 |
| 61 | +$rate->getProviderName(); // 'european_central_bank' |
| 62 | + |
| 63 | +// Convert an amount using the returned rate |
| 64 | +$amountInEUR = 100.00; |
| 65 | +$amountInUSD = $amountInEUR * $rate->getValue(); |
| 66 | + |
| 67 | +// Retrieve a historical rate |
| 68 | +$past = $swap->historical('EUR/USD', new \DateTime('-15 days')); |
| 69 | +``` |
| 70 | + |
| 71 | +Swap retrieves the rate; your application multiplies the amount by `$rate->getValue()` to perform the conversion. |
39 | 72 |
|
40 | | -// 2016-08-26 |
41 | | -$rate->getDate()->format('Y-m-d'); |
| 73 | +## Configuring multiple providers (fallback chain) |
42 | 74 |
|
43 | | -// Get the EUR/USD rate 15 days ago |
44 | | -$rate = $swap->historical('EUR/USD', (new \DateTime())->modify('-15 days')); |
| 75 | +```php |
| 76 | +$swap = (new Builder()) |
| 77 | + ->add('your_primary_provider', ['api_key' => 'YOUR_KEY']) // see Providers below |
| 78 | + ->add('your_fallback_provider', ['api_key' => 'YOUR_KEY']) |
| 79 | + ->add('european_central_bank') // free fallback for EUR-base pairs |
| 80 | + ->build(); |
45 | 81 | ``` |
46 | 82 |
|
| 83 | +Providers are tried in order. If a provider does not support the requested currency pair, it is skipped silently. If a provider throws an error, the next provider is tried. If every provider fails, a `ChainException` is thrown with all collected errors. |
| 84 | + |
| 85 | +## Common use cases |
| 86 | + |
| 87 | +- Display localized prices in multi-currency storefronts. |
| 88 | +- Compute invoice totals across currencies. |
| 89 | +- Reconcile multi-currency ledgers using historical rates. |
| 90 | +- Power internal FX dashboards with rate history. |
| 91 | +- Build currency conversion infrastructure for fintech and ERP applications. |
| 92 | + |
| 93 | +## Which package should I use? |
| 94 | + |
| 95 | +The Swap ecosystem is a layered toolkit for currency conversion in PHP: |
| 96 | + |
| 97 | +- **Swap.** The easy-to-use, high-level API (this package). |
| 98 | +- **Exchanger.** Lower-level, more granular alternative; direct access to the 30 provider implementations and the `ExchangeRateService` interface. |
| 99 | +- **Laravel Swap.** Laravel application of Swap. |
| 100 | +- **Symfony Swap.** Symfony integration of Swap. |
| 101 | + |
| 102 | +All four packages are MIT-licensed and require PHP 8.2 or newer. |
| 103 | + |
| 104 | +## Providers |
| 105 | + |
| 106 | +Swap supports 30 exchange rate providers via [Exchanger](https://github.com/florianv/exchanger). Pass the **identifier** to `Builder::add()`. |
| 107 | + |
| 108 | +### Public providers (no API key required) |
| 109 | + |
| 110 | +| Service | Identifier | Base | Quote | Historical | |
| 111 | +| ------------------------------------------ | ------------------------------------- | -------------- | -------------- | ---------- | |
| 112 | +| Bulgarian National Bank | `bulgarian_national_bank` | * | BGN | Yes | |
| 113 | +| Central Bank of the Czech Republic | `central_bank_of_czech_republic` | * | CZK | Yes | |
| 114 | +| Central Bank of the Republic of Turkey | `central_bank_of_republic_turkey` | * | TRY | Yes | |
| 115 | +| Central Bank of the Republic of Uzbekistan | `central_bank_of_republic_uzbekistan` | * | UZS | Yes | |
| 116 | +| Cryptonator | `cryptonator` | * (crypto) | * (crypto) | No | |
| 117 | +| European Central Bank | `european_central_bank` | EUR | * | Yes | |
| 118 | +| exchangerate.host | `exchangeratehost` | * | * | Yes | |
| 119 | +| National Bank of Georgia | `national_bank_of_georgia` | * | GEL | Yes | |
| 120 | +| National Bank of Romania | `national_bank_of_romania` | (limited list) | (limited list) | Yes | |
| 121 | +| National Bank of the Republic of Belarus | `national_bank_of_republic_belarus` | * | BYN | Yes | |
| 122 | +| National Bank of Ukraine | `national_bank_of_ukraine` | * | UAH | Yes | |
| 123 | +| Russian Central Bank | `russian_central_bank` | * | RUB | Yes | |
| 124 | +| WebserviceX | `webservicex` | * | * | No | |
| 125 | + |
| 126 | +### Commercial providers (require an API key) |
| 127 | + |
| 128 | +| Service | Identifier | Base | Quote | Historical | |
| 129 | +| ------------------------------- | ------------------------------ | -------------------- | ----- | ---------- | |
| 130 | +| AbstractAPI | `abstract_api` | * | * | Yes | |
| 131 | +| coinlayer | `coin_layer` | * (crypto) | * | Yes | |
| 132 | +| Currency Converter API | `currency_converter` | * | * | Yes | |
| 133 | +| Currency Data (APILayer) | `apilayer_currency_data` | USD (free), * (paid) | * | Yes | |
| 134 | +| CurrencyDataFeed | `currency_data_feed` | * | * | No | |
| 135 | +| currencylayer (direct) | `currency_layer` | USD (free), * (paid) | * | Yes | |
| 136 | +| Exchange Rates Data (APILayer) | `apilayer_exchange_rates_data` | USD (free), * (paid) | * | Yes | |
| 137 | +| exchangeratesapi (direct) | `exchange_rates_api` | USD (free), * (paid) | * | Yes | |
| 138 | +| fastFOREX.io | `fastforex` | USD (free), * (paid) | * | No | |
| 139 | +| Fixer (APILayer) | `apilayer_fixer` | EUR (free), * (paid) | * | Yes | |
| 140 | +| Fixer (direct) | `fixer` | EUR (free), * (paid) | * | Yes | |
| 141 | +| 1Forge | `forge` | * | * | No | |
| 142 | +| Open Exchange Rates | `open_exchange_rates` | USD (free), * (paid) | * | Yes | |
| 143 | +| xChangeApi.com | `xchangeapi` | * | * | Yes | |
| 144 | +| Xignite | `xignite` | * | * | Yes | |
| 145 | + |
| 146 | +You can also add your own provider by implementing the `Exchanger\Contract\ExchangeRateService` interface and passing the instance to `Builder::addExchangeRateService()`. |
| 147 | + |
| 148 | +## Caching, HTTP client, and error handling |
| 149 | + |
| 150 | +- **Caching.** Swap uses PSR-16 `SimpleCache`. Configure once on the builder: |
| 151 | + |
| 152 | + ```php |
| 153 | + $swap = (new Builder())->useSimpleCache($psr16Cache)->add('european_central_bank')->build(); |
| 154 | + ``` |
| 155 | + |
| 156 | + Disable caching for a single query: `$swap->latest('EUR/USD', ['cache' => false])`. |
| 157 | + Override the TTL for a single query: `$swap->latest('EUR/USD', ['cache_ttl' => 3600])`. |
| 158 | + |
| 159 | +- **HTTP client.** Any PSR-18 client (`symfony/http-client`, `php-http/guzzle7-adapter`, etc.) is supported and auto-discovered via `php-http/discovery`. To pass an explicit instance, use `Builder::useHttpClient()`. |
| 160 | + |
| 161 | +- **Errors.** When every configured provider has either skipped (unsupported pair) or thrown, Swap raises an `Exchanger\Exception\ChainException` containing all collected exceptions. |
| 162 | + |
47 | 163 | ## Documentation |
48 | 164 |
|
49 | | -The documentation for the current branch can be found [here](https://github.com/florianv/swap/blob/master/doc/readme.md). |
50 | | - |
51 | | -## Services |
52 | | - |
53 | | -Here is the list of the currently implemented services: |
54 | | - |
55 | | -| Service | Base Currency | Quote Currency | Historical | |
56 | | -|---------------------------------------------------------------------------|----------------------|----------------|----------------| |
57 | | -| [Fixer](https://fixer.io/) | EUR (free, no SSL), * (paid) | * | Yes | |
58 | | -| [Currency Data](https://currencylayer.com) | USD (free), * (paid) | * | Yes | |
59 | | -| [Exchange Rates Data](https://exchangeratesapi.io/) | USD (free), * (paid) | * | Yes | |
60 | | -| [Abstract](https://www.abstractapi.com) | * | * | Yes | |
61 | | -| [coinlayer](https://coinlayer.com) | * Crypto (Limited standard currencies) | * Crypto (Limited standard currencies) | Yes | |
62 | | -| [Fixer](https://fixer.io) | EUR (free, no SSL), * (paid) | * | Yes | |
63 | | -| [Currency Data](https://currencylayer.com) | USD (free), * (paid) | * | Yes | |
64 | | -| [exchangeratesapi](https://exchangeratesapi.io) | USD (free), * (paid) | * | Yes | |
65 | | -| [European Central Bank](https://www.ecb.europa.eu/home/html/index.en.html) | EUR | * | Yes | |
66 | | -| [National Bank of Georgia](https://nbg.gov.ge) | * | GEL | Yes | |
67 | | -| [National Bank of the Republic of Belarus](https://www.nbrb.by) | * | BYN (from 01-07-2016),<br>BYR (01-01-2000 - 30-06-2016),<br>BYB (25-05-1992 - 31-12-1999) | Yes | |
68 | | -| [National Bank of Romania](http://www.bnr.ro) | RON, AED, AUD, BGN, BRL, CAD, CHF, CNY, CZK, DKK, EGP, EUR, GBP, HRK, HUF, INR, JPY, KRW, MDL, MXN, NOK, NZD, PLN, RSD, RUB, SEK, TRY, UAH, USD, XAU, XDR, ZAR | RON, AED, AUD, BGN, BRL, CAD, CHF, CNY, CZK, DKK, EGP, EUR, GBP, HRK, HUF, INR, JPY, KRW, MDL, MXN, NOK, NZD, PLN, RSD, RUB, SEK, TRY, UAH, USD, XAU, XDR, ZAR | Yes | |
69 | | -| [National Bank of Ukranie](https://bank.gov.ua) | * | UAH | Yes | |
70 | | -| [Central Bank of the Republic of Turkey](http://www.tcmb.gov.tr) | * | TRY | Yes | |
71 | | -| [Central Bank of the Republic of Uzbekistan](https://cbu.uz) | * | UZS | Yes | |
72 | | -| [Central Bank of the Czech Republic](https://www.cnb.cz) | * | CZK | Yes | |
73 | | -| [Central Bank of Russia](https://cbr.ru) | * | RUB | Yes | |
74 | | -| [Bulgarian National Bank](http://bnb.bg) | * | BGN | Yes | |
75 | | -| [WebserviceX](http://www.webservicex.net) | * | * | No | |
76 | | -| [1Forge](https://1forge.com) | * (free but limited or paid) | * (free but limited or paid) | No | |
77 | | -| [Cryptonator](https://www.cryptonator.com) | * Crypto (Limited standard currencies) | * Crypto (Limited standard currencies) | No | |
78 | | -| [CurrencyDataFeed](https://currencydatafeed.com) | * (free but limited or paid) | * (free but limited or paid) | No | |
79 | | -| [Open Exchange Rates](https://openexchangerates.org) | USD (free), * (paid) | * | Yes | |
80 | | -| [Xignite](https://www.xignite.com) | * | * | Yes | |
81 | | -| [Currency Converter API](https://www.currencyconverterapi.com) | * | * | Yes (free but limited or paid) | |
82 | | -| [xChangeApi.com](https://xchangeapi.com) | * | * | Yes | |
83 | | -| [fastFOREX.io](https://www.fastforex.io) | USD (free), * (paid) | * | No | |
84 | | -| [exchangerate.host](https://www.exchangerate.host) | * | * | Yes | |
85 | | -| Array | * | * | Yes | |
86 | | - |
87 | | -Additionally, you can add your own services as long as they implement the `ExchangeRateService` interface. |
88 | | - |
89 | | -## Integrations |
90 | | - |
91 | | -- A Symfony Bundle [FlorianvSwapBundle](https://github.com/florianv/FlorianvSwapBundle) |
92 | | -- A Laravel Package [florianv/laravel-swap](https://github.com/florianv/laravel-swap) |
| 165 | +The full documentation is in [`doc/readme.md`](doc/readme.md), and is also published at [florianv.github.io/swap](https://florianv.github.io/swap/). |
93 | 166 |
|
94 | | -## Credits |
| 167 | +## Related packages |
95 | 168 |
|
96 | | -- [Florian Voutzinos](https://github.com/florianv) |
97 | | -- [All Contributors](https://github.com/florianv/swap/contributors) |
| 169 | +The Swap ecosystem: |
| 170 | + |
| 171 | +- [**Swap**](https://github.com/florianv/swap): easy-to-use PHP currency conversion library. |
| 172 | +- [**Exchanger**](https://github.com/florianv/exchanger): lower-level, more granular alternative; direct access to provider implementations. |
| 173 | +- [**Laravel Swap**](https://github.com/florianv/laravel-swap): Laravel application of Swap. |
| 174 | +- [**Symfony Swap**](https://github.com/florianv/symfony-swap): Symfony integration of Swap. |
| 175 | + |
| 176 | +## Sponsorship |
| 177 | + |
| 178 | +The Swap ecosystem is open to selected sponsorships from exchange rate API providers and financial infrastructure companies. |
| 179 | + |
| 180 | +Sponsorship can include: |
| 181 | + |
| 182 | +- Documentation visibility |
| 183 | +- Integration examples |
| 184 | +- Ecosystem-level visibility across Swap, Exchanger, Laravel Swap, and Symfony Swap |
| 185 | + |
| 186 | +For inquiries, contact the maintainer via [GitHub](https://github.com/florianv). |
| 187 | + |
| 188 | +## Contributing |
| 189 | + |
| 190 | +Issues and pull requests are welcome. Please see the existing [issues](https://github.com/florianv/swap/issues) before opening a new one. |
98 | 191 |
|
99 | 192 | ## License |
100 | 193 |
|
101 | 194 | The MIT License (MIT). Please see [LICENSE](https://github.com/florianv/swap/blob/master/LICENSE) for more information. |
| 195 | + |
| 196 | +## Credits |
| 197 | + |
| 198 | +- [Florian Voutzinos](https://github.com/florianv) |
| 199 | +- [All contributors](https://github.com/florianv/swap/contributors) |
0 commit comments