public async Task <Result <IGameResult, ErrorCode> > Spin(RequestContext <SpinArgs> requestContext) { using (logger.BeginScope(new Dictionary <string, object> { ["SessionKey"] = requestContext.UserSession.SessionKey, ["UserId"] = requestContext.UserSession.UserId, ["GameKey"] = requestContext.GameKey, ["Platform"] = requestContext.Platform, ["BettingLines"] = requestContext.Parameters.BettingLines, ["LineBet"] = requestContext.Parameters.LineBet, ["Multiplier"] = requestContext.Parameters.Multiplier })) { var module = gameModules.GetModule(requestContext.GameKey); var userSession = requestContext.UserSession; SpinResult result = null; var level = await userService.GetLevel(requestContext.UserGameKey); try { var transaction = await transactionService.GenerateGameTransactionId(requestContext.UserGameKey, GameTransactionType.Spin); requestContext.GameTransaction = transaction; var roundId = await transactionService.GenerateAutoNumber(CounterType.RoundId); requestContext.CurrentRound = roundId; var lastSpinData = await userService.GetLastSpinData(userSession, requestContext.Game); var totalBet = module.CalculateTotalBet(lastSpinData, requestContext); logger.LogInformation("User spin on level {Level}, trx id {GameTransactionId}, round {CurrentRoundId}, total bet {TotalBet}", level, transaction.Id, roundId, totalBet); var deductBet = await payoutService.DeductBetFromWallet(requestContext, totalBet); // TODO we should check the wallet result if it's success var spinResult = module.ExecuteSpin(level, lastSpinData, requestContext); if (spinResult.IsError) { return(spinResult.Error); } result = spinResult.Value; result.Bet = totalBet; result.RoundId = roundId; result.TransactionId = transaction.Id; result.UniqueID = result.TransactionId.ToString(); result.DateTimeUtc = transaction.DateTimeUtc; result.ExchangeRate = deductBet.ExchangeRate; result.Balance = Balance.Create(deductBet.Balance); if (result.HasBonus) { logger.LogInformation("User got bonus"); var bonusCreated = module.CreateBonus(result); if (bonusCreated.IsError) { logger.LogWarning("Create bonus got error {Error} with {SpinResult}", bonusCreated.Error, JsonHelper.FullString(result)); return(ErrorCode.InternalError); } var bonus = bonusCreated.Value; bonus.SpinTransactionId = transaction.Id; bonus.GameResult = result; var entity = new BonusEntity { UserId = userSession.UserId, GameId = requestContext.Game.Id, Guid = bonus.Guid.ToString("N"), Data = bonus.ToByteArray(), BonusType = bonus.GetType().Name, Version = 3, IsOptional = bonus.IsOptional, IsStarted = bonus.IsStarted, RoundId = roundId, BetReference = deductBet.BetReference }; await bonusService.UpdateBonus(userSession, entity); await userService.UpdateGameState(requestContext, UserGameState.ForBonus(roundId)); } else { await userService.UpdateGameState(requestContext, UserGameState.ForNormal(roundId)); } var paied = await payoutService.PayoutToUser(requestContext, result, new BonusExtraInfo { RoundId = roundId, BetId = deductBet.BetReference }, !result.HasBonus, deductBet.Guid); result.ExchangeRate = deductBet.ExchangeRate; result.Balance = Balance.Create(paied.Balance); await transactionService.ProfileSpinBet(requestContext); await userService.UpdateLastSpinData(userSession, requestContext.Game, result); if (!userSession.IsFunPlay) { await userService.UpdateUserGameData(new UserGameData { UserId = userSession.UserId, GameId = requestContext.Game.Id, Bet = requestContext.Parameters.LineBet, Lines = requestContext.Game.Lines, }); } } catch (InsufficientBalanceException ex) { logger.LogInformation(ex, ex.Message); return(ErrorCode.InsufficientCredit); } catch (Exception ex) { logger.LogError(ex, ex.Message); throw; } finally { await TrySaveGameHistory(requestContext, result); } return(result); } }