qbil-software/openexchangerates
Laravel wrapper for the Open Exchange Rates API. Fetch live, historical and time-series currency exchange rates, list supported currencies, and convert amounts between currencies with a simple, configurable PHP client.
composer require qbil-software/openexchangerates, then register a free App ID at openexchangerates.org and place it in .env (e.g. OPENEXCHANGERATES_APP_ID=). Never hard-code it — the free tier is rate-limited per key, and leaked keys get throttled or revoked.php artisan vendor:publish --provider=...) or reads env directly. This is a 2021-era, single-purpose wrapper, so expect a thin config array (app id, base currency, maybe a timeout) rather than a rich Laravel integration.src/ before the README. With 2 stars and no dependents, the source is short and is the real documentation. Look for the client class, how it issues HTTP calls (Guzzle? cURL? file_get_contents?), and how it returns data (raw array vs. a DTO). Confirm the exact method names — don't assume from the README.latest rates once, cache them, and do the multiplication yourself. That's 90% of real-world usage of a package like this.Before you commit, run composer why-not / check composer.json for PHP and Guzzle constraints — a 2021 package may pin Guzzle 6/7 or cap PHP at ^7.4|^8.0, which can fight a modern Laravel 11 app.
Wrap it, don't sprinkle it. Bind the client behind your own interface so the package is replaceable:
interface FxRateProvider {
public function rates(string $base = 'USD'): array;
public function rate(string $from, string $to): float;
}
Resolve the package client inside a single adapter class registered in a service provider. When this package inevitably goes unmaintained (or you outgrow the free tier), you swap the adapter for Laravel's Http::get('https://openexchangerates.org/api/latest.json', ...) in ~15 lines.
Cache aggressively; the API updates hourly. Never call the API per request. A Cache::remember('fx.rates.usd', now()->addHour(), ...) wrapper is the difference between a 40 ms page and a 400 ms page, and keeps you inside the free tier's ~1,000 requests/month. Better: a scheduled fx:warm-rates command that fetches once and writes to cache and a fx_rates table, so you have history when you need it.
Store the rate with the transaction. For orders, invoices, and payouts, persist the rate you used on the record (orders.fx_rate, orders.base_currency). Recomputing from "today's rate" on an old order is a classic support-ticket generator.
Cross-rate conversion. Most free tiers only serve USD-based rates. Convert via USD: amount * (rate[to] / rate[from]). Put this in one helper — Money::convert() — and never repeat the arithmetic inline.
Testing. Fake the provider interface in feature tests ($this->instance(FxRateProvider::class, new FakeFxRateProvider())). Don't let unit tests touch the network; if the adapter uses Guzzle directly, Http::fake() won't intercept it, so the interface binding is your only clean seam.
null/false on a bad key. Inspect what happens with an invalid App ID and a malformed response, and normalize it into your own exception type so callers have one thing to catch.config('...') after php artisan config:clear; (2) does the raw response contain a {"error": true, "status": 401} body, which many clients silently pass through; (3) is the cache masking a working call with stale data — test with the cache store set to array."qbil-software/openexchangerates": "1.0.*" rather than ^1.0, and add a calendar reminder to revisit — for a package this size, the honest long-term plan is to inline the two HTTP calls yourself.How can I help you explore Laravel packages today?