public IHttpActionResult CreateTransaction(TransactionApiInputDto transactionApiInputDto)
        {
            //Coloca valor do parâmetro show_errors numa var booleana para uso futuro
            var showErros = transactionApiInputDto.show_errors;

            //Cria lista de erros encontrados durante a validação
            List <Error> errors = new List <Error>();

            var transactionType = new TransactionType();
            var cardBrand       = new CardBrand();
            var card            = new Card();
            var clientInDB      = new Client();

            decimal transAmountInDecimal = 0m;

            //A transação é presumida recusada inicialmente
            var statusCode   = Constantes.scRecusada;
            var statusReason = Constantes.srErroInesperado;

            if (!ModelState.IsValid)
            {
                foreach (var e in ModelState.SelectMany(keyValuePair => keyValuePair.Value.Errors))
                {
                    errors.Add(new Error(500, "Erro Inesperado (" + e.ErrorMessage + ")"));
                }
                //errors.Add(new Error(500, "Erro Inesperado."));
            }

            if (errors.Count == 0)
            {
                var validador = new Validador(_unitOfWork, showErros);
                validador.ProcessTransaction(transactionApiInputDto);

                statusCode   = validador.statusCode;
                statusReason = validador.statusReason;

                transAmountInDecimal = validador.transAmountInDecimal;

                transactionType = validador.transactionType;
                cardBrand       = validador.cardBrand;
                card            = validador.card;
                clientInDB      = validador.clientInDB;

                errors = validador.errors;
            }
            ;

            // *** FIM DA VALIDAÇÂO ***


            //___/ Cria Dto para saida da API \_____________________

            var transactionApiOutputDto = new TransactionApiOutputDto();

            transactionApiOutputDto.amount           = transactionApiInputDto.amount;
            transactionApiOutputDto.card_brand       = transactionApiInputDto.card_brand;
            transactionApiOutputDto.card_holder_name = transactionApiInputDto.card_holder_name;

            var cardNumberFirst = "";
            var cardNumberLast  = "";

            if (!String.IsNullOrEmpty(transactionApiInputDto.card_number))
            {
                cardNumberFirst = transactionApiInputDto.card_number.Substring(1, 4);
                if (transactionApiInputDto.card_number.Length >= 4)
                {
                    cardNumberLast = transactionApiInputDto.card_number.Substring(transactionApiInputDto.card_number.Length - 4, 4);
                }
            }

            transactionApiOutputDto.card_number_first = cardNumberFirst;
            transactionApiOutputDto.card_number_last  = cardNumberLast;

            transactionApiOutputDto.installments       = transactionApiInputDto.installments;
            transactionApiOutputDto.transaction_type   = transactionApiInputDto.transaction_type;
            transactionApiOutputDto.creation_timestamp = DateTime.Now;

            transactionApiOutputDto.status_code   = statusCode;
            transactionApiOutputDto.status_reason = statusReason;

            //Gera registro da transação(transaction log) solicitada para ser salva na base de dados
            var transactionLog = new TransactionLog
            {
                Amount             = transactionApiInputDto.amount,
                Card_brand         = transactionApiInputDto.card_brand,
                Card_holder_name   = transactionApiInputDto.card_holder_name,
                Card_number_first  = cardNumberFirst,
                Card_number_last   = cardNumberLast,
                Installments       = transactionApiInputDto.installments,
                Transaction_type   = transactionApiInputDto.transaction_type,
                Status_code        = statusCode,
                Status_reason      = statusReason,
                Creation_timestamp = DateTime.Now,
            };

            //Adiciona registro de TransactionLog a base de dados
            _unitOfWork.TransactionLogs.Add(transactionLog);

            //Caso algum erro tenha sido encontrado...
            if (errors.Count > 0)
            {
                if (showErros)
                {
                    //Passa todos os erros em errors para errorsDto
                    List <ErrorDto> errorsDto = new List <ErrorDto>();
                    if (errors.Count > 0)
                    {
                        foreach (var e in errors)
                        {
                            var errorDto = new ErrorDto(e.error_code, e.error_message);
                            errorsDto.Add(errorDto);
                            var errorLog = new ErrorLog
                            {
                                TransactionLogId = transactionLog.Id,
                                Error_code       = e.error_code,
                                Error_message    = e.error_message
                            };
                            _unitOfWork.ErrorLogs.Add(errorLog);
                        }
                    }

                    //Adiciona erros à TransactionApiOutputDto
                    transactionApiOutputDto.errors = errorsDto;
                }

                //Atualiza a base de dados
                _unitOfWork.Complete();

                //Adiciona registro de TransactionLog a Dto de saida
                transactionApiOutputDto.transaction_log_id = transactionLog.Id;

                //Retorna TransactionApiPoutputDto como JSon
                return(Json(transactionApiOutputDto));
            }


            // Validação Terminada com Sucesso \__________________

            // Criar a transação vinculada ao cartão e coloca a data de criação
            var transaction = new Transaction {
                Amount            = transAmountInDecimal,
                Card              = card,
                TransactionType   = transactionType,
                Installments      = transactionApiInputDto.installments,
                CreationTimestamp = DateTime.Now,
                TransactionLogId  = transactionLog.Id
            };

            //Adiciona a transação à base de dados
            _unitOfWork.Transactions.Add(transaction);

            //Atualiza o saldo do cliente com o valor da transação
            clientInDB.Saldo = clientInDB.Saldo - transAmountInDecimal;

            //Atualiza a base de dados
            _unitOfWork.Complete();

            //Adiciona registro de TransactionLog a Dto de saida
            transactionApiOutputDto.transaction_log_id = transactionLog.Id;

            //Atualiza a TransactionApiOutputDto com a Id da transação
            transactionApiOutputDto.transaction_id = transaction.Id;

            //Retorna TransactionApiOutputDto como Json e disponibiliza o link para a transação criada
            return(Created(new Uri(Request.RequestUri + "/" + transaction.Id), transactionApiOutputDto));
        }
        public ActionResult Save(TransactionEditViewModel transEditView)
        {
            //Coloca valor do parâmetro show_errors numa var booleana para uso futuro
            var showErros = true;

            //Cria lista de erros encontrados durante a validação
            List <Error> errors = new List <Error>();

            TransactionApiInputDto inputTransaction;
            var transactionType = new TransactionType();
            var cardBrand       = new CardBrand();
            var card            = new Card();
            var clientInDB      = new Client();

            decimal transAmountInDecimal = 0m;

            //A transação é presumida recusada inicialmente
            var statusCode   = Constantes.scRecusada;
            var statusReason = Constantes.srErroInesperado;

            //Pega todos os Transactiontypes(tipos de transações)
            var transactionTypes = _unitOfWork.TransactionTypes.GetAll();
            //Pega todos os CardBrands(bandeiras de cartões)
            var cardBrands = _unitOfWork.CardBrands.GetAll();

            //Atribuindo os TransactionTypes e os CardBrands a viewmodel caso tenhamos que voltar para a view
            transEditView.TransactionTypes = transactionTypes;
            transEditView.CardBrands       = cardBrands;

            //Se algum erro foi gerado até aqui...
            if (!ModelState.IsValid)
            {
                //Retorna a TransactionEditViewModel para a view TransactionForm(nova transação)
                return(View("TransactionForm", transEditView));
            }

            //Esse teste é executado somente aqui e antes de chamar o validador (não é executado no validador)
            var tempAmount    = transEditView.Amount.Replace(",", "");
            var amountIsDigit = tempAmount.All(char.IsDigit);

            //Verifica se todos os caracteres do campo card_number são números
            if (!amountIsDigit)
            {
                ModelState.AddModelError("", "410 - O valor informado não é válido.");
            }

            if (!ModelState.IsValid)
            {
                //Retorna a TransactionEditViewModel para a view TransactionForm(nova transação)
                return(View("TransactionForm", transEditView));
            }

            var dblAmount = Convert.ToDouble(transEditView.Amount);
            var intAmount = Convert.ToInt32(dblAmount * 100);

            //Move os dados da Edit View para a Dto.
            inputTransaction = new TransactionApiInputDto
            {
                amount           = intAmount,
                card_brand       = transEditView.CardBrandApiName,
                card_holder_name = transEditView.CardHolderName,
                card_number      = transEditView.Number,
                cvv = transEditView.Cvv,
                expiration_month = transEditView.ExpirationMonth,
                expiration_year  = transEditView.ExpirationYear,
                installments     = transEditView.Installments,
                password         = transEditView.Password,
                show_errors      = showErros,
                transaction_type = transEditView.TransactionTypeApiName,
            };

            var validador = new Validador(_unitOfWork, showErros);

            validador.ProcessTransaction(inputTransaction);

            statusCode   = validador.statusCode;
            statusReason = validador.statusReason;

            transAmountInDecimal = validador.transAmountInDecimal;

            transactionType = validador.transactionType;
            cardBrand       = validador.cardBrand;
            card            = validador.card;
            clientInDB      = validador.clientInDB;

            errors = validador.errors;

            // *** FIM DAS VALIDAÇÕES ***

            //Adiciona todos os error do validador ao ModelState
            if (errors.Count > 0)
            {
                foreach (var e in errors)
                {
                    ModelState.AddModelError("", e.error_code + " - " + e.error_message);
                }
            }

            if (!ModelState.IsValid)
            {
                //Retorna a TransactionEditViewModel para a view TransactionForm(nova transação)
                return(View("TransactionForm", transEditView));
            }


            // Validação Terminada com Sucesso \__________________

            var cardNumberFirst = "";
            var cardNumberLast  = "";

            if (!String.IsNullOrEmpty(transEditView.Number))
            {
                cardNumberFirst = transEditView.Number.Substring(1, 4);
                if (transEditView.Number.Length >= 4)
                {
                    cardNumberLast = transEditView.Number.Substring(transEditView.Number.Length - 4, 4);
                }
            }

            //Gera registro da transação(transaction log) solicitada para ser salva na base de dados
            var transactionLog = new TransactionLog
            {
                Amount             = Convert.ToInt32(transAmountInDecimal * 100),
                Card_brand         = card.CardBrand.ApiName,
                Card_holder_name   = transEditView.CardHolderName,
                Card_number_first  = cardNumberFirst,
                Card_number_last   = cardNumberLast,
                Installments       = transEditView.Installments,
                Transaction_type   = transEditView.TransactionTypeApiName,
                Status_code        = statusCode,
                Status_reason      = statusReason,
                Creation_timestamp = DateTime.Now,
                //Errors = null
            };

            //Adiciona registro no log de transações ao banco de dados
            _unitOfWork.TransactionLogs.Add(transactionLog);

            if (errors.Count > 0)
            {
                foreach (var e in errors)
                {
                    var errorLog = new ErrorLog
                    {
                        TransactionLogId = transactionLog.Id,
                        Error_code       = e.error_code,
                        Error_message    = e.error_message
                    };
                    _unitOfWork.ErrorLogs.Add(errorLog);
                }
            }
            ;

            //Criar a transação vinculada ao cartão e coloca a data de criação
            var transaction = new Transaction {
                Amount            = transAmountInDecimal,
                CardId            = card.Id,
                CreationTimestamp = DateTime.Now,
                Installments      = transEditView.Installments,
                TransactionTypeId = transactionType.Id,
                TransactionLogId  = transactionLog.Id
            };

            //Adiciona a transação ao banco de dados
            _unitOfWork.Transactions.Add(transaction);

            //Atualiza o saldo do cliente com o valor da transação.
            clientInDB.Saldo = clientInDB.Saldo - transaction.Amount;

            _unitOfWork.Complete();

            //Retorna a TransactionEditViewModel para a view Index(listagem de transações)
            return(RedirectToAction("Index", new { cardId = transaction.CardId }));
        }
