diff --git a/cmd/main.go b/cmd/main.go index 67eef77..f78ff18 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -48,7 +48,6 @@ import ( "github.com/SamuelTariku/FortuneBet-Backend/internal/services/user" virtualgameservice "github.com/SamuelTariku/FortuneBet-Backend/internal/services/virtualGame" alea "github.com/SamuelTariku/FortuneBet-Backend/internal/services/virtualGame/Alea" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/virtualGame/veli" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/wallet" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/wallet/monitor" @@ -130,7 +129,7 @@ func main() { referalSvc := referralservice.New(referalRepo, *walletSvc, store, cfg, logger) virtualGameSvc := virtualgameservice.New(vitualGameRepo, *walletSvc, store, cfg, logger) aleaService := alea.NewAleaPlayService(vitualGameRepo, *walletSvc, cfg, logger) - veliService := veli.NewVeliPlayService(vitualGameRepo, *walletSvc, cfg, logger) + // veliService := veli.NewVeliPlayService(vitualGameRepo, *walletSvc, cfg, logger) recommendationSvc := recommendation.NewService(recommendationRepo) chapaClient := chapa.NewClient(cfg.CHAPA_BASE_URL, cfg.CHAPA_SECRET_KEY) @@ -225,7 +224,7 @@ func main() { referalSvc, virtualGameSvc, aleaService, - veliService, + // veliService, recommendationSvc, resultSvc, cfg, diff --git a/go.mod b/go.mod index cfc550d..8728f9b 100644 --- a/go.mod +++ b/go.mod @@ -77,4 +77,7 @@ require ( go.uber.org/multierr v1.10.0 // indirect ) -require go.uber.org/atomic v1.9.0 // indirect +require ( + github.com/go-resty/resty/v2 v2.16.5 // indirect + go.uber.org/atomic v1.9.0 // indirect +) diff --git a/go.sum b/go.sum index 8420e2a..a3fd1ae 100644 --- a/go.sum +++ b/go.sum @@ -49,6 +49,8 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/validator/v10 v10.26.0 h1:SP05Nqhjcvz81uJaRfEV0YBSSSGMc/iMaVtFbr3Sw2k= github.com/go-playground/validator/v10 v10.26.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo= +github.com/go-resty/resty/v2 v2.16.5 h1:hBKqmWrr7uRc3euHVqmh1HTHcKn99Smr7o5spptdhTM= +github.com/go-resty/resty/v2 v2.16.5/go.mod h1:hkJtXbA2iKHzJheXYvQ8snQES5ZLGKMwQ07xAwp/fiA= github.com/gofiber/fiber/v2 v2.32.0/go.mod h1:CMy5ZLiXkn6qwthrl03YMyW1NLfj0rhxz2LKl4t7ZTY= github.com/gofiber/fiber/v2 v2.52.6 h1:Rfp+ILPiYSvvVuIPvxrBns+HJp8qGLDnLJawAu27XVI= github.com/gofiber/fiber/v2 v2.52.6/go.mod h1:YEcBbO/FB+5M1IZNBP9FO3J9281zgPAreiI1oqg8nDw= diff --git a/internal/config/config.go b/internal/config/config.go index 802302e..a83da59 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -44,15 +44,14 @@ type AleaPlayConfig struct { SessionTimeout int `mapstructure:"session_timeout"` // In hours } -type VeliGamesConfig struct { - Enabled bool `mapstructure:"enabled"` - APIURL string `mapstructure:"api_url"` - OperatorKey string `mapstructure:"operator_key"` - SecretKey string `mapstructure:"secret_key"` - DefaultCurrency string `mapstructure:"default_currency"` - GameIDs struct { - Aviator string `mapstructure:"aviator"` - } `mapstructure:"game_ids"` +type VeliConfig struct { + APIKey string `mapstructure:"VELI_API_KEY"` + BaseURL string `mapstructure:"VELI_BASE_URL"` + SecretKey string `mapstructure:"VELI_SECRET_KEY"` + OperatorID string `mapstructure:"VELI_OPERATOR_ID"` + Currency string `mapstructure:"VELI_DEFAULT_CURRENCY"` + WebhookURL string `mapstructure:"VELI_WEBHOOK_URL"` + Enabled bool `mapstructure:"Enabled"` } type Config struct { @@ -60,6 +59,7 @@ type Config struct { FIXER_BASE_URL string BASE_CURRENCY domain.IntCurrency Port int + Service string DbUrl string RefreshExpiry int AccessExpiry int @@ -81,8 +81,8 @@ type Config struct { CHAPA_RETURN_URL string Bet365Token string PopOK domain.PopOKConfig - AleaPlay AleaPlayConfig `mapstructure:"alea_play"` - VeliGames VeliGamesConfig `mapstructure:"veli_games"` + AleaPlay AleaPlayConfig `mapstructure:"alea_play"` + VeliGames VeliConfig `mapstructure:"veli_games"` ResendApiKey string ResendSenderEmail string } @@ -236,26 +236,26 @@ func (c *Config) loadEnv() error { if apiURL == "" { apiURL = "https://api.velitech.games" // Default production URL } - c.VeliGames.APIURL = apiURL + c.VeliGames.BaseURL = apiURL operatorKey := os.Getenv("VELI_OPERATOR_KEY") if operatorKey == "" && c.VeliGames.Enabled { return ErrInvalidVeliOperatorKey } - c.VeliGames.OperatorKey = operatorKey + // c.VeliGames.OperatorKey = operatorKey secretKey := os.Getenv("VELI_SECRET_KEY") if secretKey == "" && c.VeliGames.Enabled { return ErrInvalidVeliSecretKey } c.VeliGames.SecretKey = secretKey - c.VeliGames.GameIDs.Aviator = os.Getenv("VELI_GAME_ID_AVIATOR") + // c.VeliGames.GameIDs.Aviator = os.Getenv("VELI_GAME_ID_AVIATOR") defaultCurrency := os.Getenv("VELI_DEFAULT_CURRENCY") if defaultCurrency == "" { defaultCurrency = "USD" // Default currency } - c.VeliGames.DefaultCurrency = defaultCurrency + // c.VeliGames.DefaultCurrency = defaultCurrency c.LogLevel = lvl diff --git a/internal/domain/veli_games.go b/internal/domain/veli_games.go new file mode 100644 index 0000000..3652c32 --- /dev/null +++ b/internal/domain/veli_games.go @@ -0,0 +1,36 @@ +package domain + +import "time" + +type Game struct { + ID string `json:"id"` + Name string `json:"name"` + Description string `json:"description"` + ReleaseDate string `json:"release_date"` + Developer string `json:"developer"` + Publisher string `json:"publisher"` + Genres []string `json:"genres"` + Platforms []string `json:"platforms"` + Price float64 `json:"price"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` +} + +type GameListResponse struct { + Data []Game `json:"data"` + Total int `json:"total"` + Page int `json:"page"` + PerPage int `json:"per_page"` + TotalPages int `json:"total_pages"` +} + +type GameCreateRequest struct { + Name string `json:"name" validate:"required"` + Description string `json:"description" validate:"required"` + ReleaseDate string `json:"release_date" validate:"required"` + Developer string `json:"developer" validate:"required"` + Publisher string `json:"publisher" validate:"required"` + Genres []string `json:"genres" validate:"required"` + Platforms []string `json:"platforms" validate:"required"` + Price float64 `json:"price" validate:"required"` +} diff --git a/internal/logger/mongoLogger/init.go b/internal/logger/mongoLogger/init.go index 9d4b78b..f5ec3a0 100644 --- a/internal/logger/mongoLogger/init.go +++ b/internal/logger/mongoLogger/init.go @@ -10,7 +10,7 @@ import ( func InitLogger() (*zap.Logger, error) { mongoCore, err := NewMongoCore( - "mongodb://root:secret@mongo:27017/?authSource=admin", + os.Getenv("MONGODB_URL"), "logdb", "applogs", zapcore.InfoLevel, diff --git a/internal/logger/mongoLogger/logger.go b/internal/logger/mongoLogger/logger.go index b3bec21..378b928 100644 --- a/internal/logger/mongoLogger/logger.go +++ b/internal/logger/mongoLogger/logger.go @@ -7,6 +7,7 @@ import ( "maps" + "github.com/SamuelTariku/FortuneBet-Backend/internal/config" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" @@ -17,6 +18,7 @@ type MongoCore struct { collection *mongo.Collection level zapcore.Level fields []zapcore.Field + cfg *config.Config } func NewMongoCore(uri, dbName, collectionName string, level zapcore.Level) (zapcore.Core, error) { @@ -73,8 +75,8 @@ func (mc *MongoCore) Write(entry zapcore.Entry, fields []zapcore.Field) error { "fields": logMap, "caller": entry.Caller.String(), "stacktrace": entry.Stack, - "service": "fortunebet-backend", - "env": "dev", + "service": mc.cfg.Service, + "env": mc.cfg.Env, } ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) diff --git a/internal/services/virtualGame/veli/client.go b/internal/services/virtualGame/veli/client.go new file mode 100644 index 0000000..756670d --- /dev/null +++ b/internal/services/virtualGame/veli/client.go @@ -0,0 +1,65 @@ +package veli + +import ( + "context" + "fmt" + "time" + + "github.com/SamuelTariku/FortuneBet-Backend/internal/config" + "github.com/go-resty/resty/v2" +) + +type VeliClient struct { + client *resty.Client + config *config.Config +} + +func NewVeliClient(cfg *config.Config) *VeliClient { + client := resty.New(). + SetBaseURL(cfg.VeliGames.APIKey). + SetHeader("Accept", "application/json"). + SetHeader("X-API-Key", cfg.VeliGames.APIKey). + SetTimeout(30 * time.Second) + + return &VeliClient{ + client: client, + config: cfg, + } +} + +func (vc *VeliClient) Get(ctx context.Context, endpoint string, result interface{}) error { + resp, err := vc.client.R(). + SetContext(ctx). + SetResult(result). + Get(endpoint) + + if err != nil { + return fmt.Errorf("request failed: %w", err) + } + + if resp.IsError() { + return fmt.Errorf("API error: %s", resp.Status()) + } + + return nil +} + +func (vc *VeliClient) Post(ctx context.Context, endpoint string, body interface{}, result interface{}) error { + resp, err := vc.client.R(). + SetContext(ctx). + SetBody(body). + SetResult(result). + Post(endpoint) + + if err != nil { + return fmt.Errorf("request failed: %w", err) + } + + if resp.IsError() { + return fmt.Errorf("API error: %s", resp.Status()) + } + + return nil +} + +// Add other HTTP methods as needed (Put, Delete, etc.) diff --git a/internal/services/virtualGame/veli/service.go b/internal/services/virtualGame/veli/service.go index fc9097a..e6cc57f 100644 --- a/internal/services/virtualGame/veli/service.go +++ b/internal/services/virtualGame/veli/service.go @@ -1,158 +1,162 @@ package veli -import ( - "context" - "crypto/hmac" - "crypto/sha256" - "encoding/hex" - "errors" - "fmt" - "log/slog" - "net/url" - "time" +// import ( +// "context" +// "fmt" +// "log/slog" +// "time" - "github.com/SamuelTariku/FortuneBet-Backend/internal/config" - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - "github.com/SamuelTariku/FortuneBet-Backend/internal/repository" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/wallet" -) +// "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" +// "github.com/SamuelTariku/FortuneBet-Backend/internal/repository" +// "github.com/SamuelTariku/FortuneBet-Backend/internal/services/wallet" +// ) -type VeliPlayService struct { - repo repository.VirtualGameRepository - walletSvc wallet.Service - config *config.VeliGamesConfig - logger *slog.Logger -} +// type Service struct { +// client *VeliClient +// gameRepo repository.VeliGameRepository +// playerRepo repository.VeliPlayerRepository +// txRepo repository.VeliTransactionRepository +// walletSvc wallet.Service +// logger domain.Logger +// } -func NewVeliPlayService( - repo repository.VirtualGameRepository, - walletSvc wallet.Service, - cfg *config.Config, - logger *slog.Logger, -) *VeliPlayService { - return &VeliPlayService{ - repo: repo, - walletSvc: walletSvc, - config: &cfg.VeliGames, - logger: logger, - } -} +// func NewService( +// client *VeliClient, +// gameRepo repository.VeliGameRepository, +// playerRepo repository.VeliPlayerRepository, +// txRepo repository.VeliTransactionRepository, +// walletSvc wallet.Service, +// logger *slog.Logger, +// ) *Service { +// return &Service{ +// client: client, +// gameRepo: gameRepo, +// playerRepo: playerRepo, +// txRepo: txRepo, +// walletSvc: walletSvc, +// logger: logger, +// } +// } -func (s *VeliPlayService) GenerateGameLaunchURL(ctx context.Context, userID int64, gameID, currency, mode string) (string, error) { - session := &domain.VirtualGameSession{ - UserID: userID, - GameID: gameID, - SessionToken: generateSessionToken(userID), - Currency: currency, - Status: "ACTIVE", - CreatedAt: time.Now(), - UpdatedAt: time.Now(), - ExpiresAt: time.Now().Add(24 * time.Hour), - } +// func (s *Service) SyncGames(ctx context.Context) error { +// games, err := s.client.GetGameList(ctx) +// if err != nil { +// return fmt.Errorf("failed to get game list: %w", err) +// } - if err := s.repo.CreateVirtualGameSession(ctx, session); err != nil { - return "", fmt.Errorf("failed to create game session: %w", err) - } +// for _, game := range games { +// existing, err := s.gameRepo.GetGameByID(ctx, game.ID) +// if err != nil && err != domain.ErrGameNotFound { +// return fmt.Errorf("failed to check existing game: %w", err) +// } - // Veli-specific parameters - params := url.Values{ - "operator_key": []string{s.config.OperatorKey}, // Different from Alea's operator_id - "user_id": []string{fmt.Sprintf("%d", userID)}, - "game_id": []string{gameID}, - "currency": []string{currency}, - "mode": []string{mode}, - "timestamp": []string{fmt.Sprintf("%d", time.Now().Unix())}, - } +// if existing == nil { +// // New game - create +// if err := s.gameRepo.CreateGame(ctx, game); err != nil { +// s.logger.Error("failed to create game", "game_id", game.ID, "error", err) +// continue +// } +// } else { +// // Existing game - update +// if err := s.gameRepo.UpdateGame(ctx, game); err != nil { +// s.logger.Error("failed to update game", "game_id", game.ID, "error", err) +// continue +// } +// } +// } - signature := s.generateSignature(params.Encode()) - params.Add("signature", signature) +// return nil +// } - return fmt.Sprintf("%s/launch?%s", s.config.APIURL, params.Encode()), nil -} +// func (s *Service) LaunchGame(ctx context.Context, playerID, gameID string) (string, error) { +// // Verify player exists +// player, err := s.playerRepo.GetPlayer(ctx, playerID) +// if err != nil { +// return "", fmt.Errorf("failed to get player: %w", err) +// } -func (s *VeliPlayService) HandleCallback(ctx context.Context, callback *domain.VeliCallback) error { - if !s.verifyCallbackSignature(callback) { - return errors.New("invalid callback signature") - } +// // Verify game exists +// game, err := s.gameRepo.GetGameByID(ctx, gameID) +// if err != nil { +// return "", fmt.Errorf("failed to get game: %w", err) +// } - // Veli uses round_id instead of transaction_id for idempotency - existing, err := s.repo.GetVirtualGameTransactionByExternalID(ctx, callback.RoundID) - if err != nil || existing != nil { - s.logger.Warn("duplicate round detected", "round_id", callback.RoundID) - return nil - } +// // Call Veli API +// gameURL, err := s.client.LaunchGame(ctx, playerID, gameID) +// if err != nil { +// return "", fmt.Errorf("failed to launch game: %w", err) +// } - session, err := s.repo.GetVirtualGameSessionByToken(ctx, callback.SessionID) - if err != nil { - return fmt.Errorf("failed to get game session: %w", err) - } +// // Create game session record +// session := domain.GameSession{ +// SessionID: fmt.Sprintf("%s-%s-%d", playerID, gameID, time.Now().Unix()), +// PlayerID: playerID, +// GameID: gameID, +// LaunchTime: time.Now(), +// Status: "active", +// } - // Convert amount based on event type (BET, WIN, etc.) - amount := convertAmount(callback.Amount, callback.EventType) +// if err := s.gameRepo.CreateGameSession(ctx, session); err != nil { +// s.logger.Error("failed to create game session", "error", err) +// } - tx := &domain.VirtualGameTransaction{ - SessionID: session.ID, - UserID: session.UserID, - TransactionType: callback.EventType, // e.g., "bet_placed", "game_result" - Amount: amount, - Currency: callback.Currency, - ExternalTransactionID: callback.RoundID, // Veli uses round_id as the unique identifier - Status: "COMPLETED", - CreatedAt: time.Now(), - UpdatedAt: time.Now(), - GameSpecificData: domain.GameSpecificData{ - Multiplier: callback.Multiplier, // Used for Aviator/Plinko - }, - } +// return gameURL, nil +// } - if err := s.processTransaction(ctx, tx, session.UserID); err != nil { - return fmt.Errorf("failed to process transaction: %w", err) - } +// func (s *Service) PlaceBet(ctx context.Context, playerID, gameID string, amount float64) (*domain.VeliTransaction, error) { +// // 1. Verify player balance +// balance, err := s.walletRepo.GetBalance(ctx, playerID) +// if err != nil { +// return nil, fmt.Errorf("failed to get balance: %w", err) +// } - return nil -} +// if balance < amount { +// return nil, domain.ErrInsufficientBalance +// } -func (s *VeliPlayService) generateSignature(data string) string { - h := hmac.New(sha256.New, []byte(s.config.SecretKey)) - h.Write([]byte(data)) - return hex.EncodeToString(h.Sum(nil)) -} +// // 2. Create transaction record +// tx := domain.VeliTransaction{ +// TransactionID: generateTransactionID(), +// PlayerID: playerID, +// GameID: gameID, +// Amount: amount, +// Type: "bet", +// Status: "pending", +// CreatedAt: time.Now(), +// } -func (s *VeliPlayService) verifyCallbackSignature(cb *domain.VeliCallback) bool { - signData := fmt.Sprintf("%s%s%s%.2f%s%d", - cb.RoundID, // Veli uses round_id instead of transaction_id - cb.SessionID, - cb.EventType, - cb.Amount, - cb.Currency, - cb.Timestamp, - ) - expectedSig := s.generateSignature(signData) - return expectedSig == cb.Signature -} +// if err := s.txRepo.CreateTransaction(ctx, tx); err != nil { +// return nil, fmt.Errorf("failed to create transaction: %w", err) +// } -func convertAmount(amount float64, eventType string) int64 { - cents := int64(amount * 100) - if eventType == "bet_placed" { - return -cents // Debit for bets - } - return cents // Credit for wins/results -} +// // 3. Call Veli API +// if err := s.client.PlaceBet(ctx, tx.TransactionID, playerID, gameID, amount); err != nil { +// // Update transaction status +// tx.Status = "failed" +// _ = s.txRepo.UpdateTransaction(ctx, tx) +// return nil, fmt.Errorf("failed to place bet: %w", err) +// } -func generateSessionToken(userID int64) string { - return fmt.Sprintf("veli-%d-%d", userID, time.Now().UnixNano()) -} +// // 4. Deduct from wallet +// if err := s.walletRepo.DeductBalance(ctx, playerID, amount); err != nil { +// // Attempt to rollback +// _ = s.client.RollbackBet(ctx, tx.TransactionID) +// tx.Status = "failed" +// _ = s.txRepo.UpdateTransaction(ctx, tx) +// return nil, fmt.Errorf("failed to deduct balance: %w", err) +// } -func (s *VeliPlayService) processTransaction(ctx context.Context, tx *domain.VirtualGameTransaction, userID int64) error { - wallets, err := s.walletSvc.GetWalletsByUser(ctx, userID) - if err != nil || len(wallets) == 0 { - return errors.New("no wallet available for user") - } - tx.WalletID = wallets[0].ID +// // 5. Update transaction status +// tx.Status = "completed" +// if err := s.txRepo.UpdateTransaction(ctx, tx); err != nil { +// s.logger.Error("failed to update transaction status", "error", err) +// } - if err := s.walletSvc.AddToWallet(ctx, tx.WalletID, domain.Currency(tx.Amount)); err != nil { - return fmt.Errorf("wallet update failed: %w", err) - } +// return &tx, nil +// } - return s.repo.CreateVirtualGameTransaction(ctx, tx) -} +// // Implement SettleBet, RollbackBet, GetBalance, etc. following similar patterns + +// func generateTransactionID() string { +// return fmt.Sprintf("tx-%d", time.Now().UnixNano()) +// } diff --git a/internal/web_server/app.go b/internal/web_server/app.go index d9ef3a2..246bbd5 100644 --- a/internal/web_server/app.go +++ b/internal/web_server/app.go @@ -87,7 +87,7 @@ func NewApp( referralSvc referralservice.ReferralStore, virtualGameSvc virtualgameservice.VirtualGameService, aleaVirtualGameService alea.AleaVirtualGameService, - veliVirtualGameService veli.VeliVirtualGameService, + // veliVirtualGameService veli.VeliVirtualGameService, recommendationSvc recommendation.RecommendationService, resultSvc *result.Service, cfg *config.Config, @@ -131,10 +131,10 @@ func NewApp( leagueSvc: leagueSvc, virtualGameSvc: virtualGameSvc, aleaVirtualGameService: aleaVirtualGameService, - veliVirtualGameService: veliVirtualGameService, - recommendationSvc: recommendationSvc, - resultSvc: resultSvc, - cfg: cfg, + // veliVirtualGameService: veliVirtualGameService, + recommendationSvc: recommendationSvc, + resultSvc: resultSvc, + cfg: cfg, } s.initAppRoutes() diff --git a/internal/web_server/handlers/handlers.go b/internal/web_server/handlers/handlers.go index a5e40a0..a79c8a4 100644 --- a/internal/web_server/handlers/handlers.go +++ b/internal/web_server/handlers/handlers.go @@ -23,7 +23,6 @@ import ( "github.com/SamuelTariku/FortuneBet-Backend/internal/services/user" virtualgameservice "github.com/SamuelTariku/FortuneBet-Backend/internal/services/virtualGame" alea "github.com/SamuelTariku/FortuneBet-Backend/internal/services/virtualGame/Alea" - "github.com/SamuelTariku/FortuneBet-Backend/internal/services/virtualGame/veli" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/wallet" jwtutil "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/jwt" customvalidator "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/validator" @@ -48,13 +47,13 @@ type Handler struct { leagueSvc league.Service virtualGameSvc virtualgameservice.VirtualGameService aleaVirtualGameSvc alea.AleaVirtualGameService - veliVirtualGameSvc veli.VeliVirtualGameService - recommendationSvc recommendation.RecommendationService - authSvc *authentication.Service - resultSvc result.Service - jwtConfig jwtutil.JwtConfig - validator *customvalidator.CustomValidator - Cfg *config.Config + // veliVirtualGameSvc veli.VeliVirtualGameService + recommendationSvc recommendation.RecommendationService + authSvc *authentication.Service + resultSvc result.Service + jwtConfig jwtutil.JwtConfig + validator *customvalidator.CustomValidator + Cfg *config.Config } func New( @@ -68,7 +67,7 @@ func New( referralSvc referralservice.ReferralStore, virtualGameSvc virtualgameservice.VirtualGameService, aleaVirtualGameSvc alea.AleaVirtualGameService, - veliVirtualGameSvc veli.VeliVirtualGameService, + // veliVirtualGameSvc veli.VeliVirtualGameService, recommendationSvc recommendation.RecommendationService, userSvc *user.Service, transactionSvc *transaction.Service, @@ -104,11 +103,11 @@ func New( leagueSvc: leagueSvc, virtualGameSvc: virtualGameSvc, aleaVirtualGameSvc: aleaVirtualGameSvc, - veliVirtualGameSvc: veliVirtualGameSvc, - recommendationSvc: recommendationSvc, - authSvc: authSvc, - resultSvc: resultSvc, - jwtConfig: jwtConfig, - Cfg: cfg, + // veliVirtualGameSvc: veliVirtualGameSvc, + recommendationSvc: recommendationSvc, + authSvc: authSvc, + resultSvc: resultSvc, + jwtConfig: jwtConfig, + Cfg: cfg, } } diff --git a/internal/web_server/handlers/veli_games.go b/internal/web_server/handlers/veli_games.go index 0a32aec..d096ac9 100644 --- a/internal/web_server/handlers/veli_games.go +++ b/internal/web_server/handlers/veli_games.go @@ -1,75 +1,122 @@ package handlers -import ( - "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" - "github.com/gofiber/fiber/v2" -) +// import ( +// "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" +// "github.com/gofiber/fiber/v2" +// ) -// LaunchVeliGame godoc -// @Summary Launch a Veli game -// @Description Generates authenticated launch URL for Veli games -// @Tags Veli Games -// @Security BearerAuth -// @Param game_id path string true "Game ID (e.g., veli_aviator_v1)" -// @Param currency query string false "Currency code" default(USD) -// @Param mode query string false "Game mode" Enums(real, demo) default(real) -// @Success 200 {object} map[string]string "Returns launch URL" -// @Failure 400 {object} map[string]string "Invalid request" -// @Failure 500 {object} map[string]string "Internal server error" -// @Router /api/veli/launch/{game_id} [get] -func (h *Handler) LaunchVeliGame(c *fiber.Ctx) error { - userID := c.Locals("userID").(int64) - gameID := c.Params("game_id") - currency := c.Query("currency", "USD") - mode := c.Query("mode", "real") +// // @Summary Get Veli games list +// // @Description Get list of available Veli games +// // @Tags Virtual Games - Veli Games +// // @Produce json +// // @Success 200 {array} domain.VeliGame +// // @Failure 500 {object} domain.ErrorResponse +// // @Router /veli/games [get] +// func (h *Handler) GetGames(c *fiber.Ctx) error { +// games, err := h.service.GetGames(c.Context()) +// if err != nil { +// return domain.UnExpectedErrorResponse(c) +// } - launchURL, err := h.veliVirtualGameSvc.GenerateGameLaunchURL(c.Context(), userID, gameID, currency, mode) - if err != nil { - h.logger.Error("failed to generate Veli launch URL", - "error", err, - "userID", userID, - "gameID", gameID) - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "error": "failed to launch game", - }) - } +// return c.Status(fiber.StatusOK).JSON(games) +// } - return c.JSON(fiber.Map{ - "launch_url": launchURL, - }) -} +// // @Summary Launch Veli game +// // @Description Get URL to launch a Veli game +// // @Tags Virtual Games - Veli Games +// // @Accept json +// // @Produce json +// // @Param request body LaunchGameRequest true "Launch game request" +// // @Success 200 {object} LaunchGameResponse +// // @Failure 400 {object} domain.ErrorResponse +// // @Failure 500 {object} domain.ErrorResponse +// // @Router /veli/games/launch [post] +// func (h *Handler) LaunchGame(c *fiber.Ctx) error { +// var req struct { +// PlayerID string `json:"player_id" validate:"required"` +// GameID string `json:"game_id" validate:"required"` +// } -// HandleVeliCallback godoc -// @Summary Veli Games webhook handler -// @Description Processes game round settlements from Veli -// @Tags Veli Games -// @Accept json -// @Produce json -// @Param payload body domain.VeliCallback true "Callback payload" -// @Success 200 {object} map[string]string "Callback processed" -// @Failure 400 {object} map[string]string "Invalid payload" -// @Failure 403 {object} map[string]string "Invalid signature" -// @Failure 500 {object} map[string]string "Processing error" -// @Router /webhooks/veli [post] -func (h *Handler) HandleVeliCallback(c *fiber.Ctx) error { - var cb domain.VeliCallback - if err := c.BodyParser(&cb); err != nil { - h.logger.Error("invalid Veli callback format", "error", err) - return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ - "error": "invalid payload format", - }) - } +// if err := c.BodyParser(&req); err != nil { +// return domain.BadRequestResponse(c) +// } - if err := h.veliVirtualGameSvc.HandleCallback(c.Context(), &cb); err != nil { - h.logger.Error("failed to process Veli callback", - "roundID", cb.RoundID, - "error", err) - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "error": "failed to process callback", - }) - } +// gameURL, err := h.service.LaunchGame(c.Context(), req.PlayerID, req.GameID) +// if err != nil { +// return domain.UnExpectedErrorResponse(c) +// } - return c.JSON(fiber.Map{ - "status": "processed", - }) -} +// return c.Status(fiber.StatusOK).JSON(fiber.Map{ +// "url": gameURL, +// }) +// } + +// // @Summary Place bet +// // @Description Place a bet on a Veli game +// // @Tags Virtual Games - Veli Games +// // @Accept json +// // @Produce json +// // @Param request body PlaceBetRequest true "Place bet request" +// // @Success 200 {object} domain.VeliTransaction +// // @Failure 400 {object} domain.ErrorResponse +// // @Failure 500 {object} domain.ErrorResponse +// // @Router /veli/bets [post] +// func (h *Handler) PlaceBet(c *fiber.Ctx) error { +// var req struct { +// PlayerID string `json:"player_id" validate:"required"` +// GameID string `json:"game_id" validate:"required"` +// Amount float64 `json:"amount" validate:"required,gt=0"` +// } + +// if err := c.BodyParser(&req); err != nil { +// return domain.BadRequestResponse(c) +// } + +// tx, err := h.service.PlaceBet(c.Context(), req.PlayerID, req.GameID, req.Amount) +// if err != nil { +// if err == domain.ErrInsufficientBalance { +// return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ +// Message: "Insufficient balance", +// }) +// } +// return domain.UnExpectedErrorResponse(c) +// } + +// return c.Status(fiber.StatusOK).JSON(tx) +// } + +// // @Summary Bet settlement webhook +// // @Description Handle bet settlement from Veli +// // @Tags Virtual Games - Veli Games +// // @Accept json +// // @Produce json +// // @Param request body SettlementRequest true "Settlement request" +// // @Success 200 {object} domain.Response +// // @Failure 400 {object} domain.ErrorResponse +// // @Failure 500 {object} domain.ErrorResponse +// // @Router /veli/webhooks/settlement [post] +// func (h *Handler) HandleSettlement(c *fiber.Ctx) error { +// var req struct { +// TransactionID string `json:"transaction_id" validate:"required"` +// PlayerID string `json:"player_id" validate:"required"` +// Amount float64 `json:"amount" validate:"required"` +// IsWin bool `json:"is_win"` +// } + +// if err := c.BodyParser(&req); err != nil { +// return domain.BadRequestResponse(c) +// } + +// // Verify signature +// if !h.service.VerifyWebhookSignature(c.Request().Body(), c.Get("X-Signature")) { +// return domain.UnauthorizedResponse(c) +// } + +// // Process settlement +// tx, err := h.service.SettleBet(c.Context(), req.TransactionID, req.PlayerID, req.Amount, req.IsWin) +// if err != nil { +// return domain.UnExpectedErrorResponse(c) +// } + +// return c.Status(fiber.StatusOK).JSON(tx) +// } diff --git a/internal/web_server/handlers/virtual_games_hadlers.go b/internal/web_server/handlers/virtual_games_hadlers.go index b47e55f..940c6c0 100644 --- a/internal/web_server/handlers/virtual_games_hadlers.go +++ b/internal/web_server/handlers/virtual_games_hadlers.go @@ -103,16 +103,16 @@ func (h *Handler) HandleBet(c *fiber.Ctx) error { return fiber.NewError(fiber.StatusBadRequest, "Invalid bet request") } - resp, err := h.virtualGameSvc.ProcessBet(c.Context(), &req) - if err != nil { - code := fiber.StatusInternalServerError - if err.Error() == "invalid token" { - code = fiber.StatusUnauthorized - } else if err.Error() == "insufficient balance" { - code = fiber.StatusBadRequest - } - return fiber.NewError(code, err.Error()) - } + resp, _ := h.virtualGameSvc.ProcessBet(c.Context(), &req) + // if err != nil { + // code := fiber.StatusInternalServerError + // // if err.Error() == "invalid token" { + // // code = fiber.StatusUnauthorized + // // } else if err.Error() == "insufficient balance" { + // // code = fiber.StatusBadRequest + // // } + // return fiber.NewError(code, err.Error()) + // } return response.WriteJSON(c, fiber.StatusOK, "Bet processed", resp, nil) } @@ -123,14 +123,14 @@ func (h *Handler) HandleWin(c *fiber.Ctx) error { return fiber.NewError(fiber.StatusBadRequest, "Invalid win request") } - resp, err := h.virtualGameSvc.ProcessWin(c.Context(), &req) - if err != nil { - code := fiber.StatusInternalServerError - if err.Error() == "invalid token" { - code = fiber.StatusUnauthorized - } - return fiber.NewError(code, err.Error()) - } + resp, _ := h.virtualGameSvc.ProcessWin(c.Context(), &req) + // if err != nil { + // code := fiber.StatusInternalServerError + // if err.Error() == "invalid token" { + // code = fiber.StatusUnauthorized + // } + // return fiber.NewError(code, err.Error()) + // } return response.WriteJSON(c, fiber.StatusOK, "Win processed", resp, nil) } @@ -141,17 +141,17 @@ func (h *Handler) HandleCancel(c *fiber.Ctx) error { return fiber.NewError(fiber.StatusBadRequest, "Invalid cancel request") } - resp, err := h.virtualGameSvc.ProcessCancel(c.Context(), &req) - if err != nil { - code := fiber.StatusInternalServerError - switch err.Error() { - case "invalid token": - code = fiber.StatusUnauthorized - case "original bet not found", "invalid original transaction": - code = fiber.StatusBadRequest - } - return fiber.NewError(code, err.Error()) - } + resp, _ := h.virtualGameSvc.ProcessCancel(c.Context(), &req) + // if err != nil { + // code := fiber.StatusInternalServerError + // switch err.Error() { + // case "invalid token": + // code = fiber.StatusUnauthorized + // case "original bet not found", "invalid original transaction": + // code = fiber.StatusBadRequest + // } + // return fiber.NewError(code, err.Error()) + // } return response.WriteJSON(c, fiber.StatusOK, "Cancel processed", resp, nil) } diff --git a/internal/web_server/routes.go b/internal/web_server/routes.go index 784338a..03ef2db 100644 --- a/internal/web_server/routes.go +++ b/internal/web_server/routes.go @@ -30,7 +30,7 @@ func (a *App) initAppRoutes() { a.referralSvc, a.virtualGameSvc, a.aleaVirtualGameService, - a.veliVirtualGameService, + // a.veliVirtualGameService, a.recommendationSvc, a.userSvc, a.transactionSvc, @@ -237,8 +237,8 @@ func (a *App) initAppRoutes() { group.Post("/webhooks/alea-play", a.authMiddleware, h.HandleAleaCallback) //Veli Virtual Game Routes - group.Get("/veli-games/launch", h.LaunchVeliGame) - group.Post("/webhooks/veli-games", h.HandleVeliCallback) + // group.Get("/veli-games/launch", h.LaunchVeliGame) + // group.Post("/webhooks/veli-games", h.HandleVeliCallback) //mongoDB logs ctx := context.Background()