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.Float32()), } return res, nil } func (s *Service) ProcessBet(ctx context.Context, req domain.AtlasBetRequest) (*domain.AtlasBetResponse, error) { // --- 1. Validate CasinoID --- if req.CasinoID != s.client.CasinoID { return nil, fmt.Errorf("BAD_REQUEST: invalid casino_id") } // --- 2. Validate PlayerID --- playerIDInt, err := strconv.ParseInt(req.PlayerID, 10, 64) if err != nil { return nil, fmt.Errorf("BAD_REQUEST: invalid playerID %q", req.PlayerID) } // --- 3. Fetch player wallet --- wallet, err := s.walletSvc.GetCustomerWallet(ctx, playerIDInt) if err != nil { return nil, fmt.Errorf("failed to fetch wallet for player %d: %w", playerIDInt, err) } // --- 4. Convert balance using Float32() --- realBalance := float64(wallet.RegularBalance.Float32()) // --- 5. Ensure sufficient balance --- if realBalance < req.Amount { return nil, domain.ErrInsufficientBalance } // --- 6. Deduct amount from wallet --- newBalance := realBalance - req.Amount err = s.walletSvc.UpdateBalance(ctx, wallet.RegularID, domain.ToCurrency(float32(newBalance))) if err != nil { return nil, fmt.Errorf("failed to debit wallet: %w", err) } // --- 7. Build response --- res := &domain.AtlasBetResponse{ PlayerID: req.PlayerID, Balance: newBalance, } return res, nil } func (s *Service) ProcessBetWin(ctx context.Context, req domain.AtlasBetWinRequest) (*domain.AtlasBetWinResponse, error) { // --- 1. Validate CasinoID --- if req.CasinoID != s.client.CasinoID { return nil, fmt.Errorf("BAD_REQUEST: invalid casino_id") } // --- 2. Validate PlayerID --- playerIDInt, err := strconv.ParseInt(req.PlayerID, 10, 64) if err != nil { return nil, fmt.Errorf("BAD_REQUEST: invalid playerID %q", req.PlayerID) } // --- 3. Fetch player wallet --- wallet, err := s.walletSvc.GetCustomerWallet(ctx, playerIDInt) if err != nil { return nil, fmt.Errorf("failed to fetch wallet for player %d: %w", playerIDInt, err) } // --- 4. Convert balance using Float32() --- realBalance := float64(wallet.RegularBalance.Float32()) // --- 5. Ensure sufficient balance for bet --- if realBalance < req.BetAmount { return nil, domain.ErrInsufficientBalance } // --- 6. Deduct bet amount --- debitedBalance := realBalance - req.BetAmount err = s.walletSvc.UpdateBalance(ctx, wallet.RegularID, domain.ToCurrency(float32(debitedBalance))) if err != nil { return nil, fmt.Errorf("failed to debit wallet: %w", err) } // --- 7. Apply win amount (if any) --- finalBalance := debitedBalance if req.WinAmount > 0 { finalBalance = debitedBalance + req.WinAmount err = s.walletSvc.UpdateBalance(ctx, wallet.RegularID, domain.ToCurrency(float32(finalBalance))) if err != nil { return nil, fmt.Errorf("failed to credit wallet: %w", err) } } // --- 8. Build response --- res := &domain.AtlasBetWinResponse{ PlayerID: req.PlayerID, Balance: finalBalance, } return res, nil } func (s *Service) ProcessRoundResult(ctx context.Context, req domain.RoundResultRequest) (*domain.RoundResultResponse, error) { // --- 1. Validate required fields --- if req.PlayerID == "" || req.TransactionID == "" { return nil, fmt.Errorf("BAD_REQUEST: missing player_id or transaction_id") } // --- 2. Credit player if win amount > 0 --- if req.Amount > 0 { playerIDInt, err := strconv.ParseInt(req.PlayerID, 10, 64) if err != nil { return nil, fmt.Errorf("BAD_REQUEST: invalid playerID %q", req.PlayerID) } // --- 3. Fetch player wallet --- wallet, err := s.walletSvc.GetCustomerWallet(ctx, playerIDInt) if err != nil { return nil, fmt.Errorf("failed to fetch wallet for player %d: %w", playerIDInt, err) } // --- 4. Convert balance and apply win amount --- currentBalance := float64(wallet.RegularBalance.Float32()) newBalance := currentBalance + req.Amount err = s.walletSvc.UpdateBalance(ctx, wallet.RegularID, domain.ToCurrency(float32(newBalance))) if err != nil { return nil, fmt.Errorf("failed to credit wallet: %w", err) } } // --- 5. Build response --- return &domain.RoundResultResponse{ Success: true, }, nil } func (s *Service) ProcessRollBack(ctx context.Context, req domain.RollbackRequest) (*domain.RollbackResponse, error) { // --- 1. Validate required fields --- if req.PlayerID == "" || req.BetTransactionID == "" { return nil, fmt.Errorf("BAD_REQUEST: missing player_id or bet_transaction_id") } // --- 2. Parse PlayerID --- playerIDInt, err := strconv.ParseInt(req.PlayerID, 10, 64) if err != nil { return nil, fmt.Errorf("BAD_REQUEST: invalid playerID %q", req.PlayerID) } // --- 3. Fetch player wallet --- wallet, err := s.walletSvc.GetCustomerWallet(ctx, playerIDInt) if err != nil { return nil, fmt.Errorf("failed to fetch wallet for player %d: %w", playerIDInt, err) } // --- 4. Fetch original transfer --- 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) } // --- 5. Compute new balance using Float32() conversion --- currentBalance := float64(wallet.RegularBalance.Float32()) newBalance := currentBalance + float64(transfer.Amount) // --- 6. Credit player wallet --- err = s.walletSvc.UpdateBalance(ctx, wallet.RegularID, domain.ToCurrency(float32(newBalance))) if err != nil { return nil, fmt.Errorf("failed to credit wallet: %w", err) } // --- 7. Update transfer status and verification --- 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) } // --- 8. Build response --- 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") } if req.Amount <= 0 { // No winnings to process return &domain.FreeSpinResultResponse{Success: true}, nil } 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 wallet 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) } // Optionally record the transaction in your transfer history for auditing // _ = s.transfetStore.CreateGamePayoutTransfer(...) 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") } if req.Amount <= 0 { // No jackpot winnings return &domain.JackpotResponse{Success: true}, nil } 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 wallet 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{ ReferenceNumber: domain.ValidString{ Value: req.TransactionID, Valid: true, }, // Description: fmt.Sprintf("Jackpot win - Txn: %s", req.TransactionID), }, "", ) if err != nil { return nil, fmt.Errorf("failed to credit wallet: %w", err) } return &domain.JackpotResponse{Success: true}, nil }