예제 #3
0
        public void Init()
        {
            sqlConnection = new SqlConnection();
            var connectionString = @"data source=(LocalDb)\MSSQLLocalDB;initial catalog=Lascarizador.LascarizadorDbContext;integrated security=True;MultipleActiveResultSets=True;App=EntityFramework";

            sqlBuilder = new SqlConnectionStringBuilder(connectionString);

            sqlConnection.ConnectionString = sqlBuilder.ConnectionString;
            _context    = new LascarizadorDbContext(sqlConnection);
            _unitOfWork = new UnitOfWork(_context);
            validador   = new Validador(_unitOfWork, true);

            cardPassword    = "******";
            securedPassword = new SecuredPassword(cardPassword);

            card = new Card
            {
                CardHolderName = "FREDERICO FLINSTONE",
                CardBrandId    = 2,
                CardTypeId     = 2,
                ClientId       = 1,
                Cvv            = 222,
                ExpirationDate = Convert.ToDateTime("01/01/2019"),
                HasPassword    = true,
                IsBlocked      = true,
                Number         = "5555666677778888",
                Password       = cardPassword,
                HashPassword   = securedPassword.Hash,
                SaltPassword   = securedPassword.Salt
            };

            //Os testes estão encontrando o mesmo cartão na base de dados.
            //Solução provisória: usar o cartão se ele já estiver lá
            var cardAlreadyThere = _unitOfWork.Cards.SingleOrDefault(c => c.CardBrandId == 1 && c.Number == "1111222233334444");

            if (cardAlreadyThere == null)
            {
                _unitOfWork.Cards.Add(card);
                // Guarda o identificador do cartão;
                _unitOfWork.Complete();
                cardId = card.Id;
            }
            else
            {
                cardId = cardAlreadyThere.Id;
            }


            // Cria um input padrão sem problemas para uso futuro
            inputTransaction = new TransactionApiInputDto
            {
                amount           = 10000,
                card_brand       = "bedrock_master",
                card_holder_name = "FREDERICO FLINSTONE",
                card_number      = "5555666677778888",
                cvv = "222",
                expiration_month = 01,
                expiration_year  = 2019,
                installments     = 0,
                password         = cardPassword,
                transaction_type = "credito",
                show_errors      = true
            };
        }
