From 6ed56e50e5a74c7e7fcc6450cb939f3fc3fb0151 Mon Sep 17 00:00:00 2001 From: Can Demiralp Date: Wed, 2 Oct 2024 16:06:47 +0200 Subject: [PATCH 1/5] [ECP-9352] Update csp allowed domains --- etc/csp_whitelist.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/etc/csp_whitelist.xml b/etc/csp_whitelist.xml index 1de54c94f..ed65aa1fe 100644 --- a/etc/csp_whitelist.xml +++ b/etc/csp_whitelist.xml @@ -4,6 +4,7 @@ *.adyen.com + *.payments-amazon.com @@ -14,16 +15,20 @@ *.adyen.com + *.payments-amazon.com + *.media-amazon.com *.adyen.com + payments-eu.amazon.com *.adyen.com + payments.amazon.de From 8b16ac02ec9d3bbc3f8695fe403c185d61f3b77a Mon Sep 17 00:00:00 2001 From: Can Demiralp Date: Thu, 3 Oct 2024 16:39:41 +0200 Subject: [PATCH 2/5] [ECP-9352] Update returnUrl and cancelUrl --- .../method-renderer/adyen-amazonpay-method.js | 30 +++++++++++++------ 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/view/frontend/web/js/view/payment/method-renderer/adyen-amazonpay-method.js b/view/frontend/web/js/view/payment/method-renderer/adyen-amazonpay-method.js index b8fe8d38d..93ce6cb5d 100644 --- a/view/frontend/web/js/view/payment/method-renderer/adyen-amazonpay-method.js +++ b/view/frontend/web/js/view/payment/method-renderer/adyen-amazonpay-method.js @@ -11,12 +11,14 @@ define( [ 'Magento_Checkout/js/model/quote', 'Adyen_Payment/js/view/payment/method-renderer/adyen-pm-method', - 'Adyen_Payment/js/model/adyen-checkout' + 'Adyen_Payment/js/model/adyen-checkout', + 'mage/url' ], function( quote, adyenPaymentMethod, - adyenCheckout + adyenCheckout, + urlBuilder ) { const amazonSessionKey = 'amazonCheckoutSessionId'; return adyenPaymentMethod.extend({ @@ -29,6 +31,12 @@ define( let formattedShippingAddress = {}; let formattedBillingAddress = {}; let baseComponentConfiguration = this._super(); + + baseComponentConfiguration = Object.assign( + baseComponentConfiguration, + paymentMethodsExtraInfo[paymentMethod.type].configuration + ); + if (!quote.isVirtual() && !!quote.shippingAddress()) { formattedShippingAddress = self.getFormattedAddress(quote.shippingAddress()); } @@ -36,7 +44,7 @@ define( if (!!quote.billingAddress()) { formattedBillingAddress = self.getFormattedAddress(quote.billingAddress()); } - baseComponentConfiguration.showPayButton = true; + baseComponentConfiguration.onClick = function(resolve,reject) { if (self.validate()) { resolve(); @@ -44,12 +52,6 @@ define( reject(); } } - baseComponentConfiguration = Object.assign(baseComponentConfiguration, paymentMethodsExtraInfo[paymentMethod.type].configuration); - baseComponentConfiguration.productType = 'PayAndShip'; - baseComponentConfiguration.checkoutMode = 'ProcessOrder'; - let url = new URL(location.href); - url.searchParams.delete(amazonSessionKey); - baseComponentConfiguration.returnUrl = url.href; baseComponentConfiguration.onSubmit = async (state, amazonPayComponent) => { try { await self.handleOnSubmit(state.data, amazonPayComponent); @@ -58,6 +60,15 @@ define( } }; + baseComponentConfiguration.productType = 'PayAndShip'; + baseComponentConfiguration.checkoutMode = 'ProcessOrder'; + baseComponentConfiguration.showPayButton = true; + + // Redirect shoppers to the cart page if they cancel the payment on Amazon Pay hosted page. + baseComponentConfiguration.cancelUrl = urlBuilder.build('checkout/cart'); + // Redirect shoppers to the checkout if they complete the payment on Amazon Pay hosted page. + baseComponentConfiguration.returnUrl = urlBuilder.build('checkout/#payment'); + if (formattedShippingAddress && formattedShippingAddress.telephone) { baseComponentConfiguration.addressDetails = { @@ -92,6 +103,7 @@ define( baseComponentConfiguration.addressDetails.stateOrRegion = quote.billingAddress().regionCode } } + return baseComponentConfiguration; }, From 59bae1f0a45077d915bc48cdcde50ad3914f56d8 Mon Sep 17 00:00:00 2001 From: Can Demiralp Date: Fri, 11 Oct 2024 09:41:07 -0400 Subject: [PATCH 3/5] [ECP-9352] Add Amazon Pay assets to CSP allow list --- etc/csp_whitelist.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/etc/csp_whitelist.xml b/etc/csp_whitelist.xml index ed65aa1fe..6e1bb7674 100644 --- a/etc/csp_whitelist.xml +++ b/etc/csp_whitelist.xml @@ -28,7 +28,7 @@ *.adyen.com - payments.amazon.de + *.amazon.de From c853fac87ef71a8f432094f97e26f1a8eef1af39 Mon Sep 17 00:00:00 2001 From: Can Demiralp Date: Fri, 11 Oct 2024 09:42:30 -0400 Subject: [PATCH 4/5] [ECP-9352] Implement handleOnError and handleOnFailure callbacks --- view/frontend/web/js/model/adyen-checkout.js | 11 ++++++-- .../method-renderer/adyen-amazonpay-method.js | 4 +++ .../method-renderer/adyen-pm-method.js | 25 +++++++++++++------ 3 files changed, 30 insertions(+), 10 deletions(-) diff --git a/view/frontend/web/js/model/adyen-checkout.js b/view/frontend/web/js/model/adyen-checkout.js index c64675483..3fbdcc723 100644 --- a/view/frontend/web/js/model/adyen-checkout.js +++ b/view/frontend/web/js/model/adyen-checkout.js @@ -19,7 +19,13 @@ define( ) { 'use strict'; return { - buildCheckoutComponent: function (paymentMethodsResponse, handleOnAdditionalDetails, handleOnCancel = undefined, handleOnSubmit = undefined) { + buildCheckoutComponent: function ( + paymentMethodsResponse, + handleOnAdditionalDetails, + handleOnCancel = undefined, + handleOnSubmit = undefined, + handleOnError = undefined + ) { if (!!paymentMethodsResponse.paymentMethodsResponse) { return AdyenCheckout({ locale: adyenConfiguration.getLocale(), @@ -28,7 +34,8 @@ define( paymentMethodsResponse: paymentMethodsResponse.paymentMethodsResponse, onAdditionalDetails: handleOnAdditionalDetails, onCancel: handleOnCancel, - onSubmit: handleOnSubmit + onSubmit: handleOnSubmit, + onError: handleOnError } ); } else { diff --git a/view/frontend/web/js/view/payment/method-renderer/adyen-amazonpay-method.js b/view/frontend/web/js/view/payment/method-renderer/adyen-amazonpay-method.js index 93ce6cb5d..1624f813e 100644 --- a/view/frontend/web/js/view/payment/method-renderer/adyen-amazonpay-method.js +++ b/view/frontend/web/js/view/payment/method-renderer/adyen-amazonpay-method.js @@ -139,6 +139,10 @@ define( } else{ this._super(); } + }, + + handleOnFailure: function (response, component) { + debugger; } }) } diff --git a/view/frontend/web/js/view/payment/method-renderer/adyen-pm-method.js b/view/frontend/web/js/view/payment/method-renderer/adyen-pm-method.js index e380a6413..b972ba33c 100755 --- a/view/frontend/web/js/view/payment/method-renderer/adyen-pm-method.js +++ b/view/frontend/web/js/view/payment/method-renderer/adyen-pm-method.js @@ -87,7 +87,8 @@ define( paymentMethodsResponse, this.handleOnAdditionalDetails.bind(this), this.handleOnCancel.bind(this), - this.handleOnSubmit.bind(this) + this.handleOnSubmit.bind(this), + this.handleOnError.bind(this) ); if (!!this.checkoutComponent) { @@ -179,6 +180,21 @@ define( self.isPlaceOrderAllowed(true); }); }, + + handleOnError: function (error, component) { + /* + * Passing false as the response to hide the actual error message from the shopper for security. + * This will show a generic error message instead of the actual error message. + */ + this.handleOnFailure(false, component); + }, + + handleOnFailure: function(response, component) { + this.isPlaceOrderAllowed(true); + fullScreenLoader.stopLoader(); + errorProcessor.process(response, this.currentMessageContainer); + }, + renderCheckoutComponent: function() { let methodCode = this.getMethodCode(); @@ -382,13 +398,6 @@ define( } }, - - handleOnFailure: function(response, component) { - this.isPlaceOrderAllowed(true); - fullScreenLoader.stopLoader(); - errorProcessor.process(response, this.currentMessageContainer); - }, - /** * This method is a workaround to close the modal in the right way and reconstruct the ActionModal. * This will solve issues when you cancel the 3DS2 challenge and retry the payment From 6a6cc2af464270c88678d67713d9e7ffe9519538 Mon Sep 17 00:00:00 2001 From: Can Demiralp Date: Tue, 15 Oct 2024 10:22:51 -0500 Subject: [PATCH 5/5] [ECP-9352] Update error handling --- .../method-renderer/adyen-amazonpay-method.js | 81 ++++++++++++------- .../method-renderer/adyen-pm-method.js | 7 +- 2 files changed, 53 insertions(+), 35 deletions(-) diff --git a/view/frontend/web/js/view/payment/method-renderer/adyen-amazonpay-method.js b/view/frontend/web/js/view/payment/method-renderer/adyen-amazonpay-method.js index 1624f813e..3111c8b50 100644 --- a/view/frontend/web/js/view/payment/method-renderer/adyen-amazonpay-method.js +++ b/view/frontend/web/js/view/payment/method-renderer/adyen-amazonpay-method.js @@ -12,20 +12,21 @@ define( 'Magento_Checkout/js/model/quote', 'Adyen_Payment/js/view/payment/method-renderer/adyen-pm-method', 'Adyen_Payment/js/model/adyen-checkout', + 'Adyen_Payment/js/model/adyen-payment-service', 'mage/url' ], function( quote, adyenPaymentMethod, adyenCheckout, + adyenPaymentService, urlBuilder ) { const amazonSessionKey = 'amazonCheckoutSessionId'; return adyenPaymentMethod.extend({ placeOrderButtonVisible: false, - initialize: function () { - this._super(); - }, + amazonPayComponent: null, + buildComponentConfiguration: function (paymentMethod, paymentMethodsExtraInfo) { let self = this; let formattedShippingAddress = {}; @@ -52,13 +53,6 @@ define( reject(); } } - baseComponentConfiguration.onSubmit = async (state, amazonPayComponent) => { - try { - await self.handleOnSubmit(state.data, amazonPayComponent); - } catch (error) { - amazonPayComponent.handleDeclineFlow(); - } - }; baseComponentConfiguration.productType = 'PayAndShip'; baseComponentConfiguration.checkoutMode = 'ProcessOrder'; @@ -82,6 +76,7 @@ define( countryCode: formattedShippingAddress.country, phoneNumber: formattedShippingAddress.telephone }; + if (baseComponentConfiguration.addressDetails.countryCode === 'US') { baseComponentConfiguration.addressDetails.stateOrRegion = quote.shippingAddress().regionCode } @@ -99,6 +94,7 @@ define( countryCode: formattedBillingAddress.country, phoneNumber: formattedBillingAddress.telephone }; + if (baseComponentConfiguration.addressDetails.countryCode === 'US') { baseComponentConfiguration.addressDetails.stateOrRegion = quote.billingAddress().regionCode } @@ -108,13 +104,16 @@ define( }, mountPaymentMethodComponent: function (paymentMethod, configuration) { - let self = this; const containerId = '#' + paymentMethod.type + 'Container'; - let url = new URL(location.href); - //Handles the redirect back to checkout page with amazonSessionKey in url - if (url.searchParams.has(amazonSessionKey)) { + const currentUrl = new URL(location.href); + + /* + * If the first redirect is successful and URL contains `amazonCheckoutSessionId` parameter, + * don't mount the default component but mount the second component to submit the `/payments` request. + */ + if (currentUrl.searchParams.has(amazonSessionKey)) { let componentConfig = { - amazonCheckoutSessionId: url.searchParams.get(amazonSessionKey), + amazonCheckoutSessionId: currentUrl.searchParams.get(amazonSessionKey), showOrderButton: false, amount: { currency: configuration.amount.currency, @@ -122,27 +121,47 @@ define( }, showChangePaymentDetailsButton: false } - try { - const amazonPayComponent = adyenCheckout.mountPaymentMethodComponent( // This mountPaymentMethodCOmponent isn't up to date. - self.checkoutComponent, - 'amazonpay', - componentConfig, - containerId - ); - amazonPayComponent.submit(); - } catch (err) { - // The component does not exist yet - if ('test' === adyenConfiguration.getCheckoutEnvironment()) { - console.log(err); - } - } - } else{ + + this.amazonPayComponent = adyenCheckout.mountPaymentMethodComponent( + this.checkoutComponent, + 'amazonpay', + componentConfig, + containerId + ); + + // Triggers `onSubmit` event and `handleOnSubmit()` callback in adyen-pm-method.js handles it + this.amazonPayComponent.submit(); + } else { this._super(); } }, + /* + * Try to handle decline flow if Amazon Pay session allows in case of `/payments` call fails. + * If decline flow is available, shopper will be redirected to Amazon Pay hosted page again. + * If handle decline flow is not present, the component will throw `onError` event. + */ handleOnFailure: function (response, component) { - debugger; + this.amazonPayComponent.handleDeclineFlow(); + }, + + /* + * If `handleDeclineFlow()` can not be handled for any reason, `onError` will be thrown. + * In this case, remove `amazonCheckoutSessionId` from the URL and remount the payment component. + */ + handleOnError: function (error, component) { + this.remountAmazonPayComponent(); + }, + + /* + * Remove `amazonCheckoutSessionId` from the URL and remount the component. + */ + remountAmazonPayComponent: function () { + const checkoutPaymentUrl = "checkout/#payment"; + window.history.pushState({}, document.title, "/" + checkoutPaymentUrl); + + const paymentMethodsResponse = adyenPaymentService.getPaymentMethods(); + this.createCheckoutComponent(paymentMethodsResponse()); } }) } diff --git a/view/frontend/web/js/view/payment/method-renderer/adyen-pm-method.js b/view/frontend/web/js/view/payment/method-renderer/adyen-pm-method.js index b972ba33c..604f5afe5 100755 --- a/view/frontend/web/js/view/payment/method-renderer/adyen-pm-method.js +++ b/view/frontend/web/js/view/payment/method-renderer/adyen-pm-method.js @@ -186,13 +186,13 @@ define( * Passing false as the response to hide the actual error message from the shopper for security. * This will show a generic error message instead of the actual error message. */ - this.handleOnFailure(false, component); + this.handleOnFailure(error, component); }, - handleOnFailure: function(response, component) { + handleOnFailure: function(error, component) { this.isPlaceOrderAllowed(true); fullScreenLoader.stopLoader(); - errorProcessor.process(response, this.currentMessageContainer); + errorProcessor.process(error, this.currentMessageContainer); }, renderCheckoutComponent: function() { @@ -390,7 +390,6 @@ define( try { const orderId = await placeOrderAction(data, self.currentMessageContainer); - self.afterPlaceOrder(); const responseJSON = await adyenPaymentService.getOrderPaymentStatus(orderId); self.validateActionOrPlaceOrder(responseJSON, orderId, component); } catch (response) {