Files
ladill-hosting/app/Services/Currency/CurrencyConverterService.php
T
isaaccladandCursor e251a4cf60
Deploy Ladill Hosting / deploy (push) Failing after 17s
Initial Ladill Hosting app with Gitea deploy pipeline.
Shared web hosting extracted from the platform monolith, with CI deploy
to /var/www/ladill-hosting matching Bird/Domains/Email.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-06 16:24:20 +00:00

167 lines
4.8 KiB
PHP

<?php
namespace App\Services\Currency;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
class CurrencyConverterService
{
private const CACHE_KEY = 'currency:usd_to_ghs';
private const CACHE_KEY_EUR = 'currency:eur_to_ghs';
private const CACHE_TTL_MINUTES = 60;
private const FALLBACK_RATE = 15.50;
private const FALLBACK_EUR_RATE = 16.90;
public function getUsdToGhsRate(): float
{
return Cache::remember(self::CACHE_KEY, now()->addMinutes(self::CACHE_TTL_MINUTES), function () {
return $this->fetchRateFromBase('USD') ?? self::FALLBACK_RATE;
});
}
public function getEurToGhsRate(): float
{
return Cache::remember(self::CACHE_KEY_EUR, now()->addMinutes(self::CACHE_TTL_MINUTES), function () {
return $this->fetchRateFromBase('EUR') ?? self::FALLBACK_EUR_RATE;
});
}
/**
* Convert USD amount to GHS.
*/
public function convertUsdToGhs(float $usdAmount): float
{
return $usdAmount * $this->getUsdToGhsRate();
}
/**
* Convert USD to GHS with markup percentage.
*/
public function convertWithMarkup(float $usdAmount, float $markupPercent = 35): float
{
$ghsAmount = $this->convertUsdToGhs($usdAmount);
return $ghsAmount * (1 + ($markupPercent / 100));
}
/**
* Convert USD cents to GHS pesewas with markup.
*/
public function convertCentsTopesewaswithMarkup(int $usdCents, float $markupPercent = 35): int
{
$usdAmount = $usdCents / 100;
$ghsAmount = $this->convertWithMarkup($usdAmount, $markupPercent);
return (int) round($ghsAmount * 100);
}
private function fetchRateFromBase(string $base): ?float
{
$rate = $this->fetchFromExchangeRateApi($base)
?? $this->fetchFromFreeCurrencyApi($base)
?? $this->fetchFromOpenExchangeRates($base);
if ($rate !== null) {
Log::info('Currency rate fetched', ['base' => $base, 'ghs' => $rate]);
}
return $rate;
}
private function fetchFromExchangeRateApi(string $base): ?float
{
try {
$response = Http::timeout(5)->get("https://api.exchangerate-api.com/v4/latest/{$base}");
if ($response->successful()) {
$data = $response->json();
return (float) ($data['rates']['GHS'] ?? null) ?: null;
}
} catch (\Throwable $e) {
Log::warning('ExchangeRateApi fetch failed', ['base' => $base, 'error' => $e->getMessage()]);
}
return null;
}
private function fetchFromFreeCurrencyApi(string $base): ?float
{
$apiKey = config('services.freecurrencyapi.key');
if (empty($apiKey)) {
return null;
}
try {
$response = Http::timeout(5)->get('https://api.freecurrencyapi.com/v1/latest', [
'apikey' => $apiKey,
'base_currency' => $base,
'currencies' => 'GHS',
]);
if ($response->successful()) {
$data = $response->json();
return (float) ($data['data']['GHS'] ?? null) ?: null;
}
} catch (\Throwable $e) {
Log::warning('FreeCurrencyApi fetch failed', ['base' => $base, 'error' => $e->getMessage()]);
}
return null;
}
private function fetchFromOpenExchangeRates(string $base): ?float
{
$appId = config('services.openexchangerates.app_id');
if (empty($appId)) {
return null;
}
// Free tier only supports USD as base
if ($base !== 'USD') {
return null;
}
try {
$response = Http::timeout(5)->get('https://openexchangerates.org/api/latest.json', [
'app_id' => $appId,
'symbols' => 'GHS',
]);
if ($response->successful()) {
$data = $response->json();
return (float) ($data['rates']['GHS'] ?? null) ?: null;
}
} catch (\Throwable $e) {
Log::warning('OpenExchangeRates fetch failed', ['base' => $base, 'error' => $e->getMessage()]);
}
return null;
}
/**
* Force refresh the cached rate.
*/
public function refreshRate(): float
{
Cache::forget(self::CACHE_KEY);
Cache::forget(self::CACHE_KEY_EUR);
return $this->getUsdToGhsRate();
}
/**
* Get the current cached rate info.
*/
public function getRateInfo(): array
{
return [
'rate' => $this->getUsdToGhsRate(),
'from' => 'USD',
'to' => 'GHS',
'cached' => Cache::has(self::CACHE_KEY),
'fallback' => self::FALLBACK_RATE,
];
}
}