Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Openexchangeratesbundle Laravel Package

qbil-software/openexchangeratesbundle

Symfony bundle for integrating the Open Exchange Rates API, providing configurable services to fetch currency exchange rates and use them in PHP applications. Simplifies API access and rate handling.

View on GitHub
Deep Wiki
Context7

Getting Started

This package is a Symfony bundle, not a Laravel package. There is no ServiceProvider, no config/ file that Laravel auto-merges, and no facade. Before installing, understand that you will be using it as a vendor library, not as an out-of-the-box Laravel integration.

Minimal steps:

  1. Get an API key from openexchangerates.org (free tier is USD-base only, ~1000 requests/month).
  2. Install with Composer:
    composer require qbil-software/openexchangeratesbundle
    
    Because it targets Symfony 2.8+ (~2019), expect it to pull an old openexchangerates/Guzzle client. Check composer show after install to see the actual underlying client version.
  3. Look first at vendor/qbil-software/openexchangeratesbundle/ — specifically the service classes and the bundle's Resources/config (services XML/YAML). That is where the real client is instantiated and where the API key/base URL are wired. Ignore QbilSoftwareOpenExchangeRatesBundle.php and the DependencyInjection/ folder; those are Symfony-only.
  4. Skip bundle registration entirely. In a Laravel app, registerBundles() does not exist. Bind the underlying client manually in AppServiceProvider.

First realistic use case: converting stored prices into a display currency (e.g. showing a USD-priced SaaS product in EUR), or normalizing order totals to a reporting currency. Start with a single rates()->latest()-style call in a tinker session using a hand-constructed client, cache the result, and only then wrap it in application code.

Implementation Patterns

Bind the underlying client as a singleton. Do not try to use the Symfony service container. Locate the concrete client class in the bundle and register it:

// AppServiceProvider::register()
$this->app->singleton(Client::class, function () {
    return new Client(config('services.openexchangerates.app_id'));
});

If the bundle only exposes Symfony Definitions, copy the instantiation logic from Resources/config/services.* into your provider — it is usually just new Client($appId).

Wrap it in a thin application service. Keep the vendor class out of controllers:

class ExchangeRates
{
    public function __construct(private Client $client, private Cache $cache) {}

    public function rate(string $from, string $to): float
    {
        $rates = $this->cache->remember('oer.rates', now()->addHours(6), fn () =>
            $this->client->getLatest()->getRates()
        );

        // Free tier is USD-based: cross-rate through USD.
        return ($rates[$to] ?? 1) / ($rates[$from] ?? 1);
    }
}

Cache aggressively and refresh on a schedule. The API is rate-limited and the free plan only allows monthly/hourly updates depending on tier. A scheduled command that writes rates to a currency_rates table is far better than hitting the API per request:

// app/Console/Commands/SyncRates.php
$rates = $this->client->getLatest()->getRates();
foreach ($rates as $code => $rate) {
    CurrencyRate::updateOrCreate(['code' => $code], ['rate' => $rate, 'fetched_at' => now()]);
}

Register it in routes/console.php or the kernel with ->hourly().

Read rates from your own table in hot paths. Never make a network call inside a Blade view or an API resource. Inject a CurrencyRepository that reads the cached table and falls back to the last-known value if a currency is missing.

Format with a dedicated money library. Pair the rate lookup with moneyphp/money (or brick/money) so rounding and currency metadata stay correct rather than doing round($amount * $rate, 2) by hand.

Testing pattern. Bind a fake client in tests:

$this->mock(Client::class, fn ($m) => $m->shouldReceive('getLatest')->andReturn($fakeResponse));

Or better, don't mock the vendor type — mock your own ExchangeRates interface so vendor churn never breaks tests.

Gotchas and Tips

  • It is not a Laravel package. Composer's extra.laravel.providers will be empty. There is no publishes(), no config() helper key, and no artisan command shipped with it. Everything above is manual wiring — factor that into your estimation.
  • Abandoned since 2019. No security patches, likely pinned to an old Guzzle. If composer require conflicts with your Laravel version, you're better off using the underlying openexchangerates PHP client directly, or calling the REST endpoint with Laravel's Http facade:
    Http::get("https://openexchangerates.org/api/latest.json", ['app_id' => $key])->json('rates');
    
    This is often the pragmatic "day-to-day" answer — the bundle adds little value inside Laravel.
  • Free tier is USD-only as the base currency. You cannot ask for base=EUR on the free plan; always cross-rate through USD. Watch for double rounding when chaining conversions.
  • Rate-limit errors surface as client exceptions, not Laravel HTTP exceptions. Catch the vendor exception and translate it (return 429/503 or fall back to your cached table). A missing/expired key typically returns a 401 body with a description field rather than throwing on its own.
  • Configuration quirks to inspect: the Symfony Configuration.php file defines the exact config keys (often a parameter prefixed qbil_open_exchange_rates.*). Read it to know whether the API key is a constructor argument or a setAppId() call — the answer varies by underlying client version.
  • Stale rate data is worse than no data for money. Always store fetched_at and refuse to compute prices from rates older than a threshold.
  • Timezone and DST: the API returns timestamps in UTC. Compare against now()->utc(), not local time, when deciding whether to refresh.
  • Extension point to prefer: the underlying client object. Subclass or decorate it in a Laravel binding; do not attempt to extend the Symfony bundle class — it will drag you into container/bundle bootstrapping that Laravel does not run.
  • If you need more than spot checks, consider a maintained Laravel alternative (e.g. a dedicated currency package or an adapter around exchangerate.host / Frankfurter for free EUR-base rates) rather than retrofitting this bundle.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect