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

453 lines
14 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"
"encoding/json"
"errors"
"fmt"
"log"
"strings"
"time"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
"github.com/gofiber/fiber/v2"
"go.uber.org/zap"
)
// GetAtlasVGames godoc
// @Summary List Atlas virtual games
// @Description Retrieves available Atlas virtual games from the provider
// @Tags Virtual Games - Atlas
// @Produce json
// @Success 200 {object} domain.Response{data=[]domain.AtlasGameEntity}
// @Failure 502 {object} domain.ErrorResponse
// @Router /api/v1/atlas/games [get]
func (h *Handler) GetAtlasVGames(c *fiber.Ctx) error {
// Call the service
games, err := h.veliVirtualGameSvc.GetAtlasVGames(c.Context())
if err != nil {
log.Println("GetAtlasVGames error:", err)
return c.Status(fiber.StatusBadGateway).JSON(domain.ErrorResponse{
Message: "Failed to fetch Atlas virtual games",
Error: err.Error(),
})
}
// Return the list of games
return c.Status(fiber.StatusOK).JSON(domain.Response{
Message: "Atlas virtual games retrieved successfully",
Data: games,
StatusCode: fiber.StatusOK,
Success: true,
})
}
// InitAtlasGame godoc
// @Summary Start an Atlas virtual game session
// @Description Initializes a game session for the given player using Atlas virtual game provider
// @Tags Virtual Games - Atlas
// @Accept json
// @Produce json
// @Param request body domain.AtlasGameInitRequest true "Start game input"
// @Success 200 {object} domain.Response{data=domain.AtlasGameInitResponse}
// @Failure 400 {object} domain.ErrorResponse
// @Failure 502 {object} domain.ErrorResponse
// @Router /api/v1/atlas/init-game [post]
func (h *Handler) InitAtlasGame(c *fiber.Ctx) error {
// 1⃣ Retrieve user ID from context
userId, ok := c.Locals("user_id").(int64)
if !ok {
return c.Status(fiber.StatusUnauthorized).JSON(domain.ErrorResponse{
Error: "missing user id",
Message: "Unauthorized",
})
}
// 2⃣ Parse request body
var req domain.AtlasGameInitRequest
if err := c.BodyParser(&req); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
Message: "Invalid request body",
Error: err.Error(),
})
}
// 3⃣ Attach user ID to request
req.PlayerID = fmt.Sprintf("%d", userId)
// 4⃣ Set defaults if not provided
if req.Language == "" {
req.Language = "en"
}
if req.Currency == "" {
req.Currency = "USD"
}
// 5⃣ Call the Atlas service
res, err := h.atlasVirtualGameSvc.InitGame(context.Background(), req)
if err != nil {
log.Println("InitAtlasGame error:", err)
return c.Status(fiber.StatusBadGateway).JSON(domain.ErrorResponse{
Message: "Failed to initialize Atlas game",
Error: err.Error(),
})
}
// 6⃣ Update provider report: increment total_games_played
go func() {
ctx := context.Background()
reportDate := time.Now().Truncate(24 * time.Hour)
reportType := "daily"
providerID := "atlas" // all Atlas games belong to this provider
err := h.orchestrationSvc.UpdateVirtualGameProviderReportByDate(
ctx,
providerID,
reportDate,
reportType,
1, // increment total_games_played by 1
0, // total_bets (no change)
0, // total_payouts (no change)
1, // total_players (no change)
)
if err != nil {
h.InternalServerErrorLogger().Error("Failed to update total_games_played for Atlas game",
zap.String("provider_id", providerID),
zap.Error(err),
)
}
}()
// 7⃣ Return response to user
return c.Status(fiber.StatusOK).JSON(domain.Response{
Message: "Game initialized successfully",
Data: res,
StatusCode: fiber.StatusOK,
Success: true,
})
}
// AtlasGetUserDataCallback godoc
// @Summary Atlas Get User Data callback
// @Description Callback endpoint for Atlas game server to fetch player balance
// @Tags Virtual Games - Atlas
// @Accept json
// @Produce json
// @Param request body domain.AtlasGetUserDataRequest true "Get user data input"
// @Success 200 {object} domain.AtlasGetUserDataResponse
// @Failure 400 {object} domain.ErrorResponse
// @Failure 502 {object} domain.ErrorResponse
// @Router /account [post]
func (h *Handler) AtlasGetUserDataCallback(c *fiber.Ctx) error {
var req domain.AtlasGetUserDataRequest
if err := c.BodyParser(&req); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
Message: "Invalid request body",
Error: err.Error(),
})
}
// Optional: validate casino_id matches your configured Atlas casino
if req.CasinoID != h.Cfg.Atlas.CasinoID {
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
Message: "Invalid casino_id",
Error: "unauthorized request",
})
}
// Call service to get player data
res, err := h.atlasVirtualGameSvc.GetUserData(c.Context(), req)
if err != nil {
log.Println("AtlasGetUserDataCallback error:", err)
return c.Status(fiber.StatusBadGateway).JSON(domain.ErrorResponse{
Message: "Failed to fetch user data",
Error: err.Error(),
})
}
// Return Atlas expected response
return c.JSON(res)
}
// HandleAtlasBetWin godoc
// @Summary Atlas BetWin callback
// @Description Processes a Bet and Win request from Atlas provider
// @Tags Virtual Games - Atlas
// @Accept json
// @Produce json
// @Param request body domain.AtlasBetWinRequest true "Atlas BetWin input"
// @Success 200 {object} domain.AtlasBetWinResponse
// @Failure 400 {object} domain.ErrorResponse
// @Failure 502 {object} domain.ErrorResponse
// @Router /betwin [post]
func (h *Handler) HandleAtlasBetWin(c *fiber.Ctx) error {
body := c.Body()
if len(body) == 0 {
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
Message: "Empty request body",
Error: "Request body cannot be empty",
})
}
var req domain.AtlasBetWinRequest
if err := json.Unmarshal(body, &req); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
Message: "Invalid Atlas BetWin request",
Error: err.Error(),
})
}
res, err := h.atlasVirtualGameSvc.ProcessBetWin(c.Context(), req)
if err != nil {
// Handle known errors specifically
code := fiber.StatusInternalServerError
errMsg := err.Error()
switch {
case errors.Is(err, domain.ErrInsufficientBalance):
code = fiber.StatusBadRequest
errMsg = "INSUFFICIENT_BALANCE"
case strings.Contains(err.Error(), "invalid casino_id"):
code = fiber.StatusBadRequest
case strings.Contains(err.Error(), "invalid playerID"):
code = fiber.StatusBadRequest
}
return c.Status(code).JSON(domain.ErrorResponse{
Message: "Failed to process Atlas BetWin",
Error: errMsg,
})
}
return c.Status(fiber.StatusOK).JSON(res)
}
// HandleRoundResult godoc
// @Summary Atlas Round Result callback
// @Description Processes a round result from Atlas or other providers
// @Tags Virtual Games - Atlas
// @Accept json
// @Produce json
// @Param request body domain.RoundResultRequest true "Round result input"
// @Success 200 {object} domain.RoundResultResponse
// @Failure 400 {object} domain.ErrorResponse
// @Failure 502 {object} domain.ErrorResponse
// @Router /result [post]
func (h *Handler) HandleRoundResult(c *fiber.Ctx) error {
body := c.Body()
if len(body) == 0 {
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
Message: "Empty request body",
Error: "Request body cannot be empty",
})
}
var req domain.RoundResultRequest
if err := json.Unmarshal(body, &req); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
Message: "Invalid RoundResult request",
Error: err.Error(),
})
}
res, err := h.atlasVirtualGameSvc.ProcessRoundResult(c.Context(), req)
if err != nil {
code := fiber.StatusInternalServerError
errMsg := err.Error()
// Validation errors
if strings.Contains(err.Error(), "missing player_id") || strings.Contains(err.Error(), "missing transaction_id") {
code = fiber.StatusBadRequest
}
return c.Status(code).JSON(domain.ErrorResponse{
Message: "Failed to process round result",
Error: errMsg,
})
}
return c.Status(fiber.StatusOK).JSON(res)
}
// HandleRollback godoc
// @Summary Atlas Rollback callback
// @Description Processes a rollback request from Atlas or other providers
// @Tags Virtual Games - Atlas
// @Accept json
// @Produce json
// @Param request body domain.RollbackRequest true "Rollback request input"
// @Success 200 {object} domain.RollbackResponse
// @Failure 400 {object} domain.ErrorResponse
// @Failure 502 {object} domain.ErrorResponse
// @Router /rollback [post]
func (h *Handler) HandleRollback(c *fiber.Ctx) error {
body := c.Body()
if len(body) == 0 {
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
Message: "Empty request body",
Error: "Request body cannot be empty",
})
}
var req domain.RollbackRequest
if err := json.Unmarshal(body, &req); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
Message: "Invalid Rollback request",
Error: err.Error(),
})
}
res, err := h.atlasVirtualGameSvc.ProcessRollBack(c.Context(), req)
if err != nil {
code := fiber.StatusInternalServerError
errMsg := err.Error()
// Validation errors
if strings.Contains(err.Error(), "missing player_id") || strings.Contains(err.Error(), "missing transaction_id") {
code = fiber.StatusBadRequest
}
return c.Status(code).JSON(domain.ErrorResponse{
Message: "Failed to process rollback",
Error: errMsg,
})
}
return c.Status(fiber.StatusOK).JSON(res)
}
// CreateFreeSpin godoc
// @Summary Create free spins for a player
// @Description Sends a request to Atlas to create free spins/bets for a given player
// @Tags Virtual Games - Atlas
// @Accept json
// @Produce json
// @Param request body domain.FreeSpinRequest true "Free spin input"
// @Success 200 {object} domain.Response{data=domain.FreeSpinResponse}
// @Failure 400 {object} domain.ErrorResponse
// @Failure 502 {object} domain.ErrorResponse
// @Router /api/v1/atlas/freespin [post]
func (h *Handler) CreateFreeSpin(c *fiber.Ctx) error {
// Get the authenticated user ID
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.FreeSpinRequest
if err := c.BodyParser(&req); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
Message: "Invalid request body",
Error: err.Error(),
})
}
// Attach player ID from authenticated user
req.PlayerID = fmt.Sprintf("%d", userId)
res, err := h.atlasVirtualGameSvc.CreateFreeSpin(c.Context(), req)
if err != nil {
log.Println("CreateFreeSpin error:", err)
return c.Status(fiber.StatusBadGateway).JSON(domain.ErrorResponse{
Message: "Failed to create free spins",
Error: err.Error(),
})
}
return c.Status(fiber.StatusOK).JSON(domain.Response{
Message: "Free spins created successfully",
Data: res,
StatusCode: fiber.StatusOK,
Success: true,
})
}
// FreeSpinResultCallback godoc
// @Summary Free Spin/Bet result callback
// @Description Handles the result of a free spin/bet from the game server
// @Tags Virtual Games - Atlas
// @Accept json
// @Produce json
// @Param request body domain.FreeSpinResultRequest true "Free spin result input"
// @Success 200 {object} domain.FreeSpinResultResponse
// @Failure 400 {object} domain.ErrorResponse
// @Failure 502 {object} domain.ErrorResponse
// @Router /freespin [post]
func (h *Handler) FreeSpinResultCallback(c *fiber.Ctx) error {
// Read raw request body
body := c.Body()
if len(body) == 0 {
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
Message: "Empty request body",
Error: "Request body cannot be empty",
})
}
// Unmarshal into FreeSpinResultRequest
var req domain.FreeSpinResultRequest
if err := json.Unmarshal(body, &req); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
Message: "Invalid free spin result request",
Error: err.Error(),
})
}
// Process the free spin result
res, err := h.atlasVirtualGameSvc.ProcessFreeSpinResult(c.Context(), req)
if err != nil {
log.Println("FreeSpinResultCallback error:", err)
return c.Status(fiber.StatusBadGateway).JSON(domain.ErrorResponse{
Message: "Failed to process free spin result",
Error: err.Error(),
})
}
return c.Status(fiber.StatusOK).JSON(res)
}
// JackpotCallback godoc
// @Summary Jackpot result callback
// @Description Handles the jackpot result from the game server
// @Tags Virtual Games - Atlas
// @Accept json
// @Produce json
// @Param request body domain.JackpotRequest true "Jackpot result input"
// @Success 200 {object} domain.JackpotResponse
// @Failure 400 {object} domain.ErrorResponse
// @Failure 502 {object} domain.ErrorResponse
// @Router /jackpot [post]
func (h *Handler) JackpotCallback(c *fiber.Ctx) error {
// Read raw request body
body := c.Body()
if len(body) == 0 {
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
Message: "Empty request body",
Error: "Request body cannot be empty",
})
}
// Unmarshal into JackpotRequest
var req domain.JackpotRequest
if err := json.Unmarshal(body, &req); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
Message: "Invalid jackpot request",
Error: err.Error(),
})
}
// Process the jackpot
res, err := h.atlasVirtualGameSvc.ProcessJackPot(c.Context(), req)
if err != nil {
log.Println("JackpotCallback error:", err)
return c.Status(fiber.StatusBadGateway).JSON(domain.ErrorResponse{
Message: "Failed to process jackpot",
Error: err.Error(),
})
}
return c.Status(fiber.StatusOK).JSON(res)
}