From 95f4db126e317f87a6d2cb12d083af2a49fbe73c Mon Sep 17 00:00:00 2001 From: Dimitri BOUTEILLE Date: Wed, 13 Dec 2023 11:27:50 +0100 Subject: [PATCH 1/6] Apple Pay return ApplePayError object when no delivery method is available --- view/frontend/web/js/applepay/button.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/view/frontend/web/js/applepay/button.js b/view/frontend/web/js/applepay/button.js index 243089cd..36720da6 100644 --- a/view/frontend/web/js/applepay/button.js +++ b/view/frontend/web/js/applepay/button.js @@ -263,7 +263,16 @@ define([ .then((result) => { // Stop if no shipping methods. if (result.length === 0) { - reject($t('There are no shipping methods available for you right now. Please try again or use an alternative payment method.')); + console.info('There are no shipping methods available for you right now. Please try again or use an alternative payment method.'); + reject({ + newTotal: { + label: this.getMerchantName(), + amount: this.isProductView + ? formatAmount(totalsModel().getTotal() * 100) + : formatAmount(getCartSubtotal() * 100), + }, + errors: [new ApplePayError('addressUnserviceable')], + }); } let shippingMethods = []; From 813b58f693fcd2b797419efedadc765d8c91a745 Mon Sep 17 00:00:00 2001 From: Dimitri BOUTEILLE Date: Wed, 13 Dec 2023 17:42:06 +0100 Subject: [PATCH 2/6] Create Apple Pay onPlaceOrderError callback --- view/frontend/web/js/applepay/button.js | 64 ++++++++++++++++++------- 1 file changed, 47 insertions(+), 17 deletions(-) diff --git a/view/frontend/web/js/applepay/button.js b/view/frontend/web/js/applepay/button.js index 36720da6..0b157bdc 100644 --- a/view/frontend/web/js/applepay/button.js +++ b/view/frontend/web/js/applepay/button.js @@ -485,7 +485,7 @@ define([ let componentData = self.applePayComponent.data; - setShippingInformation(payload, this.isProductView).done(function () { + setShippingInformation(payload, this.isProductView).done(function() { // Submit payment information const postData = { email: shippingContact.emailAddress, @@ -493,32 +493,62 @@ define([ method: 'adyen_hpp', additional_data: { brand_code: 'applepay', - stateData: JSON.stringify(componentData) - } - } + stateData: JSON.stringify(componentData), + }, + }, }; - if (window.checkout && window.checkout.agreementIds) { - postData.paymentMethod.extension_attributes = { - agreement_ids: window.checkout.agreementIds - }; - } + postData.paymentMethod = agreementsAssigner(postData.paymentMethod); createPayment(JSON.stringify(postData), this.isProductView) - .done(function () { + .done(function() { redirectToSuccess(); resolve(window.ApplePaySession.STATUS_SUCCESS); - }).fail(function (r) { - reject(window.ApplePaySession.STATUS_FAILURE); - console.error('Adyen ApplePay Unable to take payment', r); - }); + }) + .fail(function(error) { + this.onPlaceOrderError('payment', error, reject); + }.bind(this)); + + }.bind(this)).fail(function(error) { + this.onPlaceOrderError('shippingInformation', error, reject); + }.bind(this)); + }, - }.bind(this)).fail(function (e) { - console.error('Adyen ApplePay Unable to set shipping information', e); - reject(window.ApplePaySession.STATUS_INVALID_BILLING_POSTAL_ADDRESS); + + /** + * @param {string} step + * @param {*} error + * @param {function} reject + */ + onPlaceOrderError: function(step, error, reject) { + reject({ + status: window.ApplePaySession.STATUS_FAILURE, }); + + console.error( + `Adyen ApplePay unable to take payment, something went wrong during ${step} step.`, + error, + ); + + /** + * @see https://magento.stackexchange.com/questions/237959/how-to-use-javascript-to-display-page-messages + */ + const errorMessage = $t('Your payment failed, Please try again later'); + setTimeout(() => { + customerData.set('messages', { + messages: [{ + text: errorMessage, + type: 'error', + }], + }); + + window.scrollTo({ top: 0, behavior: 'smooth' }); + }, 1000); }, + /** + * @returns {string} + */ getMerchantName: function() { const config = configModel().getConfig(); return config?.merchantAccount ?? $t('Grand Total'); From a16877416c51dabce4a34058d8130270f11dbd75 Mon Sep 17 00:00:00 2001 From: Dimitri BOUTEILLE Date: Wed, 13 Dec 2023 17:50:59 +0100 Subject: [PATCH 3/6] CS Apple Pay error message --- view/frontend/web/js/applepay/button.js | 51 +++++++++++++++---------- 1 file changed, 30 insertions(+), 21 deletions(-) diff --git a/view/frontend/web/js/applepay/button.js b/view/frontend/web/js/applepay/button.js index 0b157bdc..b43fa338 100644 --- a/view/frontend/web/js/applepay/button.js +++ b/view/frontend/web/js/applepay/button.js @@ -358,10 +358,7 @@ define([ resolve(applePayShippingContactUpdate); // Pass shipping methods back - }).fail((e) => { - console.error('Adyen ApplePay: Unable to get totals', e); - reject($t('We\'re unable to fetch the cart totals for you. Please try an alternative payment method.')); - }); + }).fail((e) => this._onGetTotalsError(e, reject)); }).catch(reject); }, @@ -427,10 +424,7 @@ define([ self.shippingMethod = shippingMethod.identifier; resolve(applePayShippingMethodUpdate); - }).fail((e) => { - console.error('Adyen ApplePay: Unable to get totals', e); - reject($t('We\'re unable to fetch the cart totals for you. Please try an alternative payment method.')); - }); + }).fail((e) => this._onGetTotalsError(e, reject)); }, /** @@ -505,39 +499,54 @@ define([ redirectToSuccess(); resolve(window.ApplePaySession.STATUS_SUCCESS); }) - .fail(function(error) { - this.onPlaceOrderError('payment', error, reject); - }.bind(this)); + .fail((error) => this._onPlaceOrderError('payment', error, reject)); - }.bind(this)).fail(function(error) { - this.onPlaceOrderError('shippingInformation', error, reject); - }.bind(this)); + }.bind(this)).fail((error) => this._onPlaceOrderError('shippingInformation', error, reject)); }, + /** + * @param {*} error + * @param {function} reject + * @private + */ + _onGetTotalsError: function (error, reject) { + reject({ + status: window.ApplePaySession.STATUS_FAILURE, + }); + + console.error('Adyen ApplePay: Unable to get totals', error); + this._displayError($t('We\'re unable to fetch the cart totals for you. Please try an alternative payment method.')); + }, /** * @param {string} step * @param {*} error * @param {function} reject + * @private */ - onPlaceOrderError: function(step, error, reject) { + _onPlaceOrderError: function(step, error, reject) { reject({ status: window.ApplePaySession.STATUS_FAILURE, }); console.error( - `Adyen ApplePay unable to take payment, something went wrong during ${step} step.`, + `Adyen ApplePay: Unable to take payment, something went wrong during ${step} step.`, error, ); - /** - * @see https://magento.stackexchange.com/questions/237959/how-to-use-javascript-to-display-page-messages - */ - const errorMessage = $t('Your payment failed, Please try again later'); + this._displayError($t('Your payment failed, Please try again later')); + }, + + /** + * @see https://magento.stackexchange.com/questions/237959/how-to-use-javascript-to-display-page-messages + * @param {string} error + * @private + */ + _displayError: function (error) { setTimeout(() => { customerData.set('messages', { messages: [{ - text: errorMessage, + text: error, type: 'error', }], }); From 5bb8a0e45e3c84461796e373e4206d2029222036 Mon Sep 17 00:00:00 2001 From: Dimitri BOUTEILLE Date: Thu, 14 Dec 2023 09:21:47 +0100 Subject: [PATCH 4/6] Display Apple Pay explicit place order error --- view/frontend/web/js/applepay/button.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/view/frontend/web/js/applepay/button.js b/view/frontend/web/js/applepay/button.js index b43fa338..a72f6e25 100644 --- a/view/frontend/web/js/applepay/button.js +++ b/view/frontend/web/js/applepay/button.js @@ -534,7 +534,8 @@ define([ error, ); - this._displayError($t('Your payment failed, Please try again later')); + const errorMessage = error?.responseJSON.message ?? $t('Your payment failed, Please try again later'); + this._displayError(errorMessage); }, /** From 8c2757729ff1ddc0c141022e6f574c81087ca6dc Mon Sep 17 00:00:00 2001 From: Khushboo Date: Mon, 15 Sep 2025 13:03:49 +0200 Subject: [PATCH 5/6] Update button.js Resolving merge conflicts for develop-3 --- view/frontend/web/js/applepay/button.js | 524 ++++++++++++++---------- 1 file changed, 316 insertions(+), 208 deletions(-) diff --git a/view/frontend/web/js/applepay/button.js b/view/frontend/web/js/applepay/button.js index a72f6e25..5c0a6711 100644 --- a/view/frontend/web/js/applepay/button.js +++ b/view/frontend/web/js/applepay/button.js @@ -1,46 +1,51 @@ define([ - 'uiComponent', - 'mage/translate', - 'Magento_Customer/js/customer-data', - 'Adyen_Payment/js/model/adyen-configuration', - 'Adyen_Payment/js/adyen', - 'Adyen_ExpressCheckout/js/actions/activateCart', - 'Adyen_ExpressCheckout/js/actions/cancelCart', - 'Adyen_ExpressCheckout/js/actions/createPayment', - 'Adyen_ExpressCheckout/js/actions/getShippingMethods', - 'Adyen_ExpressCheckout/js/actions/getExpressMethods', - 'Adyen_ExpressCheckout/js/actions/setShippingInformation', - 'Adyen_ExpressCheckout/js/actions/setTotalsInfo', - 'Adyen_ExpressCheckout/js/helpers/formatAmount', - 'Adyen_ExpressCheckout/js/helpers/getApplePayStyles', - 'Adyen_ExpressCheckout/js/helpers/getCartSubtotal', - 'Adyen_ExpressCheckout/js/helpers/getExtensionAttributes', - 'Adyen_ExpressCheckout/js/helpers/getPaymentMethod', - 'Adyen_ExpressCheckout/js/helpers/getPdpForm', - 'Adyen_ExpressCheckout/js/helpers/getPdpPriceBox', - 'Adyen_ExpressCheckout/js/helpers/getSupportedNetworks', - 'Adyen_ExpressCheckout/js/helpers/isConfigSet', - 'Adyen_ExpressCheckout/js/helpers/getRegionId', - 'Adyen_ExpressCheckout/js/helpers/redirectToSuccess', - 'Adyen_ExpressCheckout/js/helpers/setExpressMethods', - 'Adyen_ExpressCheckout/js/helpers/validatePdpForm', - 'Adyen_ExpressCheckout/js/model/config', - 'Adyen_ExpressCheckout/js/model/countries', - 'Adyen_ExpressCheckout/js/model/totals', - 'Adyen_ExpressCheckout/js/model/currency' -], + 'uiComponent', + 'mage/translate', + 'Magento_Customer/js/customer-data', + 'Adyen_Payment/js/model/adyen-configuration', + 'Adyen_Payment/js/adyen', + 'Adyen_ExpressCheckout/js/actions/activateCart', + 'Adyen_ExpressCheckout/js/actions/cancelCart', + 'Adyen_ExpressCheckout/js/actions/createPayment', + 'Adyen_ExpressCheckout/js/actions/getShippingMethods', + 'Adyen_ExpressCheckout/js/actions/getExpressMethods', + 'Adyen_ExpressCheckout/js/actions/setShippingInformation', + 'Adyen_ExpressCheckout/js/actions/setBillingAddress', + 'Adyen_ExpressCheckout/js/actions/setTotalsInfo', + 'Adyen_ExpressCheckout/js/helpers/formatAmount', + 'Adyen_ExpressCheckout/js/helpers/getApplePayStyles', + 'Adyen_ExpressCheckout/js/helpers/getCartSubtotal', + 'Adyen_ExpressCheckout/js/helpers/getExtensionAttributes', + 'Adyen_ExpressCheckout/js/helpers/getPaymentMethod', + 'Adyen_ExpressCheckout/js/helpers/getPdpForm', + 'Adyen_ExpressCheckout/js/helpers/getPdpPriceBox', + 'Adyen_ExpressCheckout/js/helpers/getSupportedNetworks', + 'Adyen_ExpressCheckout/js/helpers/isConfigSet', + 'Adyen_ExpressCheckout/js/helpers/getRegionId', + 'Adyen_ExpressCheckout/js/helpers/redirectToSuccess', + 'Adyen_ExpressCheckout/js/helpers/setExpressMethods', + 'Adyen_ExpressCheckout/js/helpers/validatePdpForm', + 'Adyen_ExpressCheckout/js/model/config', + 'Adyen_ExpressCheckout/js/model/countries', + 'Adyen_ExpressCheckout/js/model/totals', + 'Adyen_ExpressCheckout/js/model/currency', + 'Adyen_ExpressCheckout/js/helpers/getCurrentPage', + 'Adyen_ExpressCheckout/js/model/virtualQuote', + 'Adyen_Payment/js/helper/currencyHelper' + ], function ( Component, $t, customerData, AdyenConfiguration, - AdyenCheckout, + AdyenWeb, activateCart, cancelCart, createPayment, getShippingMethods, getExpressMethods, setShippingInformation, + setBillingAddress, setTotalsInfo, formatAmount, getApplePayStyles, @@ -58,7 +63,10 @@ define([ configModel, countriesModel, totalsModel, - currencyModel + currencyModel, + getCurrentPage, + virtualQuoteModel, + currencyHelper ) { 'use strict'; @@ -70,6 +78,11 @@ define([ applePayComponent: null, }, + toAmountString: function (val) { + const n = Number(val); + return Number.isFinite(n) ? n.toFixed(2) : '0.00'; + }, + initialize: async function (config, element) { this._super(); @@ -83,13 +96,15 @@ define([ const response = await getExpressMethods().getRequest(element); const cart = customerData.get('cart'); + virtualQuoteModel().setIsVirtual(true, response); + cart.subscribe(function () { this.reloadApplePayButton(element); }.bind(this)); setExpressMethods(response); totalsModel().setTotal(response.totals.grand_total); - currencyModel().setCurrency(response.totals.quote_currency_code) + currencyModel().setCurrency(response.totals.quote_currency_code); const $priceBox = getPdpPriceBox(); const pdpForm = getPdpForm(element); @@ -117,6 +132,8 @@ define([ this.initialiseApplePayComponent(applePaymentMethod, element); } else { let applePaymentMethod = await getPaymentMethod('applepay', this.isProductView); + virtualQuoteModel().setIsVirtual(false); + if (!applePaymentMethod) { const cart = customerData.get('cart'); cart.subscribe(function () { @@ -134,10 +151,27 @@ define([ initialiseApplePayComponent: async function (applePaymentMethod, element) { const config = configModel().getConfig(); - const adyenCheckoutComponent = await new AdyenCheckout({ + const adyenData = window.adyenData; + let currentPage = getCurrentPage(this.isProductView, element); + + const adyenCheckoutComponent = await window.AdyenWeb.AdyenCheckout({ locale: config.locale, - originKey: config.originkey, environment: config.checkoutenv, + countryCode: config.countryCode, + analytics: { + analyticsData: { + applicationInfo: { + merchantApplication: { + name: adyenData['merchant-application-name'], + version: adyenData['merchant-application-version'] + }, + externalPlatform: { + name: adyenData['external-platform-name'], + version: adyenData['external-platform-version'] + } + } + } + }, risk: { enabled: false }, @@ -150,25 +184,37 @@ define([ applePayConfiguration.amount.currency = currencyModel().getCurrency(); } - this.applePayComponent = adyenCheckoutComponent.create( + this.applePayComponent = await window.AdyenWeb.createComponent( 'applepay', + adyenCheckoutComponent, applePayConfiguration ); this.applePayComponent .isAvailable() .then(() => { - element.style.display = 'block'; - this.applePayComponent.mount(element); - }).catch((e) => { + this.onAvailable(element); + }) + .catch((e) => { this.onNotAvailable(e); }); }, + /** + * @param {*} error + */ onNotAvailable: function (error) { console.log('Apple pay is unavailable.', error); }, + /** + * @param {HTMLElement} element + */ + onAvailable: function (element) { + element.style.display = 'block'; + this.applePayComponent.mount(element); + }, + unmountApplePay: function () { if (this.applePayComponent) { this.applePayComponent.unmount(); @@ -181,8 +227,11 @@ define([ if (this.isProductView) { const pdpResponse = await getExpressMethods().getRequest(element); + virtualQuoteModel().setIsVirtual(true, pdpResponse); setExpressMethods(pdpResponse); totalsModel().setTotal(pdpResponse.totals.grand_total); + } else { + virtualQuoteModel().setIsVirtual(false); } this.unmountApplePay(); @@ -199,7 +248,10 @@ define([ const config = configModel().getConfig(); const countryCode = config.countryCode === 'UK' ? 'GB' : config.countryCode; const pdpForm = getPdpForm(element); + const isVirtual = virtualQuoteModel().getIsVirtual(); let currency; + let applepayBaseConfiguration; + const currentPage = getCurrentPage(this.isProductView, element); if (this.isProductView) { currency = currencyModel().getCurrency(); @@ -210,7 +262,7 @@ define([ currency = paymentMethodExtraDetails.configuration.amount.currency; } - return { + applepayBaseConfiguration = { countryCode: countryCode, currencyCode: currency, totalPriceLabel: this.getMerchantName(), @@ -221,23 +273,36 @@ define([ }, amount: { value: this.isProductView - ? formatAmount(totalsModel().getTotal() * 100) - : formatAmount(getCartSubtotal() * 100), + ? currencyHelper.formatAmount( + totalsModel().getTotal(), + currency + ) + : currencyHelper.formatAmount( + getCartSubtotal(), + currency + ), currency: currency }, + isExpress: true, + expressPage: currentPage, supportedNetworks: getSupportedNetworks(), merchantCapabilities: ['supports3DS'], requiredShippingContactFields: ['postalAddress', 'name', 'email', 'phone'], requiredBillingContactFields: ['postalAddress', 'name'], shippingMethods: [], - onAuthorized: this.startPlaceOrder.bind(this), - onShippingContactSelected: this.onShippingContactSelect.bind(this), - onShippingMethodSelected: this.onShippingMethodSelect.bind(this), + onAuthorized: this.onAuthorized.bind(this), + onSubmit: (state, component, actions) => this.onSubmit(state, component, actions), onClick: function (resolve, reject) {validatePdpForm(resolve, reject, pdpForm);}, - onSubmit: function () {}, onError: () => cancelCart(this.isProductView), ...applePayStyles }; + + if (!isVirtual) { + applepayBaseConfiguration.onShippingContactSelected = this.onShippingContactSelect.bind(this); + applepayBaseConfiguration.onShippingMethodSelected = this.onShippingMethodSelect.bind(this); + } + + return applepayBaseConfiguration; }, onShippingContactSelect: function (resolve, reject, event) { @@ -245,16 +310,16 @@ define([ // Get the address. let address = event.shippingContact, - // Create a payload. + // Create a payload. payload = { - address: { - city: address.locality, - region: address.administrativeArea, - country_id: address.countryCode.toUpperCase(), - postcode: address.postalCode, - save_in_address_book: 0 - } - }; + address: { + city: address.locality, + region: address.administrativeArea, + country_id: address.countryCode.toUpperCase(), + postcode: address.postalCode, + save_in_address_book: 0 + } + }; self.shippingAddress = payload.address; @@ -272,8 +337,7 @@ define([ : formatAmount(getCartSubtotal() * 100), }, errors: [new ApplePayError('addressUnserviceable')], - }); - } + }); } let shippingMethods = []; self.shippingMethods = {}; @@ -286,10 +350,10 @@ define([ identifier: result[i].method_code, label: result[i].method_title, detail: result[i].carrier_title ? result[i].carrier_title : '', - amount: parseFloat(result[i].amount).toFixed(2) + amount: this.toAmountString(result[i].amount) }; - // Add method object to array. + // Add method object to array. shippingMethods.push(method); self.shippingMethods[result[i].method_code] = result[i]; if (!self.shippingMethod) { @@ -304,7 +368,6 @@ define([ 'postcode': self.shippingAddress.postcode }; - // Create payload to get totals let totalsPayload = { 'addressInformation': { @@ -317,145 +380,153 @@ define([ // Create payload to update quote let shippingInformationPayload = { 'addressInformation': { - ...totalsPayload.addressInformation, 'shipping_address': address, - 'billing_address': address + 'shipping_method_code': self.shippingMethods[shippingMethods[0].identifier].method_code, + 'shipping_carrier_code': self.shippingMethods[shippingMethods[0].identifier].carrier_code } }; - delete shippingInformationPayload.addressInformation.address; - setShippingInformation(shippingInformationPayload, this.isProductView); - - setTotalsInfo(totalsPayload, this.isProductView) - .done((response) => { - let applePayShippingContactUpdate = {}; - - applePayShippingContactUpdate.newShippingMethods = shippingMethods; - applePayShippingContactUpdate.newTotal = { - label: this.getMerchantName(), - amount: (response.grand_total).toString() - }; - applePayShippingContactUpdate.newLineItems = [ - { - type: 'final', - label: $t('Subtotal'), - amount: response.subtotal.toString() - }, - { - type: 'final', - label: $t('Shipping'), - amount: shippingMethods[0].amount.toString() - } - ]; - - if (response.tax_amount > 0) { - applePayShippingContactUpdate.newLineItems.push({ - type: 'final', - label: $t('Tax'), - amount: response.tax_amount.toString() - }) - } - - resolve(applePayShippingContactUpdate); - // Pass shipping methods back - }).fail((e) => this._onGetTotalsError(e, reject)); - }).catch(reject); + setShippingInformation(shippingInformationPayload, self.isProductView).then(() => { + setTotalsInfo(totalsPayload, self.isProductView) + .done((response) => { + self.afterSetTotalsInfo(response, shippingMethods, self.isProductView, resolve); + }) + .fail((e) => this._onGetTotalsError(e, reject)); + }); + }); }, onShippingMethodSelect: function (resolve, reject, event) { let self = this; - let shippingMethod = event.shippingMethod; + let shippingMethod = event && event.shippingMethod; + + // Fallback to the first known method if the event is missing anything + if (!shippingMethod || !self.shippingMethods || !self.shippingMethods[shippingMethod.identifier]) { + const firstKey = self.shippingMethods && Object.keys(self.shippingMethods)[0]; + if (!firstKey) { + reject($t('No shipping methods available.')); + return; + } + shippingMethod = { identifier: firstKey, amount: self.shippingMethods[firstKey].amount }; + } - let address = { - 'countryId': self.shippingAddress.country_id, - 'region': self.shippingAddress.region, - 'regionId': getRegionId(self.shippingAddress.country_id, self.shippingAddress.region), - 'postcode': self.shippingAddress.postcode + const address = { + countryId: self.shippingAddress.country_id, + region: self.shippingAddress.region, + regionId: getRegionId(self.shippingAddress.country_id, self.shippingAddress.region), + postcode: self.shippingAddress.postcode }; - let totalsPayload = { - 'addressInformation': { - 'address': address, - 'shipping_method_code': self.shippingMethods[shippingMethod.identifier].method_code, - 'shipping_carrier_code': self.shippingMethods[shippingMethod.identifier].carrier_code + const totalsPayload = { + addressInformation: { + address: address, + shipping_method_code: self.shippingMethods[shippingMethod.identifier].method_code, + shipping_carrier_code: self.shippingMethods[shippingMethod.identifier].carrier_code } }; - let shippingInformationPayload = { - 'addressInformation': { - ...totalsPayload.addressInformation, - 'shipping_address': address, - 'billing_address': address + const shippingInformationPayload = { + addressInformation: { + shipping_method_code: self.shippingMethods[shippingMethod.identifier].method_code, + shipping_carrier_code: self.shippingMethods[shippingMethod.identifier].carrier_code, + shipping_address: address } }; - delete shippingInformationPayload.addressInformation.address; - setShippingInformation(shippingInformationPayload, this.isProductView); + setShippingInformation(shippingInformationPayload, this.isProductView).then(() => { + setTotalsInfo(totalsPayload, self.isProductView) + .done((response) => { + // this ensures newTotal/newLineItems are valid and well-formed + self.afterSetTotalsInfo(response, shippingMethod, self.isProductView, resolve); + }).fail((e) => this._onGetTotalsError(e, reject)); + }); + }, - setTotalsInfo(totalsPayload, this.isProductView) - .done((response) => { - let applePayShippingMethodUpdate = {}; + afterSetTotalsInfo: function (response, shippingMethod, isPdp, resolve) { + // Guard against missing/NaN totals + const grandTotal = this.toAmountString(response && response.grand_total); + const subtotal = this.toAmountString(response && response.subtotal); + const tax = this.toAmountString(response && response.tax_amount); + + const update = { + newTotal: { + type: 'final', + label: this.getMerchantName(), + amount: grandTotal + }, + newLineItems: [ + { type: 'final', label: $t('Subtotal'), amount: subtotal } + ] + }; - applePayShippingMethodUpdate.newTotal = { - type: 'final', - label: this.getMerchantName(), - amount: (response.grand_total).toString() - }; - applePayShippingMethodUpdate.newLineItems = [ - { - type: 'final', - label: $t('Subtotal'), - amount: response.subtotal.toString() - }, - { - type: 'final', - label: $t('Shipping'), - amount: shippingMethod.amount.toString() - } - ]; - - if (response.tax_amount > 0) { - applePayShippingMethodUpdate.newLineItems.push({ - type: 'final', - label: $t('Tax'), - amount: response.tax_amount.toString() - }) - } + // If the shipping methods is an array, pass them all and use the first one for line items + if (Array.isArray(shippingMethod)) { + update.newShippingMethods = shippingMethod; + shippingMethod = shippingMethod[0]; // first item becomes the selected one + } + + // shipping line (guard amount) + const shippingAmount = this.toAmountString(shippingMethod && shippingMethod.amount); + update.newLineItems.push({ + type: 'final', + label: $t('Shipping'), + amount: shippingAmount + }); + + // tax line (only if > 0.00) + if (Number(tax) > 0) { + update.newLineItems.push({ + type: 'final', + label: $t('Tax'), + amount: tax + }); + } - self.shippingMethod = shippingMethod.identifier; - resolve(applePayShippingMethodUpdate); - }).fail((e) => this._onGetTotalsError(e, reject)); + // store identifier defensively (if present) + if (shippingMethod && shippingMethod.identifier) { + this.shippingMethod = shippingMethod.identifier; + } + + resolve(update); }, - /** - * Place the order - */ - startPlaceOrder: function (resolve, reject, event) { - let self = this; - let shippingContact = event.payment.shippingContact, - billingContact = event.payment.billingContact, - payload = { - 'addressInformation': { - 'shipping_address': { - 'email': shippingContact.emailAddress, - 'telephone': shippingContact.phoneNumber, - 'firstname': shippingContact.givenName, - 'lastname': shippingContact.familyName, - 'street': shippingContact.addressLines, - 'city': shippingContact.locality, - 'region': shippingContact.administrativeArea, - 'region_id': getRegionId( - shippingContact.countryCode.toUpperCase(), - shippingContact.administrativeArea - ), - 'region_code': null, - 'country_id': shippingContact.countryCode.toUpperCase(), - 'postcode': shippingContact.postalCode, - 'same_as_billing': 0, - 'customer_address_id': 0, - 'save_in_address_book': 0 - }, - 'billing_address': { + onAuthorized: function (data, actions) { + const isVirtual = virtualQuoteModel().getIsVirtual(); + const event = data && data.authorizedEvent; + if (!event || !event.payment) { + console.error('Adyen ApplePay: authorizedEvent missing'); + actions.reject(); + return; + } + + try { + const shippingContact = event.payment.shippingContact || {}; + const billingContact = event.payment.billingContact || {}; + + // 1) Build and stash the /payments payload for onSubmit + const componentData = this.applePayComponent.data; + const postData = { + email: shippingContact.emailAddress, + paymentMethod: { + method: 'adyen_applepay', + additional_data: { + brand_code: 'applepay', + stateData: JSON.stringify(componentData), + frontendType: 'default' + }, + extension_attributes: getExtensionAttributes(event.payment) + } + }; + if (window.checkout && window.checkout.agreementIds) { + postData.paymentMethod.extension_attributes = { + agreement_ids: window.checkout.agreementIds + }; + } + this._applePayPostData = postData; + + // 2) Prepare billing (and shipping for non-virtual), then resolve + const billingAddressPayload = { + address: { 'email': shippingContact.emailAddress, 'telephone': shippingContact.phoneNumber, 'firstname': billingContact.givenName, @@ -463,45 +534,85 @@ define([ 'street': billingContact.addressLines, 'city': billingContact.locality, 'region': billingContact.administrativeArea, - 'region_id': getRegionId(billingContact.countryCode.toUpperCase(), billingContact.administrativeArea), + 'region_id': getRegionId( + (billingContact.countryCode || '').toUpperCase(), + billingContact.administrativeArea + ), 'region_code': null, - 'country_id': billingContact.countryCode.toUpperCase(), + 'country_id': (billingContact.countryCode || '').toUpperCase(), 'postcode': billingContact.postalCode, 'same_as_billing': 0, 'customer_address_id': 0, 'save_in_address_book': 0 }, - 'shipping_method_code': self.shippingMethods[self.shippingMethod].method_code, - 'shipping_carrier_code': self.shippingMethods[self.shippingMethod].carrier_code, - 'extension_attributes': getExtensionAttributes(event.payment) - } - }; + 'useForShipping': false + }; - let componentData = self.applePayComponent.data; + activateCart(this.isProductView).then(() => { + setBillingAddress(billingAddressPayload, this.isProductView).done(() => { + if (isVirtual) { + // Done preparing – move to onSubmit + actions.resolve(); + return; + } - setShippingInformation(payload, this.isProductView).done(function() { - // Submit payment information - const postData = { - email: shippingContact.emailAddress, - paymentMethod: { - method: 'adyen_hpp', - additional_data: { - brand_code: 'applepay', - stateData: JSON.stringify(componentData), - }, - }, - }; + const shippingInformationPayload = { + 'addressInformation': { + 'shipping_address': { + 'email': shippingContact.emailAddress, + 'telephone': shippingContact.phoneNumber, + 'firstname': shippingContact.givenName, + 'lastname': shippingContact.familyName, + 'street': shippingContact.addressLines, + 'city': shippingContact.locality, + 'region': shippingContact.administrativeArea, + 'region_id': getRegionId( + shippingContact.countryCode.toUpperCase(), + shippingContact.administrativeArea + ), + 'region_code': null, + 'country_id': shippingContact.countryCode.toUpperCase(), + 'postcode': shippingContact.postalCode, + 'same_as_billing': 0, + 'customer_address_id': 0, + 'save_in_address_book': 0 + }, + 'shipping_method_code': this.shippingMethods[this.shippingMethod].method_code, + 'shipping_carrier_code': this.shippingMethods[this.shippingMethod].carrier_code, + 'extension_attributes': getExtensionAttributes(event.payment) + } + }; - postData.paymentMethod = agreementsAssigner(postData.paymentMethod); + setShippingInformation(shippingInformationPayload, this.isProductView) + .done(() => actions.resolve()) + .fail((e) => { + console.error('Adyen ApplePay Unable to set shipping information', e); + actions.reject(); + }); + }); + }); + } catch (e) { + console.error('Adyen ApplePay onAuthorized error', e); + actions.reject(); + } + }, - createPayment(JSON.stringify(postData), this.isProductView) - .done(function() { - redirectToSuccess(); - resolve(window.ApplePaySession.STATUS_SUCCESS); - }) - .fail((error) => this._onPlaceOrderError('payment', error, reject)); + onSubmit: function (state, component, actions) { + if (!this._applePayPostData) { + console.error('Adyen ApplePay: missing post data for /payments.'); + actions.reject(); + return; + } - }.bind(this)).fail((error) => this._onPlaceOrderError('shippingInformation', error, reject)); + createPayment(JSON.stringify(this._applePayPostData), this.isProductView) + .done((res) => { + redirectToSuccess(); + actions.resolve(window.ApplePaySession.STATUS_SUCCESS); + }) + .fail((err) => { + this._onPlaceOrderError('payment', error, reject); + actions.reject(window.ApplePaySession.STATUS_FAILURE); + }); }, /** @@ -556,9 +667,6 @@ define([ }, 1000); }, - /** - * @returns {string} - */ getMerchantName: function() { const config = configModel().getConfig(); return config?.merchantAccount ?? $t('Grand Total'); From 61f09bac5ab80efb08e9e0f2747a87ada0430468 Mon Sep 17 00:00:00 2001 From: Khushboo Date: Mon, 15 Sep 2025 13:30:10 +0200 Subject: [PATCH 6/6] Update button.js Resolving conflicts --- view/frontend/web/js/applepay/button.js | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/view/frontend/web/js/applepay/button.js b/view/frontend/web/js/applepay/button.js index 4ec4629b..e94af66e 100644 --- a/view/frontend/web/js/applepay/button.js +++ b/view/frontend/web/js/applepay/button.js @@ -439,7 +439,6 @@ define([ // this ensures newTotal/newLineItems are valid and well-formed self.afterSetTotalsInfo(response, shippingMethod, self.isProductView, resolve); }).fail((e) => this._onGetTotalsError(e, reject)); - }); }, @@ -611,7 +610,8 @@ define([ actions.resolve(window.ApplePaySession.STATUS_SUCCESS); }) .fail((err) => { - this._onPlaceOrderError('payment', error, reject); + this._onPlaceOrderError('payment', error); + actions.reject(window.ApplePaySession.STATUS_FAILURE); }); }, @@ -635,11 +635,7 @@ define([ * @param {function} reject * @private */ - _onPlaceOrderError: function(step, error, reject) { - reject({ - status: window.ApplePaySession.STATUS_FAILURE, - }); - + _onPlaceOrderError: function(step, error) { console.error( `Adyen ApplePay: Unable to take payment, something went wrong during ${step} step.`, error, @@ -665,7 +661,6 @@ define([ window.scrollTo({ top: 0, behavior: 'smooth' }); }, 1000); - }, getMerchantName: function() {