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

351 lines
11 KiB
Go

package atlas
import (
"context"
"errors"
"fmt"
"strconv"
"github.com/SamuelTariku/FortuneBet-Backend/internal/config"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
"github.com/SamuelTariku/FortuneBet-Backend/internal/ports"
"github.com/SamuelTariku/FortuneBet-Backend/internal/repository"
virtualgameservice "github.com/SamuelTariku/FortuneBet-Backend/internal/services/virtualGame"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/wallet"
)
type Service struct {
virtualGameSvc virtualgameservice.VirtualGameService
repo repository.VirtualGameRepository
client *Client
walletSvc *wallet.Service
transfetStore ports.TransferStore
cfg *config.Config
}
func New(virtualGameSvc virtualgameservice.VirtualGameService, repo repository.VirtualGameRepository, client *Client, walletSvc *wallet.Service, transferStore ports.TransferStore, cfg *config.Config) *Service {
return &Service{
virtualGameSvc: virtualGameSvc,
repo: repo,
client: client,
walletSvc: walletSvc,
transfetStore: transferStore,
cfg: cfg,
}
}
func (s *Service) InitGame(ctx context.Context, req domain.AtlasGameInitRequest) (*domain.AtlasGameInitResponse, error) {
body := map[string]any{
"game": req.Game,
"partner_id": s.client.PartnerID,
"casino_id": s.client.CasinoID,
"language": req.Language,
"currency": req.Currency,
"player_id": req.PlayerID,
}
// 3. Call the Atlas client
var res domain.AtlasGameInitResponse
if err := s.client.post(ctx, "/init", body, &res); err != nil {
return nil, fmt.Errorf("failed to initialize game: %w", err)
}
return &res, nil
}
func (s *Service) GetUserData(ctx context.Context, req domain.AtlasGetUserDataRequest) (*domain.AtlasGetUserDataResponse, error) {
// 1. Validate casino_id and hash if needed
if req.CasinoID != s.client.CasinoID {
return nil, fmt.Errorf("invalid casino_id")
}
// 2. Fetch player from DB
playerIDInt, err := strconv.ParseInt(req.PlayerID, 10, 64)
if err != nil {
return nil, fmt.Errorf("invalid playerID: %w", err)
}
wallet, err := s.walletSvc.GetCustomerWallet(ctx, playerIDInt)
if err != nil {
return nil, fmt.Errorf("failed to fetch walllets for player %d: %w", playerIDInt, err)
}
// 4. Build response
res := &domain.AtlasGetUserDataResponse{
PlayerID: req.PlayerID,
Balance: float64(wallet.RegularBalance.Float32()),
}
return res, nil
}
func (s *Service) ProcessBet(ctx context.Context, req domain.AtlasBetRequest) (*domain.AtlasBetResponse, error) {
// --- 1. Validate CasinoID ---
if req.CasinoID != s.client.CasinoID {
return nil, fmt.Errorf("BAD_REQUEST: invalid casino_id")
}
// --- 2. Validate PlayerID ---
playerIDInt, err := strconv.ParseInt(req.PlayerID, 10, 64)
if err != nil {
return nil, fmt.Errorf("BAD_REQUEST: invalid playerID %q", req.PlayerID)
}
// --- 3. Fetch player wallet ---
wallet, err := s.walletSvc.GetCustomerWallet(ctx, playerIDInt)
if err != nil {
return nil, fmt.Errorf("failed to fetch wallet for player %d: %w", playerIDInt, err)
}
// --- 4. Convert balance using Float32() ---
realBalance := float64(wallet.RegularBalance.Float32())
// --- 5. Ensure sufficient balance ---
if realBalance < req.Amount {
return nil, domain.ErrInsufficientBalance
}
// --- 6. Deduct amount from wallet ---
newBalance := realBalance - req.Amount
err = s.walletSvc.UpdateBalance(ctx, wallet.RegularID, domain.ToCurrency(float32(newBalance)))
if err != nil {
return nil, fmt.Errorf("failed to debit wallet: %w", err)
}
// --- 7. Build response ---
res := &domain.AtlasBetResponse{
PlayerID: req.PlayerID,
Balance: newBalance,
}
return res, nil
}
func (s *Service) ProcessBetWin(ctx context.Context, req domain.AtlasBetWinRequest) (*domain.AtlasBetWinResponse, error) {
// --- 1. Validate CasinoID ---
if req.CasinoID != s.client.CasinoID {
return nil, fmt.Errorf("BAD_REQUEST: invalid casino_id")
}
// --- 2. Validate PlayerID ---
playerIDInt, err := strconv.ParseInt(req.PlayerID, 10, 64)
if err != nil {
return nil, fmt.Errorf("BAD_REQUEST: invalid playerID %q", req.PlayerID)
}
// --- 3. Fetch player wallet ---
wallet, err := s.walletSvc.GetCustomerWallet(ctx, playerIDInt)
if err != nil {
return nil, fmt.Errorf("failed to fetch wallet for player %d: %w", playerIDInt, err)
}
// --- 4. Convert balance using Float32() ---
realBalance := float64(wallet.RegularBalance.Float32())
// --- 5. Ensure sufficient balance for bet ---
if realBalance < req.BetAmount {
return nil, domain.ErrInsufficientBalance
}
// --- 6. Deduct bet amount ---
debitedBalance := realBalance - req.BetAmount
err = s.walletSvc.UpdateBalance(ctx, wallet.RegularID, domain.ToCurrency(float32(debitedBalance)))
if err != nil {
return nil, fmt.Errorf("failed to debit wallet: %w", err)
}
// --- 7. Apply win amount (if any) ---
finalBalance := debitedBalance
if req.WinAmount > 0 {
finalBalance = debitedBalance + req.WinAmount
err = s.walletSvc.UpdateBalance(ctx, wallet.RegularID, domain.ToCurrency(float32(finalBalance)))
if err != nil {
return nil, fmt.Errorf("failed to credit wallet: %w", err)
}
}
// --- 8. Build response ---
res := &domain.AtlasBetWinResponse{
PlayerID: req.PlayerID,
Balance: finalBalance,
}
return res, nil
}
func (s *Service) ProcessRoundResult(ctx context.Context, req domain.RoundResultRequest) (*domain.RoundResultResponse, error) {
// --- 1. Validate required fields ---
if req.PlayerID == "" || req.TransactionID == "" {
return nil, fmt.Errorf("BAD_REQUEST: missing player_id or transaction_id")
}
// --- 2. Credit player if win amount > 0 ---
if req.Amount > 0 {
playerIDInt, err := strconv.ParseInt(req.PlayerID, 10, 64)
if err != nil {
return nil, fmt.Errorf("BAD_REQUEST: invalid playerID %q", req.PlayerID)
}
// --- 3. Fetch player wallet ---
wallet, err := s.walletSvc.GetCustomerWallet(ctx, playerIDInt)
if err != nil {
return nil, fmt.Errorf("failed to fetch wallet for player %d: %w", playerIDInt, err)
}
// --- 4. Convert balance and apply win amount ---
currentBalance := float64(wallet.RegularBalance.Float32())
newBalance := currentBalance + req.Amount
err = s.walletSvc.UpdateBalance(ctx, wallet.RegularID, domain.ToCurrency(float32(newBalance)))
if err != nil {
return nil, fmt.Errorf("failed to credit wallet: %w", err)
}
}
// --- 5. Build response ---
return &domain.RoundResultResponse{
Success: true,
}, nil
}
func (s *Service) ProcessRollBack(ctx context.Context, req domain.RollbackRequest) (*domain.RollbackResponse, error) {
// --- 1. Validate required fields ---
if req.PlayerID == "" || req.BetTransactionID == "" {
return nil, fmt.Errorf("BAD_REQUEST: missing player_id or bet_transaction_id")
}
// --- 2. Parse PlayerID ---
playerIDInt, err := strconv.ParseInt(req.PlayerID, 10, 64)
if err != nil {
return nil, fmt.Errorf("BAD_REQUEST: invalid playerID %q", req.PlayerID)
}
// --- 3. Fetch player wallet ---
wallet, err := s.walletSvc.GetCustomerWallet(ctx, playerIDInt)
if err != nil {
return nil, fmt.Errorf("failed to fetch wallet for player %d: %w", playerIDInt, err)
}
// --- 4. Fetch original transfer ---
transfer, err := s.transfetStore.GetTransferByReference(ctx, req.BetTransactionID)
if err != nil {
return nil, fmt.Errorf("failed to fetch transfer for reference %s: %w", req.BetTransactionID, err)
}
// --- 5. Compute new balance using Float32() conversion ---
currentBalance := float64(wallet.RegularBalance.Float32())
newBalance := currentBalance + float64(transfer.Amount)
// --- 6. Credit player wallet ---
err = s.walletSvc.UpdateBalance(ctx, wallet.RegularID, domain.ToCurrency(float32(newBalance)))
if err != nil {
return nil, fmt.Errorf("failed to credit wallet: %w", err)
}
// --- 7. Update transfer status and verification ---
err = s.transfetStore.UpdateTransferStatus(ctx, transfer.ID, string(domain.STATUS_CANCELLED))
if err != nil {
return nil, fmt.Errorf("failed to update transfer status: %w", err)
}
err = s.transfetStore.UpdateTransferVerification(ctx, transfer.ID, true)
if err != nil {
return nil, fmt.Errorf("failed to update transfer verification: %w", err)
}
// --- 8. Build response ---
return &domain.RollbackResponse{
Success: true,
}, nil
}
func (s *Service) CreateFreeSpin(ctx context.Context, req domain.FreeSpinRequest) (*domain.FreeSpinResponse, error) {
body := map[string]any{
"casino_id": s.client.CasinoID,
"freespins_count": req.FreeSpinsCount,
"end_date": req.EndDate,
"player_id": req.PlayerID,
}
// 3. Call the Atlas client
var res domain.FreeSpinResponse
if err := s.client.post(ctx, "/freespin", body, &res); err != nil {
return nil, fmt.Errorf("failed to create free spin: %w", err)
}
return &res, nil
}
func (s *Service) ProcessFreeSpinResult(ctx context.Context, req domain.FreeSpinResultRequest) (*domain.FreeSpinResultResponse, error) {
if req.PlayerID == "" || req.TransactionID == "" {
return nil, errors.New("missing player_id or transaction_id")
}
if req.Amount <= 0 {
// No winnings to process
return &domain.FreeSpinResultResponse{Success: true}, nil
}
playerIDInt, err := strconv.ParseInt(req.PlayerID, 10, 64)
if err != nil {
return nil, fmt.Errorf("invalid playerID: %w", err)
}
wallet, err := s.walletSvc.GetCustomerWallet(ctx, playerIDInt)
if err != nil {
return nil, fmt.Errorf("failed to fetch wallet for player %d: %w", playerIDInt, err)
}
err = s.walletSvc.UpdateBalance(ctx, wallet.RegularID, domain.Currency(float64(wallet.RegularBalance)+req.Amount))
if err != nil {
return nil, fmt.Errorf("failed to credit wallet: %w", err)
}
// Optionally record the transaction in your transfer history for auditing
// _ = s.transfetStore.CreateGamePayoutTransfer(...)
return &domain.FreeSpinResultResponse{Success: true}, nil
}
func (s *Service) ProcessJackPot(ctx context.Context, req domain.JackpotRequest) (*domain.JackpotResponse, error) {
if req.PlayerID == "" || req.TransactionID == "" {
return nil, errors.New("missing player_id or transaction_id")
}
if req.Amount <= 0 {
// No jackpot winnings
return &domain.JackpotResponse{Success: true}, nil
}
playerIDInt, err := strconv.ParseInt(req.PlayerID, 10, 64)
if err != nil {
return nil, fmt.Errorf("invalid playerID: %w", err)
}
wallet, err := s.walletSvc.GetCustomerWallet(ctx, playerIDInt)
if err != nil {
return nil, fmt.Errorf("failed to fetch wallet for player %d: %w", playerIDInt, err)
}
_, err = s.walletSvc.AddToWallet(
ctx,
wallet.RegularID,
domain.Currency(req.Amount),
domain.ValidInt64{},
domain.PaymentMethod(domain.DEPOSIT),
domain.PaymentDetails{
ReferenceNumber: domain.ValidString{
Value: req.TransactionID,
Valid: true,
},
// Description: fmt.Sprintf("Jackpot win - Txn: %s", req.TransactionID),
},
"",
)
if err != nil {
return nil, fmt.Errorf("failed to credit wallet: %w", err)
}
return &domain.JackpotResponse{Success: true}, nil
}