# Add currency exchange rates and conversion to Laravel

Use the Laravel HTTP facade and cache to fetch current rates. Set cache expiration from nextUpdateAt and keep CXR_API_KEY in server configuration.

## Get a free API key

Sign up at https://currencyexchangerate-api.com/auth/login, then create a key under API keys in the console. The free plan includes 3,000 requests per month with no credit card. Store the secret as CXR_API_KEY in server-side environment configuration; never expose it in browser code.

File: `.env`

```shell
CXR_API_KEY=cxr_your_secret_key
```

## Create a reusable client and cache rates

Use the standard HTTP client. Cache every /latest response until nextUpdateAt: rates change at most once by then, and reusing the response saves quota. The helper also exposes /convert.

File: `app/Services/Rates.php`

```php
<?php

namespace App\Services;

use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Http;

// config/services.php: 'cxr' => ['key' => env('CXR_API_KEY')],
class Rates
{
    private string $base = 'https://api.currencyexchangerate-api.com/api/v1';

    private function get(string $path): array
    {
        return Http::withToken(config('services.cxr.key'))
            ->acceptJson()
            ->get($this->base . $path)
            ->throw()
            ->json();
    }

    public function latest(string $base = 'USD', string $symbols = 'EUR,GBP'): array
    {
        $key = "rates:$base:$symbols";
        if (Cache::has($key)) {
            return Cache::get($key);
        }

        $query = http_build_query(compact('base', 'symbols'));
        $data = $this->get('/latest?' . $query);
        Cache::put($key, $data, Carbon::parse($data['nextUpdateAt']));

        return $data;
    }

    public function convert(string $from, string $to, float $amount): array
    {
        $query = http_build_query(compact('from', 'to', 'amount'));

        return $this->get('/convert?' . $query);
    }
}
```

## Use it in Laravel

Call the helper from a server route, controller, view, command, or script. Format converted values for the user’s locale.

File: `app/Http/Controllers/ConversionController.php`

```php
$data = $rates->convert('EUR', 'GBP', 100);
$formatter = new NumberFormatter('en_GB', NumberFormatter::CURRENCY);
$data['formatted'] = $formatter->formatCurrency($data['result'], $data['to']);

return response()->json($data);
```

## Handle API errors

A 401 means the key is invalid. A 429 can mean a rate limit or quota limit; read Retry-After in seconds before retrying. Other errors use application/problem+json.

File: `app/Http/Controllers/ConversionController.php`

```php
use Illuminate\Http\Client\RequestException;

try {
    $data = $rates->convert('EUR', 'GBP', 100);
} catch (RequestException $e) {
    $status = $e->response->status();
    if ($status === 401) {
        abort(500, 'Check CXR_API_KEY');
    }

    return response()
        ->json($e->response->json(), $status === 429 ? 429 : 502)
        ->header('Retry-After', (string) $e->response->header('Retry-After'));
}
```

## Format the amount for display

Use locale-aware currency formatting instead of adding a symbol manually.

File: `format.php`

```php
$formatter = new NumberFormatter('de_DE', NumberFormatter::CURRENCY);
echo $formatter->formatCurrency(85.249105, 'EUR');
```

## Related links

- [API documentation](https://currencyexchangerate-api.com/docs.md)
- [All integration guides](https://currencyexchangerate-api.com/guides.md)
- [Get a free API key](https://currencyexchangerate-api.com/auth/login)
