{"id":17164130,"url":"https://github.com/florianv/exchanger","last_synced_at":"2026-05-14T09:03:16.469Z","repository":{"id":37819942,"uuid":"67460216","full_name":"florianv/exchanger","owner":"florianv","description":":office: Currency exchange rates framework for PHP","archived":false,"fork":false,"pushed_at":"2024-08-01T12:11:58.000Z","size":892,"stargazers_count":185,"open_issues_count":7,"forks_count":81,"subscribers_count":12,"default_branch":"master","last_synced_at":"2025-04-01T00:38:03.667Z","etag":null,"topics":["conversion","currency","currency-exchange-rates","exchange-rates","money","php"],"latest_commit_sha":null,"homepage":"https://florianv.github.io/exchanger/","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/florianv.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-09-06T00:47:59.000Z","updated_at":"2025-03-26T19:59:38.000Z","dependencies_parsed_at":"2024-04-11T09:39:37.776Z","dependency_job_id":"e9ef5c61-6a5c-4178-9c0b-1f00a5c8a626","html_url":"https://github.com/florianv/exchanger","commit_stats":{"total_commits":386,"total_committers":62,"mean_commits":6.225806451612903,"dds":"0.46632124352331605","last_synced_commit":"3ad15f6607c66157274ea448188c8d9adcbd1048"},"previous_names":[],"tags_count":49,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/florianv%2Fexchanger","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/florianv%2Fexchanger/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/florianv%2Fexchanger/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/florianv%2Fexchanger/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/florianv","download_url":"https://codeload.github.com/florianv/exchanger/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247761049,"owners_count":20991532,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["conversion","currency","currency-exchange-rates","exchange-rates","money","php"],"created_at":"2024-10-14T22:50:59.424Z","updated_at":"2026-05-14T09:03:16.463Z","avatar_url":"https://github.com/florianv.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Exchanger\n\n[![Tests](https://github.com/florianv/exchanger/actions/workflows/tests.yml/badge.svg)](https://github.com/florianv/exchanger/actions/workflows/tests.yml)\n[![Psalm](https://github.com/florianv/exchanger/actions/workflows/psalm.yml/badge.svg)](https://github.com/florianv/exchanger/actions/workflows/psalm.yml)\n[![Total Downloads](https://img.shields.io/packagist/dt/florianv/exchanger.svg?style=flat-square)](https://packagist.org/packages/florianv/exchanger)\n[![Version](http://img.shields.io/packagist/v/florianv/exchanger.svg?style=flat-square)](https://packagist.org/packages/florianv/exchanger)\n\n\u003e _Exchange rate provider layer for PHP. Direct access to 30 provider implementations through a single `ExchangeRateService` interface, with chain fallback and PSR-16 caching. Maintained since 2016._\n\nExchanger is the **exchange rate provider layer** for PHP. It exposes 30 services (the European Central Bank, several national banks, exchangerate.host, and commercial **exchange rate APIs** that require an API key) behind a single `ExchangeRateService` interface, with chainable fallback, PSR-16 caching, and historical rates. Used in real-world PHP applications since 2016.\n\nFor most use cases, the higher-level [Swap](https://github.com/florianv/swap) library is what you want. Reach for Exchanger directly when you need finer control.\n\n## 💡 What is Exchanger?\n\n- Exchanger is a PHP library for currency conversion and exchange rate retrieval at the provider layer.\n- It contains 30 service implementations behind a common `ExchangeRateService` interface.\n- It caches results via PSR-16 SimpleCache.\n- It supports historical rates.\n- It supports a chain service for fallback. When a service errors, the next one in the chain is tried.\n\n## 🎯 When should you use Exchanger?\n\n- Use Exchanger when you need finer control than [Swap](https://github.com/florianv/swap) exposes: custom chain composition, custom caching strategy, custom HTTP middleware, or building your own facade or framework integration.\n- For most PHP applications, use [Swap](https://github.com/florianv/swap) instead. It is built on Exchanger and provides sensible defaults and a builder-style API.\n\n## 🧠 Why Exchanger over Swap?\n\nSwap is the easy-to-use, high-level API. Exchanger is the layer Swap is built on.\n\nReach for Exchanger directly when:\n\n- **Custom facade:** you want to build your own currency conversion API on top of the provider layer.\n- **Framework binding:** you are integrating into a framework that does not yet have a Swap binding.\n- **Fine-grained chain composition:** you need to wrap services with custom logic (rate limiting, observability, conditional fallback) before chaining them.\n- **Direct cache control:** you want to manage the PSR-16 cache key strategy yourself.\n- **Custom HTTP layer:** you need an HTTP middleware stack the Swap builder does not expose.\n\nIf none of these apply, use Swap.\n\n## 📦 Installation\n\nExchanger requires PHP 8.2 or newer.\n\n```bash\ncomposer require florianv/exchanger symfony/http-client nyholm/psr7\n```\n\nAny PSR-18 client paired with a PSR-17 request factory works; `php-http/discovery` finds them automatically.\n\n## ⚡ Quickstart\n\n```php\nuse Exchanger\\Exchanger;\nuse Exchanger\\ExchangeRateQueryBuilder;\nuse Exchanger\\Service\\EuropeanCentralBank;\n\n// The European Central Bank is free, no API key required.\n$service   = new EuropeanCentralBank();\n$exchanger = new Exchanger($service);\n\n// EUR → USD exchange rate\n$query = (new ExchangeRateQueryBuilder('EUR/USD'))-\u003ebuild();\n$rate  = $exchanger-\u003egetExchangeRate($query);\n\n$rate-\u003egetValue();                 // e.g. 1.0823 (a float)\n$rate-\u003egetDate()-\u003eformat('Y-m-d'); // e.g. 2026-04-29\n$rate-\u003egetProviderName();          // 'european_central_bank'\n\n// Convert an amount using the returned rate\n$amountInEUR = 100.00;\n$amountInUSD = $amountInEUR * $rate-\u003egetValue();\n\n// Historical rate\n$query = (new ExchangeRateQueryBuilder('EUR/USD'))\n    -\u003esetDate((new \\DateTime())-\u003emodify('-15 days'))\n    -\u003ebuild();\n\n$rate = $exchanger-\u003egetExchangeRate($query);\n```\n\nExchanger retrieves the rate; your application multiplies the amount by `$rate-\u003egetValue()` to perform the conversion.\n\n## 🔁 Chaining services (fallback chain)\n\nWrap multiple services in a `Chain` to fall back when one of them errors:\n\n```php\nuse Exchanger\\Exchanger;\nuse Exchanger\\Service\\Chain;\nuse Exchanger\\Service\\EuropeanCentralBank;\n\n$service = new Chain([\n    new YourPrimaryService(null, null, ['api_key' =\u003e 'YOUR_KEY']),\n    new YourFallbackService(null, null, ['api_key' =\u003e 'YOUR_KEY']),\n    new EuropeanCentralBank(), // free fallback for EUR-base pairs\n]);\n\n$exchanger = new Exchanger($service);\n```\n\nServices are tried in order. If a service does not support the requested currency pair, it is skipped silently. If a service throws an exception, the exception is collected and the next service is tried. If every service was skipped or threw, the chain raises an `Exchanger\\Exception\\ChainException` containing all collected exceptions.\n\n## 🛠 Common use cases\n\n- Build your own currency conversion facade on top of the provider layer.\n- Integrate Exchanger into a framework that does not yet have a Swap binding.\n- Wrap services with custom middleware (rate limiting, observability, request signing) before chaining.\n- Power internal FX dashboards with full control over the cache key strategy.\n- Implement custom HTTP layers (signed requests, mTLS, custom retries) on top of any PSR-18 client.\n\n## 🧭 Which package should I use?\n\nThe Swap ecosystem is a layered toolkit for currency conversion in PHP:\n\n- **Swap.** The easy-to-use, high-level API. Most apps need only Swap.\n- **Exchanger.** The lower-level, more granular layer Swap is built on (this package). Reach for it when you need finer control over chain composition, caching, or HTTP plumbing.\n- **Laravel Swap.** Laravel application of Swap.\n- **Symfony Swap.** Symfony integration of Swap.\n\nAll four packages are MIT-licensed and require PHP 8.2 or newer.\n\n## 📊 Providers\n\nExchanger ships 30 exchange rate provider implementations. Each is registered in `Exchanger\\Service\\Registry` under the **identifier** shown in the table.\n\n### Public providers (no API key required)\n\n| Service                                    | Identifier                            | Base           | Quote          | Historical |\n| ------------------------------------------ | ------------------------------------- | -------------- | -------------- | ---------- |\n| Bulgarian National Bank                    | `bulgarian_national_bank`             | *              | BGN            | Yes        |\n| Central Bank of the Czech Republic         | `central_bank_of_czech_republic`      | *              | CZK            | Yes        |\n| Central Bank of the Republic of Turkey     | `central_bank_of_republic_turkey`     | *              | TRY            | Yes        |\n| Central Bank of the Republic of Uzbekistan | `central_bank_of_republic_uzbekistan` | *              | UZS            | Yes        |\n| Cryptonator                                | `cryptonator`                         | * (crypto)     | * (crypto)     | No         |\n| European Central Bank                      | `european_central_bank`               | EUR            | *              | Yes        |\n| exchangerate.host                          | `exchangeratehost`                    | *              | *              | Yes        |\n| National Bank of Georgia                   | `national_bank_of_georgia`            | *              | GEL            | Yes        |\n| National Bank of Romania                   | `national_bank_of_romania`            | (limited list) | (limited list) | Yes        |\n| National Bank of the Republic of Belarus   | `national_bank_of_republic_belarus`   | *              | BYN            | Yes        |\n| National Bank of Ukraine                   | `national_bank_of_ukraine`            | *              | UAH            | Yes        |\n| Russian Central Bank                       | `russian_central_bank`                | *              | RUB            | Yes        |\n| WebserviceX                                | `webservicex`                         | *              | *              | No         |\n\n### Commercial providers (require an API key)\n\n| Service                          | Identifier                       | Base                  | Quote | Historical |\n| -------------------------------- | -------------------------------- | --------------------- | ----- | ---------- |\n| AbstractAPI                      | `abstract_api`                   | *                     | *     | Yes        |\n| coinlayer                        | `coin_layer`                     | * (crypto)            | *     | Yes        |\n| Currency Converter API           | `currency_converter`             | *                     | *     | Yes        |\n| Currency Data (APILayer)         | `apilayer_currency_data`         | USD (free), * (paid)  | *     | Yes        |\n| CurrencyDataFeed                 | `currency_data_feed`             | *                     | *     | No         |\n| currencylayer (direct)           | `currency_layer`                 | USD (free), * (paid)  | *     | Yes        |\n| Exchange Rates Data (APILayer)   | `apilayer_exchange_rates_data`   | USD (free), * (paid)  | *     | Yes        |\n| exchangeratesapi (direct)        | `exchange_rates_api`             | USD (free), * (paid)  | *     | Yes        |\n| fastFOREX.io                     | `fastforex`                      | *                     | *     | Yes        |\n| Fixer (APILayer)                 | `apilayer_fixer`                 | EUR (free), * (paid)  | *     | Yes        |\n| Fixer (direct)                   | `fixer`                          | EUR (free), * (paid)  | *     | Yes        |\n| 1Forge                           | `forge`                          | *                     | *     | No         |\n| Open Exchange Rates              | `open_exchange_rates`            | USD (free), * (paid)  | *     | Yes        |\n| xChangeApi.com                   | `xchangeapi`                     | *                     | *     | Yes        |\n| Xignite                          | `xignite`                        | *                     | *     | Yes        |\n\nYou can also add your own provider by implementing the `Exchanger\\Contract\\ExchangeRateService` interface (see the [documentation](doc/readme.md)).\n\n## ⚙ Caching, HTTP client, and error handling\n\n- **Caching:** PSR-16 `SimpleCache` is passed as the second constructor argument: `new Exchanger($service, $cache)`. Per-query disable: `-\u003eaddOption('cache', false)`. Per-query TTL: `-\u003eaddOption('cache_ttl', 3600)`.\n- **HTTP client:** any PSR-18 client (`symfony/http-client`, `php-http/guzzle7-adapter`, etc.). Pass it explicitly to each service constructor, or rely on `php-http/discovery` to auto-discover.\n- **Errors:** when every service in a `Chain` has either skipped (unsupported pair) or thrown, the chain raises an `Exchanger\\Exception\\ChainException` containing all collected exceptions.\n\n## 📚 Documentation\n\nThe full documentation, with the per-provider configuration reference, caching options, and how to write your own service, is in [doc/readme.md](doc/readme.md).\n\n## 🧩 Related packages\n\nThe Swap ecosystem:\n\n- [**Swap**](https://github.com/florianv/swap): easy-to-use PHP currency conversion library.\n- [**Exchanger**](https://github.com/florianv/exchanger): exchange rate provider layer (this package).\n- [**Laravel Swap**](https://github.com/florianv/laravel-swap): Laravel application of Swap.\n- [**Symfony Swap**](https://github.com/florianv/symfony-swap): Symfony integration of Swap.\n\n## 🤝 Sponsorship\n\nThe Swap ecosystem is open to selected sponsorships from exchange rate API providers and financial infrastructure companies.\n\nSponsorship can include:\n\n- Documentation visibility\n- Integration examples\n- Ecosystem-level visibility across Swap, Exchanger, Laravel Swap, and Symfony Swap\n\nFor inquiries, contact the maintainer via [GitHub](https://github.com/florianv).\n\n## 🙌 Contributing\n\nIssues and pull requests are welcome. Please see the existing [issues](https://github.com/florianv/exchanger/issues) before opening a new one.\n\n## 📄 License\n\nThe MIT License (MIT). Please see [LICENSE](https://github.com/florianv/exchanger/blob/master/LICENSE) for more information.\n\n## 👏 Credits\n\n- [Florian Voutzinos](https://github.com/florianv)\n- [All contributors](https://github.com/florianv/exchanger/contributors)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fflorianv%2Fexchanger","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fflorianv%2Fexchanger","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fflorianv%2Fexchanger/lists"}