Yimaru-BackEnd/internal/web_server/handlers/veli_games.go

507 lines
16 KiB
Go
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package handlers
import (
"context"
"errors"
"fmt"
"time"
// "fmt"
"strings"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/virtualGame/veli"
"github.com/gofiber/fiber/v2"
"go.uber.org/zap"
)
// GetProviders godoc
// @Summary Get game providers
// @Description Retrieves the list of VeliGames providers
// @Tags Virtual Games - VeliGames
// @Accept json
// @Produce json
// @Param request body domain.ProviderRequest true "Brand ID and paging options"
// @Success 200 {object} domain.Response{data=[]domain.ProviderResponse}
// @Failure 400 {object} domain.ErrorResponse
// @Failure 401 {object} domain.ErrorResponse
// @Failure 500 {object} domain.ErrorResponse
// @Router /api/v1/veli/providers [post]
func (h *Handler) GetProviders(c *fiber.Ctx) error {
var req domain.ProviderRequest
if err := c.BodyParser(&req); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
Message: "Failed to retrieve providers",
Error: err.Error(),
})
}
if req.BrandID == "" {
req.BrandID = h.Cfg.VeliGames.BrandID // default
}
res, err := h.veliVirtualGameSvc.GetProviders(context.Background(), req)
if err != nil {
h.InternalServerErrorLogger().Error("Failed to [VeliGameHandler]GetProviders",
zap.Any("request", req),
zap.Error(err),
)
return c.Status(fiber.StatusBadGateway).JSON(domain.ErrorResponse{
Message: "Failed to retrieve providers",
Error: err.Error(),
})
}
return c.Status(fiber.StatusOK).JSON(domain.Response{
Message: "Providers retrieved successfully",
Data: res,
StatusCode: 200,
Success: true,
})
}
// GetGamesByProvider godoc
// @Summary Get games by provider
// @Description Retrieves games for the specified provider
// @Tags Virtual Games - VeliGames
// @Accept json
// @Produce json
// @Param request body domain.GameListRequest true "Brand and Provider ID"
// @Success 200 {object} domain.Response
// @Failure 400 {object} domain.ErrorResponse
// @Failure 502 {object} domain.ErrorResponse
// @Router /api/v1/veli/games-list [post]
func (h *Handler) GetGamesByProvider(c *fiber.Ctx) error {
var req domain.GameListRequest
if err := c.BodyParser(&req); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
Message: "Invalid request body",
Error: err.Error(),
})
}
// Default brand if not provided
if req.BrandID == "" {
req.BrandID = h.Cfg.VeliGames.BrandID
}
res, err := h.veliVirtualGameSvc.GetGames(context.Background(), req)
if err != nil {
h.InternalServerErrorLogger().Error("Failed to [VeliGameHandler]GetGames",
zap.Any("request", req),
zap.Error(err),
)
// Handle provider disabled case specifically
if strings.Contains(err.Error(), "is disabled") {
return c.Status(fiber.StatusForbidden).JSON(domain.ErrorResponse{
Message: "Provider is disabled",
Error: err.Error(),
})
}
// Fallback for other errors
return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
Message: "Failed to retrieve games",
Error: err.Error(),
})
}
return c.Status(fiber.StatusOK).JSON(domain.Response{
Message: "Games retrieved successfully",
Data: res,
StatusCode: fiber.StatusOK,
Success: true,
})
}
// StartGame godoc
// @Summary Start a real game session
// @Description Starts a real VeliGames session with the given player and game info
// @Tags Virtual Games - VeliGames
// @Accept json
// @Produce json
// @Param request body domain.GameStartRequest true "Start game input"
// @Success 200 {object} domain.Response{data=domain.GameStartResponse}
// @Failure 400 {object} domain.ErrorResponse
// @Failure 502 {object} domain.ErrorResponse
// @Router /api/v1/veli/start-game [post]
func (h *Handler) StartGame(c *fiber.Ctx) error {
var req domain.GameStartRequest
if err := c.BodyParser(&req); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
Message: "Invalid request body",
Error: err.Error(),
})
}
// Default brand if not provided
if req.BrandID == "" {
req.BrandID = h.Cfg.VeliGames.BrandID
}
// userId := c.Locals("user_id")
req.IP = c.IP()
req.PlayerID = fmt.Sprintf("%v", c.Locals("user_id"))
// 1⃣ Call StartGame service
res, err := h.veliVirtualGameSvc.StartGame(context.Background(), req)
if err != nil {
h.InternalServerErrorLogger().Error("Failed to [VeliGameHandler]StartGame",
zap.Any("request", req),
zap.Error(err),
)
if strings.Contains(err.Error(), "is disabled") {
return c.Status(fiber.StatusForbidden).JSON(domain.ErrorResponse{
Message: "Provider is disabled",
Error: err.Error(),
})
}
return c.Status(fiber.StatusBadGateway).JSON(domain.ErrorResponse{
Message: "Failed to start game",
Error: err.Error(),
})
}
// 2⃣ Game started successfully → Update total_games_played
go func() {
ctx := context.Background()
reportDate := time.Now().Truncate(24 * time.Hour)
reportType := "daily"
// Increment total_games_played by 1
err := h.orchestrationSvc.UpdateVirtualGameProviderReportByDate(
ctx,
req.ProviderID,
reportDate,
reportType,
1, // increment total_games_played by 1
0,
0,
1,
)
if err != nil {
h.InternalServerErrorLogger().Error("Failed to update total_games_played",
zap.String("provider_id", req.ProviderID),
zap.Error(err),
)
}
}()
// 3⃣ Return response to user
return c.Status(fiber.StatusOK).JSON(domain.Response{
Message: "Game started successfully",
Data: res,
StatusCode: fiber.StatusOK,
Success: true,
})
}
// StartDemoGame godoc
// @Summary Start a demo game session
// @Description Starts a demo session of the specified game (must support demo mode)
// @Tags Virtual Games - VeliGames
// @Accept json
// @Produce json
// @Param request body domain.DemoGameRequest true "Start demo game input"
// @Success 200 {object} domain.Response{data=domain.GameStartResponse}
// @Failure 400 {object} domain.ErrorResponse
// @Failure 502 {object} domain.ErrorResponse
// @Router /api/v1/veli/start-demo-game [post]
func (h *Handler) StartDemoGame(c *fiber.Ctx) error {
var req domain.DemoGameRequest
if err := c.BodyParser(&req); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
Message: "Invalid request body",
Error: err.Error(),
})
}
// Default brand if not provided
if req.BrandID == "" {
req.BrandID = h.Cfg.VeliGames.BrandID
}
req.IP = c.IP()
res, err := h.veliVirtualGameSvc.StartDemoGame(context.Background(), req)
if err != nil {
h.InternalServerErrorLogger().Error("Failed to [VeliGameHandler]StartDemoGame",
zap.Any("request", req),
zap.Error(err),
)
// Handle provider disabled case specifically
if strings.Contains(err.Error(), "is disabled") {
return c.Status(fiber.StatusForbidden).JSON(domain.ErrorResponse{
Message: "Provider is disabled",
Error: err.Error(),
})
}
// Fallback for other errors
return c.Status(fiber.StatusBadGateway).JSON(domain.ErrorResponse{
Message: "Failed to start demo game",
Error: err.Error(),
})
}
return c.Status(fiber.StatusOK).JSON(domain.Response{
Message: "Demo game started successfully",
Data: res,
StatusCode: fiber.StatusOK,
Success: true,
})
}
func (h *Handler) GetBalance(c *fiber.Ctx) error {
var req domain.BalanceRequest
if err := c.BodyParser(&req); err != nil {
// return fiber.NewError(fiber.StatusBadRequest, "Invalid request body")
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
Message: "Invalid request body",
Error: err.Error(),
})
}
// Optionally verify signature here...
balance, err := h.veliVirtualGameSvc.GetBalance(c.Context(), req)
if err != nil {
// return fiber.NewError(fiber.StatusInternalServerError, err.Error())
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
Message: "Failed to retrieve balance",
Error: err.Error(),
})
}
return c.JSON(balance)
}
func (h *Handler) PlaceBet(c *fiber.Ctx) error {
var req domain.BetRequest
if err := c.BodyParser(&req); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
Message: "Invalid request body",
Error: err.Error(),
})
}
// 1⃣ Process the bet with the external provider
res, err := h.veliVirtualGameSvc.ProcessBet(c.Context(), req)
if err != nil {
if errors.Is(err, veli.ErrDuplicateTransaction) {
return fiber.NewError(fiber.StatusConflict, "DUPLICATE_TRANSACTION")
}
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
Message: "Failed to process bet",
Error: err.Error(),
})
}
// 2⃣ If bet successful → update total_bets in the report
go func() {
ctx := context.Background()
reportDate := time.Now().Truncate(24 * time.Hour)
reportType := "daily"
// Increment total_bets by the bet amount
err := h.orchestrationSvc.UpdateVirtualGameProviderReportByDate(
ctx,
req.ProviderID,
reportDate,
reportType,
0, // total_games_played (no change)
req.Amount.Amount, // add this bet to total_bets
0, // total_payouts (no change)
0, // total_players (no change)
)
if err != nil {
h.InternalServerErrorLogger().Error("Failed to update total_bets after bet",
zap.String("provider_id", req.ProviderID),
zap.Float64("bet_amount", req.Amount.Amount),
zap.Error(err),
)
}
}()
// 3⃣ Return success response
return c.Status(fiber.StatusOK).JSON(domain.Response{
Message: "Bet processed successfully",
Data: res,
StatusCode: fiber.StatusOK,
Success: true,
})
}
func (h *Handler) RegisterWin(c *fiber.Ctx) error {
var req domain.WinRequest
if err := c.BodyParser(&req); err != nil {
// return fiber.NewError(fiber.StatusBadRequest, "Invalid request body")
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
Message: "Invalid request body",
Error: err.Error(),
})
}
res, err := h.veliVirtualGameSvc.ProcessWin(c.Context(), req)
if err != nil {
if errors.Is(err, veli.ErrDuplicateTransaction) {
return fiber.NewError(fiber.StatusConflict, "DUPLICATE_TRANSACTION")
}
// return fiber.NewError(fiber.StatusInternalServerError, err.Error())
return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
Message: "Failed to process win",
Error: err.Error(),
})
}
return c.JSON(res)
}
func (h *Handler) CancelTransaction(c *fiber.Ctx) error {
var req domain.CancelRequest
if err := c.BodyParser(&req); err != nil {
// return fiber.NewError(fiber.StatusBadRequest, "Invalid request body")
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
Message: "Invalid request body",
Error: err.Error(),
})
}
res, err := h.veliVirtualGameSvc.ProcessCancel(c.Context(), req)
if err != nil {
if errors.Is(err, veli.ErrDuplicateTransaction) {
return fiber.NewError(fiber.StatusConflict, "DUPLICATE_TRANSACTION")
}
// return fiber.NewError(fiber.StatusInternalServerError, err.Error())
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
Message: "Failed to process cancel",
Error: err.Error(),
})
}
return c.JSON(res)
}
// GetGamingActivity godoc
// @Summary Get Veli Gaming Activity
// @Description Retrieves successfully processed gaming activity transactions (BET, WIN, CANCEL) from Veli Games
// @Tags Virtual Games - VeliGames
// @Accept json
// @Produce json
// @Param request body domain.GamingActivityRequest true "Gaming Activity Request"
// @Success 200 {object} domain.Response{data=domain.GamingActivityResponse}
// @Failure 400 {object} domain.ErrorResponse
// @Failure 500 {object} domain.ErrorResponse
// @Router /api/v1/veli/gaming-activity [post]
func (h *Handler) GetGamingActivity(c *fiber.Ctx) error {
var req domain.GamingActivityRequest
if err := c.BodyParser(&req); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
Message: "Invalid request payload",
Error: err.Error(),
})
}
resp, err := h.veliVirtualGameSvc.GetGamingActivity(c.Context(), req)
if err != nil {
h.InternalServerErrorLogger().Error("Failed to [VeliGameHandler]GetGamingActivity",
zap.Any("request", req),
zap.Error(err),
)
return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
Message: "Failed to retrieve gaming activity",
Error: err.Error(),
})
}
return c.Status(fiber.StatusOK).JSON(domain.Response{
Message: "Gaming activity retrieved successfully",
Data: resp,
StatusCode: fiber.StatusOK,
Success: true,
})
}
// GetHugeWins godoc
// @Summary Get Veli Huge Wins
// @Description Retrieves huge win transactions based on brand configuration (e.g. win > 10000 USD or 100x bet)
// @Tags Virtual Games - VeliGames
// @Accept json
// @Produce json
// @Param request body domain.HugeWinsRequest true "Huge Wins Request"
// @Success 200 {object} domain.Response{data=domain.HugeWinsResponse}
// @Failure 400 {object} domain.ErrorResponse
// @Failure 500 {object} domain.ErrorResponse
// @Router /api/v1/veli/huge-wins [post]
func (h *Handler) GetHugeWins(c *fiber.Ctx) error {
var req domain.HugeWinsRequest
if err := c.BodyParser(&req); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
Message: "Invalid request payload",
Error: err.Error(),
})
}
resp, err := h.veliVirtualGameSvc.GetHugeWins(c.Context(), req)
if err != nil {
h.InternalServerErrorLogger().Error("Failed to [VeliGameHandler]GetHugeWins",
zap.Any("request", req),
zap.Error(err),
)
return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
Message: "Failed to retrieve huge wins",
Error: err.Error(),
})
}
return c.Status(fiber.StatusOK).JSON(domain.Response{
Message: "Huge wins retrieved successfully",
Data: resp,
StatusCode: fiber.StatusOK,
Success: true,
})
}
// GetCreditBalances godoc
// @Summary Get VeliGames credit balances for a brand
// @Description Fetches current credit balances per currency for the specified brand
// @Tags Virtual Games - VeliGames
// @Accept json
// @Produce json
// @Param brandId query string true "Brand ID"
// @Success 200 {object} domain.Response{data=[]domain.CreditBalance}
// @Failure 400 {object} domain.ErrorResponse
// @Failure 502 {object} domain.ErrorResponse
// @Router /api/v1/veli/credit-balances [get]
func (h *Handler) GetCreditBalances(c *fiber.Ctx) error {
brandID := c.Query("brandId", h.Cfg.VeliGames.BrandID) // Default brand if not provided
if brandID == "" {
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
Message: "Brand ID is required",
Error: "missing brandId",
})
}
res, err := h.veliVirtualGameSvc.GetCreditBalances(c.Context(), brandID)
if err != nil {
h.InternalServerErrorLogger().Error("Failed to [VeliGameHandler]GetCreditBalances",
zap.String("brandID", brandID),
zap.Error(err),
)
return c.Status(fiber.StatusBadGateway).JSON(domain.ErrorResponse{
Message: "Failed to fetch credit balances",
Error: err.Error(),
})
}
return c.Status(fiber.StatusOK).JSON(domain.Response{
Message: "Credit balances fetched successfully",
Data: res,
StatusCode: fiber.StatusOK,
Success: true,
})
}