Ejemplo n.º 1
0
        private async Task TestWalletWithdrawMoney()
        {
            var client = _factory.CreateClient();

            var json = JsonSerializer
                       .Serialize(new WalletOperationRequest
            {
                TransactionId   = Guid.NewGuid(),
                TransactionType = WalletTransactionType.Withdrawal,
                Amount          = 1m
            });

            var requestModel = new StringContent(json, Encoding.UTF8, "application/json");

            var response = await client.PutAsync($"v1/Wallet/{SeedWalletId}", requestModel);

            response.StatusCode.ShouldBe(HttpStatusCode.Created);

            WalletOperationResult walletOperationResult = null;

            Should.NotThrow(async() => { walletOperationResult = JsonSerializer.Deserialize <WalletOperationResult>(await response.Content.ReadAsStringAsync(), _jsonSerializerOptions); });

            walletOperationResult.Success.ShouldBeTrue();
            walletOperationResult.Repeated.ShouldBeFalse();

            // get correct balance from the database
            var correctBalance = _db.Wallet
                                 .First(w => w.Id == SeedWalletId)
                                 .Balance;

            walletOperationResult.WalletState.Balance.ShouldBe(correctBalance);
        }
Ejemplo n.º 2
0
        private async Task TestWalletDepositMoneyAndRepeat()
        {
            var client = _factory.CreateClient();

            var json = JsonSerializer
                       .Serialize(new WalletOperationRequest
            {
                TransactionId   = Guid.NewGuid(),
                TransactionType = WalletTransactionType.Deposit,
                Amount          = 100m
            });

            var requestModel = new StringContent(json, Encoding.UTF8, "application/json");

            var response = await client.PutAsync($"v1/Wallet/{SeedWalletId}", requestModel);

            response.StatusCode.ShouldBe(HttpStatusCode.Created);

            WalletOperationResult walletOperationResult = null;

            Should.NotThrow(async() => { walletOperationResult = JsonSerializer.Deserialize <WalletOperationResult>(await response.Content.ReadAsStringAsync(), _jsonSerializerOptions); });

            walletOperationResult.Success.ShouldBeTrue();
            walletOperationResult.Repeated.ShouldBeFalse();

            // get correct balance from the database
            var correctBalance = (await _db.Wallet
                                  .FirstAsync(w => w.Id == SeedWalletId))
                                 .Balance;

            walletOperationResult.WalletState.Balance.ShouldBe(correctBalance);

            // repeat the same request

            var client2 = _factory.CreateClient();

            var response2 = await client2.PutAsync($"v1/Wallet/{SeedWalletId}", requestModel);

            response2.StatusCode.ShouldBe(HttpStatusCode.Created);

            WalletOperationResult walletOperationResult2 = null;

            Should.NotThrow(async() => { walletOperationResult2 = JsonSerializer.Deserialize <WalletOperationResult>(await response2.Content.ReadAsStringAsync(), _jsonSerializerOptions); });

            walletOperationResult2.Success.ShouldBeTrue();
            walletOperationResult2.Repeated.ShouldBeTrue();

            // balance should not be changed and should be the same as after the first transaction!
            walletOperationResult2.WalletState.Balance.ShouldBe(correctBalance);
        }
Ejemplo n.º 3
0
        public async Task <IActionResult> AttemptTransaction([FromRoute] Guid walletGuid, [FromBody] WalletOperationRequest model)
        {
            // At this point Model is validated
            // Get player's Wallet tracked Entity
            var playersWallet = await _db.Wallet
                                .Where(wallet => wallet.Id == walletGuid)
                                .FirstOrDefaultAsync();

            // If we're dealing with negative transaction type
            if (NegativeTransactionTypes.Contains(model.TransactionType))
            {
                // check if it results in positive Balance in the end
                if ((playersWallet.Balance - model.Amount) < decimal.Zero)
                {
                    _logger.LogWarning($@">>> WalletController.AttemptTransaction() - Operation of {nameof(model.TransactionType)} with Amount of {model.Amount} would result in negative balance!.");
                    return(new UnprocessableEntityObjectResult(UnprocessableWalletLogResponseObject));
                }

                // Update Player's Wallet Balance
                playersWallet.Balance -= model.Amount;
            }

            if (PositiveTranscationTypes.Contains(model.TransactionType))
            {
                // Update Player's Wallet Balance
                playersWallet.Balance += model.Amount;
            }

            // perform the operation and log to operations log
            // runs in an implicit transaction

            // prepare OkResult
            var result = new WalletOperationResult
            {
                Id          = model.TransactionId,
                Repeated    = false,
                Success     = true,
                WalletState = new WalletModelResponse
                {
                    Balance = playersWallet.Balance
                }
            };

            // ReSharper disable once MethodHasAsyncOverload
            // Async overload serves only purpose for special HiLo generated PKs
            _db.WalletLogs.Add(
                new WalletLog
            {
                CreatedAt     = DateTime.Now,
                ResultType    = ResultType.Created,
                WalletId      = walletGuid,
                TransactionId = model.TransactionId,
                Memento       = JsonSerializer.Serialize(result, _databaseSerializerOptions)
            }
                );

            try
            {
                await _db.SaveChangesAsync();
            }
            catch (DBConcurrencyException)
            {
                // Optimistic concurrency failed as the object
                // has been changed during this transaction.
                // Signal this back to the client which is
                // responsible for retry!
                _logger.LogDebug($@">>> WalletController.AttemptTransaction() - This transaction failed optimistic concurrency!");
                return(new CustomPreconditionFailedResult());
            }

            // TODO: Fill in uri to Get method where the transaction can be found
            return(Created("", result));
        }