package atlas import ( "context" "errors" "fmt" "strconv" "github.com/SamuelTariku/FortuneBet-Backend/internal/config" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" "github.com/SamuelTariku/FortuneBet-Backend/internal/ports" "github.com/SamuelTariku/FortuneBet-Backend/internal/repository" virtualgameservice "github.com/SamuelTariku/FortuneBet-Backend/internal/services/virtualGame" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/wallet" ) type Service struct { virtualGameSvc virtualgameservice.VirtualGameService repo repository.VirtualGameRepository client *Client walletSvc *wallet.Service transfetStore ports.TransferStore cfg *config.Config } func New(virtualGameSvc virtualgameservice.VirtualGameService, repo repository.VirtualGameRepository, client *Client, walletSvc *wallet.Service, transferStore ports.TransferStore, cfg *config.Config) *Service { return &Service{ virtualGameSvc: virtualGameSvc, repo: repo, client: client, walletSvc: walletSvc, transfetStore: transferStore, cfg: cfg, } } func (s *Service) InitGame(ctx context.Context, req domain.AtlasGameInitRequest) (*domain.AtlasGameInitResponse, error) { body := map[string]any{ "game": req.Game, "partner_id": s.client.PartnerID, "casino_id": s.client.CasinoID, "language": req.Language, "currency": req.Currency, "player_id": req.PlayerID, } // 3. Call the Atlas client var res domain.AtlasGameInitResponse if err := s.client.post(ctx, "/init", body, &res); err != nil { return nil, fmt.Errorf("failed to initialize game: %w", err) } return &res, nil } func (s *Service) GetUserData(ctx context.Context, req domain.AtlasGetUserDataRequest) (*domain.AtlasGetUserDataResponse, error) { // 1. Validate casino_id and hash if needed if req.CasinoID != s.client.CasinoID { return nil, fmt.Errorf("invalid casino_id") } // 2. Fetch player from DB playerIDInt, err := strconv.ParseInt(req.PlayerID, 10, 64) if err != nil { return nil, fmt.Errorf("invalid playerID: %w", err) } wallet, err := s.walletSvc.GetCustomerWallet(ctx, playerIDInt) if err != nil { return nil, fmt.Errorf("failed to fetch walllets for player %d: %w", playerIDInt, err) } // 4. Build response res := &domain.AtlasGetUserDataResponse{ PlayerID: req.PlayerID, Balance: float64(wallet.RegularBalance), } return res, nil } func (s *Service) ProcessBet(ctx context.Context, req domain.AtlasBetRequest) (*domain.AtlasBetResponse, error) { if req.CasinoID != s.client.CasinoID { return nil, fmt.Errorf("invalid casino_id") } playerIDInt, err := strconv.ParseInt(req.PlayerID, 10, 64) if err != nil { return nil, fmt.Errorf("invalid playerID: %w", err) } wallet, err := s.walletSvc.GetCustomerWallet(ctx, playerIDInt) if err != nil { return nil, fmt.Errorf("failed to fetch walllets for player %d: %w", playerIDInt, err) } // if player == nil { // return nil, ErrPlayerNotFound // } // 3. Check for duplicate transaction // exists, err := s.repo.TransactionExists(ctx, req.TransactionID) // if err != nil { // return nil, fmt.Errorf("failed to check transaction: %w", err) // } // if exists { // return nil, ErrDuplicateTransaction // } // // 4. Get current balance // balance, err := s.walletSvc.GetBalance(ctx, req.PlayerID) // if err != nil { // return nil, fmt.Errorf("failed to fetch wallet balance: %w", err) // } // 5. Ensure sufficient balance if float64(wallet.RegularBalance) < req.Amount { return nil, domain.ErrInsufficientBalance } // 6. Deduct amount from wallet (record transaction) err = s.walletSvc.UpdateBalance(ctx, wallet.RegularID, domain.Currency(float64(wallet.RegularBalance)-req.Amount)) if err != nil { return nil, fmt.Errorf("failed to debit wallet: %w", err) } // 7. Save transaction record to DB (optional but recommended) // if err := s.repo.SaveBetTransaction(ctx, req); err != nil { // // log warning but don’t fail response to Atlas // fmt.Printf("warning: failed to save bet transaction: %v\n", err) // } // 8. Build response res := &domain.AtlasBetResponse{ PlayerID: req.PlayerID, Balance: float64(wallet.RegularBalance) - req.Amount, } return res, nil } func (s *Service) ProcessBetWin(ctx context.Context, req domain.AtlasBetWinRequest) (*domain.AtlasBetWinResponse, error) { if req.CasinoID != s.client.CasinoID { return nil, fmt.Errorf("invalid casino_id") } playerIDInt, err := strconv.ParseInt(req.PlayerID, 10, 64) if err != nil { return nil, fmt.Errorf("invalid playerID: %w", err) } wallet, err := s.walletSvc.GetCustomerWallet(ctx, playerIDInt) if err != nil { return nil, fmt.Errorf("failed to fetch walllets for player %d: %w", playerIDInt, err) } // 5. Ensure sufficient balance if float64(wallet.RegularBalance) < req.BetAmount { return nil, domain.ErrInsufficientBalance } // 6. Deduct amount from wallet (record transaction) err = s.walletSvc.UpdateBalance(ctx, wallet.RegularID, domain.Currency(float64(wallet.RegularBalance)-req.BetAmount)) if err != nil { return nil, fmt.Errorf("failed to debit wallet: %w", err) } if req.WinAmount > 0 { err = s.walletSvc.UpdateBalance(ctx, wallet.RegularID, domain.Currency(float64(wallet.RegularBalance)+req.WinAmount)) if err != nil { return nil, fmt.Errorf("failed to credit wallet: %w", err) } } // 8. Build response res := &domain.AtlasBetWinResponse{ PlayerID: req.PlayerID, Balance: float64(wallet.RegularBalance), } return res, nil } func (s *Service) ProcessRoundResult(ctx context.Context, req domain.RoundResultRequest) (*domain.RoundResultResponse, error) { if req.PlayerID == "" || req.TransactionID == "" { return nil, errors.New("missing player_id or transaction_id") } // Credit player with win amount if > 0 if req.Amount > 0 { // This will credit player's balance playerIDInt, err := strconv.ParseInt(req.PlayerID, 10, 64) if err != nil { return nil, fmt.Errorf("invalid playerID: %w", err) } wallet, err := s.walletSvc.GetCustomerWallet(ctx, playerIDInt) if err != nil { return nil, fmt.Errorf("failed to fetch walllets for player %d: %w", playerIDInt, err) } err = s.walletSvc.UpdateBalance(ctx, wallet.RegularID, domain.Currency(float64(wallet.RegularBalance)+req.Amount)) if err != nil { return nil, fmt.Errorf("failed to credit wallet: %w", err) } } return &domain.RoundResultResponse{Success: true}, nil } func (s *Service) ProcessRollBack(ctx context.Context, req domain.RollbackRequest) (*domain.RollbackResponse, error) { if req.PlayerID == "" || req.BetTransactionID == "" { return nil, errors.New("missing player_id or transaction_id") } // Credit player with win amount if > 0 // This will credit player's balance playerIDInt, err := strconv.ParseInt(req.PlayerID, 10, 64) if err != nil { return nil, fmt.Errorf("invalid playerID: %w", err) } wallet, err := s.walletSvc.GetCustomerWallet(ctx, playerIDInt) if err != nil { return nil, fmt.Errorf("failed to fetch walllets for player %d: %w", playerIDInt, err) } transfer, err := s.transfetStore.GetTransferByReference(ctx, req.BetTransactionID) if err != nil { return nil, fmt.Errorf("failed to fetch transfer for reference %s: %w", req.BetTransactionID, err) } err = s.walletSvc.UpdateBalance(ctx, wallet.RegularID, domain.Currency(float64(wallet.RegularBalance)+float64(transfer.Amount))) if err != nil { return nil, fmt.Errorf("failed to credit wallet: %w", err) } err = s.transfetStore.UpdateTransferStatus(ctx, transfer.ID, string(domain.STATUS_CANCELLED)) if err != nil { return nil, fmt.Errorf("failed to update transfer status: %w", err) } err = s.transfetStore.UpdateTransferVerification(ctx, transfer.ID, true) if err != nil { return nil, fmt.Errorf("failed to update transfer verification: %w", err) } return &domain.RollbackResponse{Success: true}, nil } func (s *Service) CreateFreeSpin(ctx context.Context, req domain.FreeSpinRequest) (*domain.FreeSpinResponse, error) { body := map[string]any{ "casino_id": s.client.CasinoID, "freespins_count": req.FreeSpinsCount, "end_date": req.EndDate, "player_id": req.PlayerID, } // 3. Call the Atlas client var res domain.FreeSpinResponse if err := s.client.post(ctx, "/freespin", body, &res); err != nil { return nil, fmt.Errorf("failed to create free spin: %w", err) } return &res, nil } func (s *Service) ProcessFreeSpinResult(ctx context.Context, req domain.FreeSpinResultRequest) (*domain.FreeSpinResultResponse, error) { if req.PlayerID == "" || req.TransactionID == "" { return nil, errors.New("missing player_id or transaction_id") } // Credit player with win amount if > 0 if req.Amount > 0 { // This will credit player's balance playerIDInt, err := strconv.ParseInt(req.PlayerID, 10, 64) if err != nil { return nil, fmt.Errorf("invalid playerID: %w", err) } wallet, err := s.walletSvc.GetCustomerWallet(ctx, playerIDInt) if err != nil { return nil, fmt.Errorf("failed to fetch walllets for player %d: %w", playerIDInt, err) } err = s.walletSvc.UpdateBalance(ctx, wallet.RegularID, domain.Currency(float64(wallet.RegularBalance)+req.Amount)) if err != nil { return nil, fmt.Errorf("failed to credit wallet: %w", err) } } return &domain.FreeSpinResultResponse{Success: true}, nil } func (s *Service) ProcessJackPot(ctx context.Context, req domain.JackpotRequest) (*domain.JackpotResponse, error) { if req.PlayerID == "" || req.TransactionID == "" { return nil, errors.New("missing player_id or transaction_id") } // Credit player with win amount if > 0 if req.Amount > 0 { // This will credit player's balance playerIDInt, err := strconv.ParseInt(req.PlayerID, 10, 64) if err != nil { return nil, fmt.Errorf("invalid playerID: %w", err) } wallet, err := s.walletSvc.GetCustomerWallet(ctx, playerIDInt) if err != nil { return nil, fmt.Errorf("failed to fetch walllets for player %d: %w", playerIDInt, err) } _, err = s.walletSvc.AddToWallet(ctx, wallet.RegularID, domain.Currency(req.Amount), domain.ValidInt64{}, domain.PaymentMethod(domain.DEPOSIT), domain.PaymentDetails{}, "") if err != nil { return nil, fmt.Errorf("failed to credit wallet: %w", err) } } return &domain.JackpotResponse{Success: true}, nil }