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

399 lines
12 KiB
Go

package handlers
import (
"context"
"encoding/json"
"errors"
"fmt"
"log"
"strings"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
"github.com/gofiber/fiber/v2"
)
// 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 {
// 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",
})
}
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(),
})
}
// Attach user ID to request
req.PlayerID = fmt.Sprintf("%d", userId)
// Default language if not provided
if req.Language == "" {
req.Language = "en"
}
// Default currency if not provided
if req.Currency == "" {
req.Currency = "USD"
}
// Call the 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(),
})
}
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)
}