diff --git a/cmd/main.go b/cmd/main.go index a5c223e..f974c5a 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -149,12 +149,12 @@ func main() { cfg, ) - marketSettingRepo := repository.NewMarketSettingStore(store) + marketSettingRepo := repository.NewMarketSettingStore(store) if err := marketSettingRepo.EnsureAllMarketSettingsExist(context.Background()); err != nil { log.Fatalf("failed to ensure market settings: %v", err) } - + oddsSvc := odds.New( repository.NewOddStore(store), marketSettingRepo, @@ -251,7 +251,7 @@ func main() { virtualGameSvc := virtualgameservice.New(virtualGamesRepo, *walletSvc, store, cfg, logger) aleaService := alea.NewAleaPlayService(virtualGamesRepo, *walletSvc, cfg, logger) veliCLient := veli.NewClient(cfg, walletSvc) - veliVirtualGameService := veli.New(virtualGameSvc, virtualGamesRepo, veliCLient, walletSvc, repository.NewTransferStore(store), domain.MongoDBLogger, cfg) + veliVirtualGameService := veli.New(virtualGameSvc, virtualGamesRepo, *store, veliCLient, walletSvc, repository.NewTransferStore(store), domain.MongoDBLogger, cfg) orchestrationSvc := orchestration.New( virtualGameSvc, virtualGamesRepo, diff --git a/db/query/virtual_games.sql b/db/query/virtual_games.sql index 32b61d1..08110e5 100644 --- a/db/query/virtual_games.sql +++ b/db/query/virtual_games.sql @@ -166,6 +166,7 @@ RETURNING id, status, created_at, updated_at; + -- name: GetVirtualGameTransactionByExternalID :one SELECT id, session_id, @@ -180,6 +181,7 @@ SELECT id, updated_at FROM virtual_game_transactions WHERE external_transaction_id = $1; + -- name: UpdateVirtualGameTransactionStatus :exec UPDATE virtual_game_transactions SET status = $2, diff --git a/internal/services/virtualGame/veli/service.go b/internal/services/virtualGame/veli/service.go index 865cdfe..c54c740 100644 --- a/internal/services/virtualGame/veli/service.go +++ b/internal/services/virtualGame/veli/service.go @@ -30,6 +30,7 @@ var ( type Service struct { virtualGameSvc virtualgameservice.VirtualGameService repo repository.VirtualGameRepository + genRepo repository.Store client *Client walletSvc *wallet.Service transfetStore ports.TransferStore @@ -40,6 +41,7 @@ type Service struct { func New( virtualGameSvc virtualgameservice.VirtualGameService, repo repository.VirtualGameRepository, + genRepo repository.Store, client *Client, walletSvc *wallet.Service, transferStore ports.TransferStore, @@ -49,6 +51,7 @@ func New( return &Service{ virtualGameSvc: virtualGameSvc, repo: repo, + genRepo: genRepo, client: client, walletSvc: walletSvc, transfetStore: transferStore, @@ -160,6 +163,15 @@ func (s *Service) StartGame(ctx context.Context, req domain.GameStartRequest) (* // return nil, fmt.Errorf("provider %s is disabled", req.ProviderID) // } + playerIDInt64, err := strconv.ParseInt(req.PlayerID, 10, 64) + if err != nil { + return nil, fmt.Errorf("invalid PlayerID: %w", err) + } + + if _, err := s.genRepo.GetUserByID(ctx, playerIDInt64); err != nil { + return nil, ErrPlayerNotFound + } + // 2. Prepare signature params sigParams := map[string]any{ "sessionId": req.SessionID, @@ -180,10 +192,10 @@ func (s *Service) StartGame(ctx context.Context, req domain.GameStartRequest) (* return nil, fmt.Errorf("failed to start game with provider %s: %w", req.ProviderID, err) } - playerIDInt64, err := strconv.ParseInt(req.PlayerID, 10, 64) - if err != nil { - return nil, fmt.Errorf("invalid PlayerID: %w", err) - } + // playerIDInt64, err := strconv.ParseInt(req.PlayerID, 10, 64) + // if err != nil { + // return nil, fmt.Errorf("invalid PlayerID: %w", err) + // } session := &domain.VirtualGameSession{ UserID: playerIDInt64, @@ -243,6 +255,15 @@ func (s *Service) GetBalance(ctx context.Context, req domain.BalanceRequest) (*d // return nil, fmt.Errorf("PLAYER_NOT_FOUND: no wallet found for player %s", req.PlayerID) // } + // playerIDInt64, err := strconv.ParseInt(req.PlayerID, 10, 64) + // if err != nil { + // return nil, fmt.Errorf("invalid PlayerID: %w", err) + // } + + if _, err := s.genRepo.GetUserByID(ctx, playerIDInt64); err != nil { + return nil, ErrPlayerNotFound + } + wallet, err := s.walletSvc.GetCustomerWallet(ctx, playerIDInt64) if err != nil { return nil, fmt.Errorf("failed to read user wallets") @@ -285,13 +306,26 @@ func (s *Service) ProcessBet(ctx context.Context, req domain.BetRequest) (*domai return nil, fmt.Errorf("BAD_REQUEST: invalid PlayerID %s", req.PlayerID) } + if _, err := s.genRepo.GetUserByID(ctx, playerIDInt64); err != nil { + return nil, ErrPlayerNotFound + } + + existingTx, err := s.repo.GetVirtualGameTransactionByExternalID(ctx, req.TransactionID) + if err != nil { + return nil, fmt.Errorf("failed checking idempotency: %w", err) + } + + if existingTx != nil { + // Idempotent return — already processed + return nil, fmt.Errorf("DUPLICATE_TRANSACTION") + } + wallet, err := s.walletSvc.GetCustomerWallet(ctx, playerIDInt64) if err != nil { return nil, fmt.Errorf("failed to read user wallets") } - - bonusBalance := float64(wallet.StaticBalance.Float32()) + // bonusBalance := float64(wallet.StaticBalance.Float32()) // --- 4. Check sufficient balance --- if float64(wallet.RegularBalance.Float32()) < req.Amount.Amount { @@ -321,11 +355,26 @@ func (s *Service) ProcessBet(ctx context.Context, req domain.BetRequest) (*domai UsedBonusAmount: 0, } - if bonusBalance > 0 { - res.Bonus = &domain.BalanceDetail{ - Currency: req.Amount.Currency, - Amount: bonusBalance, - } + // if bonusBalance > 0 { + // res.Bonus = &domain.BalanceDetail{ + // Currency: req.Amount.Currency, + // Amount: bonusBalance, + // } + // } + + if err = s.repo.CreateVirtualGameTransaction(ctx, &domain.VirtualGameTransaction{ + UserID: playerIDInt64, + Provider: req.ProviderID, + GameID: req.GameID, + WalletID: wallet.RegularID, + TransactionType: "BET", + Amount: int64(req.Amount.Amount), + Currency: req.Amount.Currency, + ExternalTransactionID: req.TransactionID, + Status: "pending", + GameRoundID: req.RoundID, + }); err != nil { + return nil, fmt.Errorf("failed to log virtual game transaction: %w", err) } return res, nil @@ -338,6 +387,10 @@ func (s *Service) ProcessWin(ctx context.Context, req domain.WinRequest) (*domai return nil, fmt.Errorf("BAD_REQUEST: invalid PlayerID %s", req.PlayerID) } + if _, err := s.genRepo.GetUserByID(ctx, playerIDInt64); err != nil { + return nil, ErrPlayerNotFound + } + // --- 2. Get player wallets --- wallet, err := s.walletSvc.GetCustomerWallet(ctx, playerIDInt64) if err != nil { @@ -346,7 +399,7 @@ func (s *Service) ProcessWin(ctx context.Context, req domain.WinRequest) (*domai // --- 3. Convert balances safely using Float32() --- realBalance := float64(wallet.RegularBalance.Float32()) - bonusBalance := float64(wallet.StaticBalance.Float32()) + // bonusBalance := float64(wallet.StaticBalance.Float32()) // --- 4. Apply winnings --- winAmount := req.Amount.Amount @@ -378,12 +431,12 @@ func (s *Service) ProcessWin(ctx context.Context, req domain.WinRequest) (*domai UsedBonusAmount: usedBonus, } - if bonusBalance > 0 { - res.Bonus = &domain.BalanceDetail{ - Currency: req.Amount.Currency, - Amount: bonusBalance, - } - } + // if bonusBalance > 0 { + // res.Bonus = &domain.BalanceDetail{ + // Currency: req.Amount.Currency, + // Amount: bonusBalance, + // } + // } return res, nil } @@ -395,6 +448,10 @@ func (s *Service) ProcessCancel(ctx context.Context, req domain.CancelRequest) ( return nil, fmt.Errorf("BAD_REQUEST: invalid PlayerID %q", req.PlayerID) } + if _, err := s.genRepo.GetUserByID(ctx, playerIDInt64); err != nil { + return nil, ErrPlayerNotFound + } + // --- 2. Get player wallets --- wallet, err := s.walletSvc.GetCustomerWallet(ctx, playerIDInt64) if err != nil { @@ -403,7 +460,7 @@ func (s *Service) ProcessCancel(ctx context.Context, req domain.CancelRequest) ( // --- 3. Convert balances using Float32() --- realBalance := float64(wallet.RegularBalance.Float32()) - bonusBalance := float64(wallet.StaticBalance.Float32()) + // bonusBalance := float64(wallet.StaticBalance.Float32()) // --- 4. Determine refund amount --- var refundAmount float64 @@ -454,12 +511,12 @@ func (s *Service) ProcessCancel(ctx context.Context, req domain.CancelRequest) ( UsedBonusAmount: usedBonus, } - if bonusBalance > 0 { - res.Bonus = &domain.BalanceDetail{ - Currency: req.AdjustmentRefund.Currency, - Amount: bonusBalance, - } - } + // if bonusBalance > 0 { + // res.Bonus = &domain.BalanceDetail{ + // Currency: req.AdjustmentRefund.Currency, + // Amount: bonusBalance, + // } + // } return res, nil } diff --git a/internal/web_server/handlers/virtual_games_hadlers.go b/internal/web_server/handlers/virtual_games_hadlers.go index c03c1a2..2827e9a 100644 --- a/internal/web_server/handlers/virtual_games_hadlers.go +++ b/internal/web_server/handlers/virtual_games_hadlers.go @@ -312,14 +312,6 @@ func (h *Handler) HandlePlayerInfo(c *fiber.Ctx) error { } func (h *Handler) HandleBet(c *fiber.Ctx) error { - // userID := c.Locals("user_id") - // fmt.Printf("\n\nBet User ID is%v\n\n",userID) - // if userID == "" { - // return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ - // Message: "Failed to process Bet request", - // Error: "Invalid user identification", - // }) - // } // Read the raw body body := c.Body() if len(body) == 0 { @@ -354,8 +346,18 @@ func (h *Handler) HandleBet(c *fiber.Ctx) error { if err != nil { if errors.Is(err, veli.ErrDuplicateTransaction) { return c.Status(fiber.StatusConflict).JSON(domain.ErrorResponse{ - Message: "Duplicate transaction", - Error: "DUPLICATE_TRANSACTION", + // Message: "Duplicate transaction", + Error: veli.ErrDuplicateTransaction.Error(), + }) + } else if errors.Is(err, veli.ErrInsufficientBalance) { + return c.Status(fiber.StatusConflict).JSON(domain.ErrorResponse{ + // Message: "Wallet balance is insufficient", + Error: veli.ErrInsufficientBalance.Error(), + }) + } else if errors.Is(err, veli.ErrPlayerNotFound) { + return c.Status(fiber.StatusConflict).JSON(domain.ErrorResponse{ + // Message: "User not found", + Error: veli.ErrPlayerNotFound.Error(), }) } return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ @@ -471,8 +473,13 @@ func (h *Handler) HandleWin(c *fiber.Ctx) error { if err != nil { if errors.Is(err, veli.ErrDuplicateTransaction) { return c.Status(fiber.StatusConflict).JSON(domain.ErrorResponse{ - Message: "Duplicate transaction", - Error: "DUPLICATE_TRANSACTION", + // Message: "Duplicate transaction", + Error: veli.ErrDuplicateTransaction.Error(), + }) + } else if errors.Is(err, veli.ErrPlayerNotFound) { + return c.Status(fiber.StatusConflict).JSON(domain.ErrorResponse{ + // Message: "Duplicate transaction", + Error: veli.ErrPlayerNotFound.Error(), }) } return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ @@ -543,8 +550,13 @@ func (h *Handler) HandleCancel(c *fiber.Ctx) error { if err != nil { if errors.Is(err, veli.ErrDuplicateTransaction) { return c.Status(fiber.StatusConflict).JSON(domain.ErrorResponse{ - Message: "Duplicate transaction", - Error: "DUPLICATE_TRANSACTION", + // Message: "Duplicate transaction", + Error: veli.ErrDuplicateTransaction.Error(), + }) + } else if errors.Is(err, veli.ErrPlayerNotFound) { + return c.Status(fiber.StatusConflict).JSON(domain.ErrorResponse{ + // Message: "User not found", + Error: veli.ErrPlayerNotFound.Error(), }) } return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{