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 })); }
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 }; }
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; } ; }