From 38e1cf68c1c0c63eed597fde08ca55d5196f3d14 Mon Sep 17 00:00:00 2001 From: Vitalij Mik Date: Mon, 21 Oct 2024 11:43:59 +0200 Subject: [PATCH] NTR: refactored JS --- .../ApplePayDirect/ApplePayDirect.php | 6 +- .../PaypalExpress/PayPalExpress.php | 4 +- .../PaypalExpressControllerBase.php | 57 +++++++---- .../view/sw-order-detail-general/index.js | 4 +- .../core/creditcard-mandate.plugin.js | 2 +- .../plugins/apple-pay-direct.plugin.js | 10 +- .../apple-pay-payment-method.plugin.js | 2 +- .../plugins/bancomat-plugin.js | 2 +- .../creditcard-mandate-manage.plugin.js | 2 +- .../plugins/mollie-express-actions.plugin.js | 95 +++++++++++++++++++ .../plugins/paypal-express.plugin.js | 53 ++++++++--- .../plugins/pos-terminal.plugin.js | 2 +- .../repository/BuyButtonRepository.js | 17 ++++ .../repository/BuyElementRepository.js | 21 ++++ .../repository/ExpressButtonsRepository.js | 9 ++ .../repository/PrivacyNoteElement.js | 31 ++++++ .../services/ApplePaySessionFactory.js | 2 +- .../services/ExpressAddToCart.js | 29 ++++++ src/Resources/app/storefront/src/register.js | 2 + .../app/storefront/src/scss/display.scss | 1 + .../express-privacy-notice.html.twig | 4 +- .../component/paypal-express-button.twig | 2 +- src/Service/CustomFieldsInterface.php | 2 + src/Service/CustomerService.php | 8 +- src/Service/CustomerServiceInterface.php | 2 +- tests/PHPUnit/Fakes/FakeCustomerService.php | 2 +- 26 files changed, 320 insertions(+), 51 deletions(-) create mode 100644 src/Resources/app/storefront/src/mollie-payments/plugins/mollie-express-actions.plugin.js create mode 100644 src/Resources/app/storefront/src/mollie-payments/repository/BuyButtonRepository.js create mode 100644 src/Resources/app/storefront/src/mollie-payments/repository/BuyElementRepository.js create mode 100644 src/Resources/app/storefront/src/mollie-payments/repository/ExpressButtonsRepository.js create mode 100644 src/Resources/app/storefront/src/mollie-payments/repository/PrivacyNoteElement.js create mode 100644 src/Resources/app/storefront/src/mollie-payments/services/ExpressAddToCart.js diff --git a/src/Components/ApplePayDirect/ApplePayDirect.php b/src/Components/ApplePayDirect/ApplePayDirect.php index 1084ba7d0..e703de143 100644 --- a/src/Components/ApplePayDirect/ApplePayDirect.php +++ b/src/Components/ApplePayDirect/ApplePayDirect.php @@ -349,7 +349,7 @@ public function restoreCart(SalesChannelContext $context): void * @throws \Exception * @return SalesChannelContext */ - public function prepareCustomer(string $firstname, string $lastname, string $email, string $street, string $zipcode, string $city, string $countryCode, string $phone, string $paymentToken, int $acceptedDataProtection, SalesChannelContext $context): SalesChannelContext + public function prepareCustomer(string $firstname, string $lastname, string $email, string $street, string $zipcode, string $city, string $countryCode, string $phone, string $paymentToken, ?int $acceptedDataProtection, SalesChannelContext $context): SalesChannelContext { if (empty($paymentToken)) { throw new \Exception('PaymentToken not found!'); @@ -366,8 +366,8 @@ public function prepareCustomer(string $firstname, string $lastname, string $ema $customer = $this->customerService->createGuestAccount( $address, $applePayID, - $acceptedDataProtection, - $context + $context, + $acceptedDataProtection ); if (! $customer instanceof CustomerEntity) { diff --git a/src/Components/PaypalExpress/PayPalExpress.php b/src/Components/PaypalExpress/PayPalExpress.php index 49f11d139..b7df96be9 100644 --- a/src/Components/PaypalExpress/PayPalExpress.php +++ b/src/Components/PaypalExpress/PayPalExpress.php @@ -155,7 +155,7 @@ public function loadSession(string $sessionId, SalesChannelContext $context): Se * @throws \Exception * @return SalesChannelContext */ - public function prepareCustomer(AddressStruct $shippingAddress, int $acceptedDataProtection, SalesChannelContext $context, ?AddressStruct $billingAddress = null): SalesChannelContext + public function prepareCustomer(AddressStruct $shippingAddress, SalesChannelContext $context, ?int $acceptedDataProtection, ?AddressStruct $billingAddress = null): SalesChannelContext { $updateShippingAddress = true; $paypalExpressId = $this->getActivePaypalExpressID($context); @@ -176,8 +176,8 @@ public function prepareCustomer(AddressStruct $shippingAddress, int $acceptedDat $customer = $this->customerService->createGuestAccount( $shippingAddress, $paypalExpressId, - $acceptedDataProtection, $context, + $acceptedDataProtection, $billingAddress ); } diff --git a/src/Controller/Storefront/PaypalExpress/PaypalExpressControllerBase.php b/src/Controller/Storefront/PaypalExpress/PaypalExpressControllerBase.php index 465dca6c7..5a803d092 100644 --- a/src/Controller/Storefront/PaypalExpress/PaypalExpressControllerBase.php +++ b/src/Controller/Storefront/PaypalExpress/PaypalExpressControllerBase.php @@ -5,6 +5,7 @@ use Kiener\MolliePayments\Components\PaypalExpress\PayPalExpress; use Kiener\MolliePayments\Service\CartService; use Kiener\MolliePayments\Service\CustomFieldsInterface; +use Kiener\MolliePayments\Service\SettingsService; use Kiener\MolliePayments\Struct\Address\AddressStruct; use Kiener\MolliePayments\Traits\Storefront\RedirectTrait; use Mollie\Api\Exceptions\ApiException; @@ -40,6 +41,7 @@ class PaypalExpressControllerBase extends StorefrontController * @var LoggerInterface */ private $logger; + private SettingsService $settingsService; /** @@ -48,12 +50,13 @@ class PaypalExpressControllerBase extends StorefrontController * @param RouterInterface $router * @param LoggerInterface $logger */ - public function __construct(PayPalExpress $paypalExpress, CartService $cartService, RouterInterface $router, LoggerInterface $logger) + public function __construct(PayPalExpress $paypalExpress, CartService $cartService, RouterInterface $router, SettingsService $settingsService, LoggerInterface $logger) { $this->paypalExpress = $paypalExpress; $this->cartService = $cartService; $this->router = $router; $this->logger = $logger; + $this->settingsService = $settingsService; } /** @@ -64,16 +67,21 @@ public function __construct(PayPalExpress $paypalExpress, CartService $cartServi */ public function startCheckout(Request $request, SalesChannelContext $context): Response { - $productId = $request->get('productId'); - $quantity = (int)$request->get('quantity', 1); - if ($request->isMethod(Request::METHOD_POST) && $productId !== null) { - $this->cartService->addProduct($productId, $quantity, $context); + $redirectUrl = $this->getCheckoutCartPage($this->router); + + $settings = $this->settingsService->getSettings($context->getSalesChannelId()); + + if ($settings->isPaypalExpressEnabled() === false) { + $this->logger->error('Paypal Express is disabled'); + return new RedirectResponse($redirectUrl); } $cart = $this->cartService->getCalculatedMainCart($context); + $cartExtension = $cart->getExtension(CustomFieldsInterface::MOLLIE_KEY); $oldSessionId = null; + if ($cartExtension instanceof ArrayStruct) { $oldSessionId = $cartExtension[CustomFieldsInterface::PAYPAL_EXPRESS_SESSION_ID_KEY] ?? null; } @@ -83,18 +91,25 @@ public function startCheckout(Request $request, SalesChannelContext $context): R } else { $session = $this->paypalExpress->startSession($cart, $context); - $cartExtension = new ArrayStruct([ + $cartExtension = [ CustomFieldsInterface::PAYPAL_EXPRESS_SESSION_ID_KEY => $session->id - ]); - $cart->addExtension(CustomFieldsInterface::MOLLIE_KEY, $cartExtension); + ]; + + if ($settings->isRequireDataProtectionCheckbox()) { + $cartExtension[CustomFieldsInterface::ACCEPTED_DATA_PROTECTION] = (bool)$request->get(CustomFieldsInterface::ACCEPTED_DATA_PROTECTION, false); + } + $cart->addExtension(CustomFieldsInterface::MOLLIE_KEY, new ArrayStruct($cartExtension)); $this->cartService->persistCart($cart, $context); } - $redirectUrl = $session->getRedirectUrl(); - if ($redirectUrl === null) { - $redirectUrl = $this->getCheckoutCartPage($this->router); + $sessionRedirect = $session->getRedirectUrl(); + if ($sessionRedirect !== null) { + $this->logger->error('Paypal Express redirect URL is empty', [ + 'sessionId' => $session->id, + ]); + $redirectUrl = $sessionRedirect; } return new RedirectResponse($redirectUrl); @@ -106,17 +121,30 @@ public function startCheckout(Request $request, SalesChannelContext $context): R */ public function finishCheckout(SalesChannelContext $context): Response { + $returnUrl = $this->getCheckoutCartPage($this->router); + + $settings = $this->settingsService->getSettings($context->getSalesChannelId()); + + if ($settings->isPaypalExpressEnabled() === false) { + $this->logger->error('Paypal Express is disabled'); + return new RedirectResponse($returnUrl); + } + $cart = $this->cartService->getCalculatedMainCart($context); $cartExtension = $cart->getExtension(CustomFieldsInterface::MOLLIE_KEY); $payPalExpressSessionId = null; + $acceptedDataProtection = null; if ($cartExtension instanceof ArrayStruct) { $payPalExpressSessionId = $cartExtension[CustomFieldsInterface::PAYPAL_EXPRESS_SESSION_ID_KEY] ?? null; + if ($settings->isRequireDataProtectionCheckbox()) { + $acceptedDataProtection = $cartExtension[CustomFieldsInterface::ACCEPTED_DATA_PROTECTION] ?? false; + } } - $returnUrl = $this->getCheckoutCartPage($this->router); + if ($payPalExpressSessionId === null) { @@ -136,7 +164,6 @@ public function finishCheckout(SalesChannelContext $context): Response } - $methodDetails = $payPalExpressSession->methodDetails; @@ -195,7 +222,6 @@ public function finishCheckout(SalesChannelContext $context): Response } - try { # we have to update the cart extension before a new user is created and logged in, otherwise the extension is not saved $cartExtension = new ArrayStruct([ @@ -209,7 +235,7 @@ public function finishCheckout(SalesChannelContext $context): Response # create new account or find existing and login - $this->paypalExpress->prepareCustomer($shippingAddress, 1, $context, $billingAddress); + $this->paypalExpress->prepareCustomer($shippingAddress, $context, $acceptedDataProtection, $billingAddress); } catch (\Throwable $e) { $this->logger->error('Failed to create customer or cart', [ 'message' => $e->getMessage(), @@ -218,7 +244,6 @@ public function finishCheckout(SalesChannelContext $context): Response } - $returnUrl = $this->getCheckoutConfirmPage($this->router); return new RedirectResponse($returnUrl); } diff --git a/src/Resources/app/administration/src/module/mollie-payments/extension/sw-order/view/sw-order-detail-general/index.js b/src/Resources/app/administration/src/module/mollie-payments/extension/sw-order/view/sw-order-detail-general/index.js index 2d77a129e..413006563 100644 --- a/src/Resources/app/administration/src/module/mollie-payments/extension/sw-order/view/sw-order-detail-general/index.js +++ b/src/Resources/app/administration/src/module/mollie-payments/extension/sw-order/view/sw-order-detail-general/index.js @@ -228,12 +228,12 @@ Component.override('sw-order-detail-general', { * */ copyPaymentUrlToClipboard() { - let fallback = async function(e) { + const fallback = async function(e) { await navigator.clipboard.writeText(e) }; // eslint-disable-next-line no-undef - let clipboard = typeof Shopware.Utils.dom.copyToClipboard === 'function' ? Shopware.Utils.dom.copyToClipboard : fallback; + const clipboard = typeof Shopware.Utils.dom.copyToClipboard === 'function' ? Shopware.Utils.dom.copyToClipboard : fallback; // eslint-disable-next-line no-undef clipboard(this.molliePaymentUrl); this.molliePaymentUrlCopied = true; diff --git a/src/Resources/app/storefront/src/mollie-payments/core/creditcard-mandate.plugin.js b/src/Resources/app/storefront/src/mollie-payments/core/creditcard-mandate.plugin.js index f9942cb00..b8064f136 100644 --- a/src/Resources/app/storefront/src/mollie-payments/core/creditcard-mandate.plugin.js +++ b/src/Resources/app/storefront/src/mollie-payments/core/creditcard-mandate.plugin.js @@ -1,5 +1,5 @@ import HttpClient from '../services/HttpClient'; -import Plugin from "../Plugin"; +import Plugin from '../Plugin'; /** * This plugin manage the credit card mandate of the customer diff --git a/src/Resources/app/storefront/src/mollie-payments/plugins/apple-pay-direct.plugin.js b/src/Resources/app/storefront/src/mollie-payments/plugins/apple-pay-direct.plugin.js index 44a21e222..1d4816ed7 100644 --- a/src/Resources/app/storefront/src/mollie-payments/plugins/apple-pay-direct.plugin.js +++ b/src/Resources/app/storefront/src/mollie-payments/plugins/apple-pay-direct.plugin.js @@ -1,6 +1,6 @@ import HttpClient from '../services/HttpClient'; -import Plugin from "../Plugin"; -import ApplePaySessionFactory from "../services/ApplePaySessionFactory"; +import Plugin from '../Plugin'; +import ApplePaySessionFactory from '../services/ApplePaySessionFactory'; export default class MollieApplePayDirect extends Plugin { @@ -23,7 +23,7 @@ export default class MollieApplePayDirect extends Plugin { // we need to re-init all apple pay button // once the offcanvas is loaded (lazy) into the DOM - const pluginOffCanvasInstances = window.PluginManager.getPluginList().OffCanvasCart.get("instances"); + const pluginOffCanvasInstances = window.PluginManager.getPluginList().OffCanvasCart.get('instances'); if (pluginOffCanvasInstances.length > 0) { const pluginOffCanvas = pluginOffCanvasInstances[0]; pluginOffCanvas.$emitter.subscribe('offCanvasOpened', me.initCurrentPage.bind(me)); @@ -192,8 +192,8 @@ export default class MollieApplePayDirect extends Plugin { const formData = new FormData(productForm); - formData.delete("redirectTo"); - formData.append("isExpressCheckout", "1"); + formData.delete('redirectTo'); + formData.append('isExpressCheckout', '1'); fetch(productForm.action, { diff --git a/src/Resources/app/storefront/src/mollie-payments/plugins/apple-pay-payment-method.plugin.js b/src/Resources/app/storefront/src/mollie-payments/plugins/apple-pay-payment-method.plugin.js index 4b0a4cbd3..ca6674e1c 100644 --- a/src/Resources/app/storefront/src/mollie-payments/plugins/apple-pay-payment-method.plugin.js +++ b/src/Resources/app/storefront/src/mollie-payments/plugins/apple-pay-payment-method.plugin.js @@ -1,5 +1,5 @@ import HttpClient from '../services/HttpClient'; -import Plugin from "../Plugin"; +import Plugin from '../Plugin'; export default class MollieApplePayPaymentMethod extends Plugin { diff --git a/src/Resources/app/storefront/src/mollie-payments/plugins/bancomat-plugin.js b/src/Resources/app/storefront/src/mollie-payments/plugins/bancomat-plugin.js index eee0d5b46..4d0d5a005 100644 --- a/src/Resources/app/storefront/src/mollie-payments/plugins/bancomat-plugin.js +++ b/src/Resources/app/storefront/src/mollie-payments/plugins/bancomat-plugin.js @@ -1,4 +1,4 @@ -import Plugin from "../Plugin"; +import Plugin from '../Plugin'; export default class MollieBancomatPlugin extends Plugin { diff --git a/src/Resources/app/storefront/src/mollie-payments/plugins/creditcard-mandate-manage.plugin.js b/src/Resources/app/storefront/src/mollie-payments/plugins/creditcard-mandate-manage.plugin.js index 0903a86c0..10cd4e9fb 100644 --- a/src/Resources/app/storefront/src/mollie-payments/plugins/creditcard-mandate-manage.plugin.js +++ b/src/Resources/app/storefront/src/mollie-payments/plugins/creditcard-mandate-manage.plugin.js @@ -1,5 +1,5 @@ import HttpClient from '../services/HttpClient'; -import Plugin from "../Plugin"; +import Plugin from '../Plugin'; /** * This plugin manage the credit card mandate of customer diff --git a/src/Resources/app/storefront/src/mollie-payments/plugins/mollie-express-actions.plugin.js b/src/Resources/app/storefront/src/mollie-payments/plugins/mollie-express-actions.plugin.js new file mode 100644 index 000000000..335a8704e --- /dev/null +++ b/src/Resources/app/storefront/src/mollie-payments/plugins/mollie-express-actions.plugin.js @@ -0,0 +1,95 @@ +import Plugin from '../Plugin'; +import PrivacyNoteElement from '../repository/PrivacyNoteElement'; +import BuyButtonRepository from '../repository/BuyButtonRepository'; +import ExpressButtonsRepository from '../repository/ExpressButtonsRepository'; +import ExpressAddToCart from '../services/ExpressAddToCart'; + +export const MOLLIE_EXPRESS_CHECKOUT_EVENT = 'MollieStartExpressCheckout'; + +export class MollieExpressActions extends Plugin { + + + init() { + + const pluginOffCanvasInstances = window.PluginManager.getPluginList().OffCanvasCart.get('instances'); + if (pluginOffCanvasInstances.length > 0) { + pluginOffCanvasInstances.forEach((pluginOffCanvas) => { + pluginOffCanvas.$emitter.subscribe('offCanvasOpened', this.bindEvents.bind(this)); + }); + } + + this.bindEvents(); + + } + + bindEvents() { + const expressButtonsRepository = new ExpressButtonsRepository(); + const expressButtons = expressButtonsRepository.findAll(); + + if (expressButtons.length === 0) { + return; + } + + const buyButtonRepository = new BuyButtonRepository(); + + expressButtons.forEach((button) => { + + + button.classList.remove('d-none'); + button.addEventListener('click', this.onButtonClick) + + const buyButton = buyButtonRepository.find(button); + if (!(buyButton instanceof HTMLButtonElement)) { + return; + } + + if (buyButton.hasAttribute('disabled')) { + button.classList.add('d-none'); + button.removeEventListener('click', this.onButtonClick) + } + + const buyButtonForm = buyButton.closest('form'); + if (!(buyButtonForm instanceof HTMLFormElement)) { + return; + } + + buyButtonForm.addEventListener('change', () => { + + button.classList.remove('d-none'); + button.addEventListener('click', this.onButtonClick) + + if (buyButton.hasAttribute('disabled')) { + + button.classList.add('d-none'); + button.removeEventListener('click', this.onButtonClick) + } + }) + + }); + } + + async onButtonClick(event) { + let target = event.target; + if (!(target instanceof HTMLButtonElement)) { + target = target.closest('button'); + } + + const privacyNote = new PrivacyNoteElement(); + + const privacyNoteElement = privacyNote.find(target); + + if (privacyNoteElement instanceof HTMLDivElement) { + privacyNoteElement.classList.add('was-validated'); + const isValid = privacyNote.validate(privacyNoteElement); + if (isValid === false) { + return; + } + } + const mollieEvent = new Event(MOLLIE_EXPRESS_CHECKOUT_EVENT); + + const expressAddToCart = new ExpressAddToCart(); + await expressAddToCart.addItemToCart(target).then(() => { + target.dispatchEvent(mollieEvent); + }); + } +} \ No newline at end of file diff --git a/src/Resources/app/storefront/src/mollie-payments/plugins/paypal-express.plugin.js b/src/Resources/app/storefront/src/mollie-payments/plugins/paypal-express.plugin.js index 3e00d7e1c..f9aa063ad 100644 --- a/src/Resources/app/storefront/src/mollie-payments/plugins/paypal-express.plugin.js +++ b/src/Resources/app/storefront/src/mollie-payments/plugins/paypal-express.plugin.js @@ -1,27 +1,58 @@ import Plugin from '@shopware-storefront-sdk/plugin-system/plugin.class'; - +import {MOLLIE_EXPRESS_CHECKOUT_EVENT} from './mollie-express-actions.plugin'; +import ExpressButtonsRepository from '../repository/ExpressButtonsRepository'; +import PrivacyNoteElement from '../repository/PrivacyNoteElement'; export default class PayPalExpressPlugin extends Plugin { init() { - - // Shopware 6.4 has product-detail-quantity-select, shopware 6.5 product-detail-quantity-input - let shopwareQuantityInput = document.querySelector('#productDetailPageBuyProductForm *[class*="quantity"]:not(div)'); - if(shopwareQuantityInput === null){ - return; + const pluginOffCanvasInstances = window.PluginManager.getPluginList().OffCanvasCart.get('instances'); + if (pluginOffCanvasInstances.length > 0) { + pluginOffCanvasInstances.forEach((pluginOffCanvas) => { + pluginOffCanvas.$emitter.subscribe('offCanvasOpened', this.bindEvents.bind(this)); + }); } + this.bindEvents(); + + } - const paypalExpressQuantityInput = document.querySelector('#molliePayPalExpressProductDetailForm input[name="quantity"]'); - if(paypalExpressQuantityInput === null){ + bindEvents() { + const expressButtonsRepository = new ExpressButtonsRepository(); + + const expressButtons = expressButtonsRepository.findAll('.mollie-paypal-button'); + + if (expressButtons.length === 0) { return; } - shopwareQuantityInput.addEventListener('change',function (){ - - paypalExpressQuantityInput.value = this.value; + expressButtons.forEach((button) => { + button.addEventListener(MOLLIE_EXPRESS_CHECKOUT_EVENT, this.onExpressCheckout) }); } + onExpressCheckout(event) { + const clickedButton = event.target; + + const submitUrl = clickedButton.getAttribute('data-form-action'); + + const form = document.createElement('form'); + form.setAttribute('action', submitUrl); + form.setAttribute('method', 'POST'); + + const privacyNoteElement = new PrivacyNoteElement(); + const privacyNote = privacyNoteElement.find(clickedButton); + if (privacyNote instanceof HTMLDivElement) { + const checkbox = privacyNoteElement.getCheckbox(privacyNote); + const checkboxValue = checkbox.checked ? 'on' : ''; + form.setAttribute('acceptedDataProtection', checkboxValue); + } + + document.body.insertAdjacentElement('beforeend', form); + + form.submit(); + } + + } \ No newline at end of file diff --git a/src/Resources/app/storefront/src/mollie-payments/plugins/pos-terminal.plugin.js b/src/Resources/app/storefront/src/mollie-payments/plugins/pos-terminal.plugin.js index 610f1dd7c..fb7015452 100644 --- a/src/Resources/app/storefront/src/mollie-payments/plugins/pos-terminal.plugin.js +++ b/src/Resources/app/storefront/src/mollie-payments/plugins/pos-terminal.plugin.js @@ -1,5 +1,5 @@ import HttpClient from '../services/HttpClient' -import Plugin from "../Plugin"; +import Plugin from '../Plugin'; export default class MolliePosTerminalPlugin extends Plugin { diff --git a/src/Resources/app/storefront/src/mollie-payments/repository/BuyButtonRepository.js b/src/Resources/app/storefront/src/mollie-payments/repository/BuyButtonRepository.js new file mode 100644 index 000000000..1f903611c --- /dev/null +++ b/src/Resources/app/storefront/src/mollie-payments/repository/BuyButtonRepository.js @@ -0,0 +1,17 @@ +import BuyElementRepository from './BuyElementRepository'; + +export default class BuyButtonRepository { + constructor() { + this.buyElementRepository = new BuyElementRepository(); + } + + find(button) { + const buyElementContainer = this.buyElementRepository.find(button); + if (buyElementContainer === null) { + return null; + } + return buyElementContainer.querySelector('.btn-buy'); + } + + +} \ No newline at end of file diff --git a/src/Resources/app/storefront/src/mollie-payments/repository/BuyElementRepository.js b/src/Resources/app/storefront/src/mollie-payments/repository/BuyElementRepository.js new file mode 100644 index 000000000..60ecf435c --- /dev/null +++ b/src/Resources/app/storefront/src/mollie-payments/repository/BuyElementRepository.js @@ -0,0 +1,21 @@ +export default class BuyElementRepository { + find(button) { + let buyElementContainer = button.closest('.product-action'); + + if (buyElementContainer === null) { + buyElementContainer = button.closest('.product-detail-form-container'); + } + if(buyElementContainer === null){ + buyElementContainer = button.closest('.offcanvas-cart-actions'); + } + + if(buyElementContainer === null){ + buyElementContainer = button.closest('.checkout-aside-container'); + } + if(buyElementContainer === null){ + buyElementContainer = button.closest('.checkout-main'); + } + + return buyElementContainer; + } +} \ No newline at end of file diff --git a/src/Resources/app/storefront/src/mollie-payments/repository/ExpressButtonsRepository.js b/src/Resources/app/storefront/src/mollie-payments/repository/ExpressButtonsRepository.js new file mode 100644 index 000000000..5c427218a --- /dev/null +++ b/src/Resources/app/storefront/src/mollie-payments/repository/ExpressButtonsRepository.js @@ -0,0 +1,9 @@ +export default class ExpressButtonsRepository { + findAll(additionalSelector = null) { + let selector = '.mollie-express-button'; + if(additionalSelector !== null){ + selector += additionalSelector; + } + return document.querySelectorAll(selector); + } +} \ No newline at end of file diff --git a/src/Resources/app/storefront/src/mollie-payments/repository/PrivacyNoteElement.js b/src/Resources/app/storefront/src/mollie-payments/repository/PrivacyNoteElement.js new file mode 100644 index 000000000..e91271a18 --- /dev/null +++ b/src/Resources/app/storefront/src/mollie-payments/repository/PrivacyNoteElement.js @@ -0,0 +1,31 @@ +import BuyElementRepository from './BuyElementRepository'; + +export default class PrivacyNoteElement { + constructor() { + this.buyElementRepository = new BuyElementRepository(); + } + + find(button) { + const buyElementContainer = this.buyElementRepository.find(button); + if(buyElementContainer === null){ + return null; + } + return buyElementContainer.querySelector('.mollie-privacy-note'); + } + getCheckbox(privacyNote){ + return privacyNote.querySelector('input[name="acceptedDataProtection"]'); + } + validate(privacyNote) { + + const dataProtection = this.getCheckbox(privacyNote); + + const dataProtectionValue = dataProtection.checked ? 1 : 0; + dataProtection.classList.remove('is-invalid'); + + if (dataProtectionValue === 0) { + dataProtection.classList.add('is-invalid'); + return false; + } + return true; + } +} \ No newline at end of file diff --git a/src/Resources/app/storefront/src/mollie-payments/services/ApplePaySessionFactory.js b/src/Resources/app/storefront/src/mollie-payments/services/ApplePaySessionFactory.js index 54974e0c3..c01f09152 100644 --- a/src/Resources/app/storefront/src/mollie-payments/services/ApplePaySessionFactory.js +++ b/src/Resources/app/storefront/src/mollie-payments/services/ApplePaySessionFactory.js @@ -1,4 +1,4 @@ -import HttpClient from "./HttpClient"; +import HttpClient from './HttpClient'; export default class ApplePaySessionFactory { /** diff --git a/src/Resources/app/storefront/src/mollie-payments/services/ExpressAddToCart.js b/src/Resources/app/storefront/src/mollie-payments/services/ExpressAddToCart.js new file mode 100644 index 000000000..42aca8b8b --- /dev/null +++ b/src/Resources/app/storefront/src/mollie-payments/services/ExpressAddToCart.js @@ -0,0 +1,29 @@ +import BuyButtonRepository from '../repository/BuyButtonRepository'; + +export default class ExpressAddToCart { + async addItemToCart(button) { + const buyButtonRepository = new BuyButtonRepository(); + const buyButton = buyButtonRepository.find(button); + if (!(buyButton instanceof HTMLButtonElement)) { + return new Promise((resolve) => { + resolve(); + }) + + } + const buyButtonForm = buyButton.closest('form'); + if (!(buyButtonForm instanceof HTMLFormElement)) { + return new Promise((resolve) => { + resolve(); + }) + } + + const formData = new FormData(buyButtonForm); + formData.delete('redirectTo'); + formData.append('isExpressCheckout', '1'); + + return fetch(buyButtonForm.action, { + method: buyButtonForm.method, + body: formData, + }); + } +} \ No newline at end of file diff --git a/src/Resources/app/storefront/src/register.js b/src/Resources/app/storefront/src/register.js index f18062102..fd23720dd 100644 --- a/src/Resources/app/storefront/src/register.js +++ b/src/Resources/app/storefront/src/register.js @@ -6,6 +6,7 @@ import MollieCreditCardMandateManage from './mollie-payments/plugins/creditcard- import MolliePosTerminalPlugin from './mollie-payments/plugins/pos-terminal.plugin'; import PayPalExpressPlugin from './mollie-payments/plugins/paypal-express.plugin'; import MollieBancomatPlugin from './mollie-payments/plugins/bancomat-plugin'; +import {MollieExpressActions} from './mollie-payments/plugins/mollie-express-actions.plugin'; export default class MollieRegistration { @@ -20,6 +21,7 @@ export default class MollieRegistration { // global plugins // ----------------------------------------------------------------------------- // hide apple pay direct buttons across the whole shop, if not available + pluginManager.register('MollieExpressActions', MollieExpressActions); pluginManager.register('MollieApplePayDirect', MollieApplePayDirect); diff --git a/src/Resources/app/storefront/src/scss/display.scss b/src/Resources/app/storefront/src/scss/display.scss index 390a283e2..a0e65d37b 100644 --- a/src/Resources/app/storefront/src/scss/display.scss +++ b/src/Resources/app/storefront/src/scss/display.scss @@ -1,4 +1,5 @@ /* stylelint-disable selector-id-pattern, declaration-no-important */ +.mollie-express-button.d-none, .js-apple-pay .d-none, .js-apple-pay-container .d-none, .mollie-pos-terminals .d-none, diff --git a/src/Resources/views/mollie/component/express-privacy-notice.html.twig b/src/Resources/views/mollie/component/express-privacy-notice.html.twig index 3e7a3c4e1..55bb8f122 100644 --- a/src/Resources/views/mollie/component/express-privacy-notice.html.twig +++ b/src/Resources/views/mollie/component/express-privacy-notice.html.twig @@ -1,7 +1,9 @@ {% block mollie_express_privacy_notice %} {% if mollie_express_required_data_protection and visible %} - {% sw_include '@Storefront/storefront/component/privacy-notice.html.twig' %} +
+ {% sw_include '@Storefront/storefront/component/privacy-notice.html.twig' %} +
{% endif %} {% endblock %} \ No newline at end of file diff --git a/src/Resources/views/mollie/component/paypal-express-button.twig b/src/Resources/views/mollie/component/paypal-express-button.twig index 0bcdc7d5e..740c5bbb3 100644 --- a/src/Resources/views/mollie/component/paypal-express-button.twig +++ b/src/Resources/views/mollie/component/paypal-express-button.twig @@ -32,7 +32,7 @@ {% endif %}
- diff --git a/src/Service/CustomFieldsInterface.php b/src/Service/CustomFieldsInterface.php index b2e031e4f..a068deecb 100644 --- a/src/Service/CustomFieldsInterface.php +++ b/src/Service/CustomFieldsInterface.php @@ -31,4 +31,6 @@ interface CustomFieldsInterface public const PAYPAL_EXPRESS_SESSION_ID_KEY = 'mollie_ppe_session_id'; public const PAYPAL_EXPRESS_AUTHENTICATE_ID = 'mollie_ppe_auth_id'; + + public const ACCEPTED_DATA_PROTECTION = 'acceptedDataProtection'; } diff --git a/src/Service/CustomerService.php b/src/Service/CustomerService.php index bdb2091d3..41267ebf5 100644 --- a/src/Service/CustomerService.php +++ b/src/Service/CustomerService.php @@ -734,7 +734,7 @@ public function reuseOrCreateAddresses(CustomerEntity $customer, AddressStruct $ return $this->customerRepository->upsert([$customer], $context); } - public function createGuestAccount(AddressStruct $shippingAddress, string $paymentMethodId, int $acceptedDataProtection, SalesChannelContext $context, ?AddressStruct $billingAddress = null): ?CustomerEntity + public function createGuestAccount(AddressStruct $shippingAddress, string $paymentMethodId, SalesChannelContext $context, ?int $acceptedDataProtection, ?AddressStruct $billingAddress = null): ?CustomerEntity { $countryId = $this->getCountryId($shippingAddress->getCountryCode(), $context->getContext()); $salutationId = $this->getSalutationId($context->getContext()); @@ -745,7 +745,11 @@ public function createGuestAccount(AddressStruct $shippingAddress, string $payme $data->set('firstName', $shippingAddress->getFirstName()); $data->set('lastName', $shippingAddress->getLastName()); $data->set('email', $shippingAddress->getEmail()); - $data->set('acceptedDataProtection', $acceptedDataProtection); + + if ($acceptedDataProtection !== null) { + $data->set('acceptedDataProtection', $acceptedDataProtection); + } + $shippingAddressData = new RequestDataBag(); $shippingAddressData->set('firstName', $shippingAddress->getFirstName()); diff --git a/src/Service/CustomerServiceInterface.php b/src/Service/CustomerServiceInterface.php index 8ab308cf2..b131baa24 100644 --- a/src/Service/CustomerServiceInterface.php +++ b/src/Service/CustomerServiceInterface.php @@ -44,7 +44,7 @@ public function getCustomerStruct(string $customerId, Context $context): Custome */ public function getAddressArray($address, CustomerEntity $customer): array; - public function createGuestAccount(AddressStruct $shippingAddress, string $paymentMethodId, int $acceptedDataProtection, SalesChannelContext $context, ?AddressStruct $billingAddress = null): ?CustomerEntity; + public function createGuestAccount(AddressStruct $shippingAddress, string $paymentMethodId, SalesChannelContext $context, ?int $acceptedDataProtection, ?AddressStruct $billingAddress = null): ?CustomerEntity; public function getCountryId(string $countryCode, Context $context): ?string; diff --git a/tests/PHPUnit/Fakes/FakeCustomerService.php b/tests/PHPUnit/Fakes/FakeCustomerService.php index ecc8e0373..f17d01b6f 100644 --- a/tests/PHPUnit/Fakes/FakeCustomerService.php +++ b/tests/PHPUnit/Fakes/FakeCustomerService.php @@ -84,7 +84,7 @@ public function getAddressArray($address, CustomerEntity $customer): array return []; } - public function createGuestAccount(AddressStruct $shippingAddress, string $paymentMethodId, int $acceptedDataProtection, SalesChannelContext $context, ?AddressStruct $billingAddress = null): ?CustomerEntity{ + public function createGuestAccount(AddressStruct $shippingAddress, string $paymentMethodId, SalesChannelContext $context,?int $acceptedDataProtection, ?AddressStruct $billingAddress = null): ?CustomerEntity{ return null; }