Extract Ladill Servers as standalone app at servers.ladill.com.
VPS and dedicated server ordering, managed panels, SSO, billing, and launcher integration — forked from hosting infrastructure with server-focused routes and dashboard. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,166 @@
|
||||
<?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,
|
||||
];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user