From 745cb2dfdd651721a03cca7efb732c23132ce9e6 Mon Sep 17 00:00:00 2001 From: Shani <31096696+shanikantsingh@users.noreply.github.com> Date: Mon, 2 Sep 2024 11:13:49 +0200 Subject: [PATCH] Adyen endpoints optimization (#1156) * chore(SFI-914): adyen endpoints optimization --- package-lock.json | 4 +- package.json | 2 +- .../client/default/js/applePayExpress.js | 321 +++++++++--------- .../client/default/js/commons/index.js | 3 +- .../cartridge/controllers/Adyen.js | 8 +- .../cartridge/controllers/Cart.js | 14 + .../default/account/payment/paymentForm.isml | 1 + .../default/cart/checkoutButtons.isml | 3 +- .../checkout/billing/adyenComponentForm.isml | 3 +- .../cartridge/adyenConstants/constants.js | 2 +- 10 files changed, 195 insertions(+), 166 deletions(-) create mode 100644 src/cartridges/int_adyen_SFRA/cartridge/controllers/Cart.js diff --git a/package-lock.json b/package-lock.json index 12adfb6ff..e792301c2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "int_adyen_SFRA", - "version": "24.1.0", + "version": "24.1.1", "lockfileVersion": 3, "requires": true, "packages": { @@ -26364,4 +26364,4 @@ } } } -} \ No newline at end of file +} diff --git a/package.json b/package.json index 9d34cee2b..da28464b2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "int_adyen_SFRA", - "version": "24.1.0", + "version": "24.1.1", "description": "Adyen's official cartridge for SFRA and controllers-based SiteGenesis", "main": "index.js", "paths": { diff --git a/src/cartridges/int_adyen_SFRA/cartridge/client/default/js/applePayExpress.js b/src/cartridges/int_adyen_SFRA/cartridge/client/default/js/applePayExpress.js index bf99bab0b..d20f96160 100644 --- a/src/cartridges/int_adyen_SFRA/cartridge/client/default/js/applePayExpress.js +++ b/src/cartridges/int_adyen_SFRA/cartridge/client/default/js/applePayExpress.js @@ -1,14 +1,16 @@ const helpers = require('./adyen_checkout/helpers'); -const { checkIfExpressMethodsAreReady } = require('./commons/index'); -const { updateLoadedExpressMethods } = require('./commons'); +const { + checkIfExpressMethodsAreReady, + createSession, + updateLoadedExpressMethods, +} = require('./commons'); const { APPLE_PAY } = require('./constants'); let checkout; let shippingMethodsData; async function initializeCheckout() { - const session = await fetch(window.sessionsUrl); - const sessionData = await session.json(); + const sessionData = await createSession(); const shippingMethods = await fetch(window.shippingMethodsUrl); shippingMethodsData = await shippingMethods.json(); @@ -123,181 +125,184 @@ function callPaymentFromComponent(data, resolveApplePay, rejectApplePay) { }); } -initializeCheckout() - .then(async () => { - const applePayPaymentMethod = - checkout.paymentMethodsResponse.paymentMethods.find( - (pm) => pm.type === APPLE_PAY, - ); - - if (!applePayPaymentMethod) { - updateLoadedExpressMethods(APPLE_PAY); - checkIfExpressMethodsAreReady(); - return; - } +$(document).ready(() => { + initializeCheckout() + .then(async () => { + const applePayPaymentMethod = + checkout.paymentMethodsResponse.paymentMethods.find( + (pm) => pm.type === APPLE_PAY, + ); - const applePayConfig = applePayPaymentMethod.configuration; + if (!applePayPaymentMethod) { + updateLoadedExpressMethods(APPLE_PAY); + checkIfExpressMethodsAreReady(); + return; + } - const applePayButtonConfig = { - showPayButton: true, - configuration: applePayConfig, - amount: checkout.options.amount, - requiredShippingContactFields: ['postalAddress', 'email', 'phone'], - requiredBillingContactFields: ['postalAddress', 'phone'], - shippingMethods: shippingMethodsData.shippingMethods.map((sm) => ({ - label: sm.displayName, - detail: sm.description, - identifier: sm.ID, - amount: `${sm.shippingCost.value}`, - })), - onAuthorized: async (resolve, reject, event) => { - try { - const customerData = event.payment.shippingContact; - const billingData = event.payment.billingContact; - const customer = formatCustomerObject(customerData, billingData); - const stateData = { - paymentMethod: { - type: APPLE_PAY, - applePayToken: event.payment.token.paymentData, - }, - paymentType: 'express', - }; + const applePayConfig = applePayPaymentMethod.configuration; - const resolveApplePay = () => { - // ** is used instead of Math.pow - const value = - applePayButtonConfig.amount.value * - 10 ** parseInt(window.digitsNumber, 10); - const finalPriceUpdate = { - newTotal: { - type: 'final', - label: applePayConfig.merchantName, - amount: `${Math.round(value)}`, + const applePayButtonConfig = { + showPayButton: true, + configuration: applePayConfig, + amount: checkout.options.amount, + requiredShippingContactFields: ['postalAddress', 'email', 'phone'], + requiredBillingContactFields: ['postalAddress', 'phone'], + shippingMethods: shippingMethodsData.shippingMethods.map((sm) => ({ + label: sm.displayName, + detail: sm.description, + identifier: sm.ID, + amount: `${sm.shippingCost.value}`, + })), + onAuthorized: async (resolve, reject, event) => { + try { + const customerData = event.payment.shippingContact; + const billingData = event.payment.billingContact; + const customer = formatCustomerObject(customerData, billingData); + const stateData = { + paymentMethod: { + type: APPLE_PAY, + applePayToken: event.payment.token.paymentData, }, + paymentType: 'express', }; - resolve(finalPriceUpdate); - }; - await callPaymentFromComponent( - { ...stateData, customer }, - resolveApplePay, - reject, - ); - } catch (error) { - reject(error); - } - }, - onSubmit: () => { - // This handler is empty to prevent sending a second payment request - // We already do the payment in paymentFromComponent - }, - onShippingMethodSelected: async (resolve, reject, event) => { - const { shippingMethod } = event; - const matchingShippingMethod = shippingMethodsData.shippingMethods.find( - (sm) => sm.ID === shippingMethod.identifier, - ); - const calculationResponse = await fetch( - `${window.calculateAmountUrl}?${new URLSearchParams({ - shipmentUUID: matchingShippingMethod.shipmentUUID, - methodID: matchingShippingMethod.ID, - })}`, - { - method: 'POST', - }, - ); - if (calculationResponse.ok) { - const newCalculation = await calculationResponse.json(); - applePayButtonConfig.amount = { - value: newCalculation.grandTotalAmount.value, - currency: newCalculation.grandTotalAmount.currency, - }; - const applePayShippingMethodUpdate = { - newTotal: { - type: 'final', - label: applePayConfig.merchantName, - amount: newCalculation.grandTotalAmount.value, - }, - }; - resolve(applePayShippingMethodUpdate); - } else { - reject(); - } - }, - onShippingContactSelected: async (resolve, reject, event) => { - const { shippingContact } = event; - const shippingMethods = await fetch( - `${window.shippingMethodsUrl}?${new URLSearchParams({ - city: shippingContact.locality, - country: shippingContact.country, - countryCode: shippingContact.countryCode, - stateCode: shippingContact.administrativeArea, - })}`, - ); - if (shippingMethods.ok) { - shippingMethodsData = await shippingMethods.json(); - if (shippingMethodsData.shippingMethods?.length) { - const selectedShippingMethod = - shippingMethodsData.shippingMethods[0]; - const calculationResponse = await fetch( - `${window.calculateAmountUrl}?${new URLSearchParams({ - shipmentUUID: selectedShippingMethod.shipmentUUID, - methodID: selectedShippingMethod.ID, - })}`, - { - method: 'POST', - }, - ); - if (calculationResponse.ok) { - const shippingMethodsStructured = - shippingMethodsData.shippingMethods.map((sm) => ({ - label: sm.displayName, - detail: sm.description, - identifier: sm.ID, - amount: `${sm.shippingCost.value}`, - })); - const newCalculation = await calculationResponse.json(); - const applePayShippingContactUpdate = { - newShippingMethods: shippingMethodsStructured, + const resolveApplePay = () => { + // ** is used instead of Math.pow + const value = + applePayButtonConfig.amount.value * + 10 ** parseInt(window.digitsNumber, 10); + const finalPriceUpdate = { newTotal: { type: 'final', label: applePayConfig.merchantName, - amount: newCalculation.grandTotalAmount.value, + amount: `${Math.round(value)}`, }, }; - resolve(applePayShippingContactUpdate); + resolve(finalPriceUpdate); + }; + + await callPaymentFromComponent( + { ...stateData, customer }, + resolveApplePay, + reject, + ); + } catch (error) { + reject(error); + } + }, + onSubmit: () => { + // This handler is empty to prevent sending a second payment request + // We already do the payment in paymentFromComponent + }, + onShippingMethodSelected: async (resolve, reject, event) => { + const { shippingMethod } = event; + const matchingShippingMethod = + shippingMethodsData.shippingMethods.find( + (sm) => sm.ID === shippingMethod.identifier, + ); + const calculationResponse = await fetch( + `${window.calculateAmountUrl}?${new URLSearchParams({ + shipmentUUID: matchingShippingMethod.shipmentUUID, + methodID: matchingShippingMethod.ID, + })}`, + { + method: 'POST', + }, + ); + if (calculationResponse.ok) { + const newCalculation = await calculationResponse.json(); + applePayButtonConfig.amount = { + value: newCalculation.grandTotalAmount.value, + currency: newCalculation.grandTotalAmount.currency, + }; + const applePayShippingMethodUpdate = { + newTotal: { + type: 'final', + label: applePayConfig.merchantName, + amount: newCalculation.grandTotalAmount.value, + }, + }; + resolve(applePayShippingMethodUpdate); + } else { + reject(); + } + }, + onShippingContactSelected: async (resolve, reject, event) => { + const { shippingContact } = event; + const shippingMethods = await fetch( + `${window.shippingMethodsUrl}?${new URLSearchParams({ + city: shippingContact.locality, + country: shippingContact.country, + countryCode: shippingContact.countryCode, + stateCode: shippingContact.administrativeArea, + })}`, + ); + if (shippingMethods.ok) { + shippingMethodsData = await shippingMethods.json(); + if (shippingMethodsData.shippingMethods?.length) { + const selectedShippingMethod = + shippingMethodsData.shippingMethods[0]; + const calculationResponse = await fetch( + `${window.calculateAmountUrl}?${new URLSearchParams({ + shipmentUUID: selectedShippingMethod.shipmentUUID, + methodID: selectedShippingMethod.ID, + })}`, + { + method: 'POST', + }, + ); + if (calculationResponse.ok) { + const shippingMethodsStructured = + shippingMethodsData.shippingMethods.map((sm) => ({ + label: sm.displayName, + detail: sm.description, + identifier: sm.ID, + amount: `${sm.shippingCost.value}`, + })); + const newCalculation = await calculationResponse.json(); + const applePayShippingContactUpdate = { + newShippingMethods: shippingMethodsStructured, + newTotal: { + type: 'final', + label: applePayConfig.merchantName, + amount: newCalculation.grandTotalAmount.value, + }, + }; + resolve(applePayShippingContactUpdate); + } else { + reject(); + } } else { reject(); } } else { reject(); } - } else { - reject(); - } - }, - }; + }, + }; - const cartContainer = document.getElementsByClassName(APPLE_PAY); - const applePayButton = await createApplePayButton(applePayButtonConfig); - const isApplePayButtonAvailable = await applePayButton.isAvailable(); + const cartContainer = document.getElementsByClassName(APPLE_PAY); + const applePayButton = await createApplePayButton(applePayButtonConfig); + const isApplePayButtonAvailable = await applePayButton.isAvailable(); - if (isApplePayButtonAvailable) { - for ( - let expressCheckoutNodesIndex = 0; - expressCheckoutNodesIndex < cartContainer.length; - expressCheckoutNodesIndex += 1 - ) { - applePayButton.mount(cartContainer[expressCheckoutNodesIndex]); + if (isApplePayButtonAvailable) { + for ( + let expressCheckoutNodesIndex = 0; + expressCheckoutNodesIndex < cartContainer.length; + expressCheckoutNodesIndex += 1 + ) { + applePayButton.mount(cartContainer[expressCheckoutNodesIndex]); + } } - } - updateLoadedExpressMethods(APPLE_PAY); - checkIfExpressMethodsAreReady(); - }) - .catch(() => { - updateLoadedExpressMethods(APPLE_PAY); - checkIfExpressMethodsAreReady(); - }); + updateLoadedExpressMethods(APPLE_PAY); + checkIfExpressMethodsAreReady(); + }) + .catch(() => { + updateLoadedExpressMethods(APPLE_PAY); + checkIfExpressMethodsAreReady(); + }); +}); module.exports = { handleAuthorised, diff --git a/src/cartridges/int_adyen_SFRA/cartridge/client/default/js/commons/index.js b/src/cartridges/int_adyen_SFRA/cartridge/client/default/js/commons/index.js index ef78c6a92..e03943e3c 100644 --- a/src/cartridges/int_adyen_SFRA/cartridge/client/default/js/commons/index.js +++ b/src/cartridges/int_adyen_SFRA/cartridge/client/default/js/commons/index.js @@ -17,7 +17,8 @@ module.exports.onBrand = function onBrand(brandObject) { module.exports.createSession = async function createSession() { return $.ajax({ url: window.sessionsUrl, - type: 'get', + type: 'post', + data: $('#adyen-sessions-token').serialize(), }); }; diff --git a/src/cartridges/int_adyen_SFRA/cartridge/controllers/Adyen.js b/src/cartridges/int_adyen_SFRA/cartridge/controllers/Adyen.js index 5f8ace56a..d12c387b9 100644 --- a/src/cartridges/int_adyen_SFRA/cartridge/controllers/Adyen.js +++ b/src/cartridges/int_adyen_SFRA/cartridge/controllers/Adyen.js @@ -1,5 +1,6 @@ const server = require('server'); const consentTracking = require('*/cartridge/scripts/middleware/consentTracking'); +const csrf = require('*/cartridge/scripts/middleware/csrf'); const adyenGiving = require('*/cartridge/scripts/adyenGiving'); const { adyen } = require('*/cartridge/controllers/middlewares/index'); @@ -20,7 +21,12 @@ server.post( adyen.paymentsDetails, ); -server.get('Sessions', server.middleware.https, adyen.callCreateSession); +server.post( + 'Sessions', + server.middleware.https, + csrf.validateRequest, + adyen.callCreateSession, +); server.get( 'ShippingMethods', diff --git a/src/cartridges/int_adyen_SFRA/cartridge/controllers/Cart.js b/src/cartridges/int_adyen_SFRA/cartridge/controllers/Cart.js new file mode 100644 index 000000000..fba2cc4eb --- /dev/null +++ b/src/cartridges/int_adyen_SFRA/cartridge/controllers/Cart.js @@ -0,0 +1,14 @@ +const server = require('server'); +const csrf = require('*/cartridge/scripts/middleware/csrf'); + +server.extend(module.superModule); + +/* + * Prepends Cart's 'MiniCartShow' function to have csrf token. + * This is needed for csrf protection for express payments on mini cart. + */ +server.prepend('MiniCartShow', csrf.generateToken, (req, res, next) => { + next(); +}); + +module.exports = server.exports(); diff --git a/src/cartridges/int_adyen_SFRA/cartridge/templates/default/account/payment/paymentForm.isml b/src/cartridges/int_adyen_SFRA/cartridge/templates/default/account/payment/paymentForm.isml index 7e325e5ef..67c5c4662 100644 --- a/src/cartridges/int_adyen_SFRA/cartridge/templates/default/account/payment/paymentForm.isml +++ b/src/cartridges/int_adyen_SFRA/cartridge/templates/default/account/payment/paymentForm.isml @@ -65,4 +65,5 @@ + ### Custom Adyen cartridge end ### diff --git a/src/cartridges/int_adyen_SFRA/cartridge/templates/default/cart/checkoutButtons.isml b/src/cartridges/int_adyen_SFRA/cartridge/templates/default/cart/checkoutButtons.isml index 269ba7100..41042e652 100644 --- a/src/cartridges/int_adyen_SFRA/cartridge/templates/default/cart/checkoutButtons.isml +++ b/src/cartridges/int_adyen_SFRA/cartridge/templates/default/cart/checkoutButtons.isml @@ -87,4 +87,5 @@ - \ No newline at end of file + + diff --git a/src/cartridges/int_adyen_SFRA/cartridge/templates/default/checkout/billing/adyenComponentForm.isml b/src/cartridges/int_adyen_SFRA/cartridge/templates/default/checkout/billing/adyenComponentForm.isml index a7d6c8478..da51f0bd1 100644 --- a/src/cartridges/int_adyen_SFRA/cartridge/templates/default/checkout/billing/adyenComponentForm.isml +++ b/src/cartridges/int_adyen_SFRA/cartridge/templates/default/checkout/billing/adyenComponentForm.isml @@ -28,7 +28,7 @@ window.partialPaymentUrl = "${URLUtils.https('Adyen-partialPayment')}"; window.cancelPartialPaymentOrderUrl = "${URLUtils.https('Adyen-CancelPartialPaymentOrder')}"; window.fetchGiftCardsUrl = "${URLUtils.https('Adyen-fetchGiftCards')}"; - + window.remainingAmountGiftCardResource = "${Resource.msg('remainingAmount.giftCard', 'adyen', null)}"; window.discountedAmountGiftCardResource = "${Resource.msg('discountedAmount.giftCard', 'adyen', null)}"; window.deductedBalanceGiftCardResource = "${Resource.msg('deductedBalance.giftCard', 'adyen', null)}"; @@ -129,3 +129,4 @@ + diff --git a/src/cartridges/int_adyen_overlay/cartridge/adyenConstants/constants.js b/src/cartridges/int_adyen_overlay/cartridge/adyenConstants/constants.js index 5953925cb..cba9e366a 100644 --- a/src/cartridges/int_adyen_overlay/cartridge/adyenConstants/constants.js +++ b/src/cartridges/int_adyen_overlay/cartridge/adyenConstants/constants.js @@ -97,5 +97,5 @@ module.exports = { EXTERNAL_PLATFORM_VERSION : 'SFRA', CHECKOUT_COMPONENT_VERSION: '5.56.0', - VERSION: '24.1.0', + VERSION: '24.1.1', };