getAllTldPricing(); if (! isset($allPricing[$tld])) { return null; } return $allPricing[$tld]; } /** * Get pricing for multiple TLDs. * * @return array */ public function getPricingForTlds(array $tlds): array { $allPricing = $this->getAllTldPricing(); $result = []; foreach ($tlds as $tld) { $tld = ltrim(strtolower(trim($tld)), '.'); if (isset($allPricing[$tld])) { $result[$tld] = $allPricing[$tld]; } } return $result; } /** * Get only TLDs that currently have live Dynadot pricing. * * @return list */ public function getLivePricedTlds(): array { return array_keys($this->getLiveDynadotPricing()); } /** * Get all TLD pricing with conversion and markup applied. * Price overrides from DomainPricing model take precedence. * * @return array */ public function getAllTldPricing(): array { $cacheKey = self::TLD_PRICING_CACHE_KEY . ':ghs'; return Cache::remember($cacheKey, now()->addHours(self::TLD_PRICING_CACHE_TTL_HOURS), function () { $usdPricing = $this->fetchDynadotPricing(); $overrides = DomainPricing::getOverrides(); if (empty($usdPricing) && empty($overrides)) { return []; } $markup = $this->getMarkupPercent(); $usdRate = $this->currency->getUsdToGhsRate(); $converted = []; // First, add all Dynadot prices with conversion foreach ($usdPricing as $tld => $prices) { $converted[$tld] = [ 'register' => $this->convertPrice($prices['register'] ?? 0, $markup), 'renew' => $this->convertPrice($prices['renew'] ?? 0, $markup), 'transfer' => $this->convertPrice($prices['transfer'] ?? 0, $markup), 'currency' => 'GHS', 'usd_rate' => $usdRate, 'usd_register' => $prices['register'] ?? 0, 'usd_renew' => $prices['renew'] ?? 0, 'usd_transfer' => $prices['transfer'] ?? 0, 'is_override' => false, ]; } // Apply overrides (manual GHS prices take precedence) foreach ($overrides as $tld => $override) { $converted[$tld] = [ 'register' => $override['register'], 'renew' => $override['renew'], 'transfer' => $override['transfer'], 'currency' => 'GHS', 'usd_rate' => $usdRate, 'usd_register' => null, 'usd_renew' => null, 'usd_transfer' => null, 'is_override' => true, ]; } return $converted; }); } /** * Convert USD cents to GHS pesewas with markup. */ private function convertPrice(int $usdCents, float $markupPercent): int { return $this->currency->convertCentsTopesewaswithMarkup($usdCents, $markupPercent); } /** * Fetch TLD pricing from Dynadot API. * * @return array */ private function fetchDynadotPricing(): array { $livePricing = $this->getLiveDynadotPricing(); if ($livePricing !== []) { return $livePricing; } return $this->getFallbackPricing(); } /** * Fallback pricing in USD cents for common TLDs. */ private function getFallbackPricing(): array { return [ 'com' => ['register' => 1099, 'renew' => 1099, 'transfer' => 1099], 'net' => ['register' => 1199, 'renew' => 1199, 'transfer' => 1199], 'org' => ['register' => 1099, 'renew' => 1099, 'transfer' => 1099], 'io' => ['register' => 3999, 'renew' => 3999, 'transfer' => 3999], 'co' => ['register' => 2999, 'renew' => 2999, 'transfer' => 2999], 'info' => ['register' => 1899, 'renew' => 1899, 'transfer' => 1899], 'biz' => ['register' => 1699, 'renew' => 1699, 'transfer' => 1699], 'xyz' => ['register' => 1299, 'renew' => 1299, 'transfer' => 1299], 'online' => ['register' => 2999, 'renew' => 2999, 'transfer' => 2999], 'store' => ['register' => 4999, 'renew' => 4999, 'transfer' => 4999], 'tech' => ['register' => 4999, 'renew' => 4999, 'transfer' => 4999], 'site' => ['register' => 2999, 'renew' => 2999, 'transfer' => 2999], ]; } /** * Fetch raw live TLD pricing from Dynadot without fallback expansion. * * @return array */ private function getLiveDynadotPricing(): array { return Cache::remember(self::LIVE_TLD_PRICING_CACHE_KEY, now()->addHours(self::TLD_PRICING_CACHE_TTL_HOURS), function () { if (! $this->dynadot->isConfigured()) { Log::warning('DomainPricingService: Dynadot not configured'); return []; } try { $result = $this->dynadot->getTldPricing(); if (! ($result['success'] ?? false) || empty($result['pricing'])) { Log::warning('DomainPricingService: Failed to fetch Dynadot pricing', $result); return []; } return $result['pricing']; } catch (\Throwable $e) { Log::error('DomainPricingService: Exception fetching pricing', ['error' => $e->getMessage()]); return []; } }); } /** * Format price for display. */ public function formatPrice(int $pesewas): string { return 'GH₵ ' . number_format($pesewas / 100, 2); } public function convertRegistrarAmountToGhsMinor(int $amountMinor, string $currency): ?int { $currency = strtoupper(trim($currency)); if ($currency === 'GHS') { return $amountMinor; } if ($currency === 'USD') { return $this->convertPrice($amountMinor, $this->getMarkupPercent()); } return null; } /** * Get price info for a domain (for checkout/cart). */ public function getDomainPrice(string $domain, string $action = 'register'): ?array { $parts = explode('.', strtolower($domain), 2); if (count($parts) < 2) { return null; } $tld = $parts[1]; $pricing = $this->getPricingForTld($tld); if (! $pricing) { return null; } $priceKey = match ($action) { 'renew' => 'renew', 'transfer' => 'transfer', default => 'register', }; return [ 'domain' => $domain, 'tld' => $tld, 'action' => $action, 'amount_minor' => $pricing[$priceKey], 'currency' => 'GHS', 'price_label' => $this->formatPrice($pricing[$priceKey]), ]; } /** * Clear cached pricing. */ public function clearCache(): void { Cache::forget(self::TLD_PRICING_CACHE_KEY . ':ghs'); Cache::forget(self::LIVE_TLD_PRICING_CACHE_KEY); } /** * Get current exchange rate info. */ public function getExchangeRateInfo(): array { return [ ...$this->currency->getRateInfo(), 'markup_percent' => $this->getMarkupPercent(), ]; } }