예제 #4
0
        public void ProcessTransaction(TransactionApiInputDto inputTransaction)
        {
            //___/ Verifica campos requeridos - Erros 300 \_____________________

            var transactioTypeIsNull = String.IsNullOrEmpty(inputTransaction.transaction_type);
            var cardBrandIsNull      = String.IsNullOrEmpty(inputTransaction.card_brand);
            var cardHolderNameIsNull = String.IsNullOrEmpty(inputTransaction.card_holder_name);
            var cardNumberIsNull     = String.IsNullOrEmpty(inputTransaction.card_number);
            var cvvIsNull            = String.IsNullOrEmpty(inputTransaction.cvv);

            var passwordIsNull = String.IsNullOrEmpty(inputTransaction.password);

            statusReason = Constantes.srCampoRequerido;

            if (transactioTypeIsNull)
            {
                errors.Add(new Error(301, "O tipo de transação não foi informado."));
            }
            if (cardBrandIsNull)
            {
                errors.Add(new Error(302, "A bandeira do cartão não foi informada."));
            }
            if (cardHolderNameIsNull)
            {
                errors.Add(new Error(303, "O nome do portador do cartão não foi informado."));
            }
            if (cardNumberIsNull)
            {
                errors.Add(new Error(304, "O número do cartão não foi informado."));
            }
            if (cvvIsNull)
            {
                errors.Add(new Error(305, "O cvv do cartão não foi informado."));
            }


            //___/ Verifica campos inválidos - Erros 400 \_____________________

            // Se nenhum erro foi encontrado continua a validação
            if (errors.Count == 0)
            {
                statusReason = Constantes.srCampoInvalido;

                var transactionTypeApiName = inputTransaction.transaction_type;
                transactionType = _unitOfWork.TransactionTypes.SingleOrDefault(t => t.ApiName == transactionTypeApiName);
                // Validando TransactionType (Tipo da Transação)
                //Se não encontrar o tipo da transação...
                if (transactionType == null)
                {
                    errors.Add(new Error(401, "O tipo de transação informado não é válido."));
                }

                var cardBrandApiName = inputTransaction.card_brand;
                cardBrand = _unitOfWork.CardBrands.SingleOrDefault(t => t.ApiName == cardBrandApiName);
                // Validando CardBrand (bandeira do cartão)
                if (cardBrand == null)
                {
                    errors.Add(new Error(402, "A bandeira de cartão informada não é válida."));
                }

                var cardNumberIsDigit = inputTransaction.card_number.All(char.IsDigit);
                //Verifica se todos os caracteres do campo card_number são números
                if (!cardNumberIsDigit)
                {
                    errors.Add(new Error(403, "O número do cartão informado não é válido."));
                }

                var cardNumberLength = inputTransaction.card_number.Length;
                //Verifica se o tamanho do campo card_number está correto
                if (cardNumberLength < 12 || cardNumberLength > 16)
                {
                    errors.Add(new Error(404, "O número do cartão informado deve ter de 12 a 16 números."));
                }

                var cvvIsDigit = inputTransaction.cvv.All(char.IsDigit);
                //Verifica se todos os caracteres do campo card_number são números
                if (!cvvIsDigit)
                {
                    errors.Add(new Error(405, "O cvv do cartão informado não é válido."));
                }

                var cvvLength = inputTransaction.cvv.Length;
                //Verifica se o tamanho do campo cvv está correto
                if (cvvLength != 3)
                {
                    errors.Add(new Error(406, "O cvv do cartão informado deve ter 3 números."));
                }
            }

            // Se nenhum erro foi encontrado continua a validação
            if (errors.Count == 0)
            {
                //Valor Inválido (Mínimo de 10 centavos)
                if (inputTransaction.amount < 10)
                {
                    statusReason = Constantes.srValorInvalido;
                    errors.Add(new Error(407, "O valor da transação deve ser maior ou igual a 10 centavos."));
                }
            }

            // Se nenhum erro foi encontrado continua a validação
            if (errors.Count == 0)
            {
                // Validação do valor das parcelas
                if (transactionType.InstallmentsAvailable == true)
                {
                    // Parcelas fora do intervalo de [1,12]
                    if (inputTransaction.installments < 1 || inputTransaction.installments > 12)
                    {
                        statusReason = Constantes.srCampoInvalido;
                        errors.Add(new Error(408, "O campo número de parcelas deve ser um número entre 1 e 12."));
                    }
                }
                else
                {
                    // Parcela diferente de 0 para Transações que não são parceladas
                    if (inputTransaction.installments != 0)
                    {
                        statusReason = Constantes.srCampoInvalido;
                        errors.Add(new Error(409, "O campo número de parcelas deve ser 0 para transações que não podem ser parceladas."));
                    }
                }
            }

            // ***ATENÇÃO *** O Erro 410 - O valor informado não é válido., está sendo usando na classe TransactionController

            //___/ Validando parâmetros do cartão - Erros 500 \____________________

            // Os próximos erros geram status_reason = cartao_invalido

            // Se nenhum erro foi encontrado continua a validação
            if (errors.Count == 0)
            {
                // Procura pelo cartão com a Bandeira informada e o Número
                card = _unitOfWork.Cards.SingleOrDefault(c => c.CardBrand.Id == cardBrand.Id && c.Number == inputTransaction.card_number);
                //Se não encontrar o cartão...
                if (card == null)
                {
                    statusReason = Constantes.srCartaoInvalido;
                    errors.Add(new Error(501, "O cartão informado não foi encontrado."));
                }
            }

            // Se nenhum erro foi encontrado continua a validação
            if (errors.Count == 0)
            {
                statusReason = Constantes.srCartaoInvalido;

                //Validando CardHolderName
                if (inputTransaction.card_holder_name != card.CardHolderName)
                {
                    errors.Add(new Error(502, "Nome do portador do cartão não está correto."));
                }

                //Validando CVV
                if (int.Parse(inputTransaction.cvv) != card.Cvv)
                {
                    errors.Add(new Error(503, "O campo cvv não está correto."));
                }

                //Validando Mês e Ano de Expiração do cartão
                if (inputTransaction.expiration_month != card.ExpirationDate.Month || inputTransaction.expiration_year != card.ExpirationDate.Year)
                {
                    errors.Add(new Error(504, "O mês e/ou ano de expiração não estão corretos."));
                }
            }

            // Se nenhum erro foi encontrado continua a validação
            if (errors.Count == 0)
            {
                cardHasPassword = card.HasPassword;

                // Se o cartão possui senha e a senha não foi informada
                if (cardHasPassword && passwordIsNull)
                {
                    statusReason = Constantes.srSenhaRequerida;
                    errors.Add(new Error(505, "A senha é requerida para esse tipo de cartão e não foi informada."));
                }
            }

            // Se nenhum erro foi encontrado continua a validação
            if (errors.Count == 0)
            {
                var passwordLength = inputTransaction.password.Length;
                passwordLengthOk = (passwordLength >= 4 && passwordLength <= 6);

                // Se o cartão possui senha e uma senha foi informada, verifica se o tamanho da senha está incorreto
                //  (Senha deve ter entre 4 e 6 dígitos)
                if (cardHasPassword && !passwordIsNull && !passwordLengthOk)
                {
                    statusReason = Constantes.srErroTamanhoSenha;
                    errors.Add(new Error(506, "A senha deve ter no mínimo 4 e no máximo 6 caracteres."));
                }
            }

            // Se nenhum erro foi encontrado continua a validação
            if (errors.Count == 0)
            {
                var securedPassword = new SecuredPassword(card.HashPassword, card.SaltPassword);
                // Verifica se a senha está correta
                if (card.HasPassword && !passwordIsNull && passwordLengthOk)
                {
                    if (!securedPassword.Verify(inputTransaction.password))
                    {
                        statusReason = Constantes.srSenhaInvalida;
                        errors.Add(new Error(507, "Senha inválida."));
                    }
                }
            }

            // Se nenhum erro foi encontrado continua a validação
            var now = DateTime.Now;

            if (errors.Count == 0)
            {
                //Validando se o cartão está expirado
                if (card.ExpirationDate < now)
                {
                    statusReason = Constantes.srCartaoExpirado;
                    errors.Add(new Error(508, "Cartão informado está expirado."));
                }
            }

            // Se nenhum erro foi encontrado continua a validação
            if (errors.Count == 0)
            {
                //Validando Cartão Bloqueado
                if (card.IsBlocked)
                {
                    statusReason = Constantes.srCartaoBloqueado;
                    errors.Add(new Error(509, "O cartão informado se encontra bloqueado."));
                }
            }


            //___/ Validando parâmetros do cliente - Erros 600 \__________________

            // Se nenhum erro foi encontrado continua a validação
            if (errors.Count == 0)
            {
                //Achando o Cliente do cartão.
                clientInDB = _unitOfWork.Clients.SingleOrDefault(c => c.Id == card.ClientId);

                //Se não encontrar o cliente do cartão...
                if (clientInDB == null)
                {
                    statusReason = Constantes.srClienteNaoEncontrado;
                    errors.Add(new Error(600, "Cliente não encontrado para esse cartão."));
                }
            }

            // Se nenhum erro foi encontrado continua a validação
            if (errors.Count == 0)
            {
                transAmountInDecimal = Convert.ToDecimal(inputTransaction.amount / 100);
                decimal creditLimit = clientInDB.CreditLimit;
                decimal saldo       = clientInDB.Saldo;

                //Validando Saldo Insuficiente
                if ((creditLimit + saldo) < transAmountInDecimal)
                {
                    // Se o Limite de Crédito do cliente menos o saldo atual dele
                    //   for menor que o valor da transação então gera erro.
                    statusReason = Constantes.srSaldoInsuficiente;
                    errors.Add(new Error(601, "Saldo Insuficiente para realizar a transação."));
                }
            }

            // *** FIM DAS VALIDAÇÕES ***
            // Se nenhum erro foi encontrado retorna transação validada com sucesso
            if (errors.Count == 0)
            {
                statusCode   = Constantes.scAprovada;
                statusReason = Constantes.srSucesso;
            }
            ;
        }