package currency import ( "context" "fmt" "time" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" "github.com/SamuelTariku/FortuneBet-Backend/internal/repository" ) type Service struct { repo repository.CurrencyRepository baseCurrency domain.IntCurrency fixerFetcher *FixerFetcher } func NewService(repo repository.CurrencyRepository, baseCurrency domain.IntCurrency, fixerFetcher *FixerFetcher) *Service { return &Service{repo: repo} } func (s *Service) Convert(ctx context.Context, amount float64, from, to domain.IntCurrency) (float64, error) { if from == to { return amount, nil } rate, err := s.repo.GetExchangeRate(ctx, from, to) if err != nil { return 0, err } return rate.Convert(amount) } func (s *Service) GetSupportedCurrencies(ctx context.Context) ([]domain.IntCurrency, error) { return s.repo.GetSupportedCurrencies(ctx) } func (s *Service) UpdateRates(ctx context.Context) error { // Implement fetching from external API (e.g., Fixer, Open Exchange Rates) rates := map[domain.IntCurrency]map[domain.IntCurrency]float64{ domain.ETB: { domain.USD: 0.018, domain.EUR: 0.016, domain.GBP: 0.014, }, // Add other currencies... } for from, toRates := range rates { for to, rate := range toRates { err := s.repo.StoreExchangeRate(ctx, domain.IntCurrencyRate{ From: from, To: to, Rate: rate, ValidUntil: time.Now().Add(24 * time.Hour), // Refresh daily }) if err != nil { return err } } } return nil } func (s *Service) FetchAndStoreRates(ctx context.Context) error { // s.logger.Info("Starting exchange rate update") rates, err := s.fixerFetcher.FetchLatestRates(ctx, s.baseCurrency) if err != nil { // s.logger.Error("Failed to fetch rates", "error", err) return fmt.Errorf("failed to fetch rates: %w", err) } // Convert to integer rates with precision const precision = 6 // 1.000000 for currency, rate := range rates { if currency == s.baseCurrency { continue } intRate := domain.IntCurrencyRate{ From: s.baseCurrency, To: currency, Rate: rate * float64(pow10(precision)), ValidUntil: time.Now().Add(24 * time.Hour), // Rates valid for 24 hours } if err := s.repo.StoreExchangeRate(ctx, intRate); err != nil { // s.logger.Error("Failed to store rate", // "from", s.baseCurrency, // "to", currency, // "error", err) continue // Try to store other rates even if one fails } // Also store the inverse rate inverseRate := domain.IntCurrencyRate{ From: currency, To: s.baseCurrency, Rate: (1 / rate) * float64(pow10(precision)), ValidUntil: time.Now().Add(24 * time.Hour), } if err := s.repo.StoreExchangeRate(ctx, inverseRate); err != nil { // s.logger.Error("Failed to store inverse rate", // "from", currency, // "to", s.baseCurrency, // "error", err) return fmt.Errorf("Error storing exchange rates") } } // s.logger.Info("Exchange rates updated successfully") return nil } func pow10(n int) int64 { result := int64(1) for i := 0; i < n; i++ { result *= 10 } return result }