diff --git a/README.md b/README.md index c248eab37..eafb8d280 100644 --- a/README.md +++ b/README.md @@ -135,6 +135,12 @@ https://docs.adyen.com/developers/payment-methods/boleto-bancario/boleto-payment ``` is3DS2allowed = false ``` +## POS Timeout configuration +POS timeout (time calculated since initiating a payment) is max time to keep terminal connection open. It is set to 130 seconds by default already. If you want to change it, please add following property in local.properties file, build your environment and restart the server. (Change 130 to your desired time, in seconds). +``` +pos.totaltimeout = 130 +``` + ## Documentation https://docs.adyen.com/developers/plugins/hybris diff --git a/adyenv6b2ccheckoutaddon/acceleratoraddon/web/src/com/adyen/v6/controllers/pages/AdyenSummaryCheckoutStepController.java b/adyenv6b2ccheckoutaddon/acceleratoraddon/web/src/com/adyen/v6/controllers/pages/AdyenSummaryCheckoutStepController.java index 73449dd1e..867132458 100644 --- a/adyenv6b2ccheckoutaddon/acceleratoraddon/web/src/com/adyen/v6/controllers/pages/AdyenSummaryCheckoutStepController.java +++ b/adyenv6b2ccheckoutaddon/acceleratoraddon/web/src/com/adyen/v6/controllers/pages/AdyenSummaryCheckoutStepController.java @@ -20,6 +20,7 @@ */ package com.adyen.v6.controllers.pages; +import java.net.SocketTimeoutException; import java.net.UnknownHostException; import java.util.Arrays; import java.util.HashMap; @@ -43,6 +44,7 @@ import com.adyen.v6.constants.AdyenControllerConstants; import com.adyen.v6.exceptions.AdyenNonAuthorizedPaymentException; import com.adyen.v6.facades.AdyenCheckoutFacade; +import com.adyen.v6.util.TerminalAPIUtil; import de.hybris.platform.acceleratorservices.enums.CheckoutPciOptionEnum; import de.hybris.platform.acceleratorservices.urlresolver.SiteBaseUrlResolutionService; import de.hybris.platform.acceleratorstorefrontcommons.annotations.PreValidateCheckoutStep; @@ -61,7 +63,7 @@ import de.hybris.platform.commercefacades.product.ProductOption; import de.hybris.platform.commercefacades.product.data.ProductData; import de.hybris.platform.commerceservices.order.CommerceCartModificationException; -import de.hybris.platform.order.InvalidCartException; +import de.hybris.platform.servicelayer.config.ConfigurationService; import de.hybris.platform.site.BaseSiteService; import static com.adyen.constants.ApiConstants.Redirect.Data.MD; import static com.adyen.constants.ApiConstants.Redirect.Data.PAREQ; @@ -78,9 +80,10 @@ import static com.adyen.model.checkout.PaymentsResponse.ResultCodeEnum.REFUSED; import static com.adyen.v6.constants.Adyenv6coreConstants.PAYMENT_METHOD_BOLETO; import static com.adyen.v6.constants.Adyenv6coreConstants.PAYMENT_METHOD_CC; +import static com.adyen.v6.constants.Adyenv6coreConstants.PAYMENT_METHOD_MULTIBANCO; import static com.adyen.v6.constants.Adyenv6coreConstants.PAYMENT_METHOD_ONECLICK; +import static com.adyen.v6.constants.Adyenv6coreConstants.PAYMENT_METHOD_POS; import static com.adyen.v6.constants.Adyenv6coreConstants.RATEPAY; -import static com.adyen.v6.constants.Adyenv6coreConstants.PAYMENT_METHOD_MULTIBANCO; import static com.adyen.v6.facades.DefaultAdyenCheckoutFacade.MODEL_CHECKOUT_SHOPPER_HOST; import static com.adyen.v6.facades.DefaultAdyenCheckoutFacade.MODEL_ENVIRONMENT_MODE; @@ -95,6 +98,9 @@ public class AdyenSummaryCheckoutStepController extends AbstractCheckoutStepCont private static final String ADYEN_PAYLOAD = "payload"; private static final String REDIRECT_RESULT = "redirectResult"; + private static final int POS_TOTALTIMEOUT_DEFAULT = 130; + private static final String POS_TOTALTIMEOUT_KEY = "pos.totaltimeout"; + @Resource(name = "siteBaseUrlResolutionService") private SiteBaseUrlResolutionService siteBaseUrlResolutionService; @@ -107,6 +113,9 @@ public class AdyenSummaryCheckoutStepController extends AbstractCheckoutStepCont @Resource(name = "orderFacade") private OrderFacade orderFacade; + @Resource(name = "configurationService") + private ConfigurationService configurationService; + @RequestMapping(value = "/view", method = RequestMethod.GET) @RequireHardLogIn @Override @@ -144,8 +153,7 @@ public String enterStep(final Model model, final RedirectAttributes redirectAttr public String placeOrder(@ModelAttribute("placeOrderForm") final PlaceOrderForm placeOrderForm, final Model model, final HttpServletRequest request, - final RedirectAttributes redirectModel) throws CMSItemNotFoundException, // NOSONAR - InvalidCartException, CommerceCartModificationException { + final RedirectAttributes redirectModel) throws CMSItemNotFoundException, CommerceCartModificationException { if (validateOrderForm(placeOrderForm, model)) { return enterStep(model, redirectModel); } @@ -178,6 +186,43 @@ public String placeOrder(@ModelAttribute("placeOrderForm") final PlaceOrderForm } catch (Exception e) { LOGGER.error(ExceptionUtils.getStackTrace(e)); } + } else if (PAYMENT_METHOD_POS.equals(adyenPaymentMethod)) { + try { + String originalServiceId = Long.toString(System.currentTimeMillis() % 10000000000L); + request.setAttribute("originalServiceId", originalServiceId); + Long paymentStartTime = System.currentTimeMillis(); + request.setAttribute("paymentStartTime", paymentStartTime); + OrderData orderData = adyenCheckoutFacade.initiatePosPayment(request, cartData); + LOGGER.debug("Redirecting to confirmation!"); + return redirectToOrderConfirmationPage(orderData); + } catch (SocketTimeoutException e) { + try { + LOGGER.debug("POS request timed out. Checking POS Payment status "); + int totalTimeout = POS_TOTALTIMEOUT_DEFAULT; + if(configurationService.getConfiguration().containsKey(POS_TOTALTIMEOUT_KEY)) { + totalTimeout = configurationService.getConfiguration().getInt(POS_TOTALTIMEOUT_KEY); + } + request.setAttribute("totalTimeout", totalTimeout); + OrderData orderData = adyenCheckoutFacade.checkPosPaymentStatus(request, cartData); + LOGGER.debug("Redirecting to confirmation!"); + return redirectToOrderConfirmationPage(orderData); + } catch (AdyenNonAuthorizedPaymentException nx) { + LOGGER.debug("AdyenNonAuthorizedPaymentException", nx); + errorMessage = TerminalAPIUtil.getErrorMessageForNonAuthorizedPosPayment(nx.getTerminalApiResponse()); + } catch (SocketTimeoutException to) { + LOGGER.debug("POS Status request timed out. Returning error message."); + errorMessage = "checkout.error.authorization.pos.configuration"; + } catch (Exception ex) { + LOGGER.error("Exception", ex); + } + } catch (ApiException e) { + LOGGER.error("API exception " + e.getError(), e); + } catch (AdyenNonAuthorizedPaymentException e) { + LOGGER.debug("AdyenNonAuthorizedPaymentException", e); + errorMessage = TerminalAPIUtil.getErrorMessageForNonAuthorizedPosPayment(e.getTerminalApiResponse()); + } catch (Exception e) { + LOGGER.error("Exception", e); + } } else { try { cartData.setAdyenReturnUrl(getReturnUrl()); @@ -493,6 +538,7 @@ protected boolean validateOrderForm(final PlaceOrderForm placeOrderForm, final M return invalid; } + @RequestMapping(value = "/back", method = RequestMethod.GET) @RequireHardLogIn @Override diff --git a/adyenv6b2ccheckoutaddon/acceleratoraddon/web/webroot/WEB-INF/messages/base.properties b/adyenv6b2ccheckoutaddon/acceleratoraddon/web/webroot/WEB-INF/messages/base.properties index 7495c9274..4f1e77de2 100644 --- a/adyenv6b2ccheckoutaddon/acceleratoraddon/web/webroot/WEB-INF/messages/base.properties +++ b/adyenv6b2ccheckoutaddon/acceleratoraddon/web/webroot/WEB-INF/messages/base.properties @@ -6,6 +6,9 @@ checkout.error.authorization.payment.detail.not.found=The payment is REFUSED bec checkout.error.authorization.payment.refused=The payment is REFUSED. checkout.error.authorization.payment.cancelled=The payment is Cancelled. checkout.error.authorization.payment.error=An error occured. +checkout.error.authorization.pos.configuration=Error reaching POS terminal. Check the terminal connection/configuration and try again. +checkout.error.authorization.pos.busy=The terminal is busy. Wait for the current transaction to end and try again later. +checkout.error.authorization.pos.pin=The payment is REFUSED. Please check your Card details. text.account.storedCards.empty=There are no stored cards text.account.storedCard.delete=Remove diff --git a/adyenv6b2ccheckoutaddon/acceleratoraddon/web/webroot/WEB-INF/tags/responsive/alternativeMethod.tag b/adyenv6b2ccheckoutaddon/acceleratoraddon/web/webroot/WEB-INF/tags/responsive/alternativeMethod.tag index 3c7d4f91a..fbaac46ff 100644 --- a/adyenv6b2ccheckoutaddon/acceleratoraddon/web/webroot/WEB-INF/tags/responsive/alternativeMethod.tag +++ b/adyenv6b2ccheckoutaddon/acceleratoraddon/web/webroot/WEB-INF/tags/responsive/alternativeMethod.tag @@ -25,6 +25,7 @@ <%@ attribute name="showSocialSecurityNumber" required="false" type="java.lang.Boolean" %> <%@ attribute name="showFirstName" required="false" type="java.lang.Boolean" %> <%@ attribute name="showLastName" required="false" type="java.lang.Boolean" %> +<%@ attribute name="showTerminalList" required="false" type="java.lang.Boolean" %> <%@ attribute name="countryCode" required="false" type="java.lang.String" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> @@ -52,6 +53,19 @@ + + + + + + + ${connectedTerminal} + + + + + + Date of birth diff --git a/adyenv6b2ccheckoutaddon/acceleratoraddon/web/webroot/WEB-INF/views/responsive/pages/checkout/multi/checkoutSummaryPage.jsp b/adyenv6b2ccheckoutaddon/acceleratoraddon/web/webroot/WEB-INF/views/responsive/pages/checkout/multi/checkoutSummaryPage.jsp index 32046bf71..217563f92 100644 --- a/adyenv6b2ccheckoutaddon/acceleratoraddon/web/webroot/WEB-INF/views/responsive/pages/checkout/multi/checkoutSummaryPage.jsp +++ b/adyenv6b2ccheckoutaddon/acceleratoraddon/web/webroot/WEB-INF/views/responsive/pages/checkout/multi/checkoutSummaryPage.jsp @@ -11,6 +11,13 @@ + + + + Please wait while your payment is processed. Do not click back or refresh the page. + + + @@ -32,8 +39,8 @@ - - + + @@ -52,4 +59,4 @@ - \ No newline at end of file + diff --git a/adyenv6b2ccheckoutaddon/acceleratoraddon/web/webroot/WEB-INF/views/responsive/pages/checkout/multi/selectPaymentMethodPage.jsp b/adyenv6b2ccheckoutaddon/acceleratoraddon/web/webroot/WEB-INF/views/responsive/pages/checkout/multi/selectPaymentMethodPage.jsp index 0c6c70b7f..b15b5530f 100644 --- a/adyenv6b2ccheckoutaddon/acceleratoraddon/web/webroot/WEB-INF/views/responsive/pages/checkout/multi/selectPaymentMethodPage.jsp +++ b/adyenv6b2ccheckoutaddon/acceleratoraddon/web/webroot/WEB-INF/views/responsive/pages/checkout/multi/selectPaymentMethodPage.jsp @@ -110,6 +110,7 @@ + @@ -130,7 +131,7 @@ @@ -152,6 +153,13 @@ countryCode="BR" /> + + + diff --git a/adyenv6b2ccheckoutaddon/acceleratoraddon/web/webroot/_ui/responsive/common/css/adyenv6b2ccheckoutaddon.css b/adyenv6b2ccheckoutaddon/acceleratoraddon/web/webroot/_ui/responsive/common/css/adyenv6b2ccheckoutaddon.css index f3ceaa693..afaab484f 100644 --- a/adyenv6b2ccheckoutaddon/acceleratoraddon/web/webroot/_ui/responsive/common/css/adyenv6b2ccheckoutaddon.css +++ b/adyenv6b2ccheckoutaddon/acceleratoraddon/web/webroot/_ui/responsive/common/css/adyenv6b2ccheckoutaddon.css @@ -7,3 +7,75 @@ .checkout-paymentmethod li { list-style: none; } + +/* Spinner for "place order" */ +div#spinner { + position: absolute; + z-index: 1; + width: 75px; + height: 75px; + border: 10px solid #f3f3f3; + border-radius: 50%; + border-top: 10px solid #0f7384; + animation: spin 2s linear infinite; +} + +@-webkit-keyframes spin { + 0% { -webkit-transform: rotate(0deg); } + 100% { -webkit-transform: rotate(360deg); } +} + +@keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} + +/* Add animation to "page content" */ +.animate-bottom { + position: relative; + -webkit-animation-name: animatebottom; + -webkit-animation-duration: 1s; + animation-name: animatebottom; + animation-duration: 1s +} + +@-webkit-keyframes animatebottom { + from { bottom:-100px; opacity:0 } + to { bottom:0px; opacity:1 } +} + +@keyframes animatebottom { + from{ bottom:-100px; opacity:0 } + to{ bottom:0; opacity:1 } +} + +div#spinner_wrapper { + position: fixed; + padding: 0; + margin: 0; + top: 0; + left: 0; + height: 100vh; + width: 100vw; + background: rgba(0,0,0,0.65); + z-index: 999999; + justify-content: center; + align-items: center; +} + +div#spinner_text { + position: absolute; + bottom: 25%; + width: 100%; + text-align: center; + display: flex; + justify-content: center; + align-items: center; +} + +div#spinner_text p { + margin: 25px 0 25px; + color: white; + width: 50%; + padding: 20px; +} diff --git a/adyenv6b2ccheckoutaddon/acceleratoraddon/web/webroot/_ui/responsive/common/js/adyen.checkout.js b/adyenv6b2ccheckoutaddon/acceleratoraddon/web/webroot/_ui/responsive/common/js/adyen.checkout.js index 95849d7e7..e78623426 100644 --- a/adyenv6b2ccheckoutaddon/acceleratoraddon/web/webroot/_ui/responsive/common/js/adyen.checkout.js +++ b/adyenv6b2ccheckoutaddon/acceleratoraddon/web/webroot/_ui/responsive/common/js/adyen.checkout.js @@ -48,6 +48,14 @@ var AdyenCheckoutHybris = (function () { } } + if ( paymentMethod === "pos" ) { + var terminalId = $( '#adyen_pos_terminal' ); + if( terminalId.val() === "" ) { + window.alert("Please select a terminal"); + return false; + } + } + $( 'input[name="txvariant"]' ).remove(); return true; @@ -80,7 +88,6 @@ var AdyenCheckoutHybris = (function () { */ setCustomPaymentMethodValues: function () { var paymentMethod = $( 'input[type=radio][name=paymentMethod]:checked' ).val(); - var dob = $( '#p_method_adyen_hpp_' + paymentMethod + '_dob' ); if ( dob ) { $( "#dob" ).val( dob.val() ); @@ -90,6 +97,11 @@ var AdyenCheckoutHybris = (function () { if ( ssn ) { $( "#socialSecurityNumber" ).val( ssn.val() ); } + + var terminalId = $( '#adyen_pos_terminal' ); + if ( terminalId ) { + $( "#terminalId" ).val( terminalId.val() ); + } }, /** @@ -189,6 +201,10 @@ var AdyenCheckoutHybris = (function () { } catch (e) { console.log('Something went wrong trying to mount the iDEAL component: ${e}'); } + }, + + showSpinner: function () { + document.getElementById("spinner_wrapper").style.display = "flex"; } }; })(); diff --git a/adyenv6b2ccheckoutaddon/resources/adyenv6b2ccheckoutaddon.build.number b/adyenv6b2ccheckoutaddon/resources/adyenv6b2ccheckoutaddon.build.number index 4b6128f04..261cf7a34 100644 --- a/adyenv6b2ccheckoutaddon/resources/adyenv6b2ccheckoutaddon.build.number +++ b/adyenv6b2ccheckoutaddon/resources/adyenv6b2ccheckoutaddon.build.number @@ -7,5 +7,5 @@ module.name=platform-module name=adyenv6b2ccheckoutaddon releasedate=20170509 1903 vendor=adyen -version=6.1.1 -version.api=6.1.1 \ No newline at end of file +version=6.2.0 +version.api=6.2.0 \ No newline at end of file diff --git a/adyenv6backoffice/resources/adyenv6backoffice-backoffice-config.xml b/adyenv6backoffice/resources/adyenv6backoffice-backoffice-config.xml index 88f8d2e08..37d588199 100644 --- a/adyenv6backoffice/resources/adyenv6backoffice-backoffice-config.xml +++ b/adyenv6backoffice/resources/adyenv6backoffice-backoffice-config.xml @@ -16,7 +16,7 @@ - + @@ -25,21 +25,28 @@ - + - - - - - - - - + + + + + + + + + + + + + + + diff --git a/adyenv6backoffice/resources/adyenv6backoffice-backoffice-labels/labels_en.properties b/adyenv6backoffice/resources/adyenv6backoffice-backoffice-labels/labels_en.properties index 3ef6c8079..d6dff5f40 100644 --- a/adyenv6backoffice/resources/adyenv6backoffice-backoffice-labels/labels_en.properties +++ b/adyenv6backoffice/resources/adyenv6backoffice-backoffice-labels/labels_en.properties @@ -23,3 +23,4 @@ hmc.adyen.notifications=Server Notification Settings hmc.adyen.api=API Settings hmc.adyen.hpp=Hosted Payment Pages Settings hmc.adyen.advanced=Advanced Settings +hmc.adyen.pos=POS settings diff --git a/adyenv6backoffice/resources/adyenv6backoffice.build.number b/adyenv6backoffice/resources/adyenv6backoffice.build.number index d9026cb8d..f79eab86f 100644 --- a/adyenv6backoffice/resources/adyenv6backoffice.build.number +++ b/adyenv6backoffice/resources/adyenv6backoffice.build.number @@ -7,5 +7,5 @@ module.name=platform-module name=adyenv6backoffice releasedate=20170509 1903 vendor=adyen -version=6.1.1 -version.api=6.1.1 \ No newline at end of file +version=6.2.0 +version.api=6.2.0 \ No newline at end of file diff --git a/adyenv6core/lib/adyen-java-api-library-2.4.1.jar b/adyenv6core/lib/adyen-java-api-library-2.7.0.jar similarity index 81% rename from adyenv6core/lib/adyen-java-api-library-2.4.1.jar rename to adyenv6core/lib/adyen-java-api-library-2.7.0.jar index dbd42338e..d9a1d8af0 100644 Binary files a/adyenv6core/lib/adyen-java-api-library-2.4.1.jar and b/adyenv6core/lib/adyen-java-api-library-2.7.0.jar differ diff --git a/adyenv6core/resources/adyenv6core-beans.xml b/adyenv6core/resources/adyenv6core-beans.xml index fe5c01d62..61e07340e 100644 --- a/adyenv6core/resources/adyenv6core-beans.xml +++ b/adyenv6core/resources/adyenv6core-beans.xml @@ -33,6 +33,7 @@ + @@ -46,6 +47,7 @@ + @@ -63,6 +65,7 @@ + @@ -76,6 +79,7 @@ + diff --git a/adyenv6core/resources/adyenv6core-items.xml b/adyenv6core/resources/adyenv6core-items.xml index 0d848af3c..8bac7b675 100644 --- a/adyenv6core/resources/adyenv6core-items.xml +++ b/adyenv6core/resources/adyenv6core-items.xml @@ -202,6 +202,34 @@ Boolean.FALSE + + Enable POS Transactions + + + Boolean.FALSE + + + POS Merchant Account + + + + + POS API Key + + + + + POS Store ID + + + + + + + em().getEnumerationValue("RecurringContractMode","NONE") + How the payment will be stored on Adyen + @@ -285,6 +313,10 @@ Number of Installments + + Terminal ID + + Shopper's last name (applicable for Boleto) @@ -330,6 +362,15 @@ Multibanco reference + + + POS Receipt + + + HYBRIS.LONG_STRING + + + Paypal ECS payment token diff --git a/adyenv6core/resources/adyenv6core-spring.xml b/adyenv6core/resources/adyenv6core-spring.xml index 9c1e63d02..51016c88a 100644 --- a/adyenv6core/resources/adyenv6core-spring.xml +++ b/adyenv6core/resources/adyenv6core-spring.xml @@ -1,13 +1,13 @@ @@ -19,26 +19,26 @@ - + - - + + - + - - - - - + @@ -135,6 +135,8 @@ + + @@ -197,6 +199,7 @@ + diff --git a/adyenv6core/resources/adyenv6core.build.number b/adyenv6core/resources/adyenv6core.build.number index d3694486d..8e15fd7df 100644 --- a/adyenv6core/resources/adyenv6core.build.number +++ b/adyenv6core/resources/adyenv6core.build.number @@ -6,5 +6,5 @@ group.id=com.adyen.v6 module.name=platform-module name=adyenv6core vendor=adyen -version=6.1.1 -version.api=6.1.1 \ No newline at end of file +version=6.2.0 +version.api=6.2.0 \ No newline at end of file diff --git a/adyenv6core/resources/localization/adyenv6core-locales_en.properties b/adyenv6core/resources/localization/adyenv6core-locales_en.properties index 402791ced..954cbdb85 100644 --- a/adyenv6core/resources/localization/adyenv6core-locales_en.properties +++ b/adyenv6core/resources/localization/adyenv6core-locales_en.properties @@ -40,6 +40,13 @@ type.basestore.adyenRecurringContractMode.name=Recurring contract type type.basestore.adyenRecurringContractMode.description=How the payment will be stored on Adyen type.basestore.adyenBoleto.name=Enable Boleto type.basestore.adyenBoleto.description=Enables Boleto as an available payment method when delivery country is Brasil and currency is BRL. Requires Web Service User configuration. +type.basestore.adyenPosEnabled.name=Enable POS +type.basestore.adyenPosEnabled.description=Enable POS as an available payment method +type.basestore.adyenPosMerchantAccount.name=POS Merchant Account +type.basestore.adyenPosApiKey.name=POS API key +type.basestore.adyenPosStoreId.name=POS Store ID +type.basestore.adyenPosStoreId.description=POS store ID field is optional +type.basestore.adyenPosRecurringContractMode.name=POS Recurring contract type type.paymentinfo.adyenPaymentMethod.name=Payment Method type.paymentinfo.adyenIssuerId.name=Issuer ID @@ -71,7 +78,13 @@ type.paymentinfo.adyenMultibancoAmount.name= Multibanco Amount type.paymentinfo.adyenMultibancoDeadline.name= Multibanco Deadline type.paymentinfo.adyenMultibancoEntity.name=Multibanco Entity type.paymentinfo.adyenMultibancoReference.name=Multibanco Reference +type.PaymentInfo.adyenPosReceipt.name=POS Receipt +type.PaymentInfo.adyenTerminalId.name=POS Terminal ID +type.PaymentInfo.encryptedExpiryMonth.name = Encrypted Credit Card Expiry Month +type.PaymentInfo.encryptedExpiryYear.name = Encrypted Credit Card Expiry Year +type.PaymentInfo.encryptedSecurityCode.name = Encrypted Credit Card CVC +type.PaymentInfo.encryptedCardNumber.name = Encrypted Credit Card Number #adyen-cse-web pattern: "(.*)" : "(.*)", -> type.AdyenCardTypeEnum.$1.name=$2 type.AdyenCardTypeEnum.mc.name=MasterCard diff --git a/adyenv6core/resources/test/SaleToPOIResponse.json b/adyenv6core/resources/test/SaleToPOIResponse.json new file mode 100644 index 000000000..9794513e8 --- /dev/null +++ b/adyenv6core/resources/test/SaleToPOIResponse.json @@ -0,0 +1,65 @@ +{ + "SaleToPOIResponse" : { + "PaymentResponse" : { + "Response" : { + "AdditionalResponse" : "applicationLabel=MCNL&cardHolderVerificationMethodResults=1E0000&AID=A000000004101001&applicationPreferredName=mc%20nl&tid=12000000&merchantReference=A574E345-9AF5-4653-A1F9-F4D56CD2558B&tc=E891E*****0747AD&txdate=04-10-2017&paymentMethod=eftpos_australia&transactionReferenceNumber=9815071072212937&transactionType=GOODS_SERVICES&cardType=mc&offline=false&cardScheme=eftpos_australia&mid=1009&txtime=08%3a53%3a29&cardHolderName=TC02_MC_Approved", + "Result" : "Success" + }, + "PaymentResult" : { + "PaymentInstrumentData" : { + "PaymentInstrumentType" : "Card", + "CardData" : { + "SensitiveCardData" : { + "ExpiryDate" : "0228", + "CardSeqNumb" : "53" + }, + "CardCountryCode" : "056", + "PaymentBrand" : "eftpos_australia", + "MaskedPan" : "541333 **** 9999", + "PaymentToken" : { + "TokenValue" : "K722246565280932", + "TokenRequestedType" : "Customer" + }, + "EntryMode" : [ + "ICC" + ] + } + }, + "AmountsResp" : { + "Currency" : "EUR", + "AuthorizedAmount" : 5 + }, + "PaymentAcquirerData" : { + "AcquirerPOIID" : "MX925-260193322", + "MerchantID" : "TestMerchantPOS", + "AcquirerTransactionID" : { + "TimeStamp" : "2017-10-04T08:53:29.000Z", + "TransactionID" : "9815071072212937" + } + } + }, + "SaleData" : { + "SaleTransactionID" : { + "TimeStamp" : "2017-10-04T08:53:28.278Z", + "TransactionID" : "A574E345-9AF5-4653-A1F9-F4D56CD2558B" + }, + "SaleReferenceID" : "Ref123" + }, + "POIData" : { + "POITransactionID" : { + "TimeStamp" : "2017-10-04T08:53:29.000Z", + "TransactionID" : "oLkO001507107209001.9815071072212937" + } + } + }, + "MessageHeader" : { + "POIID" : "VX820-123456789", + "MessageType" : "Response", + "SaleID" : "John", + "MessageClass" : "Service", + "ServiceID" : "33191", + "MessageCategory" : "Payment", + "ProtocolVersion" : "3.0" + } + } +} diff --git a/adyenv6core/resources/test/SaleToPOIResponseInProgress.json b/adyenv6core/resources/test/SaleToPOIResponseInProgress.json new file mode 100644 index 000000000..809e2bf2f --- /dev/null +++ b/adyenv6core/resources/test/SaleToPOIResponseInProgress.json @@ -0,0 +1,20 @@ +{ + "SaleToPOIResponse": { + "MessageHeader": { + "ProtocolVersion": "3.0", + "MessageClass": "Service", + "MessageCategory": "TransactionStatus", + "MessageType": "Response", + "ServiceID": "1057464462", + "SaleID": "electronics", + "POIID": "V400m-346489243" + }, + "TransactionStatusResponse": { + "Response": { + "AdditionalResponse": "status=Uncompleted%20transaction&tenderReference=93yV001571057440002&detailedStatus=TENDER_CREATED", + "Result": "Failure", + "ErrorCondition": "InProgress" + } + } + } +} \ No newline at end of file diff --git a/adyenv6core/resources/test/SaleToPOIResponsePaymentCancelled.json b/adyenv6core/resources/test/SaleToPOIResponsePaymentCancelled.json new file mode 100644 index 000000000..9ecc2b09c --- /dev/null +++ b/adyenv6core/resources/test/SaleToPOIResponsePaymentCancelled.json @@ -0,0 +1,44 @@ +{ + "SaleToPOIResponse": { + "MessageHeader": { + "ProtocolVersion": "3.0", + "MessageClass": "Service", + "MessageCategory": "Payment", + "MessageType": "Response", + "ServiceID": "1058665998", + "SaleID": "electronics", + "POIID": "V400m-346489243" + }, + "PaymentResponse": { + "Response": { + "AdditionalResponse": "tid=46489243&transactionType=GOODS_SERVICES&backendGiftcardIndicator=false&posadditionalamounts.originalAmountValue=800&posAmountGratuityValue=0&giftcardIndicator=false&store=MyStore&txtime=15%3a11%3a07&iso8601TxDate=2019-10-14T13%3a11%3a07.0000000%2b0000&posOriginalAmountValue=800&txdate=14-10-2019&shopperReference=16eebd8c-8c1b-48e3-b6e8-67734d960af5&shopperEmail=renato.martins%40adyen.com&merchantReference=00025003&pinBlockISOFormat=0&posadditionalamounts.originalAmountCurrency=EUR&posAuthAmountCurrency=EUR&message=108%20Shopper%20cancelled%20tx&posAmountCashbackValue=0&posAuthAmountValue=800", + "Result": "Failure", + "ErrorCondition": "Cancel" + }, + "SaleData": { + "SaleTransactionID": { + "TransactionID": "00025003", + "TimeStamp": "2019-10-14T13:11:06.334Z" + } + }, + "POIData": { + "POITransactionID": { + "TransactionID": "93yV001571058667005", + "TimeStamp": "2019-10-14T13:11:07.000Z" + }, + "POIReconciliationID": "1000" + }, + "PaymentResult": { + "PaymentInstrumentData": { + "CardData": {}, + "PaymentInstrumentType": "Card" + }, + "PaymentAcquirerData": { + "MerchantID": "TestMerchantRenatoTest", + "AcquirerPOIID": "V400m-346489243" + }, + "OnlineFlag": true + } + } + } +} \ No newline at end of file diff --git a/adyenv6core/resources/test/SaleToPOIResponseStatusCancelled.json b/adyenv6core/resources/test/SaleToPOIResponseStatusCancelled.json new file mode 100644 index 000000000..c342e1c08 --- /dev/null +++ b/adyenv6core/resources/test/SaleToPOIResponseStatusCancelled.json @@ -0,0 +1,67 @@ +{ + "SaleToPOIResponse": { + "MessageHeader": { + "ProtocolVersion": "3.0", + "MessageClass": "Service", + "MessageCategory": "TransactionStatus", + "MessageType": "Response", + "ServiceID": "1058107493", + "SaleID": "electronics", + "POIID": "V400m-346489243" + }, + "TransactionStatusResponse": { + "Response": { + "Result": "Success" + }, + "MessageReference": { + "MessageCategory": "Payment", + "ServiceID": "1057984063", + "SaleID": "electronics" + }, + "RepeatedMessageResponse": { + "RepeatedResponseMessageBody": { + "PaymentResponse": { + "Response": { + "AdditionalResponse": "tid=46489243&transactionType=GOODS_SERVICES&backendGiftcardIndicator=false&posadditionalamounts.originalAmountValue=800&posAmountGratuityValue=0&giftcardIndicator=false&store=MyStore&txtime=14%3a59%3a45&iso8601TxDate=2019-10-14T12%3a59%3a45.0000000%2b0000&posOriginalAmountValue=800&txdate=14-10-2019&shopperReference=16eebd8c-8c1b-48e3-b6e8-67734d960af5&shopperEmail=renato.martins%40adyen.com&merchantReference=00025003&pinBlockISOFormat=0&posadditionalamounts.originalAmountCurrency=EUR&posAuthAmountCurrency=EUR&message=126%20Shopper%20did%20not%20present%20card&posAmountCashbackValue=0&posAuthAmountValue=800", + "Result": "Failure", + "ErrorCondition": "Cancel" + }, + "SaleData": { + "SaleTransactionID": { + "TransactionID": "00025003", + "TimeStamp": "2019-10-14T12:59:44.066Z" + } + }, + "POIData": { + "POITransactionID": { + "TransactionID": "93yV001571057985004", + "TimeStamp": "2019-10-14T12:59:45.000Z" + }, + "POIReconciliationID": "1000" + }, + "PaymentResult": { + "PaymentInstrumentData": { + "CardData": {}, + "PaymentInstrumentType": "Card" + }, + "PaymentAcquirerData": { + "MerchantID": "TestMerchantRenatoTest", + "AcquirerPOIID": "V400m-346489243" + }, + "OnlineFlag": true + } + } + }, + "MessageHeader": { + "ProtocolVersion": "3.0", + "MessageClass": "Service", + "MessageCategory": "Payment", + "MessageType": "Response", + "ServiceID": "1057984063", + "SaleID": "electronics", + "POIID": "V400m-346489243" + } + } + } + } +} \ No newline at end of file diff --git a/adyenv6core/resources/test/SaleToPOIResponseStatusResponseWithReceipt.json b/adyenv6core/resources/test/SaleToPOIResponseStatusResponseWithReceipt.json new file mode 100644 index 000000000..1019e331c --- /dev/null +++ b/adyenv6core/resources/test/SaleToPOIResponseStatusResponseWithReceipt.json @@ -0,0 +1,343 @@ +{ + "SaleToPOIResponse": { + "MessageHeader": { + "ProtocolVersion": "3.0", + "MessageClass": "Service", + "MessageCategory": "TransactionStatus", + "MessageType": "Response", + "ServiceID": "1057526683", + "SaleID": "electronics", + "POIID": "V400m-346489243" + }, + "TransactionStatusResponse": { + "Response": { + "Result": "Success" + }, + "MessageReference": { + "MessageCategory": "Payment", + "ServiceID": "1057439341", + "SaleID": "electronics" + }, + "RepeatedMessageResponse": { + "RepeatedResponseMessageBody": { + "PaymentResponse": { + "Response": { + "AdditionalResponse": "tid=46489243&AID=A0000000041010&transactionType=GOODS_SERVICES&backendGiftcardIndicator=false&posadditionalamounts.originalAmountValue=1798&expiryYear=2025&acquirerAccountCode=TestPmmAcquirerAccount&alias=M900978995070104&posAmountGratuityValue=0&giftcardIndicator=false&authorisedAmountValue=1798&pspReference=851571057526088G&paymentMethodVariant=mc&cardHolderName=N%2fA&refusalReasonRaw=APPROVED&authorisationMid=1000&expiryDate=12%2f2025&applicationPreferredName=MCC%20351%20v1%202&acquirerCode=TestPmmAcquirer&store=MyStore&recurring.recurringDetailReference=8415699210776615&txtime=14%3a50%3a40&iso8601TxDate=2019-10-14T12%3a50%3a40.0000000%2b0000&cardType=mc&posOriginalAmountValue=1798&offline=false&aliasType=Default&recurring.shopperReference=16eebd8c-8c1b-48e3-b6e8-67734d960af5&txdate=14-10-2019&paymentMethod=mc&cvcResult=0%20Unknown&authorisedAmountCurrency=EUR&shopperReference=16eebd8c-8c1b-48e3-b6e8-67734d960af5&shopperEmail=renato.martins%40adyen.com&avsResult=0%20Unknown&mid=1000&merchantReference=00025002&transactionReferenceNumber=851571057526088G&expiryMonth=12&cardSummary=3511&pinBlockISOFormat=0&posadditionalamounts.originalAmountCurrency=EUR&posAuthAmountCurrency=EUR&cardHolderVerificationMethodResults=3F0300&authCode=123456&posAmountCashbackValue=0&shopperCountry=NL&posEntryMode=CLESS_SWIPE&issuerCountry=unknown&cardScheme=mc&cardBin=541333&posAuthAmountValue=1798", + "Result": "Success" + }, + "SaleData": { + "SaleTransactionID": { + "TransactionID": "00025002", + "TimeStamp": "2019-10-14T12:50:39.343Z" + } + }, + "POIData": { + "POITransactionID": { + "TransactionID": "93yV001571057440002.851571057526088G", + "TimeStamp": "2019-10-14T12:50:40.000Z" + }, + "POIReconciliationID": "1000" + }, + "PaymentResult": { + "PaymentInstrumentData": { + "CardData": { + "SensitiveCardData": { + "ExpiryDate": "1225" + }, + "PaymentBrand": "mc", + "MaskedPan": "541333 ** 3511", + "EntryMode": [ + "Tapped" + ] + }, + "PaymentInstrumentType": "Card" + }, + "AmountsResp": { + "Currency": "EUR", + "AuthorizedAmount": 17.98 + }, + "PaymentAcquirerData": { + "AcquirerTransactionID": { + "TransactionID": "851571057526088G", + "TimeStamp": "2019-10-14T12:50:40.000Z" + }, + "ApprovalCode": "123456", + "MerchantID": "TestMerchantRenatoTest", + "AcquirerPOIID": "V400m-346489243" + }, + "OnlineFlag": true + }, + "PaymentReceipt": [ + { + "OutputContent": { + "OutputText": [ + { + "Text": "key=header1", + "CharacterStyle": "Bold", + "EndOfLineFlag": true + }, + { + "Text": "key=header2", + "CharacterStyle": "Bold", + "EndOfLineFlag": true + }, + { + "Text": "name=MERCHANT%20COPY&key=merchantTitle", + "CharacterStyle": "Bold", + "EndOfLineFlag": true + }, + { + "Text": "key=filler", + "EndOfLineFlag": true + }, + { + "Text": "name=Date&value=14%2f10%2f2019&key=txdate", + "EndOfLineFlag": true + }, + { + "Text": "name=Time&value=14%3a50%3a40&key=txtime", + "EndOfLineFlag": true + }, + { + "Text": "key=filler", + "EndOfLineFlag": true + }, + { + "Text": "name=Card&value=%2a%2a%2a%2a%2a%2a%2a%2a%2a%2a%2a%2a3511&key=pan", + "EndOfLineFlag": true + }, + { + "Text": "name=Pref.%20name&value=MCC%20351%20v1%202&key=preferredName", + "EndOfLineFlag": true + }, + { + "Text": "name=Card%20type&value=mc&key=cardType", + "EndOfLineFlag": true + }, + { + "Text": "name=Payment%20method&value=mc&key=paymentMethod", + "EndOfLineFlag": true + }, + { + "Text": "name=Payment%20variant&value=mc&key=paymentMethodVariant", + "EndOfLineFlag": true + }, + + { + "Text": "name=Entry%20mode&value=Contactless%20swipe&key=posEntryMode", + "EndOfLineFlag": true + }, + { + "Text": "key=filler", + "EndOfLineFlag": true + }, + { + "Text": "name=AID&value=A0000000041010&key=aid", + "EndOfLineFlag": true + }, + { + "Text": "name=MID&value=1000&key=mid", + "EndOfLineFlag": true + }, + { + "Text": "name=TID&value=V400m-346489243&key=tid", + "EndOfLineFlag": true + }, + { + "Text": "name=PTID&value=46489243&key=ptid", + "EndOfLineFlag": true + }, + { + "Text": "key=filler", + "EndOfLineFlag": true + }, + { + "Text": "name=Auth.%20code&value=123456&key=authCode", + "EndOfLineFlag": true + }, + { + "Text": "name=Tender&value=93yV001571057440002&key=txRef", + "EndOfLineFlag": true + }, + { + "Text": "name=Reference&value=00025002&key=mref", + "EndOfLineFlag": true + }, + { + "Text": "key=filler", + "EndOfLineFlag": true + }, + { + "Text": "name=Type&value=GOODS_SERVICES&key=txtype", + "EndOfLineFlag": true + }, + { + "Text": "name=TOTAL&value=%e2%82%ac%2017.98&key=totalAmount", + "CharacterStyle": "Bold", + "EndOfLineFlag": true + }, + { + "Text": "key=filler", + "EndOfLineFlag": true + }, + { + "Text": "name=APPROVED&key=approved", + "CharacterStyle": "Bold", + "EndOfLineFlag": true + } + ], + "OutputFormat": "Text" + }, + "DocumentQualifier": "CashierReceipt", + "RequiredSignatureFlag": false + }, + { + "OutputContent": { + "OutputText": [ + { + "Text": "key=header1", + "CharacterStyle": "Bold", + "EndOfLineFlag": true + }, + { + "Text": "key=header2", + "CharacterStyle": "Bold", + "EndOfLineFlag": true + }, + { + "Text": "name=CARDHOLDER%20COPY&key=cardholderHeader", + "CharacterStyle": "Bold", + "EndOfLineFlag": true + }, + { + "Text": "key=filler", + "EndOfLineFlag": true + }, + { + "Text": "name=Date&value=14%2f10%2f2019&key=txdate", + "EndOfLineFlag": true + }, + { + "Text": "name=Time&value=14%3a50%3a40&key=txtime", + "EndOfLineFlag": true + }, + { + "Text": "key=filler", + "EndOfLineFlag": true + }, + { + "Text": "name=Card&value=%2a%2a%2a%2a%2a%2a%2a%2a%2a%2a%2a%2a3511&key=pan", + "EndOfLineFlag": true + }, + { + "Text": "name=Pref.%20name&value=MCC%20351%20v1%202&key=preferredName", + "EndOfLineFlag": true + }, + { + "Text": "name=Card%20type&value=mc&key=cardType", + "EndOfLineFlag": true + }, + { + "Text": "name=Payment%20method&value=mc&key=paymentMethod", + "EndOfLineFlag": true + }, + { + "Text": "name=Payment%20variant&value=mc&key=paymentMethodVariant", + "EndOfLineFlag": true + }, + { + "Text": "name=Entry%20mode&value=Contactless%20swipe&key=posEntryMode", + "EndOfLineFlag": true + }, + { + "Text": "key=filler", + "EndOfLineFlag": true + }, + { + "Text": "name=AID&value=A0000000041010&key=aid", + "EndOfLineFlag": true + }, + { + "Text": "name=MID&value=1000&key=mid", + "EndOfLineFlag": true + }, + { + "Text": "name=TID&value=V400m-346489243&key=tid", + "EndOfLineFlag": true + }, + { + "Text": "name=PTID&value=46489243&key=ptid", + "EndOfLineFlag": true + }, + { + "Text": "key=filler", + "EndOfLineFlag": true + }, + { + "Text": "name=Auth.%20code&value=123456&key=authCode", + "EndOfLineFlag": true + }, + { + "Text": "name=Tender&value=93yV001571057440002&key=txRef", + "EndOfLineFlag": true + }, + { + "Text": "name=Reference&value=00025002&key=mref", + "EndOfLineFlag": true + }, + { + "Text": "key=filler", + "EndOfLineFlag": true + }, + { + "Text": "name=Type&value=GOODS_SERVICES&key=txtype", + "EndOfLineFlag": true + }, + { + "Text": "name=TOTAL&value=%e2%82%ac%2017.98&key=totalAmount", + "CharacterStyle": "Bold", + "EndOfLineFlag": true + }, + { + "Text": "key=filler", + "EndOfLineFlag": true + }, + { + "Text": "name=APPROVED&key=approved", + "CharacterStyle": "Bold", + "EndOfLineFlag": true + }, + { + "Text": "key=filler", + "EndOfLineFlag": true + }, + { + "Text": "name=Please%20retain%20for%20your%20records&key=retain", + "EndOfLineFlag": true + }, + { + "Text": "name=Thank%20you&key=thanks", + "EndOfLineFlag": true + } + ], + "OutputFormat": "Text" + }, + "DocumentQualifier": "CustomerReceipt", + "RequiredSignatureFlag": false + } + ] + } + }, + "MessageHeader": { + "ProtocolVersion": "3.0", + "MessageClass": "Service", + "MessageCategory": "Payment", + "MessageType": "Response", + "ServiceID": "1057439341", + "SaleID": "electronics", + "POIID": "V400m-346489243" + } + } + } + } +} \ No newline at end of file diff --git a/adyenv6core/resources/test/SaleToPOIResponseWithReceipt.json b/adyenv6core/resources/test/SaleToPOIResponseWithReceipt.json new file mode 100644 index 000000000..1019e331c --- /dev/null +++ b/adyenv6core/resources/test/SaleToPOIResponseWithReceipt.json @@ -0,0 +1,343 @@ +{ + "SaleToPOIResponse": { + "MessageHeader": { + "ProtocolVersion": "3.0", + "MessageClass": "Service", + "MessageCategory": "TransactionStatus", + "MessageType": "Response", + "ServiceID": "1057526683", + "SaleID": "electronics", + "POIID": "V400m-346489243" + }, + "TransactionStatusResponse": { + "Response": { + "Result": "Success" + }, + "MessageReference": { + "MessageCategory": "Payment", + "ServiceID": "1057439341", + "SaleID": "electronics" + }, + "RepeatedMessageResponse": { + "RepeatedResponseMessageBody": { + "PaymentResponse": { + "Response": { + "AdditionalResponse": "tid=46489243&AID=A0000000041010&transactionType=GOODS_SERVICES&backendGiftcardIndicator=false&posadditionalamounts.originalAmountValue=1798&expiryYear=2025&acquirerAccountCode=TestPmmAcquirerAccount&alias=M900978995070104&posAmountGratuityValue=0&giftcardIndicator=false&authorisedAmountValue=1798&pspReference=851571057526088G&paymentMethodVariant=mc&cardHolderName=N%2fA&refusalReasonRaw=APPROVED&authorisationMid=1000&expiryDate=12%2f2025&applicationPreferredName=MCC%20351%20v1%202&acquirerCode=TestPmmAcquirer&store=MyStore&recurring.recurringDetailReference=8415699210776615&txtime=14%3a50%3a40&iso8601TxDate=2019-10-14T12%3a50%3a40.0000000%2b0000&cardType=mc&posOriginalAmountValue=1798&offline=false&aliasType=Default&recurring.shopperReference=16eebd8c-8c1b-48e3-b6e8-67734d960af5&txdate=14-10-2019&paymentMethod=mc&cvcResult=0%20Unknown&authorisedAmountCurrency=EUR&shopperReference=16eebd8c-8c1b-48e3-b6e8-67734d960af5&shopperEmail=renato.martins%40adyen.com&avsResult=0%20Unknown&mid=1000&merchantReference=00025002&transactionReferenceNumber=851571057526088G&expiryMonth=12&cardSummary=3511&pinBlockISOFormat=0&posadditionalamounts.originalAmountCurrency=EUR&posAuthAmountCurrency=EUR&cardHolderVerificationMethodResults=3F0300&authCode=123456&posAmountCashbackValue=0&shopperCountry=NL&posEntryMode=CLESS_SWIPE&issuerCountry=unknown&cardScheme=mc&cardBin=541333&posAuthAmountValue=1798", + "Result": "Success" + }, + "SaleData": { + "SaleTransactionID": { + "TransactionID": "00025002", + "TimeStamp": "2019-10-14T12:50:39.343Z" + } + }, + "POIData": { + "POITransactionID": { + "TransactionID": "93yV001571057440002.851571057526088G", + "TimeStamp": "2019-10-14T12:50:40.000Z" + }, + "POIReconciliationID": "1000" + }, + "PaymentResult": { + "PaymentInstrumentData": { + "CardData": { + "SensitiveCardData": { + "ExpiryDate": "1225" + }, + "PaymentBrand": "mc", + "MaskedPan": "541333 ** 3511", + "EntryMode": [ + "Tapped" + ] + }, + "PaymentInstrumentType": "Card" + }, + "AmountsResp": { + "Currency": "EUR", + "AuthorizedAmount": 17.98 + }, + "PaymentAcquirerData": { + "AcquirerTransactionID": { + "TransactionID": "851571057526088G", + "TimeStamp": "2019-10-14T12:50:40.000Z" + }, + "ApprovalCode": "123456", + "MerchantID": "TestMerchantRenatoTest", + "AcquirerPOIID": "V400m-346489243" + }, + "OnlineFlag": true + }, + "PaymentReceipt": [ + { + "OutputContent": { + "OutputText": [ + { + "Text": "key=header1", + "CharacterStyle": "Bold", + "EndOfLineFlag": true + }, + { + "Text": "key=header2", + "CharacterStyle": "Bold", + "EndOfLineFlag": true + }, + { + "Text": "name=MERCHANT%20COPY&key=merchantTitle", + "CharacterStyle": "Bold", + "EndOfLineFlag": true + }, + { + "Text": "key=filler", + "EndOfLineFlag": true + }, + { + "Text": "name=Date&value=14%2f10%2f2019&key=txdate", + "EndOfLineFlag": true + }, + { + "Text": "name=Time&value=14%3a50%3a40&key=txtime", + "EndOfLineFlag": true + }, + { + "Text": "key=filler", + "EndOfLineFlag": true + }, + { + "Text": "name=Card&value=%2a%2a%2a%2a%2a%2a%2a%2a%2a%2a%2a%2a3511&key=pan", + "EndOfLineFlag": true + }, + { + "Text": "name=Pref.%20name&value=MCC%20351%20v1%202&key=preferredName", + "EndOfLineFlag": true + }, + { + "Text": "name=Card%20type&value=mc&key=cardType", + "EndOfLineFlag": true + }, + { + "Text": "name=Payment%20method&value=mc&key=paymentMethod", + "EndOfLineFlag": true + }, + { + "Text": "name=Payment%20variant&value=mc&key=paymentMethodVariant", + "EndOfLineFlag": true + }, + + { + "Text": "name=Entry%20mode&value=Contactless%20swipe&key=posEntryMode", + "EndOfLineFlag": true + }, + { + "Text": "key=filler", + "EndOfLineFlag": true + }, + { + "Text": "name=AID&value=A0000000041010&key=aid", + "EndOfLineFlag": true + }, + { + "Text": "name=MID&value=1000&key=mid", + "EndOfLineFlag": true + }, + { + "Text": "name=TID&value=V400m-346489243&key=tid", + "EndOfLineFlag": true + }, + { + "Text": "name=PTID&value=46489243&key=ptid", + "EndOfLineFlag": true + }, + { + "Text": "key=filler", + "EndOfLineFlag": true + }, + { + "Text": "name=Auth.%20code&value=123456&key=authCode", + "EndOfLineFlag": true + }, + { + "Text": "name=Tender&value=93yV001571057440002&key=txRef", + "EndOfLineFlag": true + }, + { + "Text": "name=Reference&value=00025002&key=mref", + "EndOfLineFlag": true + }, + { + "Text": "key=filler", + "EndOfLineFlag": true + }, + { + "Text": "name=Type&value=GOODS_SERVICES&key=txtype", + "EndOfLineFlag": true + }, + { + "Text": "name=TOTAL&value=%e2%82%ac%2017.98&key=totalAmount", + "CharacterStyle": "Bold", + "EndOfLineFlag": true + }, + { + "Text": "key=filler", + "EndOfLineFlag": true + }, + { + "Text": "name=APPROVED&key=approved", + "CharacterStyle": "Bold", + "EndOfLineFlag": true + } + ], + "OutputFormat": "Text" + }, + "DocumentQualifier": "CashierReceipt", + "RequiredSignatureFlag": false + }, + { + "OutputContent": { + "OutputText": [ + { + "Text": "key=header1", + "CharacterStyle": "Bold", + "EndOfLineFlag": true + }, + { + "Text": "key=header2", + "CharacterStyle": "Bold", + "EndOfLineFlag": true + }, + { + "Text": "name=CARDHOLDER%20COPY&key=cardholderHeader", + "CharacterStyle": "Bold", + "EndOfLineFlag": true + }, + { + "Text": "key=filler", + "EndOfLineFlag": true + }, + { + "Text": "name=Date&value=14%2f10%2f2019&key=txdate", + "EndOfLineFlag": true + }, + { + "Text": "name=Time&value=14%3a50%3a40&key=txtime", + "EndOfLineFlag": true + }, + { + "Text": "key=filler", + "EndOfLineFlag": true + }, + { + "Text": "name=Card&value=%2a%2a%2a%2a%2a%2a%2a%2a%2a%2a%2a%2a3511&key=pan", + "EndOfLineFlag": true + }, + { + "Text": "name=Pref.%20name&value=MCC%20351%20v1%202&key=preferredName", + "EndOfLineFlag": true + }, + { + "Text": "name=Card%20type&value=mc&key=cardType", + "EndOfLineFlag": true + }, + { + "Text": "name=Payment%20method&value=mc&key=paymentMethod", + "EndOfLineFlag": true + }, + { + "Text": "name=Payment%20variant&value=mc&key=paymentMethodVariant", + "EndOfLineFlag": true + }, + { + "Text": "name=Entry%20mode&value=Contactless%20swipe&key=posEntryMode", + "EndOfLineFlag": true + }, + { + "Text": "key=filler", + "EndOfLineFlag": true + }, + { + "Text": "name=AID&value=A0000000041010&key=aid", + "EndOfLineFlag": true + }, + { + "Text": "name=MID&value=1000&key=mid", + "EndOfLineFlag": true + }, + { + "Text": "name=TID&value=V400m-346489243&key=tid", + "EndOfLineFlag": true + }, + { + "Text": "name=PTID&value=46489243&key=ptid", + "EndOfLineFlag": true + }, + { + "Text": "key=filler", + "EndOfLineFlag": true + }, + { + "Text": "name=Auth.%20code&value=123456&key=authCode", + "EndOfLineFlag": true + }, + { + "Text": "name=Tender&value=93yV001571057440002&key=txRef", + "EndOfLineFlag": true + }, + { + "Text": "name=Reference&value=00025002&key=mref", + "EndOfLineFlag": true + }, + { + "Text": "key=filler", + "EndOfLineFlag": true + }, + { + "Text": "name=Type&value=GOODS_SERVICES&key=txtype", + "EndOfLineFlag": true + }, + { + "Text": "name=TOTAL&value=%e2%82%ac%2017.98&key=totalAmount", + "CharacterStyle": "Bold", + "EndOfLineFlag": true + }, + { + "Text": "key=filler", + "EndOfLineFlag": true + }, + { + "Text": "name=APPROVED&key=approved", + "CharacterStyle": "Bold", + "EndOfLineFlag": true + }, + { + "Text": "key=filler", + "EndOfLineFlag": true + }, + { + "Text": "name=Please%20retain%20for%20your%20records&key=retain", + "EndOfLineFlag": true + }, + { + "Text": "name=Thank%20you&key=thanks", + "EndOfLineFlag": true + } + ], + "OutputFormat": "Text" + }, + "DocumentQualifier": "CustomerReceipt", + "RequiredSignatureFlag": false + } + ] + } + }, + "MessageHeader": { + "ProtocolVersion": "3.0", + "MessageClass": "Service", + "MessageCategory": "Payment", + "MessageType": "Response", + "ServiceID": "1057439341", + "SaleID": "electronics", + "POIID": "V400m-346489243" + } + } + } + } +} \ No newline at end of file diff --git a/adyenv6core/src/com/adyen/v6/constants/Adyenv6coreConstants.java b/adyenv6core/src/com/adyen/v6/constants/Adyenv6coreConstants.java index 3d0bbfc0c..45a52d570 100644 --- a/adyenv6core/src/com/adyen/v6/constants/Adyenv6coreConstants.java +++ b/adyenv6core/src/com/adyen/v6/constants/Adyenv6coreConstants.java @@ -31,7 +31,7 @@ public final class Adyenv6coreConstants extends GeneratedAdyenv6coreConstants { public static final String EXTENSIONNAME = "adyenv6core"; public static final String PLUGIN_NAME = "adyen-hybris"; - public static final String PLUGIN_VERSION = "6.1.1"; + public static final String PLUGIN_VERSION = "6.2.0"; public static final String PAYMENT_PROVIDER = "Adyen"; public static final String PAYMENT_METHOD ="paymentMethod"; @@ -40,8 +40,8 @@ public final class Adyenv6coreConstants extends GeneratedAdyenv6coreConstants { final public static String PAYMENT_METHOD_ONECLICK = "adyen_oneclick_"; final public static String PAYMENT_METHOD_BOLETO = "boleto"; final public static String PAYMENT_METHOD_BOLETO_SANTANDER = "boletobancario_santander"; - final public static String PAYMENT_METHOD_MULTIBANCO = "multibanco"; + final public static String PAYMENT_METHOD_POS = "pos"; public static final String PROCESS_EVENT_ADYEN_CAPTURED = "AdyenCaptured"; public static final String PROCESS_EVENT_ADYEN_AUTHORIZED = "AdyenAuthorized"; diff --git a/adyenv6core/src/com/adyen/v6/converters/PosPaymentResponseConverter.java b/adyenv6core/src/com/adyen/v6/converters/PosPaymentResponseConverter.java new file mode 100644 index 000000000..870169c3b --- /dev/null +++ b/adyenv6core/src/com/adyen/v6/converters/PosPaymentResponseConverter.java @@ -0,0 +1,94 @@ +/* + * ###### + * ###### + * ############ ####( ###### #####. ###### ############ ############ + * ############# #####( ###### #####. ###### ############# ############# + * ###### #####( ###### #####. ###### ##### ###### ##### ###### + * ###### ###### #####( ###### #####. ###### ##### ##### ##### ###### + * ###### ###### #####( ###### #####. ###### ##### ##### ###### + * ############# ############# ############# ############# ##### ###### + * ############ ############ ############# ############ ##### ###### + * ###### + * ############# + * ############ + * + * Adyen Hybris Extension + * + * Copyright (c) 2019 Adyen B.V. + * This file is open source and available under the MIT license. + * See the LICENSE file for more info. + */ +package com.adyen.v6.converters; + +import com.adyen.model.checkout.PaymentsResponse; +import com.adyen.model.nexo.SaleToPOIResponse; +import org.apache.commons.lang3.StringUtils; +import org.apache.http.NameValuePair; +import org.apache.http.client.utils.URLEncodedUtils; +import org.springframework.core.convert.converter.Converter; + +import java.nio.charset.Charset; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class PosPaymentResponseConverter implements Converter { + @Override + public PaymentsResponse convert(SaleToPOIResponse saleToPOIResponse) { + String pspReference = null; + Map additionalData = null; + String additionalResponse = null; + + if (saleToPOIResponse != null && saleToPOIResponse.getPaymentResponse() != null) { + pspReference = saleToPOIResponse.getPaymentResponse() + .getPaymentResult() + .getPaymentAcquirerData() + .getAcquirerTransactionID() + .getTransactionID(); + + additionalResponse = saleToPOIResponse.getPaymentResponse() + .getResponse() + .getAdditionalResponse(); + + } else if (saleToPOIResponse != null && saleToPOIResponse.getTransactionStatusResponse() != null) { + pspReference = saleToPOIResponse.getTransactionStatusResponse() + .getRepeatedMessageResponse() + .getRepeatedResponseMessageBody() + .getPaymentResponse() + .getPaymentResult() + .getPaymentAcquirerData() + .getAcquirerTransactionID() + .getTransactionID(); + + additionalResponse = saleToPOIResponse.getTransactionStatusResponse() + .getRepeatedMessageResponse() + .getRepeatedResponseMessageBody() + .getPaymentResponse() + .getResponse() + .getAdditionalResponse(); + } + if (additionalResponse != null) { + additionalData = parseAdditionalResponse(additionalResponse); + } + PaymentsResponse paymentsResponse = new PaymentsResponse(); + paymentsResponse.setPspReference(pspReference); + paymentsResponse.setAdditionalData(additionalData); + + return paymentsResponse; + } + + /* + * Parse additionalResponse with a query string format, i.e. "tid=123&AID=123&transactionType=GOODS_SERVICES&..."), + * and return as a name/value map + */ + private Map parseAdditionalResponse(String additionalResponse) { + Map additionalData = new HashMap<>(); + if (StringUtils.isNotEmpty(additionalResponse)) { + List parsedNameValues = URLEncodedUtils.parse(additionalResponse, Charset.forName("UTF-8")); + for (NameValuePair nameValue : parsedNameValues) { + additionalData.put(nameValue.getName(), nameValue.getValue()); + } + } + return additionalData; + } +} diff --git a/adyenv6core/src/com/adyen/v6/exceptions/AdyenNonAuthorizedPaymentException.java b/adyenv6core/src/com/adyen/v6/exceptions/AdyenNonAuthorizedPaymentException.java index b53087c7d..982d785bf 100644 --- a/adyenv6core/src/com/adyen/v6/exceptions/AdyenNonAuthorizedPaymentException.java +++ b/adyenv6core/src/com/adyen/v6/exceptions/AdyenNonAuthorizedPaymentException.java @@ -22,10 +22,12 @@ import com.adyen.model.PaymentResult; import com.adyen.model.checkout.PaymentsResponse; +import com.adyen.model.terminal.TerminalAPIResponse; public class AdyenNonAuthorizedPaymentException extends Exception { private PaymentResult paymentResult; private PaymentsResponse paymentsResponse; + private TerminalAPIResponse terminalApiResponse; public AdyenNonAuthorizedPaymentException(PaymentResult paymentResult) { this.paymentResult = paymentResult; @@ -35,6 +37,10 @@ public AdyenNonAuthorizedPaymentException(PaymentsResponse paymentsResponse) { this.paymentsResponse = paymentsResponse; } + public AdyenNonAuthorizedPaymentException(TerminalAPIResponse terminalApiResponse) { + this.terminalApiResponse = terminalApiResponse; + } + public PaymentResult getPaymentResult() { return paymentResult; } @@ -50,4 +56,12 @@ public PaymentsResponse getPaymentsResponse() { public void setPaymentsResponse(PaymentsResponse paymentsResponse) { this.paymentsResponse = paymentsResponse; } + + public TerminalAPIResponse getTerminalApiResponse() { + return terminalApiResponse; + } + + public void setTerminalApiResponse(TerminalAPIResponse terminalApiResponse) { + this.terminalApiResponse = terminalApiResponse; + } } diff --git a/adyenv6core/src/com/adyen/v6/facades/AdyenCheckoutFacade.java b/adyenv6core/src/com/adyen/v6/facades/AdyenCheckoutFacade.java index 9dde937d0..399fe4400 100644 --- a/adyenv6core/src/com/adyen/v6/facades/AdyenCheckoutFacade.java +++ b/adyenv6core/src/com/adyen/v6/facades/AdyenCheckoutFacade.java @@ -179,6 +179,11 @@ public interface AdyenCheckoutFacade { */ boolean showBoleto(); + /** + * Returns whether POS should be shown as an available payment method on the checkout page + */ + boolean showPos(); + /** * Returns whether CC can be stored depending on the recurring contract settings */ @@ -203,4 +208,14 @@ public interface AdyenCheckoutFacade { void handlePaymentForm(AdyenPaymentForm adyenPaymentForm, BindingResult bindingResult); PaymentDetailsListWsDTO getPaymentDetails(String userId) throws IOException, ApiException; + + /** + * Initiate POS Payment using Adyen Terminal API + */ + OrderData initiatePosPayment(HttpServletRequest request, CartData cartData) throws Exception; + + /** + * Check POS Payment status using Adyen Terminal API + */ + OrderData checkPosPaymentStatus(HttpServletRequest request, CartData cartData) throws Exception; } diff --git a/adyenv6core/src/com/adyen/v6/facades/DefaultAdyenCheckoutFacade.java b/adyenv6core/src/com/adyen/v6/facades/DefaultAdyenCheckoutFacade.java index 25ac478e0..c9e16d734 100644 --- a/adyenv6core/src/com/adyen/v6/facades/DefaultAdyenCheckoutFacade.java +++ b/adyenv6core/src/com/adyen/v6/facades/DefaultAdyenCheckoutFacade.java @@ -32,6 +32,7 @@ import java.util.SortedMap; import java.util.TreeMap; import java.util.UUID; +import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; @@ -49,10 +50,14 @@ import com.adyen.model.PaymentResult; import com.adyen.model.checkout.PaymentMethod; import com.adyen.model.checkout.PaymentsResponse; +import com.adyen.model.nexo.ErrorConditionType; +import com.adyen.model.nexo.ResultType; import com.adyen.model.recurring.Recurring; import com.adyen.model.recurring.RecurringDetail; +import com.adyen.model.terminal.TerminalAPIResponse; import com.adyen.service.exception.ApiException; import com.adyen.v6.converters.PaymentsResponseConverter; +import com.adyen.v6.converters.PosPaymentResponseConverter; import com.adyen.v6.enums.RecurringContractMode; import com.adyen.v6.exceptions.AdyenNonAuthorizedPaymentException; import com.adyen.v6.factory.AdyenPaymentServiceFactory; @@ -63,6 +68,7 @@ import com.adyen.v6.service.AdyenOrderService; import com.adyen.v6.service.AdyenPaymentService; import com.adyen.v6.service.AdyenTransactionService; +import com.adyen.v6.util.TerminalAPIUtil; import com.google.gson.Gson; import de.hybris.platform.commercefacades.i18n.I18NFacade; import de.hybris.platform.commercefacades.order.CheckoutFacade; @@ -92,7 +98,6 @@ import de.hybris.platform.store.BaseStoreModel; import de.hybris.platform.store.services.BaseStoreService; import static com.adyen.constants.ApiConstants.Redirect.Data.MD; -import static com.adyen.constants.ApiConstants.Redirect.Data.PAYMENT_DATA; import static com.adyen.constants.ApiConstants.ThreeDS2Property.CHALLENGE_RESULT; import static com.adyen.constants.ApiConstants.ThreeDS2Property.FINGERPRINT_RESULT; import static com.adyen.constants.ApiConstants.ThreeDS2Property.THREEDS2_CHALLENGE_TOKEN; @@ -143,6 +148,8 @@ public class DefaultAdyenCheckoutFacade implements AdyenCheckoutFacade { private PaymentsResponseConverter paymentsResponseConverter; private FlexibleSearchService flexibleSearchService; private Converter addressReverseConverter; + private PosPaymentResponseConverter posPaymentResponseConverter; + @Resource(name = "i18NFacade") private I18NFacade i18NFacade; @@ -172,9 +179,11 @@ public class DefaultAdyenCheckoutFacade implements AdyenCheckoutFacade { public static final String MODEL_OPEN_INVOICE_METHODS = "openInvoiceMethods"; public static final String MODEL_SHOW_SOCIAL_SECURITY_NUMBER = "showSocialSecurityNumber"; public static final String MODEL_SHOW_BOLETO = "showBoleto"; + public static final String MODEL_SHOW_POS = "showPos"; public static final String CHECKOUT_SHOPPER_HOST_TEST = "checkoutshopper-test.adyen.com"; public static final String CHECKOUT_SHOPPER_HOST_LIVE = "checkoutshopper-live.adyen.com"; public static final String MODEL_IDEAL_ISSUER_LIST = "iDealissuerList"; + public static final String MODEL_CONNECTED_TERMINAL_LIST = "connectedTerminalList"; public static final String MODEL_ENVIRONMENT_MODE = "environmentMode"; protected static final Set HPP_RESPONSE_PARAMETERS = new HashSet<>(Arrays.asList(HPPConstants.Response.MERCHANT_REFERENCE, @@ -681,6 +690,8 @@ private OrderData createOrderFromPaymentsResponse(final PaymentsResponse payment orderData.setAdyenMultibancoDeadline(paymentsResponse.getMultibancoDeadline()); orderData.setAdyenMultibancoReference(paymentsResponse.getMultibancoReference()); + orderData.setAdyenPosReceipt(paymentsResponse.getAdditionalData().get("pos.receipt")); + return orderData; } @@ -709,6 +720,7 @@ public void initializeCheckoutData(Model model) { //Set APMs from Adyen HPP Directory Lookup List alternativePaymentMethods = new ArrayList<>(); String iDealissuerList = null; + List connectedTerminalList = null; try { alternativePaymentMethods = adyenPaymentService.getPaymentMethods(cartData.getTotalPrice().getValue(), @@ -720,6 +732,9 @@ public void initializeCheckoutData(Model model) { .filter(paymentMethod -> ! paymentMethod.getType().isEmpty() && PAYMENT_METHOD_IDEAL.equals(paymentMethod.getType())) .findFirst() .orElse(null); + if (showPos()) { + connectedTerminalList = adyenPaymentService.getConnectedTerminals().getUniqueTerminalIds(); + } if (idealPaymentMethod != null) { Gson gson = new Gson(); @@ -784,7 +799,10 @@ public void initializeCheckoutData(Model model) { //Include Boleto banks model.addAttribute(MODEL_SHOW_BOLETO, showBoleto()); - + //Include POS Enable configuration + model.addAttribute(MODEL_SHOW_POS, showPos()); + //Include connnected terminal List for POS + model.addAttribute(MODEL_CONNECTED_TERMINAL_LIST, connectedTerminalList); //Include Issuer List for iDEAL model.addAttribute(MODEL_IDEAL_ISSUER_LIST, iDealissuerList); modelService.save(cartModel); @@ -805,7 +823,6 @@ private boolean isHiddenPaymentMethod(PaymentMethod paymentMethod) { return false; } - @Override public boolean showBoleto() { BaseStoreModel baseStore = baseStoreService.getCurrentBaseStore(); @@ -822,6 +839,16 @@ public boolean showBoleto() { return "BRL".equals(currency) && "BR".equals(country); } + @Override + public boolean showPos() { + BaseStoreModel baseStore = baseStoreService.getCurrentBaseStore(); + //Check base store settings for POS Enabled or not. + if (baseStore.getAdyenPosEnabled() == null || ! baseStore.getAdyenPosEnabled()) { + return false; + } + return true; + } + @Override public boolean showRememberDetails() { BaseStoreModel baseStore = baseStoreService.getCurrentBaseStore(); @@ -846,7 +873,7 @@ public boolean showSocialSecurityNumber() { CartData cart = getCheckoutFacade().getCheckoutCart(); final AddressData deliveryAddress = cart.getDeliveryAddress(); String countryCode = deliveryAddress.getCountry().getIsocode(); - if (OPENINVOICE_METHODS_API.contains(cart.getAdyenPaymentMethod()) && OPENINVOICE_METHODS_ALLOW_SOCIAL_SECURITY_NUMBER.contains(countryCode)) { + if (RATEPAY.equals(cart.getAdyenPaymentMethod()) && OPENINVOICE_METHODS_ALLOW_SOCIAL_SECURITY_NUMBER.contains(countryCode)) { showSocialSecurityNumber = true; } return showSocialSecurityNumber; @@ -885,6 +912,9 @@ public PaymentInfoModel createPaymentInfo(final CartModel cartModel, AdyenPaymen //required for 3DS2 paymentInfo.setAdyenBrowserInfo(adyenPaymentForm.getBrowserInfo()); + //pos field(s) + paymentInfo.setAdyenTerminalId(adyenPaymentForm.getTerminalId()); + modelService.save(paymentInfo); return paymentInfo; @@ -910,6 +940,7 @@ public PaymentInfoModel createPaymentInfo(final CartModel cartModel, PaymentDeta paymentInfo.setAdyenFirstName(paymentDetails.getAdyenFirstName()); paymentInfo.setAdyenLastName(paymentDetails.getAdyenLastName()); paymentInfo.setOwner(cartModel.getOwner()); + paymentInfo.setAdyenTerminalId(paymentDetails.getTerminalId()); paymentInfo.setAdyenInstallments(paymentDetails.getInstallments()); return paymentInfo; } @@ -1038,6 +1069,88 @@ public AdyenPaymentService getAdyenPaymentService() { return adyenPaymentServiceFactory.createFromBaseStore(baseStoreService.getCurrentBaseStore()); } + /** + * Initiate POS Payment using Adyen Terminal API + */ + @Override + public OrderData initiatePosPayment(HttpServletRequest request, CartData cartData) throws Exception { + CustomerModel customer = null; + if (! getCheckoutCustomerStrategy().isAnonymousCheckout()) { + customer = getCheckoutCustomerStrategy().getCurrentUserForCheckout(); + } + //This will be used to check status later + + String serviceId = request.getAttribute("originalServiceId").toString(); + TerminalAPIResponse terminalApiResponse = getAdyenPaymentService().sendSyncPosPaymentRequest(cartData, customer, serviceId); + ResultType resultType = TerminalAPIUtil.getPaymentResultFromStatusOrPaymentResponse(terminalApiResponse); + + if (ResultType.SUCCESS == resultType) { + PaymentsResponse paymentsResponse = getPosPaymentResponseConverter().convert(terminalApiResponse.getSaleToPOIResponse()); + String posReceipt = TerminalAPIUtil.getReceiptFromPaymentResponse(terminalApiResponse); + if (StringUtils.isNotEmpty(posReceipt)) { + paymentsResponse.putAdditionalDataItem("pos.receipt", posReceipt); + } + return createAuthorizedOrder(paymentsResponse); + } + throw new AdyenNonAuthorizedPaymentException(terminalApiResponse); + } + + /** + * Check POS Payment Status using Adyen Terminal API + */ + @Override + public OrderData checkPosPaymentStatus(HttpServletRequest request, CartData cartData) throws Exception { + + String originalServiceId = request.getAttribute("originalServiceId").toString(); + TerminalAPIResponse terminalApiResponse = getAdyenPaymentService().sendSyncPosStatusRequest(cartData, originalServiceId); + ResultType statusResult = TerminalAPIUtil.getStatusResultFromStatusResponse(terminalApiResponse); + + if (statusResult != null) { + if (statusResult == ResultType.SUCCESS) { + //this will be success even if payment is failed. because this belongs to status call not payment call + ResultType paymentResult = TerminalAPIUtil.getPaymentResultFromStatusOrPaymentResponse(terminalApiResponse); + if (paymentResult == ResultType.SUCCESS) { + PaymentsResponse paymentsResponse = getPosPaymentResponseConverter().convert(terminalApiResponse.getSaleToPOIResponse()); + String posReceipt = TerminalAPIUtil.getReceiptFromStatusResponse(terminalApiResponse); + if (StringUtils.isNotEmpty(posReceipt)) { + paymentsResponse.putAdditionalDataItem("pos.receipt", posReceipt); + } + return createAuthorizedOrder(paymentsResponse); + } else { + throw new AdyenNonAuthorizedPaymentException(terminalApiResponse); + } + } else { + ErrorConditionType errorCondition = TerminalAPIUtil.getErrorConditionForStatusFromStatusResponse(terminalApiResponse); + //If transaction is still in progress, keep retrying in 5 seconds. + if (errorCondition == ErrorConditionType.IN_PROGRESS) { + TimeUnit.SECONDS.sleep(5); + if (isPosTimedOut(request)) { + throw new AdyenNonAuthorizedPaymentException(terminalApiResponse); + } else { + return checkPosPaymentStatus(request, cartData); + } + } else { + throw new AdyenNonAuthorizedPaymentException(terminalApiResponse); + } + } + } + + //probably returned SaleToPOIRequest, that means terminal unreachable, return the response as error + throw new AdyenNonAuthorizedPaymentException(terminalApiResponse); + } + + private boolean isPosTimedOut(HttpServletRequest request) { + long currentTime = System.currentTimeMillis(); + long processStartTime = (long) request.getAttribute("paymentStartTime"); + int totalTimeout = ((int) request.getAttribute("totalTimeout")) * 1000; + long timeDiff = currentTime - processStartTime; + if (timeDiff >= totalTimeout) { + return true; + } else { + return false; + } + } + public BaseStoreService getBaseStoreService() { return baseStoreService; } @@ -1181,4 +1294,12 @@ public I18NFacade getI18NFacade() { public void setI18NFacade(I18NFacade i18NFacade) { this.i18NFacade = i18NFacade; } + + public PosPaymentResponseConverter getPosPaymentResponseConverter() { + return posPaymentResponseConverter; + } + + public void setPosPaymentResponseConverter(PosPaymentResponseConverter posPaymentResponseConverter) { + this.posPaymentResponseConverter = posPaymentResponseConverter; + } } diff --git a/adyenv6core/src/com/adyen/v6/factory/AdyenRequestFactory.java b/adyenv6core/src/com/adyen/v6/factory/AdyenRequestFactory.java index 04500dad6..86dcb57be 100644 --- a/adyenv6core/src/com/adyen/v6/factory/AdyenRequestFactory.java +++ b/adyenv6core/src/com/adyen/v6/factory/AdyenRequestFactory.java @@ -20,22 +20,14 @@ */ package com.adyen.v6.factory; -import java.math.BigDecimal; -import java.util.ArrayList; -import java.util.Currency; -import java.util.HashMap; -import java.util.List; -import javax.servlet.http.HttpServletRequest; -import org.apache.commons.configuration.Configuration; -import org.apache.commons.lang3.StringUtils; -import org.apache.log4j.Logger; import com.adyen.Util.Util; +import com.adyen.builders.terminal.TerminalAPIRequestBuilder; import com.adyen.enums.VatCategory; import com.adyen.model.AbstractPaymentRequest; import com.adyen.model.Address; import com.adyen.model.Amount; -import com.adyen.model.Installments; import com.adyen.model.BrowserInfo; +import com.adyen.model.Installments; import com.adyen.model.Name; import com.adyen.model.PaymentRequest; import com.adyen.model.PaymentRequest3d; @@ -50,9 +42,18 @@ import com.adyen.model.modification.CancelOrRefundRequest; import com.adyen.model.modification.CaptureRequest; import com.adyen.model.modification.RefundRequest; +import com.adyen.model.nexo.AmountsReq; +import com.adyen.model.nexo.DocumentQualifierType; +import com.adyen.model.nexo.MessageCategoryType; +import com.adyen.model.nexo.MessageReference; +import com.adyen.model.nexo.PaymentTransaction; +import com.adyen.model.nexo.SaleData; +import com.adyen.model.nexo.TransactionIdentification; +import com.adyen.model.nexo.TransactionStatusRequest; import com.adyen.model.recurring.DisableRequest; import com.adyen.model.recurring.Recurring; import com.adyen.model.recurring.RecurringDetailsRequest; +import com.adyen.model.terminal.TerminalAPIRequest; import com.adyen.v6.enums.AdyenCardTypeEnum; import com.adyen.v6.enums.RecurringContractMode; import com.adyen.v6.model.RequestInfo; @@ -64,13 +65,28 @@ import de.hybris.platform.core.model.user.CustomerModel; import de.hybris.platform.servicelayer.config.ConfigurationService; import de.hybris.platform.util.TaxValue; +import org.apache.commons.configuration.Configuration; +import org.apache.commons.lang3.StringUtils; +import org.apache.http.client.utils.URIBuilder; +import org.apache.log4j.Logger; + +import javax.servlet.http.HttpServletRequest; +import javax.xml.datatype.DatatypeFactory; +import javax.xml.datatype.XMLGregorianCalendar; +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Currency; +import java.util.GregorianCalendar; +import java.util.HashMap; +import java.util.List; + import static com.adyen.constants.BrandCodes.PAYPAL_ECS; import static com.adyen.v6.constants.Adyenv6coreConstants.KLARNA; -import static com.adyen.v6.constants.Adyenv6coreConstants.PAYMENT_METHOD_FACILPAY_PREFIX; import static com.adyen.v6.constants.Adyenv6coreConstants.OPENINVOICE_METHODS_API; import static com.adyen.v6.constants.Adyenv6coreConstants.PAYMENT_METHOD_BOLETO; import static com.adyen.v6.constants.Adyenv6coreConstants.PAYMENT_METHOD_BOLETO_SANTANDER; import static com.adyen.v6.constants.Adyenv6coreConstants.PAYMENT_METHOD_CC; +import static com.adyen.v6.constants.Adyenv6coreConstants.PAYMENT_METHOD_FACILPAY_PREFIX; import static com.adyen.v6.constants.Adyenv6coreConstants.PAYMENT_METHOD_IDEAL; import static com.adyen.v6.constants.Adyenv6coreConstants.PAYMENT_METHOD_ONECLICK; import static com.adyen.v6.constants.Adyenv6coreConstants.PLUGIN_NAME; @@ -414,6 +430,69 @@ private T createBasePaymentRequest(T abstract return abstractPaymentRequest; } + + public TerminalAPIRequest createTerminalAPIRequestForStatus(final CartData cartData, String originalServiceId) { + TransactionStatusRequest transactionStatusRequest = new TransactionStatusRequest(); + transactionStatusRequest.setReceiptReprintFlag(true); + + MessageReference messageReference = new MessageReference(); + messageReference.setMessageCategory(MessageCategoryType.PAYMENT); + messageReference.setSaleID(cartData.getStore()); + messageReference.setServiceID(originalServiceId); + + transactionStatusRequest.setMessageReference(messageReference); + transactionStatusRequest.getDocumentQualifier().add(DocumentQualifierType.CASHIER_RECEIPT); + transactionStatusRequest.getDocumentQualifier().add(DocumentQualifierType.CUSTOMER_RECEIPT); + + String serviceId = Long.toString(System.currentTimeMillis() % 10000000000L); + + TerminalAPIRequestBuilder builder = new TerminalAPIRequestBuilder(cartData.getStore(), serviceId, cartData.getAdyenTerminalId()); + builder.withTransactionStatusRequest(transactionStatusRequest); + + return builder.build(); + } + + public TerminalAPIRequest createTerminalAPIRequest(final CartData cartData, CustomerModel customer, RecurringContractMode recurringContractMode, String serviceId) throws Exception { + com.adyen.model.nexo.PaymentRequest paymentRequest = new com.adyen.model.nexo.PaymentRequest(); + + SaleData saleData = new SaleData(); + TransactionIdentification transactionIdentification = new TransactionIdentification(); + transactionIdentification.setTransactionID(cartData.getCode()); + XMLGregorianCalendar timestamp = DatatypeFactory.newInstance().newXMLGregorianCalendar(new GregorianCalendar()); + transactionIdentification.setTimeStamp(timestamp); + saleData.setSaleTransactionID(transactionIdentification); + + //Set recurring contract, if exists + if(customer != null) { + String shopperReference = customer.getCustomerID(); + String shopperEmail = customer.getContactEmail(); + Recurring recurringContract = getRecurringContractType(recurringContractMode); + + if(recurringContract != null && StringUtils.isNotEmpty(shopperReference) && StringUtils.isNotEmpty(shopperEmail)) { + URIBuilder builder = new URIBuilder(); + builder.addParameter("shopperEmail", shopperEmail); + builder.addParameter("shopperReference", shopperReference); + builder.addParameter("recurringContract", recurringContract.getContract().toString()); + saleData.setSaleToAcquirerData(builder.build().getQuery()); + } + } + + paymentRequest.setSaleData(saleData); + + PaymentTransaction paymentTransaction = new PaymentTransaction(); + AmountsReq amountsReq = new AmountsReq(); + amountsReq.setCurrency(cartData.getTotalPrice().getCurrencyIso()); + amountsReq.setRequestedAmount(cartData.getTotalPrice().getValue()); + paymentTransaction.setAmountsReq(amountsReq); + + paymentRequest.setPaymentTransaction(paymentTransaction); + + TerminalAPIRequestBuilder builder = new TerminalAPIRequestBuilder(cartData.getStore(), serviceId, cartData.getAdyenTerminalId()); + builder.withPaymentRequest(paymentRequest); + + return builder.build(); + } + /** * Set Address Data into API */ diff --git a/adyenv6core/src/com/adyen/v6/forms/AdyenPaymentForm.java b/adyenv6core/src/com/adyen/v6/forms/AdyenPaymentForm.java index 0c75c0b72..ebcc2d088 100644 --- a/adyenv6core/src/com/adyen/v6/forms/AdyenPaymentForm.java +++ b/adyenv6core/src/com/adyen/v6/forms/AdyenPaymentForm.java @@ -72,6 +72,9 @@ public class AdyenPaymentForm { //3DS 2.0 private String browserInfo; + //POS + private String terminalId; + public String getBrowserInfo() { return browserInfo; } @@ -241,6 +244,14 @@ public void setInstallments(int installments) { this.installments = installments; } + public String getTerminalId() { + return terminalId; + } + + public void setTerminalId(String terminalId) { + this.terminalId = terminalId; + } + @Override public String toString() { StringBuilder sb = new StringBuilder(); @@ -262,6 +273,7 @@ public String toString() { sb.append(" lastName: ").append(Util.toIndentedString(lastName)).append("\n"); sb.append(" dfValue: ").append(Util.toIndentedString(dfValue)).append("\n"); sb.append(" cardBrand: ").append(Util.toIndentedString(cardBrand)).append("\n"); + sb.append(" terminalId: ").append(Util.toIndentedString(terminalId)).append("\n"); sb.append(" browserInfo: ").append(Util.toIndentedString(browserInfo)).append("\n"); sb.append("}"); return sb.toString(); diff --git a/adyenv6core/src/com/adyen/v6/forms/validation/AdyenPaymentFormValidator.java b/adyenv6core/src/com/adyen/v6/forms/validation/AdyenPaymentFormValidator.java index be561955a..64256a4ff 100644 --- a/adyenv6core/src/com/adyen/v6/forms/validation/AdyenPaymentFormValidator.java +++ b/adyenv6core/src/com/adyen/v6/forms/validation/AdyenPaymentFormValidator.java @@ -26,6 +26,7 @@ import org.springframework.validation.Validator; import com.adyen.v6.forms.AdyenPaymentForm; import static com.adyen.v6.constants.Adyenv6coreConstants.OPENINVOICE_METHODS_API; +import static com.adyen.v6.constants.Adyenv6coreConstants.RATEPAY; public class AdyenPaymentFormValidator implements Validator { private Set storedCards; @@ -78,7 +79,7 @@ public void validate(Object o, Errors errors) { } // check if date or social seucrity number is set - if (OPENINVOICE_METHODS_API.contains(form.getPaymentMethod())) { + if (RATEPAY.equals(form.getPaymentMethod())) { if (showSocialSecurityNumber) { if (form.getSocialSecurityNumber().isEmpty()) { diff --git a/adyenv6core/src/com/adyen/v6/populator/AbstractOrderPopulator.java b/adyenv6core/src/com/adyen/v6/populator/AbstractOrderPopulator.java index f44fcaed4..cf8db709b 100644 --- a/adyenv6core/src/com/adyen/v6/populator/AbstractOrderPopulator.java +++ b/adyenv6core/src/com/adyen/v6/populator/AbstractOrderPopulator.java @@ -57,6 +57,8 @@ public void populate(final AbstractOrderModel source, final AbstractOrderData ta target.setAdyenMultibancoDeadline(paymentInfo.getAdyenMultibancoDeadline()); target.setAdyenMultibancoReference(paymentInfo.getAdyenMultibancoReference()); target.setAdyenMultibancoEntity(paymentInfo.getAdyenMultibancoEntity()); + //Set POS Receipt + target.setAdyenPosReceipt(paymentInfo.getAdyenPosReceipt()); } } diff --git a/adyenv6core/src/com/adyen/v6/populator/CartPopulator.java b/adyenv6core/src/com/adyen/v6/populator/CartPopulator.java index 0fc77ee9a..c9405ed3f 100644 --- a/adyenv6core/src/com/adyen/v6/populator/CartPopulator.java +++ b/adyenv6core/src/com/adyen/v6/populator/CartPopulator.java @@ -53,6 +53,7 @@ public void populate(final CartModel source, final CartData target) throws Conve target.setAdyenEncryptedExpiryYear(paymentInfo.getEncryptedExpiryYear()); target.setAdyenEncryptedSecurityCode(paymentInfo.getEncryptedSecurityCode()); target.setAdyenInstallments(paymentInfo.getAdyenInstallments()); + target.setAdyenTerminalId(paymentInfo.getAdyenTerminalId()); target.setAdyenBrowserInfo(paymentInfo.getAdyenBrowserInfo()); } } diff --git a/adyenv6core/src/com/adyen/v6/service/AdyenPaymentService.java b/adyenv6core/src/com/adyen/v6/service/AdyenPaymentService.java index 4e34d075b..c72cd16e5 100644 --- a/adyenv6core/src/com/adyen/v6/service/AdyenPaymentService.java +++ b/adyenv6core/src/com/adyen/v6/service/AdyenPaymentService.java @@ -33,6 +33,8 @@ import com.adyen.model.checkout.PaymentsResponse; import com.adyen.model.modification.ModificationResult; import com.adyen.model.recurring.RecurringDetail; +import com.adyen.model.terminal.ConnectedTerminalsResponse; +import com.adyen.model.terminal.TerminalAPIResponse; import com.adyen.service.exception.ApiException; import com.adyen.v6.model.RequestInfo; import de.hybris.platform.commercefacades.order.data.CartData; @@ -44,6 +46,8 @@ public interface AdyenPaymentService { */ PaymentResult authorise(CartData cartData, HttpServletRequest request, CustomerModel customerModel) throws Exception; + ConnectedTerminalsResponse getConnectedTerminals() throws IOException, ApiException ; + PaymentsResponse authorisePayment(CartData cartData, RequestInfo requestInfo, CustomerModel customerModel) throws Exception; /** @@ -115,4 +119,13 @@ public interface AdyenPaymentService { * Returns the Device Fingerprint url */ String getDeviceFingerprintUrl(); + + /** + * Send POS Payment Request using Adyen Terminal API + */ + TerminalAPIResponse sendSyncPosPaymentRequest(CartData cartData, CustomerModel customer, String serviceId) throws Exception; + /** + * Send POS Status Request using Adyen Terminal API + */ + TerminalAPIResponse sendSyncPosStatusRequest(CartData cartData, String serviceId) throws Exception; } diff --git a/adyenv6core/src/com/adyen/v6/service/DefaultAdyenOrderService.java b/adyenv6core/src/com/adyen/v6/service/DefaultAdyenOrderService.java index 50e7e5cd9..c1d67db5f 100644 --- a/adyenv6core/src/com/adyen/v6/service/DefaultAdyenOrderService.java +++ b/adyenv6core/src/com/adyen/v6/service/DefaultAdyenOrderService.java @@ -148,6 +148,9 @@ public void updateOrderFromPaymentsResponse(OrderModel order, PaymentsResponse p paymentInfo.setAdyenMultibancoDeadline(paymentsResponse.getMultibancoDeadline()); paymentInfo.setAdyenMultibancoReference(paymentsResponse.getMultibancoReference()); + //pos receipt + paymentInfo.setAdyenPosReceipt(paymentsResponse.getAdditionalDataByKey("pos.receipt")); + modelService.save(paymentInfo); storeFraudReportFromPaymentsResponse(order, paymentsResponse); diff --git a/adyenv6core/src/com/adyen/v6/service/DefaultAdyenPaymentService.java b/adyenv6core/src/com/adyen/v6/service/DefaultAdyenPaymentService.java index a9c2eb5cb..300fbd1c4 100644 --- a/adyenv6core/src/com/adyen/v6/service/DefaultAdyenPaymentService.java +++ b/adyenv6core/src/com/adyen/v6/service/DefaultAdyenPaymentService.java @@ -20,23 +20,6 @@ */ package com.adyen.v6.service; -import java.io.IOException; -import java.math.BigDecimal; -import java.security.SignatureException; -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Calendar; -import java.util.Currency; -import java.util.Date; -import java.util.HashMap; -import java.util.List; -import java.util.stream.Collectors; -import javax.servlet.http.HttpServletRequest; -import org.apache.commons.lang3.StringUtils; -import org.apache.log4j.Logger; -import org.springframework.util.Assert; import com.adyen.Client; import com.adyen.Config; import com.adyen.Util.Util; @@ -62,17 +45,44 @@ import com.adyen.model.recurring.RecurringDetail; import com.adyen.model.recurring.RecurringDetailsRequest; import com.adyen.model.recurring.RecurringDetailsResult; +import com.adyen.model.terminal.ConnectedTerminalsRequest; +import com.adyen.model.terminal.ConnectedTerminalsResponse; +import com.adyen.model.terminal.TerminalAPIRequest; +import com.adyen.model.terminal.TerminalAPIResponse; import com.adyen.service.Checkout; import com.adyen.service.CheckoutUtility; import com.adyen.service.Modification; import com.adyen.service.Payment; +import com.adyen.service.PosPayment; +import com.adyen.service.TerminalCloudAPI; import com.adyen.service.exception.ApiException; +import com.adyen.terminal.serialization.TerminalAPIGsonBuilder; import com.adyen.v6.converters.PaymentMethodConverter; +import com.adyen.v6.enums.RecurringContractMode; import com.adyen.v6.factory.AdyenRequestFactory; import com.adyen.v6.model.RequestInfo; import de.hybris.platform.commercefacades.order.data.CartData; import de.hybris.platform.core.model.user.CustomerModel; import de.hybris.platform.store.BaseStoreModel; +import org.apache.commons.lang3.StringUtils; +import org.apache.log4j.Logger; +import org.springframework.util.Assert; + +import javax.servlet.http.HttpServletRequest; +import java.io.IOException; +import java.math.BigDecimal; +import java.security.SignatureException; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Calendar; +import java.util.Currency; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.stream.Collectors; + import static com.adyen.v6.constants.Adyenv6coreConstants.PLUGIN_NAME; import static com.adyen.v6.constants.Adyenv6coreConstants.PLUGIN_VERSION; @@ -81,8 +91,13 @@ public class DefaultAdyenPaymentService implements AdyenPaymentService { private AdyenRequestFactory adyenRequestFactory; private Config config; private Client client; + private Config posConfig; + private Client posClient; + private PaymentMethodConverter paymentMethodConverter; + private static final int POS_REQUEST_TIMEOUT = 25000; + private static final Logger LOG = Logger.getLogger(DefaultAdyenPaymentService.class); /** @@ -100,7 +115,23 @@ public DefaultAdyenPaymentService(final BaseStoreModel baseStore) { String hmacKey = baseStore.getAdyenSkinHMAC(); String apiEndpointPrefix = baseStore.getAdyenAPIEndpointPrefix(); boolean isTestMode = baseStore.getAdyenTestMode(); - + boolean isPosEnabled = baseStore.getAdyenPosEnabled(); + if (isPosEnabled) { + String posApiKey = baseStore.getAdyenPosApiKey(); + String posMerchantAccount = baseStore.getAdyenPosMerchantAccount(); + posConfig = new Config(); + posConfig.setApiKey(posApiKey); + posConfig.setMerchantAccount(posMerchantAccount); + posConfig.setReadTimeoutMillis(POS_REQUEST_TIMEOUT); + posConfig.setApplicationName(PLUGIN_NAME + " v" + PLUGIN_VERSION); + posClient = new Client(posConfig); + + if (isTestMode) { + posClient.setEnvironment(Environment.TEST, null); + } else { + posClient.setEnvironment(Environment.LIVE, null); + } + } Assert.notNull(merchantAccount); config = new Config(); @@ -136,6 +167,21 @@ public PaymentResult authorise(final CartData cartData, final HttpServletRequest return paymentResult; } + @Override + public ConnectedTerminalsResponse getConnectedTerminals() throws IOException, ApiException { + PosPayment posPayment = new PosPayment(posClient); + ConnectedTerminalsRequest connectedTerminalsRequest = new ConnectedTerminalsRequest(); + connectedTerminalsRequest.setMerchantAccount(posConfig.getMerchantAccount()); + if (baseStore.getAdyenPosStoreId() != null && StringUtils.isNotEmpty(baseStore.getAdyenPosStoreId())) { + connectedTerminalsRequest.setStore(baseStore.getAdyenPosStoreId()); + } + LOG.debug(connectedTerminalsRequest); + ConnectedTerminalsResponse connectedTerminalsResponse = posPayment.connectedTerminals(connectedTerminalsRequest); + LOG.debug(connectedTerminalsResponse); + return connectedTerminalsResponse; + + } + @Override public PaymentsResponse authorisePayment(final CartData cartData, final RequestInfo requestInfo, final CustomerModel customerModel) throws Exception { Checkout checkout = new Checkout(client); @@ -361,6 +407,39 @@ public String getDeviceFingerprintUrl() { return "https://live.adyen.com/hpp/js/df.js?v=" + df.format(today); } + /** + * Send POS Payment Request using Adyen Terminal API + */ + @Override + public TerminalAPIResponse sendSyncPosPaymentRequest(CartData cartData, CustomerModel customer, String serviceId) throws Exception { + TerminalCloudAPI terminalCloudAPI = new TerminalCloudAPI(posClient); + + RecurringContractMode recurringContractMode = getBaseStore().getAdyenPosRecurringContractMode(); + TerminalAPIRequest terminalApiRequest = adyenRequestFactory.createTerminalAPIRequest(cartData, customer, recurringContractMode, serviceId); + + LOG.debug(TerminalAPIGsonBuilder.create().toJson(terminalApiRequest)); + TerminalAPIResponse terminalApiResponse = terminalCloudAPI.sync(terminalApiRequest); + + LOG.debug(TerminalAPIGsonBuilder.create().toJson(terminalApiResponse)); + return terminalApiResponse; + } + + /** + * Send POS Status Request using Adyen Terminal API + */ + @Override + public TerminalAPIResponse sendSyncPosStatusRequest(CartData cartData, String originalServiceId) throws Exception { + TerminalCloudAPI terminalCloudAPI = new TerminalCloudAPI(posClient); + + TerminalAPIRequest terminalApiRequest = adyenRequestFactory.createTerminalAPIRequestForStatus(cartData, originalServiceId); + + LOG.debug(TerminalAPIGsonBuilder.create().toJson(terminalApiRequest)); + TerminalAPIResponse terminalApiResponse = terminalCloudAPI.sync(terminalApiRequest); + + LOG.debug(TerminalAPIGsonBuilder.create().toJson(terminalApiResponse)); + return terminalApiResponse; + } + public AdyenRequestFactory getAdyenRequestFactory() { return adyenRequestFactory; } diff --git a/adyenv6core/src/com/adyen/v6/util/TerminalAPIUtil.java b/adyenv6core/src/com/adyen/v6/util/TerminalAPIUtil.java new file mode 100644 index 000000000..b2252a1d8 --- /dev/null +++ b/adyenv6core/src/com/adyen/v6/util/TerminalAPIUtil.java @@ -0,0 +1,216 @@ +package com.adyen.v6.util; + +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.util.List; +import java.util.Map; +import org.apache.log4j.Logger; +import com.adyen.model.nexo.DocumentQualifierType; +import com.adyen.model.nexo.ErrorConditionType; +import com.adyen.model.nexo.OutputText; +import com.adyen.model.nexo.PaymentReceipt; +import com.adyen.model.nexo.ResultType; +import com.adyen.model.terminal.TerminalAPIResponse; +import com.google.common.base.Splitter; + +public class TerminalAPIUtil { + public static final Logger LOGGER = Logger.getLogger(TerminalAPIUtil.class); + + public static ResultType getStatusResultFromStatusResponse(TerminalAPIResponse terminalApiResponse) { + if (terminalApiResponse.getSaleToPOIResponse() != null + && terminalApiResponse.getSaleToPOIResponse().getTransactionStatusResponse() != null + && terminalApiResponse.getSaleToPOIResponse().getTransactionStatusResponse().getResponse() != null) { + + return terminalApiResponse.getSaleToPOIResponse().getTransactionStatusResponse().getResponse().getResult(); + } + return null; + } + + public static ErrorConditionType getErrorConditionForPaymentFromStatusResponse(TerminalAPIResponse terminalApiResponse) { + if (terminalApiResponse != null + && terminalApiResponse.getSaleToPOIResponse() != null + && terminalApiResponse.getSaleToPOIResponse().getTransactionStatusResponse() != null + && terminalApiResponse.getSaleToPOIResponse().getTransactionStatusResponse().getRepeatedMessageResponse() != null + && terminalApiResponse.getSaleToPOIResponse().getTransactionStatusResponse().getRepeatedMessageResponse().getRepeatedResponseMessageBody() != null + && terminalApiResponse.getSaleToPOIResponse().getTransactionStatusResponse().getRepeatedMessageResponse().getRepeatedResponseMessageBody().getPaymentResponse() != null + && terminalApiResponse.getSaleToPOIResponse().getTransactionStatusResponse().getRepeatedMessageResponse().getRepeatedResponseMessageBody().getPaymentResponse().getResponse() != null) { + return terminalApiResponse.getSaleToPOIResponse() + .getTransactionStatusResponse() + .getRepeatedMessageResponse() + .getRepeatedResponseMessageBody() + .getPaymentResponse() + .getResponse() + .getErrorCondition(); + } + return null; + } + + public static ErrorConditionType getErrorConditionForPaymentResponse(TerminalAPIResponse terminalApiResponse) { + if (terminalApiResponse != null + && terminalApiResponse.getSaleToPOIResponse() != null + && terminalApiResponse.getSaleToPOIResponse().getPaymentResponse() != null + && terminalApiResponse.getSaleToPOIResponse().getPaymentResponse().getResponse() != null) { + + return terminalApiResponse.getSaleToPOIResponse().getPaymentResponse().getResponse().getErrorCondition(); + } + return null; + } + + public static ErrorConditionType getErrorConditionForStatusFromStatusResponse(TerminalAPIResponse terminalApiResponse) { + if (terminalApiResponse != null && terminalApiResponse.getSaleToPOIResponse() != null && terminalApiResponse.getSaleToPOIResponse().getTransactionStatusResponse() != null) { + return terminalApiResponse.getSaleToPOIResponse().getTransactionStatusResponse().getResponse().getErrorCondition(); + } + return null; + } + + /** + * @param terminalApiResponse + * @return Result from payment response present in paymentResponse in terminalApiResponse for POS payment or POS status call, if present. + * Otherwise returns Failure. + */ + public static ResultType getPaymentResultFromStatusOrPaymentResponse(TerminalAPIResponse terminalApiResponse) { + + if (terminalApiResponse != null && terminalApiResponse.getSaleToPOIResponse() != null) { + + if (terminalApiResponse.getSaleToPOIResponse().getPaymentResponse() != null) { + if (terminalApiResponse.getSaleToPOIResponse().getPaymentResponse().getResponse() != null) { + return terminalApiResponse.getSaleToPOIResponse().getPaymentResponse().getResponse().getResult(); + } + } else if (terminalApiResponse.getSaleToPOIResponse().getTransactionStatusResponse() != null) { + if (terminalApiResponse.getSaleToPOIResponse().getTransactionStatusResponse().getRepeatedMessageResponse() != null + && terminalApiResponse.getSaleToPOIResponse().getTransactionStatusResponse().getRepeatedMessageResponse().getRepeatedResponseMessageBody() != null + && terminalApiResponse.getSaleToPOIResponse().getTransactionStatusResponse().getRepeatedMessageResponse().getRepeatedResponseMessageBody().getPaymentResponse() != null + && terminalApiResponse.getSaleToPOIResponse().getTransactionStatusResponse().getRepeatedMessageResponse().getRepeatedResponseMessageBody().getPaymentResponse().getResponse() + != null) { + return terminalApiResponse.getSaleToPOIResponse() + .getTransactionStatusResponse() + .getRepeatedMessageResponse() + .getRepeatedResponseMessageBody() + .getPaymentResponse() + .getResponse() + .getResult(); + } + } + } + return ResultType.FAILURE; + } + + public static String getReceiptFromPaymentResponse(TerminalAPIResponse terminalApiResponse) { + String posReceipt = null; + if (terminalApiResponse != null && terminalApiResponse.getSaleToPOIResponse() != null && terminalApiResponse.getSaleToPOIResponse().getPaymentResponse() != null) { + posReceipt = TerminalAPIUtil.formatTerminalAPIReceipt(terminalApiResponse.getSaleToPOIResponse().getPaymentResponse().getPaymentReceipt()); + } + return posReceipt; + } + + public static String getReceiptFromStatusResponse(TerminalAPIResponse terminalApiResponse) { + + String posReceipt = null; + + if (terminalApiResponse != null + && terminalApiResponse.getSaleToPOIResponse() != null + && terminalApiResponse.getSaleToPOIResponse().getTransactionStatusResponse() != null + && terminalApiResponse.getSaleToPOIResponse().getTransactionStatusResponse().getRepeatedMessageResponse() != null + && terminalApiResponse.getSaleToPOIResponse().getTransactionStatusResponse().getRepeatedMessageResponse().getRepeatedResponseMessageBody() != null + && terminalApiResponse.getSaleToPOIResponse().getTransactionStatusResponse().getRepeatedMessageResponse().getRepeatedResponseMessageBody().getPaymentResponse() != null) { + + + posReceipt = formatTerminalAPIReceipt(terminalApiResponse.getSaleToPOIResponse() + .getTransactionStatusResponse() + .getRepeatedMessageResponse() + .getRepeatedResponseMessageBody() + .getPaymentResponse() + .getPaymentReceipt()); + } + return posReceipt; + } + + public static String formatTerminalAPIReceipt(List paymentReceipts) { + String formattedHtml = ""; + for (PaymentReceipt paymentReceipt : paymentReceipts) { + if (paymentReceipt.getDocumentQualifier() == DocumentQualifierType.CUSTOMER_RECEIPT) { + for (OutputText outputText : paymentReceipt.getOutputContent().getOutputText()) { + Map map = Splitter.on("&").withKeyValueSeparator('=').split(outputText.getText()); + formattedHtml += ""; + if ((map.get("name") != null)) { + formattedHtml += "" + map.get("name") + ""; + } else { + formattedHtml += " "; + } + if (map.get("value") != null) { + formattedHtml += "" + map.get("value") + ""; + } else { + formattedHtml += " "; + } + formattedHtml += ""; + } + } + } + formattedHtml += ""; + try { + formattedHtml = URLDecoder.decode(formattedHtml, "UTF-8"); + } catch (UnsupportedEncodingException e) { + LOGGER.debug("Exception in receipt generation " + e.getMessage()); + } + return formattedHtml; + } + + public static String getErrorMessageForNonAuthorizedPosPayment(TerminalAPIResponse terminalApiResponse) { + String errorMessage; + + if (terminalApiResponse.getSaleToPOIResponse() != null && terminalApiResponse.getSaleToPOIResponse().getPaymentResponse() != null) { + ErrorConditionType errorCondition = TerminalAPIUtil.getErrorConditionForPaymentResponse(terminalApiResponse); + errorMessage = TerminalAPIUtil.getErrorMessageByPosErrorCondition(errorCondition); + } else if (terminalApiResponse.getSaleToPOIResponse() != null && terminalApiResponse.getSaleToPOIResponse().getTransactionStatusResponse() != null) { + ErrorConditionType errorCondition = TerminalAPIUtil.getErrorConditionForPaymentFromStatusResponse(terminalApiResponse); + errorMessage = TerminalAPIUtil.getErrorMessageByPosErrorCondition(errorCondition); + } else { + // probably SaleToPOIRequest, that means terminal unreachable, return the response as error + errorMessage = "checkout.error.authorization.pos.configuration"; + } + return errorMessage; + } + + public static String getErrorMessageByPosErrorCondition(ErrorConditionType errorCondition) { + LOGGER.debug("getErrorMessageByPosErrorCondition: " + errorCondition); + + String errorMessage = "checkout.error.authorization.payment.error"; + + if (errorCondition != null) { + switch (errorCondition) { + case ABORTED: + case CANCEL: + errorMessage = "checkout.error.authorization.payment.cancelled"; + break; + case BUSY: + case IN_PROGRESS: + errorMessage = "checkout.error.authorization.pos.busy"; + break; + case INVALID_CARD: + case NOT_ALLOWED: + case PAYMENT_RESTRICTION: + errorMessage = "checkout.error.authorization.transaction.not.permitted"; + break; + case REFUSAL: + errorMessage = "checkout.error.authorization.payment.refused"; + break; + case DEVICE_OUT: + case MESSAGE_FORMAT: + case NOT_FOUND: + case UNAVAILABLE_DEVICE: + case UNAVAILABLE_SERVICE: + case UNREACHABLE_HOST: + errorMessage = "checkout.error.authorization.pos.configuration"; + break; + case WRONG_PIN: + errorMessage = "checkout.error.authorization.pos.pin"; + break; + default: + errorMessage = "checkout.error.authorization.payment.error"; + } + } + + return errorMessage; + } + +} diff --git a/adyenv6core/testsrc/com/adyen/v6/converters/PosPaymentResponseConverterTest.java b/adyenv6core/testsrc/com/adyen/v6/converters/PosPaymentResponseConverterTest.java new file mode 100644 index 000000000..46df81a02 --- /dev/null +++ b/adyenv6core/testsrc/com/adyen/v6/converters/PosPaymentResponseConverterTest.java @@ -0,0 +1,150 @@ +/* + * ###### + * ###### + * ############ ####( ###### #####. ###### ############ ############ + * ############# #####( ###### #####. ###### ############# ############# + * ###### #####( ###### #####. ###### ##### ###### ##### ###### + * ###### ###### #####( ###### #####. ###### ##### ##### ##### ###### + * ###### ###### #####( ###### #####. ###### ##### ##### ###### + * ############# ############# ############# ############# ##### ###### + * ############ ############ ############# ############ ##### ###### + * ###### + * ############# + * ############ + * + * Adyen Hybris Extension + * + * Copyright (c) 2019 Adyen B.V. + * This file is open source and available under the MIT license. + * See the LICENSE file for more info. + */ +package com.adyen.v6.converters; + +import com.adyen.model.checkout.PaymentsResponse; +import com.adyen.model.nexo.PaymentAcquirerData; +import com.adyen.model.nexo.PaymentResponse; +import com.adyen.model.nexo.PaymentResult; +import com.adyen.model.nexo.RepeatedMessageResponse; +import com.adyen.model.nexo.RepeatedResponseMessageBody; +import com.adyen.model.nexo.Response; +import com.adyen.model.nexo.SaleToPOIResponse; +import com.adyen.model.nexo.TransactionIdentification; +import com.adyen.model.nexo.TransactionStatusRequest; +import com.adyen.model.nexo.TransactionStatusResponse; +import de.hybris.bootstrap.annotations.UnitTest; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.mockito.Mock; + +import java.util.Arrays; +import java.util.Collection; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.mockito.Mockito.when; +import static org.mockito.MockitoAnnotations.initMocks; + +@UnitTest +@RunWith(Parameterized.class) +public class PosPaymentResponseConverterTest { + + @Parameterized.Parameters + public static Collection data() { + return Arrays.asList(new Object[][] { + { "tid=1234&expiryYear=2028&expiryDate=2%2f2028&cardHolderName=test&avsResult=0%20Unknown&authCode=1234", true, 6, "1234" }, + { "authCode=1234", true, 1, "1234" }, + { "", true, 0, null }, + { null, false, null, null } + }); + } + + private String additionalResponse; + private Boolean shouldExpectAdditionalData; + private Integer expectedAdditionalDataSize; + private String expectedAuthCode; + + private PosPaymentResponseConverter posPaymentResponseConverter; + + @Mock + private SaleToPOIResponse saleToPoiResponse; + + //Payment Response + @Mock + private PaymentResponse paymentResponse; + @Mock + private Response response; + @Mock + private PaymentResult paymentResult; + @Mock + private PaymentAcquirerData paymentAcquirerData; + @Mock + private TransactionIdentification acquirerTransactionId; + + //Status response + @Mock + TransactionStatusResponse transactionStatusResponse; + @Mock + RepeatedMessageResponse repeatedMessageResponse; + @Mock + RepeatedResponseMessageBody repeatedResponseMessageBody; + + public PosPaymentResponseConverterTest(String additionalResponse, Boolean shouldExpectAdditionalData, Integer expectedAdditionalDataSize, String expectedAuthCode) { + this.additionalResponse = additionalResponse; + this.shouldExpectAdditionalData = shouldExpectAdditionalData; + this.expectedAdditionalDataSize = expectedAdditionalDataSize; + this.expectedAuthCode = expectedAuthCode; + } + + @Before + public void setUp() { + initMocks(this); + posPaymentResponseConverter = new PosPaymentResponseConverter(); + + when(paymentResponse.getPaymentResult()).thenReturn(paymentResult); + when(paymentResult.getPaymentAcquirerData()).thenReturn(paymentAcquirerData); + when(paymentAcquirerData.getAcquirerTransactionID()).thenReturn(acquirerTransactionId); + when(acquirerTransactionId.getTransactionID()).thenReturn("psp"); + + when(paymentResponse.getResponse()).thenReturn(response); + when(response.getAdditionalResponse()).thenReturn(additionalResponse); + } + + @Test + public void testConverterForPaymentResponse() { + when(saleToPoiResponse.getPaymentResponse()).thenReturn(paymentResponse); + + PaymentsResponse paymentsResponse = posPaymentResponseConverter.convert(saleToPoiResponse); + + assertNotNull(paymentsResponse); + assertEquals("psp", paymentsResponse.getPspReference()); + assertEquals(shouldExpectAdditionalData, paymentsResponse.getAdditionalData() != null); + + if(shouldExpectAdditionalData) { + assertEquals((int) expectedAdditionalDataSize, paymentsResponse.getAdditionalData().size()); + assertEquals(expectedAuthCode, paymentsResponse.getAuthCode()); + } + } + + @Test + public void testConverterForStatusResponse() { + when(saleToPoiResponse.getPaymentResponse()).thenReturn(null); + + when(saleToPoiResponse.getTransactionStatusResponse()).thenReturn(transactionStatusResponse); + when(transactionStatusResponse.getRepeatedMessageResponse()).thenReturn(repeatedMessageResponse); + when(repeatedMessageResponse.getRepeatedResponseMessageBody()).thenReturn(repeatedResponseMessageBody); + when(repeatedResponseMessageBody.getPaymentResponse()).thenReturn(paymentResponse); + + PaymentsResponse paymentsResponse = posPaymentResponseConverter.convert(saleToPoiResponse); + + assertNotNull(paymentsResponse); + assertEquals("psp", paymentsResponse.getPspReference()); + assertEquals(shouldExpectAdditionalData, paymentsResponse.getAdditionalData() != null); + + if (shouldExpectAdditionalData) { + assertEquals((int) expectedAdditionalDataSize, paymentsResponse.getAdditionalData().size()); + assertEquals(expectedAuthCode, paymentsResponse.getAuthCode()); + } + } +} diff --git a/adyenv6core/testsrc/com/adyen/v6/facade/AdyenCheckoutFacadeTest.java b/adyenv6core/testsrc/com/adyen/v6/facade/AdyenCheckoutFacadeTest.java new file mode 100644 index 000000000..b0ca0c1e7 --- /dev/null +++ b/adyenv6core/testsrc/com/adyen/v6/facade/AdyenCheckoutFacadeTest.java @@ -0,0 +1,309 @@ +package com.adyen.v6.facade; + +import com.adyen.model.checkout.PaymentsResponse; +import com.adyen.model.nexo.DocumentQualifierType; +import com.adyen.model.nexo.ErrorConditionType; +import com.adyen.model.nexo.OutputContent; +import com.adyen.model.nexo.OutputText; +import com.adyen.model.nexo.PaymentReceipt; +import com.adyen.model.nexo.PaymentResponse; +import com.adyen.model.nexo.RepeatedMessageResponse; +import com.adyen.model.nexo.RepeatedResponseMessageBody; +import com.adyen.model.nexo.Response; +import com.adyen.model.nexo.ResultType; +import com.adyen.model.nexo.SaleToPOIResponse; +import com.adyen.model.nexo.TransactionStatusResponse; +import com.adyen.model.terminal.TerminalAPIResponse; +import com.adyen.v6.converters.PosPaymentResponseConverter; +import com.adyen.v6.exceptions.AdyenNonAuthorizedPaymentException; +import com.adyen.v6.facades.AdyenCheckoutFacade; +import com.adyen.v6.facades.DefaultAdyenCheckoutFacade; +import com.adyen.v6.factory.AdyenPaymentServiceFactory; +import com.adyen.v6.repository.OrderRepository; +import com.adyen.v6.service.AdyenOrderService; +import com.adyen.v6.service.AdyenPaymentService; +import com.adyen.v6.service.AdyenTransactionService; +import de.hybris.bootstrap.annotations.UnitTest; +import de.hybris.platform.commercefacades.order.CheckoutFacade; +import de.hybris.platform.commercefacades.order.data.CartData; +import de.hybris.platform.commercefacades.order.data.OrderData; +import de.hybris.platform.commerceservices.strategies.CheckoutCustomerStrategy; +import de.hybris.platform.core.model.order.CartModel; +import de.hybris.platform.core.model.order.OrderModel; +import de.hybris.platform.core.model.user.CustomerModel; +import de.hybris.platform.order.CartService; +import de.hybris.platform.order.InvalidCartException; +import de.hybris.platform.payment.model.PaymentTransactionModel; +import de.hybris.platform.store.BaseStoreModel; +import de.hybris.platform.store.services.BaseStoreService; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; + +import javax.servlet.http.HttpServletRequest; +import java.util.Arrays; +import java.util.Collections; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.atLeast; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@UnitTest +@RunWith(MockitoJUnitRunner.class) +public class AdyenCheckoutFacadeTest { + + private static final String SERVICE_ID = "serviceId"; + + @InjectMocks + DefaultAdyenCheckoutFacade adyenCheckoutFacade = new DefaultAdyenCheckoutFacade(); + + @Mock + AdyenPaymentServiceFactory adyenPaymentServiceFactory; + @Mock + AdyenPaymentService adyenPaymentService; + @Mock + BaseStoreService baseStoreService; + @Mock + CheckoutCustomerStrategy checkoutCustomerStrategy; + @Mock + PosPaymentResponseConverter posPaymentResponseConverter; + @Mock + CartService cartService; + @Mock + AdyenTransactionService adyenTransactionService; + @Mock + CheckoutFacade checkoutFacade; + @Mock + OrderRepository orderRepository; + @Mock + AdyenOrderService adyenOrderService; + + @Mock + HttpServletRequest request; + @Mock + CartData cartData; + @Mock + PaymentsResponse paymentsResponse; + @Mock + OrderData orderData; + + @Mock + TerminalAPIResponse terminalApiResponse; + @Mock + SaleToPOIResponse saleToPoiResponse; + //Payment Response + @Mock + PaymentResponse paymentResponse; + @Mock + Response response; + //Status response + @Mock + Response statusResponse; + @Mock + TransactionStatusResponse transactionStatusResponse; + @Mock + RepeatedMessageResponse repeatedMessageResponse; + @Mock + RepeatedResponseMessageBody repeatedResponseMessageBody; + //Receipts + @Mock + PaymentReceipt receipt; + @Mock + OutputContent outputContent; + @Mock + OutputText textWithNameValue; + @Mock + OutputText textJustName; + @Mock + OutputText textJustValue; + + @Before + public void setUp() { + when(checkoutCustomerStrategy.isAnonymousCheckout()).thenReturn(true); + when(checkoutCustomerStrategy.getCurrentUserForCheckout()).thenReturn(new CustomerModel()); + when(request.getAttribute("originalServiceId")).thenReturn(SERVICE_ID); + when(baseStoreService.getCurrentBaseStore()).thenReturn(new BaseStoreModel()); + when(adyenPaymentServiceFactory.createFromBaseStore(any())).thenReturn(adyenPaymentService); + } + + @Test + public void testInitiatePosPaymentSuccess() throws Exception { + when(adyenPaymentService.sendSyncPosPaymentRequest(eq(cartData), any(), eq(SERVICE_ID))).thenReturn(terminalApiResponse); + when(posPaymentResponseConverter.convert(saleToPoiResponse)).thenReturn(paymentsResponse); + //TerminalAPIUtil.getPaymentResult + when(terminalApiResponse.getSaleToPOIResponse()).thenReturn(saleToPoiResponse); + when(saleToPoiResponse.getPaymentResponse()).thenReturn(paymentResponse); + when(paymentResponse.getResponse()).thenReturn(response); + when(response.getResult()).thenReturn(ResultType.SUCCESS); + + setSuccessfulPaymentStubs(); + + OrderData orderDataResult = adyenCheckoutFacade.initiatePosPayment(request, cartData); + assertEquals(orderData, orderDataResult); + verify(adyenPaymentService, times(1)).sendSyncPosPaymentRequest(eq(cartData), any(), eq(SERVICE_ID)); + verify(posPaymentResponseConverter, times(1)).convert(saleToPoiResponse); + verify(adyenTransactionService, times(1)).authorizeOrderModel(any(), any(), any()); + verify(checkoutFacade, times(1)).placeOrder(); + } + + @Test + public void testInitiatePosPaymentFailure() throws Exception { + when(adyenPaymentService.sendSyncPosPaymentRequest(eq(cartData), any(), eq(SERVICE_ID))).thenReturn(terminalApiResponse); + when(posPaymentResponseConverter.convert(saleToPoiResponse)).thenReturn(paymentsResponse); + //TerminalAPIUtil.getPaymentResult + when(terminalApiResponse.getSaleToPOIResponse()).thenReturn(saleToPoiResponse); + when(saleToPoiResponse.getPaymentResponse()).thenReturn(paymentResponse); + when(paymentResponse.getResponse()).thenReturn(response); + when(response.getResult()).thenReturn(ResultType.FAILURE); + + try { + adyenCheckoutFacade.initiatePosPayment(request, cartData); + fail("Expected AdyenNonAuthorizedPaymentException"); + } catch (AdyenNonAuthorizedPaymentException e) { + assertEquals(terminalApiResponse, e.getTerminalApiResponse()); + } + } + + @Test + public void testInitiatePosPaymentBadRequest() throws Exception { + when(adyenPaymentService.sendSyncPosPaymentRequest(eq(cartData), any(), eq(SERVICE_ID))).thenReturn(terminalApiResponse); + when(posPaymentResponseConverter.convert(saleToPoiResponse)).thenReturn(paymentsResponse); + //TerminalAPIUtil.getPaymentResult + when(terminalApiResponse.getSaleToPOIResponse()).thenReturn(null); + + try { + adyenCheckoutFacade.initiatePosPayment(request, cartData); + fail("Expected AdyenNonAuthorizedPaymentException"); + } catch (AdyenNonAuthorizedPaymentException e) { + assertEquals(terminalApiResponse, e.getTerminalApiResponse()); + } + } + + @Test + public void testCheckPosPaymentStatusSuccessAndPaymentSuccess() throws Exception { + when(adyenPaymentService.sendSyncPosStatusRequest(cartData, SERVICE_ID)).thenReturn(terminalApiResponse); + when(posPaymentResponseConverter.convert(saleToPoiResponse)).thenReturn(paymentsResponse); + //TerminalAPIUtil.getStatusResult + when(terminalApiResponse.getSaleToPOIResponse()).thenReturn(saleToPoiResponse); + when(saleToPoiResponse.getTransactionStatusResponse()).thenReturn(transactionStatusResponse); + when(transactionStatusResponse.getResponse()).thenReturn(statusResponse); + when(statusResponse.getResult()).thenReturn(ResultType.SUCCESS); + //TerminalAPIUtil.getPaymentResult + when(saleToPoiResponse.getPaymentResponse()).thenReturn(null); + when(transactionStatusResponse.getRepeatedMessageResponse()).thenReturn(repeatedMessageResponse); + when(repeatedMessageResponse.getRepeatedResponseMessageBody()).thenReturn(repeatedResponseMessageBody); + when(repeatedResponseMessageBody.getPaymentResponse()).thenReturn(paymentResponse); + when(paymentResponse.getResponse()).thenReturn(response); + when(response.getResult()).thenReturn(ResultType.SUCCESS); + + setSuccessfulPaymentStubs(); + + OrderData orderDataResult = adyenCheckoutFacade.checkPosPaymentStatus(request, cartData); + assertEquals(orderData, orderDataResult); + verify(adyenPaymentService, times(1)).sendSyncPosStatusRequest(cartData, SERVICE_ID); + verify(posPaymentResponseConverter, times(1)).convert(saleToPoiResponse); + verify(adyenTransactionService, times(1)).authorizeOrderModel(any(), any(), any()); + verify(checkoutFacade, times(1)).placeOrder(); + } + + @Test + public void testCheckPosPaymentStatusSuccessButPaymentFailure() throws Exception { + when(adyenPaymentService.sendSyncPosStatusRequest(cartData, SERVICE_ID)).thenReturn(terminalApiResponse); + when(posPaymentResponseConverter.convert(saleToPoiResponse)).thenReturn(paymentsResponse); + //TerminalAPIUtil.getStatusResult + when(terminalApiResponse.getSaleToPOIResponse()).thenReturn(saleToPoiResponse); + when(saleToPoiResponse.getTransactionStatusResponse()).thenReturn(transactionStatusResponse); + when(transactionStatusResponse.getResponse()).thenReturn(statusResponse); + when(statusResponse.getResult()).thenReturn(ResultType.SUCCESS); + //TerminalAPIUtil.getPaymentResult + when(saleToPoiResponse.getPaymentResponse()).thenReturn(null); + when(transactionStatusResponse.getRepeatedMessageResponse()).thenReturn(repeatedMessageResponse); + when(repeatedMessageResponse.getRepeatedResponseMessageBody()).thenReturn(repeatedResponseMessageBody); + when(repeatedResponseMessageBody.getPaymentResponse()).thenReturn(paymentResponse); + when(paymentResponse.getResponse()).thenReturn(response); + when(response.getResult()).thenReturn(ResultType.FAILURE); + + try { + adyenCheckoutFacade.checkPosPaymentStatus(request, cartData); + fail("Expected AdyenNonAuthorizedPaymentException"); + } catch (AdyenNonAuthorizedPaymentException e) { + assertEquals(terminalApiResponse, e.getTerminalApiResponse()); + } + } + + @Test + public void testCheckPosPaymentStatusTimeout() throws Exception { + when(adyenPaymentService.sendSyncPosStatusRequest(cartData, SERVICE_ID)).thenReturn(terminalApiResponse); + when(posPaymentResponseConverter.convert(saleToPoiResponse)).thenReturn(paymentsResponse); + //TerminalAPIUtil.getStatusResult + when(terminalApiResponse.getSaleToPOIResponse()).thenReturn(saleToPoiResponse); + when(saleToPoiResponse.getTransactionStatusResponse()).thenReturn(transactionStatusResponse); + when(transactionStatusResponse.getResponse()).thenReturn(statusResponse); + when(statusResponse.getResult()).thenReturn(ResultType.FAILURE); + //TerminalAPIUtil.getErrorConditionForStatus + when(statusResponse.getErrorCondition()).thenReturn(ErrorConditionType.IN_PROGRESS); + //isPosTimedOut (will timeout after 10 seconds) + long processStartTime = System.currentTimeMillis(); + when(request.getAttribute("paymentStartTime")).thenReturn(processStartTime); + when(request.getAttribute("totalTimeout")).thenReturn(10); + + AdyenCheckoutFacade adyenCheckoutFacadeSpy = spy(adyenCheckoutFacade); + try { + adyenCheckoutFacadeSpy.checkPosPaymentStatus(request, cartData); + fail("Expected AdyenNonAuthorizedPaymentException"); + } catch (AdyenNonAuthorizedPaymentException e) { + assertEquals(terminalApiResponse, e.getTerminalApiResponse()); + verify(adyenCheckoutFacadeSpy, atLeast(2)).checkPosPaymentStatus(request, cartData); + } + } + + @Test + public void testCheckPosPaymentStatusError() throws Exception { + when(adyenPaymentService.sendSyncPosStatusRequest(cartData, SERVICE_ID)).thenReturn(terminalApiResponse); + when(posPaymentResponseConverter.convert(saleToPoiResponse)).thenReturn(paymentsResponse); + //TerminalAPIUtil.getStatusResult + when(terminalApiResponse.getSaleToPOIResponse()).thenReturn(saleToPoiResponse); + when(saleToPoiResponse.getTransactionStatusResponse()).thenReturn(transactionStatusResponse); + when(transactionStatusResponse.getResponse()).thenReturn(statusResponse); + when(statusResponse.getResult()).thenReturn(ResultType.FAILURE); + //TerminalAPIUtil.getErrorConditionForStatus + when(statusResponse.getErrorCondition()).thenReturn(ErrorConditionType.CANCEL); + + try { + adyenCheckoutFacade.checkPosPaymentStatus(request, cartData); + fail("Expected AdyenNonAuthorizedPaymentException"); + } catch (AdyenNonAuthorizedPaymentException e) { + assertEquals(terminalApiResponse, e.getTerminalApiResponse()); + } + } + + + private void setSuccessfulPaymentStubs() throws InvalidCartException { + //Receipt + when(paymentResponse.getPaymentReceipt()).thenReturn(Collections.singletonList(receipt)); + when(receipt.getDocumentQualifier()).thenReturn(DocumentQualifierType.CUSTOMER_RECEIPT); + when(receipt.getOutputContent()).thenReturn(outputContent); + when(outputContent.getOutputText()).thenReturn(Arrays.asList(textWithNameValue, textJustName, textJustValue)); + when(textWithNameValue.getText()).thenReturn("name=some_name&value=some_value"); + when(textJustName.getText()).thenReturn("name=some_name"); + when(textJustValue.getText()).thenReturn("value=some_value"); + //createAuthorizedOrder + when(cartService.getSessionCart()).thenReturn(new CartModel()); + when(adyenTransactionService.authorizeOrderModel(any(), any(), any())).thenReturn(new PaymentTransactionModel()); + //createOrderFromPaymentsResponse + when(checkoutFacade.placeOrder()).thenReturn(orderData); + when(orderRepository.getOrderModel(any())).thenReturn(new OrderModel()); + //updateOrder + doNothing().when(adyenOrderService).updateOrderFromPaymentsResponse(any(), any()); + } +} diff --git a/adyenv6core/testsrc/com/adyen/v6/factory/AdyenRequestFactoryTest.java b/adyenv6core/testsrc/com/adyen/v6/factory/AdyenRequestFactoryTest.java index 44c957cd2..48ad1b8e7 100644 --- a/adyenv6core/testsrc/com/adyen/v6/factory/AdyenRequestFactoryTest.java +++ b/adyenv6core/testsrc/com/adyen/v6/factory/AdyenRequestFactoryTest.java @@ -21,6 +21,14 @@ package com.adyen.v6.factory; import java.math.BigDecimal; + +import com.adyen.model.nexo.AmountsReq; +import com.adyen.model.nexo.MessageCategoryType; +import com.adyen.model.nexo.MessageHeader; +import com.adyen.model.nexo.MessageReference; +import com.adyen.model.nexo.SaleData; +import com.adyen.model.nexo.TransactionStatusRequest; +import com.adyen.model.terminal.TerminalAPIRequest; import org.apache.commons.configuration.BaseConfiguration; import org.apache.commons.configuration.Configuration; import org.junit.Before; @@ -42,10 +50,13 @@ import de.hybris.platform.commercefacades.user.data.CountryData; import de.hybris.platform.core.model.user.CustomerModel; import de.hybris.platform.servicelayer.config.ConfigurationService; + import static com.adyen.v6.constants.Adyenv6coreConstants.PAYMENT_METHOD_CC; import static com.adyen.v6.constants.Adyenv6coreConstants.PAYMENT_METHOD_ONECLICK; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -53,6 +64,29 @@ @UnitTest @RunWith(MockitoJUnitRunner.class) public class AdyenRequestFactoryTest { + + private static final String MERCHANT_ACCOUNT = "merchantAccount"; + private static final String BILLING_TOWN = "billingTown"; + private static final String BILLING_COUNTRY = "GR"; + private static final String DELIVERY_TOWN = "deliveryTown"; + private static final String DELIVERY_COUNTRY = "NL"; + private static final String CUSTOMER_ID = "uuid"; + private static final String CUSTOMER_EMAIL = "email"; + private static final String CART_CODE = "code"; + private static final String STORE_NAME = "StoreName"; + private static final String CURRENCY = "EUR"; + private static final String AMOUNT = "12.34"; + private static final String RECURRING_REFERENCE = "recurring_reference"; + //Request + private static final String ACCEPT_HEADER = "Accept"; + private static final String USER_AGENT_HEADER = "User-Agent"; + private static final String REMOTE_ADDRESS = "1.2.3.4"; + private static final String REQUEST_URL = "https://localhost:9002/electronics/en/checkout/multi/adyen/summary/placeOrder"; + private static final String REQUEST_URI = "/electronics/en/checkout/multi/adyen/summary/placeOrder"; + //POS + private static final String SERVICE_ID = "serviceId"; + private static final String TERMINAL_ID = "V400m-123456789"; + private AdyenRequestFactory adyenRequestFactory; @Mock @@ -86,29 +120,34 @@ public class AdyenRequestFactoryTest { public void setUp() { adyenRequestFactory = new AdyenRequestFactory(); PriceData priceData = new PriceData(); - priceData.setValue(new BigDecimal("12.34")); - priceData.setCurrencyIso("EUR"); + priceData.setValue(new BigDecimal(AMOUNT)); + priceData.setCurrencyIso(CURRENCY); when(cartDataMock.getTotalPrice()).thenReturn(priceData); - when(cartDataMock.getCode()).thenReturn("code"); + when(cartDataMock.getCode()).thenReturn(CART_CODE); when(cartDataMock.getDeliveryAddress()).thenReturn(deliveryAddressMock); when(cartDataMock.getPaymentInfo()).thenReturn(paymentInfoMock); when(cartDataMock.getAdyenPaymentMethod()).thenReturn(PAYMENT_METHOD_CC); + when(cartDataMock.getStore()).thenReturn(STORE_NAME); + when(cartDataMock.getAdyenTerminalId()).thenReturn(TERMINAL_ID); when(paymentInfoMock.getBillingAddress()).thenReturn(billingAddressMock); - when(deliveryAddressMock.getTown()).thenReturn("deliveryTown"); - when(billingAddressMock.getTown()).thenReturn("billingTown"); + when(deliveryAddressMock.getTown()).thenReturn(DELIVERY_TOWN); + when(billingAddressMock.getTown()).thenReturn(BILLING_TOWN); when(deliveryAddressMock.getCountry()).thenReturn(deliveryCountryDataMock); when(billingAddressMock.getCountry()).thenReturn(billingCountryDataMock); - when(deliveryCountryDataMock.getIsocode()).thenReturn("NL"); - when(billingCountryDataMock.getIsocode()).thenReturn("GR"); + when(deliveryCountryDataMock.getIsocode()).thenReturn(DELIVERY_COUNTRY); + when(billingCountryDataMock.getIsocode()).thenReturn(BILLING_COUNTRY); - when(customerModelMock.getCustomerID()).thenReturn("uuid"); - when(customerModelMock.getContactEmail()).thenReturn("email"); + when(customerModelMock.getCustomerID()).thenReturn(CUSTOMER_ID); - when(requestMock.getHeader("User-Agent")).thenReturn("User-Agent"); - when(requestMock.getHeader("Accept")).thenReturn("Accept"); - when(requestMock.getRemoteAddr()).thenReturn("1.2.3.4"); + when(customerModelMock.getContactEmail()).thenReturn(CUSTOMER_EMAIL); + + when(requestMock.getHeader(USER_AGENT_HEADER)).thenReturn(USER_AGENT_HEADER); + when(requestMock.getHeader(ACCEPT_HEADER)).thenReturn(ACCEPT_HEADER); + when(requestMock.getRemoteAddr()).thenReturn(REMOTE_ADDRESS); + when(requestMock.getRequestURL()).thenReturn(new StringBuffer(REQUEST_URL)); + when(requestMock.getRequestURI()).thenReturn(REQUEST_URI); Configuration configurationMock = mock(BaseConfiguration.class); when(configurationMock.getString(any(String.class))).thenReturn("dummy"); @@ -122,19 +161,19 @@ public void testAuthorise() throws Exception { PaymentsRequest paymentsRequest; //Test anonymous - paymentsRequest = adyenRequestFactory.createPaymentsRequest("merchantAccount", cartDataMock, new RequestInfo(requestMock), null, RecurringContractMode.NONE); + paymentsRequest = adyenRequestFactory.createPaymentsRequest(MERCHANT_ACCOUNT, cartDataMock, new RequestInfo(requestMock), null, RecurringContractMode.NONE); //use delivery/billing address from cart - assertEquals("deliveryTown", paymentsRequest.getDeliveryAddress().getCity()); - assertEquals("NL", paymentsRequest.getDeliveryAddress().getCountry()); - assertEquals("billingTown", paymentsRequest.getBillingAddress().getCity()); - assertEquals("GR", paymentsRequest.getBillingAddress().getCountry()); + assertEquals(DELIVERY_TOWN, paymentsRequest.getDeliveryAddress().getCity()); + assertEquals(DELIVERY_COUNTRY, paymentsRequest.getDeliveryAddress().getCountry()); + assertEquals(BILLING_TOWN, paymentsRequest.getBillingAddress().getCity()); + assertEquals(BILLING_COUNTRY, paymentsRequest.getBillingAddress().getCountry()); assertNull(paymentsRequest.getShopperReference()); - assertEquals("User-Agent", paymentsRequest.getBrowserInfo().getUserAgent()); - assertEquals("Accept", paymentsRequest.getBrowserInfo().getAcceptHeader()); - assertEquals("1.2.3.4", paymentsRequest.getShopperIP()); + assertEquals(USER_AGENT_HEADER, paymentsRequest.getBrowserInfo().getUserAgent()); + assertEquals(ACCEPT_HEADER, paymentsRequest.getBrowserInfo().getAcceptHeader()); + assertEquals(REMOTE_ADDRESS, paymentsRequest.getShopperIP()); //Test recurring contract when remember-me is NOT set when(cartDataMock.getAdyenRememberTheseDetails()).thenReturn(false); @@ -154,17 +193,17 @@ public void testAuthorise() throws Exception { //When a store card is selected, send the reference and include the recurring contract when(cartDataMock.getAdyenPaymentMethod()).thenReturn(PAYMENT_METHOD_ONECLICK); - when(cartDataMock.getAdyenSelectedReference()).thenReturn("recurring_reference"); + when(cartDataMock.getAdyenSelectedReference()).thenReturn(RECURRING_REFERENCE); when(cartDataMock.getAdyenRememberTheseDetails()).thenReturn(false); - paymentsRequest = adyenRequestFactory.createPaymentsRequest("merchantAccount", cartDataMock, new RequestInfo(requestMock), customerModelMock, null); + paymentsRequest = adyenRequestFactory.createPaymentsRequest(MERCHANT_ACCOUNT, cartDataMock, new RequestInfo(requestMock), customerModelMock, null); DefaultPaymentMethodDetails paymentMethodDetails = (DefaultPaymentMethodDetails) paymentsRequest.getPaymentMethod(); - assertEquals("recurring_reference", paymentMethodDetails.getRecurringDetailReference()); + assertEquals(RECURRING_REFERENCE, paymentMethodDetails.getRecurringDetailReference()); } private void testRecurringOption(final RecurringContractMode recurringContractModeSetting, final Recurring.ContractEnum expectedRecurringContractMode) { - PaymentRequest paymentRequest = adyenRequestFactory.createAuthorizationRequest("merchantAccount", cartDataMock, requestMock, customerModelMock, recurringContractModeSetting); + PaymentRequest paymentRequest = adyenRequestFactory.createAuthorizationRequest(MERCHANT_ACCOUNT, cartDataMock, requestMock, customerModelMock, recurringContractModeSetting); if (expectedRecurringContractMode == null) { assertNull(paymentRequest.getRecurring()); @@ -173,7 +212,78 @@ private void testRecurringOption(final RecurringContractMode recurringContractMo } //when customer is set, shopperReference and Email should be set as well - assertEquals("uuid", paymentRequest.getShopperReference()); - assertEquals("email", paymentRequest.getShopperEmail()); + assertEquals(CUSTOMER_ID, paymentRequest.getShopperReference()); + assertEquals(CUSTOMER_EMAIL, paymentRequest.getShopperEmail()); + } + + @Test + public void testTerminalApiPaymentRequestAnonymous() throws Exception { + TerminalAPIRequest terminalApiRequest = adyenRequestFactory.createTerminalAPIRequest(cartDataMock, null, null, SERVICE_ID); + + validateTerminalApiPaymentRequest(terminalApiRequest); + } + + @Test + public void testTerminalApiPaymentRequestWithRecurring() throws Exception { + TerminalAPIRequest terminalApiRequest = adyenRequestFactory.createTerminalAPIRequest(cartDataMock, customerModelMock, RecurringContractMode.ONECLICK_RECURRING, SERVICE_ID); + + validateTerminalApiPaymentRequest(terminalApiRequest); + + assertNotNull(terminalApiRequest.getSaleToPOIRequest().getPaymentRequest().getSaleData().getSaleToAcquirerData()); + + String saleToAcquirerData = terminalApiRequest.getSaleToPOIRequest().getPaymentRequest().getSaleData().getSaleToAcquirerData(); + assertTrue(saleToAcquirerData.contains("recurringContract=" + Recurring.ContractEnum.ONECLICK_RECURRING.toString())); + assertTrue(saleToAcquirerData.contains("shopperEmail=" + CUSTOMER_EMAIL)); + assertTrue(saleToAcquirerData.contains("shopperReference=" + CUSTOMER_ID)); + } + + private void validateTerminalApiPaymentRequest(TerminalAPIRequest terminalApiRequest) { + assertNotNull(terminalApiRequest); + assertNotNull(terminalApiRequest.getSaleToPOIRequest()); + assertNotNull(terminalApiRequest.getSaleToPOIRequest().getMessageHeader()); + + MessageHeader messageHeader = terminalApiRequest.getSaleToPOIRequest().getMessageHeader(); + assertNotNull(messageHeader.getServiceID()); + assertEquals(STORE_NAME, messageHeader.getSaleID()); + assertEquals(TERMINAL_ID, messageHeader.getPOIID()); + + assertNotNull(terminalApiRequest.getSaleToPOIRequest().getPaymentRequest()); + assertNotNull(terminalApiRequest.getSaleToPOIRequest().getPaymentRequest().getSaleData()); + + SaleData saleData = terminalApiRequest.getSaleToPOIRequest().getPaymentRequest().getSaleData(); + assertNotNull(saleData.getSaleTransactionID()); + assertEquals(CART_CODE, saleData.getSaleTransactionID().getTransactionID()); + + assertNotNull(terminalApiRequest.getSaleToPOIRequest().getPaymentRequest().getPaymentTransaction()); + assertNotNull(terminalApiRequest.getSaleToPOIRequest().getPaymentRequest().getPaymentTransaction().getAmountsReq()); + + AmountsReq amountsReq = terminalApiRequest.getSaleToPOIRequest().getPaymentRequest().getPaymentTransaction().getAmountsReq(); + assertEquals(CURRENCY, amountsReq.getCurrency()); + assertEquals(AMOUNT, amountsReq.getRequestedAmount().toString()); + } + + @Test + public void testTerminalApiStatusRequest() throws Exception { + TerminalAPIRequest terminalApiRequest = adyenRequestFactory.createTerminalAPIRequestForStatus(cartDataMock, SERVICE_ID); + + assertNotNull(terminalApiRequest); + assertNotNull(terminalApiRequest.getSaleToPOIRequest()); + assertNotNull(terminalApiRequest.getSaleToPOIRequest().getMessageHeader()); + + MessageHeader messageHeader = terminalApiRequest.getSaleToPOIRequest().getMessageHeader(); + assertNotNull(messageHeader.getServiceID()); + assertEquals(STORE_NAME, messageHeader.getSaleID()); + assertEquals(TERMINAL_ID, messageHeader.getPOIID()); + + assertNotNull(terminalApiRequest.getSaleToPOIRequest().getTransactionStatusRequest()); + TransactionStatusRequest transactionStatusRequest = terminalApiRequest.getSaleToPOIRequest().getTransactionStatusRequest(); + + assertNotNull(transactionStatusRequest.getMessageReference()); + assertNotNull(transactionStatusRequest.getMessageReference().getMessageCategory()); + assertEquals(MessageCategoryType.PAYMENT, transactionStatusRequest.getMessageReference().getMessageCategory()); + assertNotNull(transactionStatusRequest.getMessageReference().getSaleID()); + assertEquals(STORE_NAME, transactionStatusRequest.getMessageReference().getSaleID()); + assertNotNull(transactionStatusRequest.getMessageReference().getServiceID()); + assertEquals(SERVICE_ID, transactionStatusRequest.getMessageReference().getServiceID()); } } diff --git a/adyenv6core/testsrc/com/adyen/v6/util/TerminalAPIUtilTest.java b/adyenv6core/testsrc/com/adyen/v6/util/TerminalAPIUtilTest.java new file mode 100644 index 000000000..402580492 --- /dev/null +++ b/adyenv6core/testsrc/com/adyen/v6/util/TerminalAPIUtilTest.java @@ -0,0 +1,120 @@ +package com.adyen.v6.util; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.runners.MockitoJUnitRunner; +import com.adyen.model.nexo.ErrorConditionType; +import com.adyen.model.nexo.ResultType; +import com.adyen.model.terminal.TerminalAPIResponse; +import com.adyen.terminal.serialization.TerminalAPIGsonBuilder; +import com.google.gson.reflect.TypeToken; +import de.hybris.bootstrap.annotations.UnitTest; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +@UnitTest +@RunWith(MockitoJUnitRunner.class) +public class TerminalAPIUtilTest { + + public static String TEST_RESPONSE_DIR ="resources/test/"; + @Test + public void testGetPaymentResultFromStatusOrPaymentResponseSuccess() throws IOException { + TerminalAPIResponse terminalAPIResponse = createResponseFromFile(TEST_RESPONSE_DIR+"SaleToPOIResponse.json"); + ResultType paymentResult = TerminalAPIUtil.getPaymentResultFromStatusOrPaymentResponse(terminalAPIResponse); + assertNotNull(paymentResult); + assertEquals(ResultType.SUCCESS, paymentResult); + } + + @Test + public void testGetPaymentResultFromStatusOrPaymentResponseFailure() throws IOException { + TerminalAPIResponse terminalAPIResponse = createResponseFromFile(TEST_RESPONSE_DIR+"SaleToPOIResponseInProgress.json"); + ResultType statusResult = TerminalAPIUtil.getPaymentResultFromStatusOrPaymentResponse(terminalAPIResponse); + assertNotNull(statusResult); + assertEquals(ResultType.FAILURE, statusResult); + } + + @Test + public void testGetErrorConditionForPaymentResponse() throws IOException { + TerminalAPIResponse terminalAPIResponse = createResponseFromFile(TEST_RESPONSE_DIR+"SaleToPOIResponsePaymentCancelled.json"); + assertNotNull(terminalAPIResponse); + ErrorConditionType errorCondition = TerminalAPIUtil.getErrorConditionForPaymentResponse(terminalAPIResponse); + assertNotNull(errorCondition); + assertEquals(ErrorConditionType.CANCEL, errorCondition); + } + + @Test + public void testGetErrorConditionForPaymentFromStatusResponse() throws IOException { + TerminalAPIResponse terminalAPIResponse = createResponseFromFile(TEST_RESPONSE_DIR+"SaleToPOIResponseStatusCancelled.json"); + assertNotNull(terminalAPIResponse); + ErrorConditionType errorCondition = TerminalAPIUtil.getErrorConditionForPaymentFromStatusResponse(terminalAPIResponse); + assertNotNull(errorCondition); + assertEquals(ErrorConditionType.CANCEL, errorCondition); + } + + @Test + public void testGetErrorConditionForStatusFromStatusResponse() throws IOException { + TerminalAPIResponse terminalAPIResponse = createResponseFromFile(TEST_RESPONSE_DIR+"SaleToPOIResponseInProgress.json"); + assertNotNull(terminalAPIResponse); + ErrorConditionType errorCondition = TerminalAPIUtil.getErrorConditionForStatusFromStatusResponse(terminalAPIResponse); + assertNotNull(errorCondition); + assertEquals(ErrorConditionType.IN_PROGRESS, errorCondition); + } + + @Test + public void testGetReceiptFromPaymentResponseWithoutReceipt() throws IOException { + TerminalAPIResponse terminalAPIResponse = createResponseFromFile(TEST_RESPONSE_DIR+"SaleToPOIResponse.json"); + String receipt = TerminalAPIUtil.getReceiptFromPaymentResponse(terminalAPIResponse); + assertNotNull(receipt); + assertEquals("", receipt); + } + + @Test + public void testGetReceiptFromPaymentResponse() throws IOException { + TerminalAPIResponse terminalAPIResponse = createResponseFromFile(TEST_RESPONSE_DIR+"SaleToPOIResponsePaymentCancelled.json"); + String receipt = TerminalAPIUtil.getReceiptFromPaymentResponse(terminalAPIResponse); + assertNotNull(receipt); + assertEquals("", receipt); + } + + @Test + public void testGetReceiptFromStatusResponse() throws IOException { + TerminalAPIResponse terminalAPIResponse = createResponseFromFile(TEST_RESPONSE_DIR+"SaleToPOIResponseStatusResponseWithReceipt.json"); + String receipt = TerminalAPIUtil.getReceiptFromStatusResponse(terminalAPIResponse); + assertNotNull(receipt); + assertNotEquals("", receipt); + } + + @Test + public void testFormatTerminalAPIReceipt() throws IOException { + TerminalAPIResponse terminalAPIResponse = createResponseFromFile(TEST_RESPONSE_DIR+"SaleToPOIResponseStatusResponseWithReceipt.json"); + String formattedReceipt = TerminalAPIUtil.formatTerminalAPIReceipt(terminalAPIResponse.getSaleToPOIResponse() + .getTransactionStatusResponse() + .getRepeatedMessageResponse() + .getRepeatedResponseMessageBody() + .getPaymentResponse() + .getPaymentReceipt()); + assertNotNull(formattedReceipt); + assertTrue(formattedReceipt.contains("table class")); + } + + @Test + public void testGetErrorMessageForNonAuthorizedPosPayment() throws IOException { + TerminalAPIResponse terminalAPIResponse = createResponseFromFile(TEST_RESPONSE_DIR+"SaleToPOIResponsePaymentCancelled.json"); + assertNotNull(terminalAPIResponse); + String errorMessage = TerminalAPIUtil.getErrorMessageForNonAuthorizedPosPayment(terminalAPIResponse); + assertNotNull(errorMessage); + assertEquals("checkout.error.authorization.payment.cancelled", errorMessage); + } + + private static TerminalAPIResponse createResponseFromFile(String fileName) throws IOException { + String json = new String(Files.readAllBytes(Paths.get(fileName)), StandardCharsets.UTF_8); + return TerminalAPIGsonBuilder.create().fromJson(json, new TypeToken() { + }.getType()); + } +} diff --git a/adyenv6fulfilmentprocess/resources/adyenv6fulfilmentprocess.build.number b/adyenv6fulfilmentprocess/resources/adyenv6fulfilmentprocess.build.number index 4705985ac..bc07049ea 100644 --- a/adyenv6fulfilmentprocess/resources/adyenv6fulfilmentprocess.build.number +++ b/adyenv6fulfilmentprocess/resources/adyenv6fulfilmentprocess.build.number @@ -6,5 +6,5 @@ group.id=com.adyen.v6 module.name=platform-module name=adyenv6fulfilmentprocess vendor=adyen -version=6.1.1 -version.api=6.1.1 +version=6.2.0 +version.api=6.2.0 diff --git a/adyenv6notification/resources/adyenv6notification.build.number b/adyenv6notification/resources/adyenv6notification.build.number index 2390d6ae2..3ebf081b6 100644 --- a/adyenv6notification/resources/adyenv6notification.build.number +++ b/adyenv6notification/resources/adyenv6notification.build.number @@ -5,5 +5,5 @@ description=adyenv6notification name=adyenv6notification releasedate=20170803 1323 vendor=adyen -version=6.1.1 -version.api=6.1.1 +version=6.2.0 +version.api=6.2.0 diff --git a/adyenv6ordermanagement/resources/adyenv6ordermanagement.build.number b/adyenv6ordermanagement/resources/adyenv6ordermanagement.build.number index 4e0f31e80..3a85f2cfd 100644 --- a/adyenv6ordermanagement/resources/adyenv6ordermanagement.build.number +++ b/adyenv6ordermanagement/resources/adyenv6ordermanagement.build.number @@ -7,5 +7,5 @@ module.name=platform-module name=adyenv6ordermanagement releasedate=20170509 1754 vendor=adyen -version=6.1.1 -version.api=6.1.1 +version=6.2.0 +version.api=6.2.0
Please wait while your payment is processed. Do not click back or refresh the page.