diff --git a/in-person-payments-example/src/main/java/com/adyen/ipp/api/InPersonPaymentsController.java b/in-person-payments-example/src/main/java/com/adyen/ipp/api/InPersonPaymentsController.java index bb56bb0..2193c48 100644 --- a/in-person-payments-example/src/main/java/com/adyen/ipp/api/InPersonPaymentsController.java +++ b/in-person-payments-example/src/main/java/com/adyen/ipp/api/InPersonPaymentsController.java @@ -1,11 +1,13 @@ package com.adyen.ipp.api; -import com.adyen.Client; -import com.adyen.enums.Environment; +import com.adyen.model.nexo.*; import com.adyen.ipp.ApplicationProperty; -import com.adyen.model.checkout.CreateCheckoutSessionResponse; +import com.adyen.ipp.model.*; +import com.adyen.ipp.request.*; +import com.adyen.ipp.response.*; +import com.adyen.ipp.service.*; +import com.adyen.ipp.util.IdUtility; import com.adyen.service.exception.ApiException; -import jakarta.servlet.http.HttpServletRequest; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -24,22 +26,182 @@ public class InPersonPaymentsController { private final ApplicationProperty applicationProperty; + @Autowired + private PosAbortService posAbortService; + + @Autowired + private PosPaymentService posPaymentService; + + @Autowired + private PosReversalService posReversalService; + + @Autowired + private PosTransactionStatusService posTransactionStatusService; + + @Autowired + private TableService tableService; + + private String saleId; + + private String poiId; + @Autowired public InPersonPaymentsController(ApplicationProperty applicationProperty) { this.applicationProperty = applicationProperty; + saleId = applicationProperty.getSaleId(); + poiId = applicationProperty.getPoiId(); + } - if(applicationProperty.getApiKey() == null) { - log.warn("ADYEN_KEY is UNDEFINED"); - throw new RuntimeException("ADYEN_KEY is UNDEFINED"); + @PostMapping("/create-payment") + public ResponseEntity createPayment(@RequestBody CreatePaymentRequest request) throws IOException, ApiException { + Table table = tableService.getTables().stream() + .filter(t -> t.getTableName().equals(request.getTableName())) + .findFirst() + .orElse(null); + + if (table == null) { + return ResponseEntity + .status(404) + .body(new CreatePaymentResponse() + .result("failure") + .refusalReason("Table " + request.getTableName() + " not found")); } - var client = new Client(applicationProperty.getApiKey(), Environment.TEST); + String serviceId = IdUtility.getRandomAlphanumericId(10); + + table.getPaymentStatusDetails().setServiceId(serviceId); + table.setPaymentStatus(PaymentStatus.PaymentInProgress); + + try { + var response = posPaymentService.sendPaymentRequestAsync(serviceId, poiId, saleId, request.getCurrency(), request.getAmount()); + + if (response == null || response.getSaleToPOIResponse() == null || response.getSaleToPOIResponse().getPaymentResponse() == null) { + table.setPaymentStatus(PaymentStatus.NotPaid); + return ResponseEntity + .badRequest() + .body(new CreatePaymentResponse() + .result("failure") + .refusalReason("Empty payment response")); + } + + var paymentResponse = response.getSaleToPOIResponse().getPaymentResponse(); + + switch (paymentResponse.getResponse().getResult()) { + case SUCCESS: + table.setPaymentStatus(PaymentStatus.Paid); + table.getPaymentStatusDetails().setPoiTransactionId(paymentResponse.getPOIData().getPOITransactionID().getTransactionID()); + table.getPaymentStatusDetails().setPoiTransactionTimeStamp(paymentResponse.getPOIData().getPOITransactionID().getTimeStamp()); + table.getPaymentStatusDetails().setSaleTransactionId(paymentResponse.getSaleData().getSaleTransactionID().getTransactionID()); + table.getPaymentStatusDetails().setSaleTransactionTimeStamp(paymentResponse.getSaleData().getSaleTransactionID().getTimeStamp()); + + return ResponseEntity + .ok() + .body(new CreatePaymentResponse() + .result("success")); + case FAILURE: + table.setPaymentStatus(PaymentStatus.NotPaid); + table.getPaymentStatusDetails().setRefusalReason("Payment terminal responded with: " + paymentResponse.getResponse().getErrorCondition()); + table.getPaymentStatusDetails().setPoiTransactionId(paymentResponse.getPOIData().getPOITransactionID().getTransactionID()); + table.getPaymentStatusDetails().setPoiTransactionTimeStamp(paymentResponse.getPOIData().getPOITransactionID().getTimeStamp()); + table.getPaymentStatusDetails().setSaleTransactionId(paymentResponse.getSaleData().getSaleTransactionID().getTransactionID()); + table.getPaymentStatusDetails().setSaleTransactionTimeStamp(paymentResponse.getSaleData().getSaleTransactionID().getTimeStamp()); + + return ResponseEntity + .ok() + .body(new CreatePaymentResponse() + .result("failure") + .refusalReason(table.getPaymentStatusDetails().getRefusalReason())); + default: + table.setPaymentStatus(PaymentStatus.NotPaid); + + return ResponseEntity + .badRequest() + .body(new CreatePaymentResponse() + .result("failure") + .refusalReason("Could not reach payment terminal with POI ID " + poiId)); + } + + } catch (IOException | ApiException e) { + log.error(e.toString()); + table.setPaymentStatus(PaymentStatus.NotPaid); + throw e; + } + } + + @PostMapping("/create-reversal") + public ResponseEntity createReversal(@RequestBody CreateReversalRequest request) throws IOException, ApiException { + Table table = tableService.getTables().stream() + .filter(t -> t.getTableName().equals(request.getTableName())) + .findFirst() + .orElse(null); + + if (table == null) { + return ResponseEntity + .status(404) + .body(new CreateReversalResponse() + .result("failure") + .refusalReason("Table " + request.getTableName() + " not found")); + } + + try { + var response = posReversalService.sendReversalRequestAsync(ReversalReasonType.MERCHANT_CANCEL, table.getPaymentStatusDetails().getSaleTransactionId(), table.getPaymentStatusDetails().getPoiTransactionId(), poiId, saleId); + + if (response == null || response.getSaleToPOIResponse() == null || response.getSaleToPOIResponse().getReversalResponse() == null) { + return ResponseEntity + .badRequest() + .body(new CreateReversalResponse() + .result("failure") + .refusalReason("Empty reversal response")); + } + + var reversalResponse = response.getSaleToPOIResponse().getReversalResponse(); + + switch (reversalResponse.getResponse().getResult()) { + case SUCCESS: + table.setPaymentStatus(PaymentStatus.RefundInProgress); + return ResponseEntity + .ok() + .body(new CreateReversalResponse() + .result("success")); + case FAILURE: + table.setPaymentStatus(PaymentStatus.RefundFailed); + return ResponseEntity + .ok() + .body(new CreateReversalResponse() + .result("failure") + .refusalReason("Payment terminal responded with: " + java.net.URLDecoder.decode(reversalResponse.getResponse().getAdditionalResponse()))); + default: + return ResponseEntity + .badRequest() + .body(new CreateReversalResponse() + .result("failure") + .refusalReason("Could not reach payment terminal with POI ID " + poiId)); + } + } catch (IOException | ApiException e) { + log.error(e.toString()); + throw e; + } } - @PostMapping("/ipp") - public ResponseEntity sessions(@RequestHeader String host, HttpServletRequest request) throws IOException, ApiException { - var response = ""; - return ResponseEntity.ok().body(response); + @GetMapping("/abort/{tableName}") + public ResponseEntity abort(@PathVariable String tableName) throws IOException, ApiException { + try { + Table table = tableService.getTables().stream() + .filter(t -> t.getTableName().equals(tableName)) + .findFirst() + .orElse(null); + + if (table == null || table.getPaymentStatusDetails() == null || table.getPaymentStatusDetails().getServiceId() == null) { + return ResponseEntity.notFound().build(); + } + + var abortResponse = posAbortService.sendAbortRequestAsync(table.getPaymentStatusDetails().getServiceId(), poiId, saleId); + + return ResponseEntity.ok().body(abortResponse); + } catch (IOException | ApiException e) { + log.error(e.toString()); + throw e; + } } -} +} \ No newline at end of file diff --git a/in-person-payments-example/src/main/java/com/adyen/ipp/model/PaymentStatusDetails.java b/in-person-payments-example/src/main/java/com/adyen/ipp/model/PaymentStatusDetails.java index 106f2fe..8c7a841 100644 --- a/in-person-payments-example/src/main/java/com/adyen/ipp/model/PaymentStatusDetails.java +++ b/in-person-payments-example/src/main/java/com/adyen/ipp/model/PaymentStatusDetails.java @@ -1,6 +1,6 @@ package com.adyen.ipp.model; -import java.time.LocalDateTime; +import javax.xml.datatype.XMLGregorianCalendar; public class PaymentStatusDetails { /** @@ -23,7 +23,7 @@ public class PaymentStatusDetails { /** * Date of the POI transaction. */ - private LocalDateTime poiTransactionTimeStamp; + private XMLGregorianCalendar poiTransactionTimeStamp; /** * The SaleTransactionId (SaleReferenceId), populated when a TableStatus is set to PaymentStatus.Paid. @@ -34,7 +34,7 @@ public class PaymentStatusDetails { /** * Date of the Sale transaction. */ - private LocalDateTime saleTransactionTimeStamp; + private XMLGregorianCalendar saleTransactionTimeStamp; /** * The unique ID of a message pair, which processes the transaction. The value is assigned when you initiate a payment transaction to the terminal, and used to cancel/abort the request. @@ -66,11 +66,11 @@ public void setPoiTransactionId(String poiTransactionId) { this.poiTransactionId = poiTransactionId; } - public LocalDateTime getPoiTransactionTimeStamp() { + public XMLGregorianCalendar getPoiTransactionTimeStamp() { return poiTransactionTimeStamp; } - public void setPoiTransactionTimeStamp(LocalDateTime poiTransactionTimeStamp) { + public void setPoiTransactionTimeStamp(XMLGregorianCalendar poiTransactionTimeStamp) { this.poiTransactionTimeStamp = poiTransactionTimeStamp; } @@ -82,11 +82,11 @@ public void setSaleTransactionId(String saleTransactionId) { this.saleTransactionId = saleTransactionId; } - public LocalDateTime getSaleTransactionTimeStamp() { + public XMLGregorianCalendar getSaleTransactionTimeStamp() { return saleTransactionTimeStamp; } - public void setSaleTransactionTimeStamp(LocalDateTime saleTransactionTimeStamp) { + public void setSaleTransactionTimeStamp(XMLGregorianCalendar saleTransactionTimeStamp) { this.saleTransactionTimeStamp = saleTransactionTimeStamp; } diff --git a/in-person-payments-example/src/main/java/com/adyen/ipp/model/Table.java b/in-person-payments-example/src/main/java/com/adyen/ipp/model/Table.java index e1604a3..4ec3f4a 100644 --- a/in-person-payments-example/src/main/java/com/adyen/ipp/model/Table.java +++ b/in-person-payments-example/src/main/java/com/adyen/ipp/model/Table.java @@ -62,7 +62,5 @@ public PaymentStatusDetails getPaymentStatusDetails() { return paymentStatusDetails; } - public void setPaymentStatusDetails(PaymentStatusDetails paymentStatusDetails) { - this.paymentStatusDetails = paymentStatusDetails; - } + public void setPaymentStatusDetails(PaymentStatusDetails paymentStatusDetails) { this.paymentStatusDetails = paymentStatusDetails; } } diff --git a/in-person-payments-example/src/main/java/com/adyen/ipp/response/CreatePaymentResponse.java b/in-person-payments-example/src/main/java/com/adyen/ipp/response/CreatePaymentResponse.java index 22f6f74..9d43615 100644 --- a/in-person-payments-example/src/main/java/com/adyen/ipp/response/CreatePaymentResponse.java +++ b/in-person-payments-example/src/main/java/com/adyen/ipp/response/CreatePaymentResponse.java @@ -19,4 +19,13 @@ public String getRefusalReason() { public void setRefusalReason(String refusalReason) { this.refusalReason = refusalReason; } + + public CreatePaymentResponse refusalReason(String refusalReason){ + this.refusalReason = refusalReason; + return this; + } + public CreatePaymentResponse result(String result){ + this.result = result; + return this; + } } diff --git a/in-person-payments-example/src/main/java/com/adyen/ipp/response/CreateReversalResponse.java b/in-person-payments-example/src/main/java/com/adyen/ipp/response/CreateReversalResponse.java index 25e7a83..f8087a0 100644 --- a/in-person-payments-example/src/main/java/com/adyen/ipp/response/CreateReversalResponse.java +++ b/in-person-payments-example/src/main/java/com/adyen/ipp/response/CreateReversalResponse.java @@ -1,4 +1,5 @@ package com.adyen.ipp.response; + public class CreateReversalResponse { private String result; private String refusalReason; @@ -18,4 +19,13 @@ public String getRefusalReason() { public void setRefusalReason(String refusalReason) { this.refusalReason = refusalReason; } + + public CreateReversalResponse refusalReason(String refusalReason){ + this.refusalReason = refusalReason; + return this; + } + public CreateReversalResponse result(String result){ + this.result = result; + return this; + } } diff --git a/in-person-payments-example/src/main/java/com/adyen/ipp/service/PosAbortService.java b/in-person-payments-example/src/main/java/com/adyen/ipp/service/PosAbortService.java new file mode 100644 index 0000000..e6e8183 --- /dev/null +++ b/in-person-payments-example/src/main/java/com/adyen/ipp/service/PosAbortService.java @@ -0,0 +1,50 @@ +package com.adyen.ipp.service; + +import com.adyen.ipp.util.IdUtility; +import com.adyen.service.exception.ApiException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.adyen.model.nexo.*; +import com.adyen.model.terminal.*; + +import java.io.IOException; + +@Service +public class PosAbortService { + @Autowired + private TerminalCloudApiService terminalCloudAPIService; + + public TerminalAPIResponse sendAbortRequestAsync(String serviceId, String poiId, String saleId) throws IOException, ApiException { + TerminalAPIRequest request = getAbortRequest(serviceId, poiId, saleId); + return terminalCloudAPIService.getTerminalCloudApi().sync(request); + } + + private TerminalAPIRequest getAbortRequest(String serviceId, String poiId, String saleId) { + var messageHeader = new MessageHeader(); + messageHeader.setMessageCategory(MessageCategoryType.ABORT); + messageHeader.setMessageClass(MessageClassType.SERVICE); + messageHeader.setMessageType(MessageType.REQUEST); + messageHeader.setPOIID(poiId); + messageHeader.setSaleID(saleId); + messageHeader.setServiceID(IdUtility.getRandomAlphanumericId(10)); + + var messageReference = new MessageReference(); + messageReference.setMessageCategory(MessageCategoryType.PAYMENT); + messageReference.setServiceID(serviceId); + messageReference.setPOIID(poiId); + messageReference.setSaleID(saleId); + + var abortRequest = new AbortRequest(); + abortRequest.setAbortReason("MerchantAbort"); + abortRequest.setMessageReference(messageReference); + + var saleToPOIRequest = new SaleToPOIRequest(); + saleToPOIRequest.setMessageHeader(messageHeader); + saleToPOIRequest.setAbortRequest(abortRequest); + + var terminalAPIRequest = new TerminalAPIRequest(); + terminalAPIRequest.setSaleToPOIRequest(saleToPOIRequest); + + return terminalAPIRequest; + } +} \ No newline at end of file diff --git a/in-person-payments-example/src/main/java/com/adyen/ipp/service/PosPaymentService.java b/in-person-payments-example/src/main/java/com/adyen/ipp/service/PosPaymentService.java new file mode 100644 index 0000000..487fa16 --- /dev/null +++ b/in-person-payments-example/src/main/java/com/adyen/ipp/service/PosPaymentService.java @@ -0,0 +1,69 @@ +package com.adyen.ipp.service; + +import com.adyen.model.nexo.*; +import com.adyen.model.terminal.TerminalAPIRequest; +import com.adyen.model.terminal.TerminalAPIResponse; +import com.adyen.service.exception.ApiException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import javax.xml.datatype.DatatypeConfigurationException; +import javax.xml.datatype.DatatypeFactory; +import java.io.IOException; +import java.math.BigDecimal; +import java.util.GregorianCalendar; +import java.util.UUID; + +@Service +public class PosPaymentService { + @Autowired + private TerminalCloudApiService terminalCloudAPIService; + + private DatatypeFactory dataTypeFactory; + + public PosPaymentService() throws DatatypeConfigurationException { + dataTypeFactory = DatatypeFactory.newInstance(); + } + + public TerminalAPIResponse sendPaymentRequestAsync(String serviceId, String poiId, String saleId, String currency, BigDecimal amount) throws IOException, ApiException { + TerminalAPIRequest request = getPaymentRequest(serviceId, poiId, saleId, currency, amount); + return terminalCloudAPIService.getTerminalCloudApi().sync(request); + } + + private TerminalAPIRequest getPaymentRequest(String serviceId, String poiId, String saleId, String currency, BigDecimal amount) { + var messageHeader = new MessageHeader(); + messageHeader.setMessageCategory(MessageCategoryType.PAYMENT); + messageHeader.setMessageClass(MessageClassType.SERVICE); + messageHeader.setMessageType(MessageType.REQUEST); + messageHeader.setPOIID(poiId); + messageHeader.setSaleID(saleId); + messageHeader.setServiceID(serviceId); + + var saleTransactionIdentification = new TransactionIdentification(); + saleTransactionIdentification.setTransactionID(UUID.randomUUID().toString()); + saleTransactionIdentification.setTimeStamp(dataTypeFactory.newXMLGregorianCalendar(new GregorianCalendar())); + + var saleData = new SaleData(); + saleData.setSaleTransactionID(saleTransactionIdentification); + + var amountsReq = new AmountsReq(); + amountsReq.setCurrency(currency); + amountsReq.setRequestedAmount(amount); + + var paymentTransaction = new PaymentTransaction(); + paymentTransaction.setAmountsReq(amountsReq); + + var paymentRequest = new PaymentRequest(); + paymentRequest.setSaleData(saleData); + paymentRequest.setPaymentTransaction(paymentTransaction); + + var saleToPOIRequest = new SaleToPOIRequest(); + saleToPOIRequest.setMessageHeader(messageHeader); + saleToPOIRequest.setPaymentRequest(paymentRequest); + + var terminalAPIRequest = new TerminalAPIRequest(); + terminalAPIRequest.setSaleToPOIRequest(saleToPOIRequest); + + return terminalAPIRequest; + } +} \ No newline at end of file diff --git a/in-person-payments-example/src/main/java/com/adyen/ipp/service/PosReversalService.java b/in-person-payments-example/src/main/java/com/adyen/ipp/service/PosReversalService.java new file mode 100644 index 0000000..743ec2c --- /dev/null +++ b/in-person-payments-example/src/main/java/com/adyen/ipp/service/PosReversalService.java @@ -0,0 +1,73 @@ +package com.adyen.ipp.service; + +import com.adyen.ipp.util.IdUtility; +import com.adyen.model.nexo.*; +import com.adyen.model.terminal.TerminalAPIRequest; +import com.adyen.model.terminal.TerminalAPIResponse; +import com.adyen.service.exception.ApiException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import javax.xml.datatype.DatatypeConfigurationException; +import javax.xml.datatype.DatatypeFactory; +import java.io.IOException; +import java.math.BigDecimal; +import java.util.GregorianCalendar; +import java.util.UUID; + +@Service +public class PosReversalService { + @Autowired + private TerminalCloudApiService terminalCloudAPIService; + + private DatatypeFactory dataTypeFactory; + + public PosReversalService() throws DatatypeConfigurationException { + dataTypeFactory = DatatypeFactory.newInstance(); + } + + public TerminalAPIResponse sendReversalRequestAsync(ReversalReasonType reversalReasonType, String saleTransactionId, String poiTransactionId, String poiId, String saleId) throws IOException, ApiException { + TerminalAPIRequest request = getReversalRequest(reversalReasonType, saleTransactionId, poiTransactionId, poiId, saleId); + return terminalCloudAPIService.getTerminalCloudApi().sync(request); + } + + private TerminalAPIRequest getReversalRequest(ReversalReasonType reversalReasonType, String saleTransactionId, String poiTransactionId, String poiId, String saleId) { + var messageHeader = new MessageHeader(); + messageHeader.setMessageCategory(MessageCategoryType.REVERSAL); + messageHeader.setMessageClass(MessageClassType.SERVICE); + messageHeader.setMessageType(MessageType.REQUEST); + messageHeader.setPOIID(poiId); + messageHeader.setSaleID(saleId); + messageHeader.setServiceID(IdUtility.getRandomAlphanumericId(10)); + + var poiTransactionIdentification = new TransactionIdentification(); + poiTransactionIdentification.setTransactionID(poiTransactionId); + poiTransactionIdentification.setTimeStamp(dataTypeFactory.newXMLGregorianCalendar(new GregorianCalendar())); + + var originalPOITransaction = new OriginalPOITransaction(); + originalPOITransaction.setPOIID(poiId); + originalPOITransaction.setSaleID(saleId); + originalPOITransaction.setPOITransactionID(poiTransactionIdentification); + + var saleTransactionIdentification = new TransactionIdentification(); + saleTransactionIdentification.setTransactionID(saleTransactionId); + saleTransactionIdentification.setTimeStamp(dataTypeFactory.newXMLGregorianCalendar(new GregorianCalendar())); + + var saleData = new SaleData(); + saleData.setSaleTransactionID(saleTransactionIdentification); + + var reversalRequest = new ReversalRequest(); + reversalRequest.setReversalReason(reversalReasonType); + reversalRequest.setOriginalPOITransaction(originalPOITransaction); + reversalRequest.setSaleData(saleData); + + var saleToPOIRequest = new SaleToPOIRequest(); + saleToPOIRequest.setMessageHeader(messageHeader); + saleToPOIRequest.setReversalRequest(reversalRequest); + + var terminalAPIRequest = new TerminalAPIRequest(); + terminalAPIRequest.setSaleToPOIRequest(saleToPOIRequest); + + return terminalAPIRequest; + } +} \ No newline at end of file diff --git a/in-person-payments-example/src/main/java/com/adyen/ipp/service/PosTransactionStatusService.java b/in-person-payments-example/src/main/java/com/adyen/ipp/service/PosTransactionStatusService.java new file mode 100644 index 0000000..ee799dc --- /dev/null +++ b/in-person-payments-example/src/main/java/com/adyen/ipp/service/PosTransactionStatusService.java @@ -0,0 +1,57 @@ +package com.adyen.ipp.service; + +import com.adyen.ipp.util.IdUtility; +import com.adyen.model.nexo.*; +import com.adyen.model.terminal.TerminalAPIRequest; +import com.adyen.model.terminal.TerminalAPIResponse; +import com.adyen.service.exception.ApiException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import javax.xml.datatype.DatatypeConfigurationException; +import javax.xml.datatype.DatatypeFactory; +import java.io.IOException; +import java.math.BigDecimal; +import java.util.GregorianCalendar; +import java.util.UUID; + +@Service +public class PosTransactionStatusService { + @Autowired + private TerminalCloudApiService terminalCloudAPIService; + + public TerminalAPIResponse sendTransactionStatusRequestAsync(String serviceId, String poiId, String saleId) throws IOException, ApiException { + TerminalAPIRequest request = getTransactionStatusRequest(serviceId, poiId, saleId); + return terminalCloudAPIService.getTerminalCloudApi().sync(request); + } + + private TerminalAPIRequest getTransactionStatusRequest(String serviceId, String poiId, String saleId) { + var messageHeader = new MessageHeader(); + messageHeader.setMessageCategory(MessageCategoryType.TRANSACTION_STATUS); + messageHeader.setMessageClass(MessageClassType.SERVICE); + messageHeader.setMessageType(MessageType.REQUEST); + messageHeader.setPOIID(poiId); + messageHeader.setSaleID(saleId); + messageHeader.setServiceID(IdUtility.getRandomAlphanumericId(10)); + + var messageReference = new MessageReference(); + messageReference.setMessageCategory(MessageCategoryType.PAYMENT); + messageReference.setServiceID(serviceId); + messageReference.setSaleID(saleId); + + var transactionStatusRequest = new TransactionStatusRequest(); + transactionStatusRequest.setMessageReference(messageReference); + transactionStatusRequest.setReceiptReprintFlag(true); + transactionStatusRequest.getDocumentQualifier().add(DocumentQualifierType.CASHIER_RECEIPT); + transactionStatusRequest.getDocumentQualifier().add(DocumentQualifierType.CUSTOMER_RECEIPT); + + var saleToPOIRequest = new SaleToPOIRequest(); + saleToPOIRequest.setMessageHeader(messageHeader); + saleToPOIRequest.setTransactionStatusRequest(transactionStatusRequest); + + var terminalAPIRequest = new TerminalAPIRequest(); + terminalAPIRequest.setSaleToPOIRequest(saleToPOIRequest); + + return terminalAPIRequest; + } +} \ No newline at end of file diff --git a/in-person-payments-example/src/main/java/com/adyen/ipp/service/TableService.java b/in-person-payments-example/src/main/java/com/adyen/ipp/service/TableService.java index e286759..1f3658b 100644 --- a/in-person-payments-example/src/main/java/com/adyen/ipp/service/TableService.java +++ b/in-person-payments-example/src/main/java/com/adyen/ipp/service/TableService.java @@ -1,8 +1,6 @@ package com.adyen.ipp.service; -import com.adyen.ipp.model.PaymentStatus; -import com.adyen.ipp.model.PaymentStatusDetails; -import com.adyen.ipp.model.Table; +import com.adyen.ipp.model.*; import org.springframework.stereotype.Service; import java.math.BigDecimal; diff --git a/in-person-payments-example/src/main/java/com/adyen/ipp/service/TerminalCloudApiService.java b/in-person-payments-example/src/main/java/com/adyen/ipp/service/TerminalCloudApiService.java new file mode 100644 index 0000000..bef2e27 --- /dev/null +++ b/in-person-payments-example/src/main/java/com/adyen/ipp/service/TerminalCloudApiService.java @@ -0,0 +1,31 @@ +package com.adyen.ipp.service; + +import com.adyen.Client; +import com.adyen.enums.Environment; +import com.adyen.ipp.ApplicationProperty; +import com.adyen.service.TerminalCloudAPI; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class TerminalCloudApiService { + private ApplicationProperty applicationProperty; + private Client client; + private TerminalCloudAPI terminalCloudAPI; + + @Autowired + public TerminalCloudApiService(ApplicationProperty applicationProperty) { + this.applicationProperty = applicationProperty; + + if(applicationProperty.getApiKey() == null) { + throw new RuntimeException("ADYEN_API_KEY is UNDEFINED"); + } + + client = new Client(applicationProperty.getApiKey(), Environment.TEST); + terminalCloudAPI = new TerminalCloudAPI(client); + } + + public TerminalCloudAPI getTerminalCloudApi() { + return terminalCloudAPI; + } +} diff --git a/in-person-payments-example/src/main/java/com/adyen/ipp/web/InPersonPaymentsWebController.java b/in-person-payments-example/src/main/java/com/adyen/ipp/web/InPersonPaymentsWebController.java index dfbaf81..3d9f362 100644 --- a/in-person-payments-example/src/main/java/com/adyen/ipp/web/InPersonPaymentsWebController.java +++ b/in-person-payments-example/src/main/java/com/adyen/ipp/web/InPersonPaymentsWebController.java @@ -1,7 +1,10 @@ package com.adyen.ipp.web; import com.adyen.ipp.ApplicationProperty; +import com.adyen.ipp.model.Table; +import com.adyen.ipp.service.PosTransactionStatusService; import com.adyen.ipp.service.TableService; +import com.adyen.service.exception.ApiException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -9,6 +12,9 @@ import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestParam; + +import java.io.IOException; @Controller public class InPersonPaymentsWebController { @@ -18,20 +24,21 @@ public class InPersonPaymentsWebController { @Autowired private TableService tableService; + @Autowired + private PosTransactionStatusService posTransactionStatusService; + @Autowired public InPersonPaymentsWebController(ApplicationProperty applicationProperty) { this.applicationProperty = applicationProperty; - - if(this.applicationProperty.getApiKey() == null) { - log.warn("ADYEN_API_KEY is undefined "); - } } @Autowired private ApplicationProperty applicationProperty; @GetMapping("/") - public String index() { return "index"; } + public String index() { + return "index"; + } @GetMapping("/cashregister") public String cashregister(Model model) { @@ -42,13 +49,46 @@ public String cashregister(Model model) { } @GetMapping("/transaction-status/{tableName}") - public String transactionstatus(@PathVariable String tableName) { + public String transactionstatus(@PathVariable String tableName, Model model) throws IOException, ApiException { + Table table = tableService.getTables().stream() + .filter(t -> t.getTableName().equals(tableName)) + .findFirst() + .orElse(null); + + if (table == null || table.getPaymentStatusDetails() == null || table.getPaymentStatusDetails().getServiceId() == null) { + model.addAttribute("errorMessage", "table not found"); + return "transactionstatus"; + } + + var response = posTransactionStatusService.sendTransactionStatusRequestAsync(table.getPaymentStatusDetails().getServiceId(), applicationProperty.getPoiId(), applicationProperty.getSaleId()); + + if (response == null || + response.getSaleToPOIResponse() == null || + response.getSaleToPOIResponse().getTransactionStatusResponse() == null) { + model.addAttribute("errorMessage", "transaction status response is empty"); + return "transactionstatus"; + } + + var transactionStatusResponse = response.getSaleToPOIResponse().getTransactionStatusResponse(); + + if (transactionStatusResponse.getRepeatedMessageResponse() == null || + transactionStatusResponse.getRepeatedMessageResponse().getRepeatedResponseMessageBody() == null || + transactionStatusResponse.getRepeatedMessageResponse().getRepeatedResponseMessageBody().getPaymentResponse() == null) { + model.addAttribute("errorMessage", "repeated message response is empty"); + return "transactionstatus"; + } + + var paymentResponse = transactionStatusResponse.getRepeatedMessageResponse().getRepeatedResponseMessageBody().getPaymentResponse(); + + model.addAttribute("paymentResponse", paymentResponse); + return "transactionstatus"; } - @GetMapping("/result/{type}") - public String result(@PathVariable String type, Model model) { + @GetMapping("/result/{type}/{refusalReason}") + public String result(@PathVariable String type, @PathVariable(required = false) String refusalReason, Model model) { model.addAttribute("type", type); + model.addAttribute("refusalReason", refusalReason); return "result"; } } diff --git a/in-person-payments-example/src/main/resources/templates/result.html b/in-person-payments-example/src/main/resources/templates/result.html index a4efdbc..9650722 100644 --- a/in-person-payments-example/src/main/resources/templates/result.html +++ b/in-person-payments-example/src/main/resources/templates/result.html @@ -13,6 +13,7 @@

Thank you! + Learn how to resolve Terminal API errors and handle declined payments in our documentation. diff --git a/in-person-payments-example/src/main/resources/templates/transactionstatus.html b/in-person-payments-example/src/main/resources/templates/transactionstatus.html index 8d52c1e..1e7481c 100644 --- a/in-person-payments-example/src/main/resources/templates/transactionstatus.html +++ b/in-person-payments-example/src/main/resources/templates/transactionstatus.html @@ -18,11 +18,11 @@

Transaction Status for [[${tableName}]]

Payment Response Result: [[${paymentResponse.response.result}]]

- POI Transaction ID: [[${paymentResponse.poiData?.poiTransactionID?.transactionID}]]
- POI Transaction TimeStamp: [[${paymentResponse.poiData?.poiTransactionID?.timeStamp}]]
- Sale Transaction ID: [[${paymentResponse.saleData?.saleTransactionID?.transactionID}]]
- Sale Transaction TimeStamp: [[${paymentResponse.saleData?.saleTransactionID?.timeStamp}]]
- Authorized amount: [[${paymentResponse.paymentResult?.amountsResp?.currency}]] [[${paymentResponse.paymentResult?.amountsResp?.authorizedAmount}]]
+ POI Transaction ID: [[${paymentResponse.getPOIData().getPOITransactionID().getTransactionID()}]]
+ POI Transaction TimeStamp: [[${paymentResponse.getPOIData().getPOITransactionID().getTimeStamp()}]]
+ Sale Transaction ID: [[${paymentResponse.getSaleData().getSaleTransactionID().getTransactionID()}]]
+ Sale Transaction TimeStamp: [[${paymentResponse.getSaleData().getSaleTransactionID().getTimeStamp()}]]
+ Authorized amount: [[${paymentResponse.getPaymentResult().getAmountsResp().getCurrency()}]] [[${paymentResponse.getPaymentResult().getAmountsResp().getAuthorizedAmount()}]]