From 52bbd022e76929e2bd109fe1750eb9731b3422c7 Mon Sep 17 00:00:00 2001 From: Nickolas Malovanets Date: Tue, 30 Jul 2024 19:45:48 +0300 Subject: [PATCH 1/8] Test Magento2.3.x with Fastlane. --- view/frontend/web/js/model/fastlane.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/view/frontend/web/js/model/fastlane.js b/view/frontend/web/js/model/fastlane.js index 0b42c36..8e25f98 100644 --- a/view/frontend/web/js/model/fastlane.js +++ b/view/frontend/web/js/model/fastlane.js @@ -77,6 +77,9 @@ define([ window.localStorage.setItem('axoEnv', 'sandbox'); window.localStorage.setItem('fastlaneEnv', 'sandbox'); } + if (!window.braintree) { + window.braintree = {}; + } switch (gatewayData.type) { case 'braintree': await this.buildBraintreeFastlaneInstance(gatewayData); @@ -101,7 +104,6 @@ define([ * @return {Promise} */ buildBraintreeFastlaneInstance: async function (gatewayData) { - await this.loadAxo(); await this.loadScript('bold_braintree_fastlane_hosted_fields', 'hostedFields'); const client = await this.loadScript('bold_braintree_fastlane_client'); const dataCollector = await this.loadScript('bold_braintree_fastlane_data_collector'); @@ -183,7 +185,6 @@ define([ * @return {Promise} */ buildPPCPFastlaneInstance: async function (gatewayData) { - await this.loadAxo(); await this.loadScript('bold_paypal_fastlane_hosted_fields', 'hostedFields'); await this.loadScript('bold_paypal_fastlane_client', 'client'); let debugMode = ''; From a0c428f6440f37b9b84629400ea77ab18da1fb8f Mon Sep 17 00:00:00 2001 From: Nickolas Malovanets Date: Tue, 30 Jul 2024 19:47:12 +0300 Subject: [PATCH 2/8] Test Magento2.3.x with Fastlane. --- etc/adminhtml/system.xml | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/etc/adminhtml/system.xml b/etc/adminhtml/system.xml index 7332341..9fd1691 100644 --- a/etc/adminhtml/system.xml +++ b/etc/adminhtml/system.xml @@ -9,21 +9,21 @@ Magento\Config\Model\Config\Source\Yesno - - - - - - - - - - - - - - - + + + Magento\Config\Model\Config\Source\Yesno + + + 1 + + + + + + + 1 + + From af35c5332e6a5c7392428786a3572f5cde8297c3 Mon Sep 17 00:00:00 2001 From: Nickolas Malovanets Date: Wed, 31 Jul 2024 18:35:36 +0300 Subject: [PATCH 3/8] Update Braintree Fastlane scripts versions. --- view/frontend/requirejs-config.js | 8 ++++---- view/frontend/web/js/model/fastlane.js | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/view/frontend/requirejs-config.js b/view/frontend/requirejs-config.js index c1b2ee5..0d05a26 100644 --- a/view/frontend/requirejs-config.js +++ b/view/frontend/requirejs-config.js @@ -7,10 +7,10 @@ let config = { }, }, paths: { - bold_braintree_fastlane_client: 'https://js.braintreegateway.com/web/3.101.0-fastlane-beta.7.2/js/client.min', - bold_braintree_fastlane: 'https://js.braintreegateway.com/web/3.101.0-fastlane-beta.7.2/js/fastlane.min', - bold_braintree_fastlane_data_collector: 'https://js.braintreegateway.com/web/3.101.0-fastlane-beta.7.2/js/data-collector.min', - bold_braintree_fastlane_hosted_fields: 'https://js.braintreegateway.com/web/3.101.0-fastlane-beta.7.2/js/hosted-fields.min', + bold_braintree_fastlane_client: 'https://js.braintreegateway.com/web/3.104.0/js/client.min', + bold_braintree_fastlane: 'https://js.braintreegateway.com/web/3.104.0/js/fastlane', + bold_braintree_fastlane_data_collector: 'https://js.braintreegateway.com/web/3.104.0/js/data-collector.min', + bold_braintree_fastlane_hosted_fields: 'https://js.braintreegateway.com/web/3.104.0/js/hosted-fields.min', bold_paypal_fastlane_client: 'https://js.braintreegateway.com/web/3.97.3-connect-alpha.6.1/js/client.min', bold_paypal_fastlane_hosted_fields: 'https://js.braintreegateway.com/web/3.97.3-connect-alpha.6.1/js/hosted-fields.min' }, diff --git a/view/frontend/web/js/model/fastlane.js b/view/frontend/web/js/model/fastlane.js index 8e25f98..359bd63 100644 --- a/view/frontend/web/js/model/fastlane.js +++ b/view/frontend/web/js/model/fastlane.js @@ -139,7 +139,7 @@ define([ loadAxo: async function () { require.config({ paths: { - bold_axo: 'https://www.paypalobjects.com/connect-boba/axo.min' + bold_axo: 'https://www.paypalobjects.com/connect-boba/axo' }, attributes: { "bold_axo": { @@ -203,7 +203,7 @@ define([ attributes: { "bold_paypal_fastlane": { 'data-user-id-token': gatewayData.client_token, - 'data-client-metadata-id': 'Magento2' + 'data-client-metadata-id': window.checkoutConfig.bold.publicOrderId } }, onNodeCreated: function (node, config, name) { From e7e4395e56c9f5d72c9f2387dffdfaf289dff942 Mon Sep 17 00:00:00 2001 From: Nickolas Malovanets Date: Wed, 31 Jul 2024 18:39:28 +0300 Subject: [PATCH 4/8] Update Braintree Fastlane scripts versions. --- view/frontend/requirejs-config.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/view/frontend/requirejs-config.js b/view/frontend/requirejs-config.js index 0d05a26..076200f 100644 --- a/view/frontend/requirejs-config.js +++ b/view/frontend/requirejs-config.js @@ -7,10 +7,10 @@ let config = { }, }, paths: { - bold_braintree_fastlane_client: 'https://js.braintreegateway.com/web/3.104.0/js/client.min', - bold_braintree_fastlane: 'https://js.braintreegateway.com/web/3.104.0/js/fastlane', - bold_braintree_fastlane_data_collector: 'https://js.braintreegateway.com/web/3.104.0/js/data-collector.min', - bold_braintree_fastlane_hosted_fields: 'https://js.braintreegateway.com/web/3.104.0/js/hosted-fields.min', + bold_braintree_fastlane_client: 'https://js.braintreegateway.com/web/3.105.0/js/client.min', + bold_braintree_fastlane: 'https://js.braintreegateway.com/web/3.105.0/js/fastlane', + bold_braintree_fastlane_data_collector: 'https://js.braintreegateway.com/web/3.105.0/js/data-collector.min', + bold_braintree_fastlane_hosted_fields: 'https://js.braintreegateway.com/web/3.105.0/js/hosted-fields.min', bold_paypal_fastlane_client: 'https://js.braintreegateway.com/web/3.97.3-connect-alpha.6.1/js/client.min', bold_paypal_fastlane_hosted_fields: 'https://js.braintreegateway.com/web/3.97.3-connect-alpha.6.1/js/hosted-fields.min' }, From ffd727f55642218be2836c5773b89d91ecb4e475 Mon Sep 17 00:00:00 2001 From: Nickolas Malovanets Date: Wed, 31 Jul 2024 21:57:44 +0300 Subject: [PATCH 5/8] Update Braintree Fastlane scripts versions. --- view/frontend/web/js/model/fastlane.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/view/frontend/web/js/model/fastlane.js b/view/frontend/web/js/model/fastlane.js index 8b7aaad..5d23bdd 100644 --- a/view/frontend/web/js/model/fastlane.js +++ b/view/frontend/web/js/model/fastlane.js @@ -202,7 +202,7 @@ define([ }, attributes: { "bold_paypal_fastlane": { - 'data-user-id-token': gatewayData.client_token, + 'data-sdk-client-token': gatewayData.client_token, 'data-client-metadata-id': window.checkoutConfig.bold.publicOrderId } }, From c7ee34df3b892eaa986c64e46da1cbfb9c78dcb9 Mon Sep 17 00:00:00 2001 From: Nickolas Malovanets Date: Wed, 31 Jul 2024 22:00:22 +0300 Subject: [PATCH 6/8] Update Braintree Fastlane scripts versions. --- view/frontend/web/js/model/fastlane.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/view/frontend/web/js/model/fastlane.js b/view/frontend/web/js/model/fastlane.js index 5d23bdd..74b9378 100644 --- a/view/frontend/web/js/model/fastlane.js +++ b/view/frontend/web/js/model/fastlane.js @@ -193,7 +193,7 @@ define([ } require.config({ paths: { - bold_paypal_fastlane: 'https://www.paypal.com/sdk/js?client-id=' + gatewayData.client_id + '&components=fastlane' + debugMode + bold_paypal_fastlane: 'https://www.paypal.com/sdk/js?client-id=' + gatewayData.client_id + '&components=buttons,fastlane' + debugMode }, shim: { 'bold_paypal_fastlane': { From 96d8480f47c242eccaa7080a1efb9201c114b973 Mon Sep 17 00:00:00 2001 From: Nickolas Malovanets Date: Thu, 1 Aug 2024 15:20:58 +0300 Subject: [PATCH 7/8] Make Fastlane Component Compatible with Magento2.3.x. --- etc/adminhtml/system.xml | 30 +-- etc/csp_whitelist.xml | 3 + view/frontend/web/js/model/fastlane.js | 191 +++++++++++------- .../view/form/element/email/fastlane-mixin.js | 1 + .../method-renderer/bold-fastlane-method.js | 9 - 5 files changed, 141 insertions(+), 93 deletions(-) diff --git a/etc/adminhtml/system.xml b/etc/adminhtml/system.xml index 9fd1691..7332341 100644 --- a/etc/adminhtml/system.xml +++ b/etc/adminhtml/system.xml @@ -9,21 +9,21 @@ Magento\Config\Model\Config\Source\Yesno - - - Magento\Config\Model\Config\Source\Yesno - - - 1 - - - - - - - 1 - - + + + + + + + + + + + + + + + diff --git a/etc/csp_whitelist.xml b/etc/csp_whitelist.xml index 1d7b3c3..74d7c46 100644 --- a/etc/csp_whitelist.xml +++ b/etc/csp_whitelist.xml @@ -9,6 +9,9 @@ cashier.boldcommerce.com *.sandbox.braintree-api.com *.braintree-api.com + *.paypal.com + *.braintreegateway.com + www.paypalobjects.com diff --git a/view/frontend/web/js/model/fastlane.js b/view/frontend/web/js/model/fastlane.js index 74b9378..59a911a 100644 --- a/view/frontend/web/js/model/fastlane.js +++ b/view/frontend/web/js/model/fastlane.js @@ -1,7 +1,7 @@ define([ - 'ko', + 'ko', ], function ( - ko + ko, ) { 'use strict'; @@ -21,8 +21,8 @@ define([ */ isEnabled: function () { return window.checkoutConfig.bold - && window.checkoutConfig.bold.fastlane - && !window.isCustomerLoggedIn + && window.checkoutConfig.bold.fastlane + && !window.isCustomerLoggedIn; }, /** * Retrieve Fastlane type (PPCP / Braintree). @@ -104,62 +104,34 @@ define([ * @return {Promise} */ buildBraintreeFastlaneInstance: async function (gatewayData) { + this.loadAxo(gatewayData); await this.loadScript('bold_braintree_fastlane_hosted_fields', 'hostedFields'); const client = await this.loadScript('bold_braintree_fastlane_client'); const dataCollector = await this.loadScript('bold_braintree_fastlane_data_collector'); const fastlane = await this.loadScript('bold_braintree_fastlane'); const clientInstance = await client.create( - { - authorization: gatewayData.client_token, - } + { + authorization: gatewayData.client_token, + }, ); const dataCollectorInstance = await dataCollector.create( - { - client: clientInstance, - } + { + client: clientInstance, + }, ); const styles = window.checkoutConfig.bold.fastlane.styles.length > 0 - ? window.checkoutConfig.bold.fastlane.styles - : {}; - const {deviceData} = dataCollectorInstance; + ? window.checkoutConfig.bold.fastlane.styles + : {}; + const { deviceData } = dataCollectorInstance; window.boldFastlaneInstance = await fastlane.create( - { - authorization: gatewayData.client_token, - client: clientInstance, - deviceData: deviceData, - styles: styles - } - ); - }, - /** - * Load Axo script with require js. - * - * @return {Promise} - */ - loadAxo: async function () { - require.config({ - paths: { - bold_axo: 'https://www.paypalobjects.com/connect-boba/axo' - }, - attributes: { - "bold_axo": { - 'id': 'axo_id', - 'async': null - } + { + authorization: gatewayData.client_token, + client: clientInstance, + deviceData: deviceData, + styles: styles, }, - onNodeCreated: function (node, config, name) { - if (config.attributes && config.attributes[name]) { - Object.keys(config.attributes[name]).forEach(attribute => { - node.setAttribute(attribute, config.attributes[name][attribute]); - }); - } - } - }); - await new Promise((resolve, reject) => { - require(['bold_axo'], resolve, reject); - }); + ); }, - /** * Load given script with require js. * @@ -185,6 +157,7 @@ define([ * @return {Promise} */ buildPPCPFastlaneInstance: async function (gatewayData) { + this.loadAxo(gatewayData); await this.loadScript('bold_paypal_fastlane_hosted_fields', 'hostedFields'); await this.loadScript('bold_paypal_fastlane_client', 'client'); let debugMode = ''; @@ -193,32 +166,112 @@ define([ } require.config({ paths: { - bold_paypal_fastlane: 'https://www.paypal.com/sdk/js?client-id=' + gatewayData.client_id + '&components=buttons,fastlane' + debugMode + bold_paypal_fastlane: 'https://www.paypal.com/sdk/js?client-id=' + gatewayData.client_id + '&components=buttons,fastlane' + debugMode, }, - shim: { - 'bold_paypal_fastlane': { - exports: 'paypal.fastlane' - } + }); + await new Promise((resolve, reject) => { + require(['bold_paypal_fastlane'], resolve, reject); + }); + + window.boldFastlaneInstance = await window.paypal.Fastlane(); + }, + /** + * Load Axo script with require js. + * + * @param {{client_token: string}} gatewayData + * @return {void} + */ + loadAxo: function (gatewayData) { + this.saveEventListeners(); + const originalAppendChild = Element.prototype.appendChild; + const self = this; + Element.prototype.appendChild = function (element) { + if (element.tagName === 'SCRIPT' + && element.id === 'axo-id' + && element.attributes['data-requiremodule']?.value !== 'bold_axo') { + self.loadWithRequireJs(element); + // prevent axo to be loaded without require js. + return element; + } + if (element.tagName === 'SCRIPT' + && element.attributes['data-requiremodule']?.value === 'bold_paypal_fastlane') { + // Magento 2.3.x has no onNodeCreate event, so we need to set the client token manually. + element.setAttribute('data-sdk-client-token', gatewayData.client_token); + element.setAttribute('data-client-metadata-id', window.checkoutConfig.bold.publicOrderId); + return originalAppendChild.call(this, element); + } + return originalAppendChild.call(this, element); + }; + }, + /** + * Save event listeners for original axo script, to attach them to axo script loaded via require js. + * + * @return {void} + */ + saveEventListeners: function () { + const originalAddEventListener = Element.prototype.addEventListener; + Element.prototype.addEventListener = function (type, listener, options) { + this._eventListeners = this._eventListeners || []; + this._eventListeners.push({ type, listener, options }); + originalAddEventListener.call(this, type, listener, options); + }; + }, + /** + * Load Axo script with require js. + * + * @return {Promise} + */ + loadWithRequireJs: async function (originalScript) { + const events = this.getEventListeners(originalScript); + require.config({ + paths: { + bold_axo: originalScript.src.replace('.js', ''), }, - attributes: { - "bold_paypal_fastlane": { - 'data-sdk-client-token': gatewayData.client_token, - 'data-client-metadata-id': window.checkoutConfig.bold.publicOrderId + }); + await new Promise((resolve, reject) => { + require(['bold_axo'], () => { + const newScript = document.querySelector('[data-requiremodule = "bold_axo"]'); + if (!newScript) { + reject(new Error('AXO script element not found.')); } - }, - onNodeCreated: function (node, config, name) { - if (config.attributes && config.attributes[name]) { - Object.keys(config.attributes[name]).forEach(attribute => { - node.setAttribute(attribute, config.attributes[name][attribute]); + // copy attributes from original script to the script loaded with require js. + const attributeNames = originalScript.getAttributeNames(); + attributeNames.forEach((attributeName) => { + if (attributeName === 'src') { + return; + } + newScript.setAttribute(attributeName, originalScript.getAttribute(attributeName)); + }); + // copy event listeners from original script to the script loaded with require js to notify fastlane axo is loaded. + for (const [event, listeners] of Object.entries(events)) { + listeners.forEach(({ listener, options }) => { + newScript.addEventListener(event, listener, options); }); } - } + const loadEvent = new Event('load'); + // Notify fastlane axo script is loaded. + newScript.dispatchEvent(loadEvent); + resolve(newScript); + }, reject); }); - await new Promise((resolve, reject) => { - require(['bold_paypal_fastlane'], resolve, reject); + }, + /** + * Retrieve event listeners from given element. + * + * @param element + * @return {{}} + */ + getEventListeners: function (element) { + const events = {}; + const listeners = element._eventListeners || []; + listeners.forEach(({ type, listener, options }) => { + if (!events[type]) { + events[type] = []; + } + events[type].push({ listener, options }); }); - window.boldFastlaneInstance = await window.paypal.Fastlane(); + return events; }, /** @@ -235,12 +288,12 @@ define([ 'zh_us', ]; let locale = window.LOCALE - ? window.LOCALE.toLowerCase().replace('-', '_') - : 'en_us'; + ? window.LOCALE.toLowerCase().replace('-', '_') + : 'en_us'; if (!availableLocales.includes(locale)) { locale = 'en_us'; } window.boldFastlaneInstance.setLocale(locale); - } + }, }; }); diff --git a/view/frontend/web/js/view/form/element/email/fastlane-mixin.js b/view/frontend/web/js/view/form/element/email/fastlane-mixin.js index 9b3a4ea..85548ef 100644 --- a/view/frontend/web/js/view/form/element/email/fastlane-mixin.js +++ b/view/frontend/web/js/view/form/element/email/fastlane-mixin.js @@ -87,6 +87,7 @@ define( fullScreenLoader.startLoader(); const fastlaneInstance = await fastlane.getFastlaneInstance(); if (!fastlaneInstance) { + fullScreenLoader.stopLoader(); return; } diff --git a/view/frontend/web/js/view/payment/method-renderer/bold-fastlane-method.js b/view/frontend/web/js/view/payment/method-renderer/bold-fastlane-method.js index 2763806..1385a56 100644 --- a/view/frontend/web/js/view/payment/method-renderer/bold-fastlane-method.js +++ b/view/frontend/web/js/view/payment/method-renderer/bold-fastlane-method.js @@ -250,15 +250,6 @@ define( }, ); this.fastlanePaymentToken = walletPayResult.data?.payment_data?.id; - } else { - await boldFrontendClient.put( - 'payments', - { - 'gateway_public_id': fastlane.getGatewayPublicId(), - 'currency': quote.totals().quote_currency_code, - 'token': this.fastlanePaymentToken, - }, - ); } const orderPlacementResult = await boldFrontendClient.post('process_order'); if (orderPlacementResult.errors) { From 011f50d61e232edac33740429e22106a6125ede2 Mon Sep 17 00:00:00 2001 From: Nickolas Malovanets Date: Thu, 1 Aug 2024 16:50:40 +0300 Subject: [PATCH 8/8] Make Fastlane Component Compatible with Magento2.3.x --- view/frontend/web/js/model/fastlane.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/view/frontend/web/js/model/fastlane.js b/view/frontend/web/js/model/fastlane.js index 59a911a..4df8444 100644 --- a/view/frontend/web/js/model/fastlane.js +++ b/view/frontend/web/js/model/fastlane.js @@ -104,7 +104,7 @@ define([ * @return {Promise} */ buildBraintreeFastlaneInstance: async function (gatewayData) { - this.loadAxo(gatewayData); + this.rewriteAxoLoading(gatewayData); //todo: remove as soon as axo.js is compatible with require js. await this.loadScript('bold_braintree_fastlane_hosted_fields', 'hostedFields'); const client = await this.loadScript('bold_braintree_fastlane_client'); const dataCollector = await this.loadScript('bold_braintree_fastlane_data_collector'); @@ -157,7 +157,7 @@ define([ * @return {Promise} */ buildPPCPFastlaneInstance: async function (gatewayData) { - this.loadAxo(gatewayData); + this.rewriteAxoLoading(gatewayData); //todo: remove as soon as axo.js is compatible with require js. await this.loadScript('bold_paypal_fastlane_hosted_fields', 'hostedFields'); await this.loadScript('bold_paypal_fastlane_client', 'client'); let debugMode = ''; @@ -181,7 +181,7 @@ define([ * @param {{client_token: string}} gatewayData * @return {void} */ - loadAxo: function (gatewayData) { + rewriteAxoLoading: function (gatewayData) { this.saveEventListeners(); const originalAppendChild = Element.prototype.appendChild; const self = this;