Yimaru-BackEnd/internal/services/virtualGame/veli/service.go

303 lines
9.0 KiB
Go

package veli
import (
"context"
"errors"
"fmt"
"strconv"
"strings"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/wallet"
)
var (
ErrPlayerNotFound = errors.New("PLAYER_NOT_FOUND")
ErrSessionExpired = errors.New("SESSION_EXPIRED")
ErrInsufficientBalance = errors.New("INSUFFICIENT_BALANCE")
ErrDuplicateTransaction = errors.New("DUPLICATE_TRANSACTION")
)
type service struct {
client *Client
walletSvc *wallet.Service
}
func New(client *Client, walletSvc *wallet.Service) VeliVirtualGameService {
return &service{
client: client,
walletSvc: walletSvc,
}
}
func (s *service) GetProviders(ctx context.Context, req domain.ProviderRequest) (*domain.ProviderResponse, error) {
// Always mirror request body fields into sigParams
sigParams := map[string]string{
"brandId": req.BrandID,
}
// Optional fields
sigParams["extraData"] = fmt.Sprintf("%t", req.ExtraData) // false is still included
if req.Size > 0 {
sigParams["size"] = fmt.Sprintf("%d", req.Size)
} else {
sigParams["size"] = "" // keep empty if not set
}
if req.Page > 0 {
sigParams["page"] = fmt.Sprintf("%d", req.Page)
} else {
sigParams["page"] = ""
}
var res domain.ProviderResponse
err := s.client.post(ctx, "/game-lists/public/providers", req, sigParams, &res)
return &res, err
}
func (s *service) GetGames(ctx context.Context, req domain.GameListRequest) ([]domain.GameEntity, error) {
sigParams := map[string]string{
"brandId": req.BrandID, "providerId": req.ProviderID,
}
var res struct {
Items []domain.GameEntity `json:"items"`
}
err := s.client.post(ctx, "/game-lists/public/games", req, sigParams, &res)
return res.Items, err
}
func (s *service) StartGame(ctx context.Context, req domain.GameStartRequest) (*domain.GameStartResponse, error) {
sigParams := map[string]string{
"sessionId": req.SessionID, "providerId": req.ProviderID,
"gameId": req.GameID, "language": req.Language, "playerId": req.PlayerID,
"currency": req.Currency, "deviceType": req.DeviceType, "country": req.Country,
"ip": req.IP, "brandId": req.BrandID,
}
var res domain.GameStartResponse
err := s.client.post(ctx, "/unified-api/public/start-game", req, sigParams, &res)
return &res, err
}
func (s *service) StartDemoGame(ctx context.Context, req domain.DemoGameRequest) (*domain.GameStartResponse, error) {
sigParams := map[string]string{
"providerId": req.ProviderID, "gameId": req.GameID,
"language": req.Language, "deviceType": req.DeviceType,
"ip": req.IP, "brandId": req.BrandID,
}
var res domain.GameStartResponse
err := s.client.post(ctx, "/unified-api/public/start-demo-game", req, sigParams, &res)
return &res, err
}
func (s *service) GetBalance(ctx context.Context, req domain.BalanceRequest) (*domain.BalanceResponse, error) {
// Retrieve player's real balance from wallet service
playerIDInt64, err := strconv.ParseInt(req.PlayerID, 10, 64)
if err != nil {
return nil, fmt.Errorf("invalid PlayerID: %w", err)
}
playerWallets, err := s.walletSvc.GetWalletsByUser(ctx, playerIDInt64)
if err != nil {
return nil, fmt.Errorf("failed to get real balance: %w", err)
}
realBalance := playerWallets[0].Balance
// Retrieve bonus balance if applicable
var bonusBalance float64
if len(playerWallets) > 1 {
bonusBalance = float64(playerWallets[1].Balance)
} else {
bonusBalance = 0
}
// Build the response
res := &domain.BalanceResponse{
Real: struct {
Currency string `json:"currency"`
Amount float64 `json:"amount"`
}{
Currency: string(playerWallets[0].Currency),
Amount: float64(realBalance),
},
}
if bonusBalance > 0 {
res.Bonus = &struct {
Currency string `json:"currency"`
Amount float64 `json:"amount"`
}{
Currency: req.Currency,
Amount: bonusBalance,
}
}
return res, nil
}
func (s *service) ProcessBet(ctx context.Context, req domain.BetRequest) (*domain.BetResponse, error) {
sigParams := map[string]string{
"sessionId": req.SessionID,
"providerId": req.ProviderID,
"playerId": req.PlayerID,
"currency": req.Amount.Currency,
"brandId": req.BrandID,
"gameId": req.GameID,
"roundId": req.RoundID,
"transactionId": req.TransactionID,
"correlationId": req.CorrelationID,
}
if req.GameType != "" {
sigParams["gameType"] = req.GameType
}
if req.IsAdjustment {
sigParams["isAdjustment"] = "true"
}
if req.JackpotID != "" {
sigParams["jackpotId"] = req.JackpotID
sigParams["jackpotContribution"] = fmt.Sprintf("%.2f", req.JackpotContribution)
}
var res domain.BetResponse
err := s.client.post(ctx, "/bet", req, sigParams, &res)
playerIDInt64, err := strconv.ParseInt(req.PlayerID, 10, 64)
if err != nil {
return &domain.BetResponse{}, fmt.Errorf("invalid PlayerID: %w", err)
}
wallets, err := s.client.walletSvc.GetWalletsByUser(ctx, playerIDInt64)
if err != nil {
return &domain.BetResponse{}, err
}
s.client.walletSvc.DeductFromWallet(ctx, wallets[0].ID, domain.Currency(req.Amount.Amount), domain.ValidInt64{}, domain.TRANSFER_DIRECT,
fmt.Sprintf("Deducting %v from wallet for creating Veli Game Bet", req.Amount.Amount),
)
return &res, err
}
func (s *service) ProcessWin(ctx context.Context, req domain.WinRequest) (*domain.WinResponse, error) {
sigParams := map[string]string{
"sessionId": req.SessionID,
"providerId": req.ProviderID,
"playerId": req.PlayerID,
"currency": req.Amount.Currency,
"brandId": req.BrandID,
"gameId": req.GameID,
"roundId": req.RoundID,
"transactionId": req.TransactionID,
"correlationId": req.CorrelationID,
"winType": req.WinType,
}
if req.GameType != "" {
sigParams["gameType"] = req.GameType
}
if req.RewardID != "" {
sigParams["rewardId"] = req.RewardID
}
if req.IsCashOut {
sigParams["isCashOut"] = "true"
}
var res domain.WinResponse
err := s.client.post(ctx, "/win", req, sigParams, &res)
playerIDInt64, err := strconv.ParseInt(req.PlayerID, 10, 64)
if err != nil {
return &domain.WinResponse{}, fmt.Errorf("invalid PlayerID: %w", err)
}
wallets, err := s.client.walletSvc.GetWalletsByUser(ctx, playerIDInt64)
if err != nil {
return &domain.WinResponse{}, err
}
s.client.walletSvc.AddToWallet(ctx, wallets[0].ID, domain.Currency(req.Amount.Amount), domain.ValidInt64{}, domain.TRANSFER_DIRECT, domain.PaymentDetails{},
fmt.Sprintf("Adding %v to wallet due to winning Veli Games bet", req.Amount),
)
return &res, err
}
func (s *service) ProcessCancel(ctx context.Context, req domain.CancelRequest) (*domain.CancelResponse, error) {
sigParams := map[string]string{
"sessionId": req.SessionID,
"providerId": req.ProviderID,
"playerId": req.PlayerID,
"brandId": req.BrandID,
"gameId": req.GameID,
"roundId": req.RoundID,
"transactionId": req.TransactionID,
"cancelType": req.CancelType,
}
if req.GameType != "" {
sigParams["gameType"] = req.GameType
}
if req.CorrelationID != "" {
sigParams["correlationId"] = req.CorrelationID
}
if req.RefTransactionID != "" {
sigParams["refTransactionId"] = req.RefTransactionID
}
if req.AdjustmentRefund.Amount > 0 {
sigParams["adjustmentRefundAmount"] = fmt.Sprintf("%.2f", req.AdjustmentRefund.Amount)
sigParams["adjustmentRefundCurrency"] = req.AdjustmentRefund.Currency
}
var res domain.CancelResponse
err := s.client.post(ctx, "/cancel", req, sigParams, &res)
playerIDInt64, err := strconv.ParseInt(req.PlayerID, 10, 64)
if err != nil {
return &domain.CancelResponse{}, fmt.Errorf("invalid PlayerID: %w", err)
}
wallets, err := s.client.walletSvc.GetWalletsByUser(ctx, playerIDInt64)
if err != nil {
return &domain.CancelResponse{}, err
}
s.client.walletSvc.AddToWallet(ctx, wallets[0].ID, domain.Currency(req.AdjustmentRefund.Amount), domain.ValidInt64{}, domain.TRANSFER_DIRECT, domain.PaymentDetails{},
fmt.Sprintf("Adding %v to wallet due to cancelling virtual game bet", req.AdjustmentRefund.Amount),
)
return &res, err
}
func (s *service) GetGamingActivity(ctx context.Context, req domain.GamingActivityRequest) (*domain.GamingActivityResponse, error) {
// Prepare signature parameters (sorted string map of non-nested fields)
sigParams := map[string]string{
"fromDate": req.FromDate,
"toDate": req.ToDate,
"brandId": req.BrandID,
}
// Optional filters
if req.ProviderID != "" {
sigParams["providerId"] = req.ProviderID
}
if len(req.PlayerIDs) > 0 {
sigParams["playerIds"] = strings.Join(req.PlayerIDs, ",")
}
if len(req.GameIDs) > 0 {
sigParams["gameIds"] = strings.Join(req.GameIDs, ",")
}
if len(req.Currencies) > 0 {
sigParams["currencies"] = strings.Join(req.Currencies, ",")
}
if req.Page > 0 {
sigParams["page"] = fmt.Sprintf("%d", req.Page)
}
if req.Size > 0 {
sigParams["size"] = fmt.Sprintf("%d", req.Size)
}
if req.ExcludeFreeWin != nil {
sigParams["excludeFreeWin"] = fmt.Sprintf("%t", *req.ExcludeFreeWin)
}
var res domain.GamingActivityResponse
err := s.client.post(ctx, "/report-api/public/gaming-activity", req, sigParams, &res)
if err != nil {
return nil, err
}
return &res, nil
}