Yimaru-BackEnd/internal/web_server/handlers/veli_games.go
2025-09-17 12:57:37 +03:00

457 lines
14 KiB
Go

package handlers
import (
"context"
"errors"
"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 {
userId, ok := c.Locals("user_id").(int64)
if !ok {
return c.Status(fiber.StatusUnauthorized).JSON(domain.ErrorResponse{
Error: "missing user id",
Message: "Unauthorized",
})
}
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(),
})
}
// There needs to be a way to generate a sessionID
// Attach user ID to request
req.PlayerID = fmt.Sprintf("%d", userId)
// Default brand if not provided
if req.BrandID == "" {
req.BrandID = h.Cfg.VeliGames.BrandID
}
req.IP = c.IP()
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),
)
// 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 game",
Error: err.Error(),
})
}
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 fiber.NewError(fiber.StatusBadRequest, "Invalid request body")
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
Message: "Invalid request body",
Error: err.Error(),
})
}
// Signature check optional here
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(),
})
// return fiber.NewError(fiber.StatusBadRequest, err.Error())
}
return c.JSON(res)
}
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,
})
}