diff --git a/etc/csp_whitelist.xml b/etc/csp_whitelist.xml
index 1de54c94f..6e1bb7674 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
+ *.amazon.de
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 b8fe8d38d..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
@@ -11,24 +11,33 @@ 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',
+ 'Adyen_Payment/js/model/adyen-payment-service',
+ 'mage/url'
],
function(
quote,
adyenPaymentMethod,
- adyenCheckout
+ 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 = {};
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 +45,7 @@ define(
if (!!quote.billingAddress()) {
formattedBillingAddress = self.getFormattedAddress(quote.billingAddress());
}
- baseComponentConfiguration.showPayButton = true;
+
baseComponentConfiguration.onClick = function(resolve,reject) {
if (self.validate()) {
resolve();
@@ -44,19 +53,15 @@ 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);
- } catch (error) {
- amazonPayComponent.handleDeclineFlow();
- }
- };
+ 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) {
@@ -71,6 +76,7 @@ define(
countryCode: formattedShippingAddress.country,
phoneNumber: formattedShippingAddress.telephone
};
+
if (baseComponentConfiguration.addressDetails.countryCode === 'US') {
baseComponentConfiguration.addressDetails.stateOrRegion = quote.shippingAddress().regionCode
}
@@ -88,21 +94,26 @@ define(
countryCode: formattedBillingAddress.country,
phoneNumber: formattedBillingAddress.telephone
};
+
if (baseComponentConfiguration.addressDetails.countryCode === 'US') {
baseComponentConfiguration.addressDetails.stateOrRegion = quote.billingAddress().regionCode
}
}
+
return baseComponentConfiguration;
},
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,
@@ -110,23 +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) {
+ 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 e380a6413..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
@@ -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(error, component);
+ },
+
+ handleOnFailure: function(error, component) {
+ this.isPlaceOrderAllowed(true);
+ fullScreenLoader.stopLoader();
+ errorProcessor.process(error, this.currentMessageContainer);
+ },
+
renderCheckoutComponent: function() {
let methodCode = this.getMethodCode();
@@ -374,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) {
@@ -382,13 +397,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