From 528e3191b32f3ae3fd64d46ca8ffe8a5a99aedd8 Mon Sep 17 00:00:00 2001 From: Lucas Mantovani Date: Tue, 15 Mar 2022 19:42:35 -0300 Subject: [PATCH] feat: secure fields events --- pom.xml | 2 +- .../controller/CardPaymentController.java | 7 +- .../sample/dto/CardPaymentDTO.java | 7 +- .../sample/dto/PaymentResponseDTO.java | 8 +- .../sample/service/CardPaymentService.java | 107 +++++++------- src/main/resources/static/css/index.css | 8 ++ src/main/resources/static/js/index.js | 134 ++++++++++++------ src/main/resources/templates/index.html | 6 +- 8 files changed, 160 insertions(+), 119 deletions(-) diff --git a/pom.xml b/pom.xml index e0e9df8..46fdfa7 100644 --- a/pom.xml +++ b/pom.xml @@ -32,7 +32,7 @@ com.mercadopago sdk-java - 1.11.0 + 2.0.0 com.google.code.gson diff --git a/src/main/java/com/mercadopago/sample/controller/CardPaymentController.java b/src/main/java/com/mercadopago/sample/controller/CardPaymentController.java index 7a64f4e..2b6d325 100644 --- a/src/main/java/com/mercadopago/sample/controller/CardPaymentController.java +++ b/src/main/java/com/mercadopago/sample/controller/CardPaymentController.java @@ -1,8 +1,9 @@ package com.mercadopago.sample.controller; -import com.mercadopago.sample.dto.PaymentResponseDTO; import com.mercadopago.sample.dto.CardPaymentDTO; +import com.mercadopago.sample.dto.PaymentResponseDTO; import com.mercadopago.sample.service.CardPaymentService; +import javax.validation.Valid; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -11,12 +12,10 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import javax.validation.Valid; - @RestController @RequestMapping("/process_payment") public class CardPaymentController { - private CardPaymentService cardPaymentService; + private final CardPaymentService cardPaymentService; @Autowired public CardPaymentController(CardPaymentService cardPaymentService) { diff --git a/src/main/java/com/mercadopago/sample/dto/CardPaymentDTO.java b/src/main/java/com/mercadopago/sample/dto/CardPaymentDTO.java index 0242dcb..fd6202a 100644 --- a/src/main/java/com/mercadopago/sample/dto/CardPaymentDTO.java +++ b/src/main/java/com/mercadopago/sample/dto/CardPaymentDTO.java @@ -2,6 +2,7 @@ import com.fasterxml.jackson.annotation.JsonProperty; +import java.math.BigDecimal; import javax.validation.constraints.NotNull; public class CardPaymentDTO { @@ -14,7 +15,7 @@ public class CardPaymentDTO { private String paymentMethodId; @NotNull - private Float transactionAmount; + private BigDecimal transactionAmount; @NotNull private Integer installments; @@ -53,11 +54,11 @@ public void setPaymentMethodId(String paymentMethodId) { this.paymentMethodId = paymentMethodId; } - public Float getTransactionAmount() { + public BigDecimal getTransactionAmount() { return transactionAmount; } - public void setTransactionAmount(Float transactionAmount) { + public void setTransactionAmount(BigDecimal transactionAmount) { this.transactionAmount = transactionAmount; } diff --git a/src/main/java/com/mercadopago/sample/dto/PaymentResponseDTO.java b/src/main/java/com/mercadopago/sample/dto/PaymentResponseDTO.java index 44f36a5..092e40b 100644 --- a/src/main/java/com/mercadopago/sample/dto/PaymentResponseDTO.java +++ b/src/main/java/com/mercadopago/sample/dto/PaymentResponseDTO.java @@ -1,21 +1,21 @@ package com.mercadopago.sample.dto; public class PaymentResponseDTO { - private String id; + private Long id; private String status; private String detail; - public PaymentResponseDTO(String id, String status, String detail) { + public PaymentResponseDTO(Long id, String status, String detail) { this.id = id; this.status = status; this.detail = detail; } - public String getId() { + public Long getId() { return id; } - public void setId(String id) { + public void setId(Long id) { this.id = id; } diff --git a/src/main/java/com/mercadopago/sample/service/CardPaymentService.java b/src/main/java/com/mercadopago/sample/service/CardPaymentService.java index 4af1165..716e490 100644 --- a/src/main/java/com/mercadopago/sample/service/CardPaymentService.java +++ b/src/main/java/com/mercadopago/sample/service/CardPaymentService.java @@ -1,69 +1,60 @@ package com.mercadopago.sample.service; +import com.mercadopago.MercadoPagoConfig; +import com.mercadopago.client.common.IdentificationRequest; +import com.mercadopago.client.payment.PaymentClient; +import com.mercadopago.client.payment.PaymentCreateRequest; +import com.mercadopago.client.payment.PaymentPayerRequest; +import com.mercadopago.exceptions.MPApiException; +import com.mercadopago.exceptions.MPException; +import com.mercadopago.resources.payment.Payment; +import com.mercadopago.sample.dto.CardPaymentDTO; import com.mercadopago.sample.dto.PaymentResponseDTO; import com.mercadopago.sample.exception.MercadoPagoException; -import com.mercadopago.sample.dto.CardPaymentDTO; -import com.mercadopago.MercadoPago; -import com.mercadopago.exceptions.MPException; -import com.mercadopago.resources.Payment; -import com.mercadopago.resources.datastructures.payment.Identification; -import com.mercadopago.resources.datastructures.payment.Payer; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; @Service public class CardPaymentService { - @Value("${mercado_pago_sample_access_token}") - private String mercadoPagoAccessToken; - - public PaymentResponseDTO processPayment(CardPaymentDTO cardPaymentDTO) { - try { - MercadoPago.SDK.setAccessToken(mercadoPagoAccessToken); - - Payment payment = new Payment(); - payment.setTransactionAmount(cardPaymentDTO.getTransactionAmount()) - .setToken(cardPaymentDTO.getToken()) - .setDescription(cardPaymentDTO.getProductDescription()) - .setInstallments(cardPaymentDTO.getInstallments()) - .setPaymentMethodId(cardPaymentDTO.getPaymentMethodId()); - - Identification identification = new Identification(); - identification.setType(cardPaymentDTO.getPayer().getIdentification().getType()) - .setNumber(cardPaymentDTO.getPayer().getIdentification().getNumber()); - - Payer payer = new Payer(); - payer.setEmail(cardPaymentDTO.getPayer().getEmail()); - payer.setIdentification(identification); - - payment.setPayer(payer); - - Payment createdPayment = payment.save(); - - this.validatePaymentResult(createdPayment); - - PaymentResponseDTO paymentResponseDTO = new PaymentResponseDTO( - createdPayment.getId(), - String.valueOf(createdPayment.getStatus()), - createdPayment.getStatusDetail() - ); - - return paymentResponseDTO; - } catch (MPException exception) { - System.out.println(exception.getMessage()); - throw new MercadoPagoException(exception.getMessage()); - } - } - - private void validatePaymentResult(Payment createdPayment) throws MPException { - if(createdPayment.getId() == null) { - String errorMessage = "Unknown error cause"; - - if(createdPayment.getLastApiResponse() != null) { - String sdkErrorMessage = createdPayment.getLastApiResponse().getJsonElementResponse().getAsJsonObject().get("message").getAsString(); - errorMessage = sdkErrorMessage != null ? sdkErrorMessage : errorMessage; - } - - throw new MPException(errorMessage); - } + @Value("${mercado_pago_sample_access_token}") + private String mercadoPagoAccessToken; + + public PaymentResponseDTO processPayment(CardPaymentDTO cardPaymentDTO) { + try { + MercadoPagoConfig.setAccessToken(mercadoPagoAccessToken); + + PaymentClient paymentClient = new PaymentClient(); + + PaymentCreateRequest paymentCreateRequest = + PaymentCreateRequest.builder() + .transactionAmount(cardPaymentDTO.getTransactionAmount()) + .token(cardPaymentDTO.getToken()) + .description(cardPaymentDTO.getProductDescription()) + .installments(cardPaymentDTO.getInstallments()) + .paymentMethodId(cardPaymentDTO.getPaymentMethodId()) + .payer( + PaymentPayerRequest.builder() + .email(cardPaymentDTO.getPayer().getEmail()) + .identification( + IdentificationRequest.builder() + .type(cardPaymentDTO.getPayer().getIdentification().getType()) + .number(cardPaymentDTO.getPayer().getIdentification().getNumber()) + .build()) + .build()) + .build(); + + Payment createdPayment = paymentClient.create(paymentCreateRequest); + + return new PaymentResponseDTO( + createdPayment.getId(), + String.valueOf(createdPayment.getStatus()), + createdPayment.getStatusDetail()); + } catch (MPApiException apiException) { + System.out.println(apiException.getApiResponse().getContent()); + throw new MercadoPagoException(apiException.getApiResponse().getContent()); + } catch (MPException exception) { + System.out.println(exception.getMessage()); + throw new MercadoPagoException(exception.getMessage()); } + } } diff --git a/src/main/resources/static/css/index.css b/src/main/resources/static/css/index.css index 087fd76..4c48446 100644 --- a/src/main/resources/static/css/index.css +++ b/src/main/resources/static/css/index.css @@ -405,4 +405,12 @@ footer p a:hover { #fail-response, #success-response { display: none; +} + +.validation-error { + border-color: red; +} + +#validation-error-messages p { + color: red; } \ No newline at end of file diff --git a/src/main/resources/static/js/index.js b/src/main/resources/static/js/index.js index a3d3c70..99b8fbe 100644 --- a/src/main/resources/static/js/index.js +++ b/src/main/resources/static/js/index.js @@ -4,57 +4,61 @@ const mercadopago = new MercadoPago(publicKey); function loadCardForm() { const productCost = document.getElementById('amount').value; const productDescription = document.getElementById('product-description').innerText; + const payButton = document.getElementById("form-checkout__submit"); + const validationErrorMessages= document.getElementById('validation-error-messages'); - const cardForm = mercadopago.cardForm({ - amount: productCost, - iframe: true, - form: { - id: "form-checkout", - cardholderName: { - id: "form-checkout__cardholderName", - placeholder: "Holder name", - }, - cardholderEmail: { - id: "form-checkout__cardholderEmail", - placeholder: "E-mail", - }, - cardNumber: { - id: "form-checkout__cardNumber", - placeholder: "Card number", - style: { - fontSize: "1rem" - }, - }, - cardExpirationDate: { - id: "form-checkout__cardExpirationDate", - placeholder: "MM/YYYY", - style: { - fontSize: "1rem" - }, - }, - securityCode: { - id: "form-checkout__securityCode", - placeholder: "Security code", - style: { - fontSize: "1rem" - }, - }, - installments: { - id: "form-checkout__installments", - placeholder: "Installments", - }, - identificationType: { - id: "form-checkout__identificationType", + const form = { + id: "form-checkout", + cardholderName: { + id: "form-checkout__cardholderName", + placeholder: "Holder name", + }, + cardholderEmail: { + id: "form-checkout__cardholderEmail", + placeholder: "E-mail", + }, + cardNumber: { + id: "form-checkout__cardNumber", + placeholder: "Card number", + style: { + fontSize: "1rem" }, - identificationNumber: { - id: "form-checkout__identificationNumber", - placeholder: "Identification number", + }, + expirationDate: { + id: "form-checkout__expirationDate", + placeholder: "MM/YYYY", + style: { + fontSize: "1rem" }, - issuer: { - id: "form-checkout__issuer", - placeholder: "Issuer", + }, + securityCode: { + id: "form-checkout__securityCode", + placeholder: "Security code", + style: { + fontSize: "1rem" }, }, + installments: { + id: "form-checkout__installments", + placeholder: "Installments", + }, + identificationType: { + id: "form-checkout__identificationType", + }, + identificationNumber: { + id: "form-checkout__identificationNumber", + placeholder: "Identification number", + }, + issuer: { + id: "form-checkout__issuer", + placeholder: "Issuer", + }, + }; + + const cardForm = mercadopago.cardForm({ + amount: productCost, + iframe: true, + form, callbacks: { onFormMounted: error => { if (error) @@ -120,7 +124,6 @@ function loadCardForm() { }, onFetching: (resource) => { console.log("Fetching resource: ", resource); - const payButton = document.getElementById("form-checkout__submit"); payButton.setAttribute('disabled', true); return () => { payButton.removeAttribute("disabled"); @@ -134,11 +137,48 @@ function loadCardForm() { } return token; + }, + onValidityChange: (error, field) => { + const input = document.getElementById(form[field].id); + removeFieldErrorMessages(input, validationErrorMessages); + addFieldErrorMessages(input, validationErrorMessages, error); + enableOrDisablePayButton(validationErrorMessages, payButton); } }, }); }; +function removeFieldErrorMessages(input, validationErrorMessages) { + Array.from(validationErrorMessages.children).forEach(child => { + const shouldRemoveChild = child.id.includes(input.id); + if (shouldRemoveChild) { + validationErrorMessages.removeChild(child); + } + }); +} + +function addFieldErrorMessages(input, validationErrorMessages, error) { + if (error) { + input.classList.add('validation-error'); + error.forEach((e, index) => { + const p = document.createElement('p'); + p.id = `${input.id}-${index}`; + p.innerText = e.message; + validationErrorMessages.appendChild(p); + }); + } else { + input.classList.remove('validation-error'); + } +} + +function enableOrDisablePayButton(validationErrorMessages, payButton) { + if (validationErrorMessages.children.length > 0) { + payButton.setAttribute('disabled', true); + } else { + payButton.removeAttribute('disabled'); + } +} + // Handle transitions document.getElementById('checkout-btn').addEventListener('click', function(){ $('.container__cart').fadeOut(500); diff --git a/src/main/resources/templates/index.html b/src/main/resources/templates/index.html index 5bd9896..d31d5f2 100644 --- a/src/main/resources/templates/index.html +++ b/src/main/resources/templates/index.html @@ -7,7 +7,7 @@ - + @@ -102,7 +102,7 @@

Card Details

-
+
@@ -120,6 +120,8 @@

Card Details

+
+