From 719ca14c3a58daafcee604ced80f8fb93afa27d2 Mon Sep 17 00:00:00 2001 From: Vitalij Mik Date: Mon, 2 Sep 2024 13:01:05 +0200 Subject: [PATCH] PISHPS-338: custom product compatibility (#820) * NTR: PISHPS-338: Add compatiblity with custom products * NTR: stan fix * NTR: fix recursion in sw6.4 * NTR: fix container * NTR: fix container --------- Co-authored-by: Vitalij Mik --- src/Checkout/Cart/ExpressCartItemAddRoute.php | 70 ++++ .../ApplePayDirect/ApplePayDirect.php | 6 - .../ApplePayDirectControllerBase.php | 42 +-- .../ApplePayDirectControllerBase.php | 35 +- .../plugins/apple-pay-direct.plugin.js | 330 ++++-------------- .../services/ApplePaySessionFactory.js | 224 ++++++++++++ src/Resources/config/services.xml | 6 + src/Resources/config/services/controller.xml | 2 - src/Resources/config/services/subscriber.xml | 6 + .../ExpressCartRestoreSubscriber.php | 43 +++ 10 files changed, 446 insertions(+), 318 deletions(-) create mode 100644 src/Checkout/Cart/ExpressCartItemAddRoute.php create mode 100644 src/Resources/app/storefront/src/mollie-payments/services/ApplePaySessionFactory.js create mode 100644 src/Subscriber/ExpressCartRestoreSubscriber.php diff --git a/src/Checkout/Cart/ExpressCartItemAddRoute.php b/src/Checkout/Cart/ExpressCartItemAddRoute.php new file mode 100644 index 000000000..39d212fab --- /dev/null +++ b/src/Checkout/Cart/ExpressCartItemAddRoute.php @@ -0,0 +1,70 @@ +cartItemAddRoute = $cartItemAddRoute; + $this->container = $container; + } + + public function getDecorated(): AbstractCartItemAddRoute + { + return $this->cartItemAddRoute; + } + + /** + * @param Request $request + * @param Cart $cart + * @param SalesChannelContext $context + * @param ?array $items + * @return CartResponse + */ + public function add(Request $request, Cart $cart, SalesChannelContext $context, ?array $items): CartResponse + { + //we have to create a new request from global variables, because the request is not set here in the route + $request = Request::createFromGlobals(); + + $isExpressCheckout = (bool)$request->get('isExpressCheckout', false); + if ($isExpressCheckout === false) { + return $this->getDecorated()->add($request, $cart, $context, $items); + } + $cartBackupService = $this->container->get(CartBackupService::class); //Shopware 6.4 have circular injection, we have to use contaier + if (!$cartBackupService->isBackupExisting($context)) { + $cartBackupService->backupCart($context); + } + + $cartService = $this->container->get(CartService::class); + + $cart = $cartService->getCalculatedMainCart($context); + + # clear existing cart and also update it to save it + $cart->setLineItems(new LineItemCollection()); + $cartService->updateCart($cart); + + return $this->getDecorated()->add($request, $cart, $context, $items); + } +} diff --git a/src/Components/ApplePayDirect/ApplePayDirect.php b/src/Components/ApplePayDirect/ApplePayDirect.php index 6247d3e35..320c97e51 100644 --- a/src/Components/ApplePayDirect/ApplePayDirect.php +++ b/src/Components/ApplePayDirect/ApplePayDirect.php @@ -355,12 +355,6 @@ public function prepareCustomer(string $firstname, string $lastname, string $ema } - # we clear our cart backup now - # we are in the user redirection process where a restoring wouldn't make sense - # because from now on we would end on the cart page where we could even switch payment method. - $this->cartBackupService->clearBackup($context); - - $applePayID = $this->getActiveApplePayID($context); # if we are not logged in, diff --git a/src/Controller/StoreApi/ApplePayDirect/ApplePayDirectControllerBase.php b/src/Controller/StoreApi/ApplePayDirect/ApplePayDirectControllerBase.php index 3c64c5e5c..5ffb41850 100644 --- a/src/Controller/StoreApi/ApplePayDirect/ApplePayDirectControllerBase.php +++ b/src/Controller/StoreApi/ApplePayDirect/ApplePayDirectControllerBase.php @@ -203,29 +203,29 @@ public function pay(RequestDataBag $data, SalesChannelContext $context): StoreAp if (empty($paymentToken)) { throw new \Exception('PaymentToken not found!'); } + try { + # make sure to create a customer if necessary + # then update to our apple pay payment method + # and return the new context + $newContext = $this->applePay->prepareCustomer( + $firstname, + $lastname, + $email, + $street, + $zipcode, + $city, + $countryCode, + $phone, + $paymentToken, + $context + ); + + # we only start our TRY/CATCH here! + # we always need to throw exceptions on an API level + # but if something BELOW breaks, we want to navigate to the error page. + # customers are ready, data is ready, but the handling has a problem. - # make sure to create a customer if necessary - # then update to our apple pay payment method - # and return the new context - $newContext = $this->applePay->prepareCustomer( - $firstname, - $lastname, - $email, - $street, - $zipcode, - $city, - $countryCode, - $phone, - $paymentToken, - $context - ); - - # we only start our TRY/CATCH here! - # we always need to throw exceptions on an API level - # but if something BELOW breaks, we want to navigate to the error page. - # customers are ready, data is ready, but the handling has a problem. - try { # create our new Shopware Order $order = $this->applePay->createOrder($newContext); diff --git a/src/Controller/Storefront/ApplePayDirect/ApplePayDirectControllerBase.php b/src/Controller/Storefront/ApplePayDirect/ApplePayDirectControllerBase.php index a892125fc..23a730737 100644 --- a/src/Controller/Storefront/ApplePayDirect/ApplePayDirectControllerBase.php +++ b/src/Controller/Storefront/ApplePayDirect/ApplePayDirectControllerBase.php @@ -8,7 +8,6 @@ use Kiener\MolliePayments\Components\ApplePayDirect\ApplePayDirect; use Kiener\MolliePayments\Controller\Storefront\AbstractStoreFrontController; use Kiener\MolliePayments\Repository\Customer\CustomerRepositoryInterface; -use Kiener\MolliePayments\Service\Cart\CartBackupService; use Kiener\MolliePayments\Service\OrderService; use Kiener\MolliePayments\Traits\Storefront\RedirectTrait; use Psr\Log\LoggerInterface; @@ -21,7 +20,6 @@ use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\HttpFoundation\Session\Flash\FlashBag; use Symfony\Component\Routing\RouterInterface; use Throwable; @@ -42,20 +40,12 @@ class ApplePayDirectControllerBase extends AbstractStoreFrontController */ private $applePay; - /** - * @var CartBackupService - */ - private $cartBackupService; /** * @var RouterInterface */ private $router; - /** - * @var ?FlashBag - */ - private $flashBag; /** * @var FlowBuilderDispatcherAdapterInterface @@ -87,21 +77,18 @@ class ApplePayDirectControllerBase extends AbstractStoreFrontController * @param ApplePayDirect $applePay * @param RouterInterface $router * @param LoggerInterface $logger - * @param CartBackupService $cartBackup - * @param null|FlashBag $sessionFlashBag * @param FlowBuilderFactory $flowBuilderFactory * @param FlowBuilderEventFactory $flowBuilderEventFactory * @param CustomerRepositoryInterface $repoCustomers * @param OrderService $orderService * @throws \Exception */ - public function __construct(ApplePayDirect $applePay, RouterInterface $router, LoggerInterface $logger, CartBackupService $cartBackup, ?FlashBag $sessionFlashBag, FlowBuilderFactory $flowBuilderFactory, FlowBuilderEventFactory $flowBuilderEventFactory, CustomerRepositoryInterface $repoCustomers, OrderService $orderService) + public function __construct(ApplePayDirect $applePay, RouterInterface $router, LoggerInterface $logger, FlowBuilderFactory $flowBuilderFactory, FlowBuilderEventFactory $flowBuilderEventFactory, CustomerRepositoryInterface $repoCustomers, OrderService $orderService) { $this->applePay = $applePay; $this->router = $router; $this->logger = $logger; - $this->flashBag = $sessionFlashBag; - $this->cartBackupService = $cartBackup; + $this->repoCustomers = $repoCustomers; $this->orderService = $orderService; @@ -329,12 +316,6 @@ public function setShippingMethod(SalesChannelContext $context, Request $request public function startPayment(SalesChannelContext $context, Request $request): Response { try { - # we clear our cart backup now - # we are in the user redirection process where a restoring wouldnt make sense - # because from now on we would end on the cart page where we could even switch payment method. - $this->cartBackupService->clearBackup($context); - - $email = (string)$request->get('email', ''); $firstname = (string)$request->get('firstname', ''); $lastname = (string)$request->get('lastname', ''); @@ -375,10 +356,8 @@ public function startPayment(SalesChannelContext $context, Request $request): Re # if we have an error here, we have to redirect to the confirm page $returnUrl = $this->getCheckoutConfirmPage($this->router); # also add an error for our target page - if ($this->flashBag !== null) { - $this->flashBag->add('danger', $this->trans(self::SNIPPET_ERROR)); - } + $this->addFlash('danger', $this->trans(self::SNIPPET_ERROR)); return new RedirectResponse($returnUrl); } } @@ -420,10 +399,8 @@ public function finishPayment(SalesChannelContext $context, Request $request): R # if we have an error here, we have to redirect to the confirm page $returnUrl = $this->getCheckoutConfirmPage($this->router); # also add an error for our target page - if ($this->flashBag !== null) { - $this->flashBag->add('danger', $this->trans(self::SNIPPET_ERROR)); - } + $this->addFlash('danger', $this->trans(self::SNIPPET_ERROR)); return new RedirectResponse($returnUrl); } @@ -463,9 +440,7 @@ public function finishPayment(SalesChannelContext $context, Request $request): R $returnUrl = $this->getEditOrderPage($order->getId(), $this->router); # also add an error for our target page - if ($this->flashBag !== null) { - $this->flashBag->add('danger', $this->trans(self::SNIPPET_ERROR)); - } + $this->addFlash('danger', $this->trans(self::SNIPPET_ERROR)); # fire our custom storefront event $this->fireFlowBuilderStorefrontEvent(self::FLOWBUILDER_FAILED, $order, $context->getContext()); 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 2902af28f..3921d29e1 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,5 +1,6 @@ import HttpClient from '../services/HttpClient'; import Plugin from "../Plugin"; +import ApplePaySessionFactory from "../services/ApplePaySessionFactory"; export default class MollieApplePayDirect extends Plugin { @@ -23,27 +24,29 @@ export default class MollieApplePayDirect extends Plugin { // once the offcanvas is loaded (lazy) into the DOM const pluginOffCanvasInstances = window.PluginManager.getPluginList().OffCanvasCart.get("instances"); - if(pluginOffCanvasInstances.length === 0){ - return; + if (pluginOffCanvasInstances.length > 0) { + const pluginOffCanvas = pluginOffCanvasInstances[0]; + pluginOffCanvas.$emitter.subscribe('offCanvasOpened', me.initCurrentPage.bind(me)); } - const pluginOffCanvas = pluginOffCanvasInstances[0]; - pluginOffCanvas.$emitter.subscribe('offCanvasOpened', me.onOffCanvasOpened.bind(me)); + + + const submitForm = document.querySelector('#productDetailPageBuyProductForm'); + + if(submitForm !== null){ + this.checkSubmitButton(submitForm); + submitForm.addEventListener('change', (event) => { + this.checkSubmitButton(event.target.closest('form#productDetailPageBuyProductForm')); + this.initCurrentPage(); + }); + } // now update our current page this.initCurrentPage(); - } - /** - * - */ - onOffCanvasOpened() { - // as soon as the offcanvas is loaded - // we refresh our current page, because - // there might be a new apple pay button - this.initCurrentPage(); } + /** * */ @@ -63,7 +66,7 @@ export default class MollieApplePayDirect extends Plugin { // to avoid any wrong margins being displayed if (applePayContainers) { - applePayContainers.forEach(function (container){ + applePayContainers.forEach(function (container) { container.style.display = 'none'; container.classList.add('d-none'); }); @@ -82,6 +85,8 @@ export default class MollieApplePayDirect extends Plugin { const shopUrl = me.getShopUrl(applePayButtons[0]); + + // verify if apple pay is even allowed // in our current sales channel me.client.get( @@ -91,22 +96,50 @@ export default class MollieApplePayDirect extends Plugin { return; } - applePayContainers.forEach(function (container){ + applePayContainers.forEach(function (container) { container.classList.remove('d-none'); }); applePayButtons.forEach(function (button) { + + if(button.hasAttribute('disabled')){ + button.classList.add('d-none'); + button.removeEventListener('click', me.onButtonClick); + return; + } // Remove display none button.classList.remove('d-none'); // remove previous handlers (just in case) - button.removeEventListener('click', me.onButtonClick.bind(me)); + button.removeEventListener('click', me.onButtonClick); // add click event handlers - button.addEventListener('click', me.onButtonClick.bind(me)); + button.addEventListener('click', me.onButtonClick); }); } ); } + checkSubmitButton(form){ + const buyButton = form.querySelector('.btn-buy'); + + if(buyButton === null){ + return; + } + + const applePayButton = form.querySelector('.js-apple-pay'); + + if(applePayButton === null){ + return; + } + + if(applePayButton.hasAttribute('disabled')){ + applePayButton.removeAttribute('disabled'); + } + if(buyButton.hasAttribute('disabled')){ + applePayButton.setAttribute('disabled','disabled'); + } + + } + /** * * @param event @@ -115,14 +148,18 @@ export default class MollieApplePayDirect extends Plugin { event.preventDefault(); - const me = this; - const button = event.target; const form = button.parentNode; - const shopUrl = me.getShopUrl(button); + // get sales channel base URL + // so that our shop slug is correctly + let shopSlug = button.getAttribute('data-shop-url'); + + // remove trailing slash if existing + if (shopSlug.substr(-1) === '/') { + shopSlug = shopSlug.substr(0, shopSlug.length - 1); + } - const productId = form.querySelector('input[name="id"]').value; const countryCode = form.querySelector('input[name="countryCode"]').value; const currency = form.querySelector('input[name="currency"]').value; const mode = form.querySelector('input[name="mode"]').value; @@ -135,254 +172,29 @@ export default class MollieApplePayDirect extends Plugin { const isProductMode = (mode === 'productMode'); if (isProductMode) { - // our fallback is quantity 1 - var quantity = 1; - // if we have our sQuantity dropdown, use that quantity when adding the product - var quantitySelects = document.getElementsByClassName('product-detail-quantity-select') - if (quantitySelects.length > 0) { - quantity = quantitySelects[0].value; + let productForm = document.querySelector('#productDetailPageBuyProductForm'); + if (productForm === null) { + productForm = button.closest('.product-box').querySelector('form'); } - // also try our Shopware 6.5 selector - const sw65Selector = 'lineItems[' + productId + '][quantity]'; - quantitySelects = document.getElementsByName(sw65Selector) - if (quantitySelects.length > 0) { - quantity = quantitySelects[0].value; - } - me.addProductToCart(productId, quantity, shopUrl); - } + const formData = new FormData(productForm); + formData.delete("redirectTo"); + formData.append("isExpressCheckout", "1"); - var session = me.createApplePaySession(isProductMode, countryCode, currency,withPhone, shopUrl); - session.begin(); - } - /** - * - * @param id - * @param quantity - * @param shopSlug - */ - addProductToCart(id, quantity, shopSlug) { - this.client.post( - shopSlug + '/mollie/apple-pay/add-product', - JSON.stringify({ - 'id': id, - 'quantity': quantity, - }) - ) - } - - /** - * - * @param isProductMode - * @param country - * @param currency - * @param shopSlug - * @param withPhone - * @returns {ApplePaySession} - */ - createApplePaySession(isProductMode, country, currency, withPhone, shopSlug) { - - const me = this; - var shippingFields = [ - 'name', - 'email', - 'postalAddress', - ]; - - if(withPhone === 1){ - shippingFields.push('phone'); - } - - var request = { - countryCode: country, - currencyCode: currency, - requiredShippingContactFields: shippingFields, - supportedNetworks: [ - 'amex', - 'maestro', - 'masterCard', - 'visa', - 'vPay', - ], - merchantCapabilities: ['supports3DS'], - total: { - label: '', - amount: 0, - }, - }; - - // eslint-disable-next-line no-undef - const session = new ApplePaySession(this.APPLE_PAY_VERSION, request); - - session.onvalidatemerchant = function (event) { - me.client.post( - shopSlug + '/mollie/apple-pay/validate', - JSON.stringify({ - validationUrl: event.validationURL, - }), - (validationData) => { - if(validationData.success === false){ - throw new Error('Validation failed for URL: '+ event.validationURL); - } - const data = JSON.parse(validationData.session); - session.completeMerchantValidation(data); - }, - () => { - session.abort(); - } - ); - }; - - session.onshippingcontactselected = function (event) { - - var countryCode = ''; - - if (event.shippingContact.countryCode !== undefined) { - countryCode = event.shippingContact.countryCode; - } - - me.client.post( - shopSlug + '/mollie/apple-pay/shipping-methods', - JSON.stringify({ - countryCode: countryCode, - }), - (data) => { - if (data.success) { - session.completeShippingContactSelection( - // eslint-disable-next-line no-undef - ApplePaySession.STATUS_SUCCESS, - data.shippingmethods, - data.cart.total, - data.cart.items - ); - } else { - session.completeShippingContactSelection( - // eslint-disable-next-line no-undef - ApplePaySession.STATUS_FAILURE, - [], - { - label: '', - amount: 0, - pending: true, - }, - [] - ); - } - }, - () => { - session.abort(); - } - ); - }; - - session.onshippingmethodselected = function (event) { - - me.client.post( - shopSlug + '/mollie/apple-pay/set-shipping', - JSON.stringify({ - identifier: event.shippingMethod.identifier, - }), - (data) => { - if (data.success) { - session.completeShippingMethodSelection( - // eslint-disable-next-line no-undef - ApplePaySession.STATUS_SUCCESS, - data.cart.total, - data.cart.items - ); - } else { - session.completeShippingMethodSelection( - // eslint-disable-next-line no-undef - ApplePaySession.STATUS_FAILURE, - { - label: '', - amount: 0, - pending: true, - }, - [] - ); - } - }, - () => { - session.abort(); - } - ); - }; - - session.onpaymentauthorized = function (event) { - var paymentToken = event.payment.token; - paymentToken = JSON.stringify(paymentToken); - - // complete the session and notify the - // devices and the system that everything worked - // eslint-disable-next-line no-undef - session.completePayment(ApplePaySession.STATUS_SUCCESS); + fetch(productForm.action, { + method: productForm.method, + body: formData, + }); - // now finish our payment by filling a form - // and submitting it along with our payment token - me.finishPayment(shopSlug + '/mollie/apple-pay/start-payment', paymentToken, event.payment); - }; - - session.oncancel = function () { - - // if we are in product mode - // we should restore our original cart - if (isProductMode) { - me.client.post(shopSlug + '/mollie/apple-pay/restore-cart'); - } - }; - return session; - } - - /** - * - * @param checkoutURL - * @param paymentToken - * @param payment - */ - finishPayment(checkoutURL, paymentToken, payment) { - const createInput = function (name, val) { - const input = document.createElement('input'); - input.type = 'hidden'; - input.name = name; - input.value = val; - - return input; } + const applePaySessionFactory = new ApplePaySessionFactory(); + const session = applePaySessionFactory.create(isProductMode,countryCode,currency,withPhone,shopSlug); + session.begin(); - const form = document.createElement('form'); - form.action = checkoutURL; - form.method = 'POST'; - - let street = payment.shippingContact.addressLines[0]; - - if (payment.shippingContact.addressLines.length > 1) { - street += ' ' + payment.shippingContact.addressLines[1]; - } - - // add billing data - form.insertAdjacentElement('beforeend', createInput('email', payment.shippingContact.emailAddress)); - form.insertAdjacentElement('beforeend', createInput('lastname', payment.shippingContact.familyName)); - form.insertAdjacentElement('beforeend', createInput('firstname', payment.shippingContact.givenName)); - form.insertAdjacentElement('beforeend', createInput('street', street)); - form.insertAdjacentElement('beforeend', createInput('postalCode', payment.shippingContact.postalCode)); - form.insertAdjacentElement('beforeend', createInput('city', payment.shippingContact.locality)); - - if(payment.shippingContact.phoneNumber !== undefined && payment.shippingContact.phoneNumber.length > 0){ - form.insertAdjacentElement('beforeend', createInput('phone', payment.shippingContact.phoneNumber)); - } - - form.insertAdjacentElement('beforeend', createInput('countryCode', payment.shippingContact.countryCode)); - // also add our payment token - form.insertAdjacentElement('beforeend', createInput('paymentToken', paymentToken)); - - document.body.insertAdjacentElement('beforeend', form); - - form.submit(); } /** diff --git a/src/Resources/app/storefront/src/mollie-payments/services/ApplePaySessionFactory.js b/src/Resources/app/storefront/src/mollie-payments/services/ApplePaySessionFactory.js new file mode 100644 index 000000000..e228db462 --- /dev/null +++ b/src/Resources/app/storefront/src/mollie-payments/services/ApplePaySessionFactory.js @@ -0,0 +1,224 @@ +import HttpClient from "./HttpClient"; + +export default class ApplePaySessionFactory { + /** + * + * @type {number} + */ + APPLE_PAY_VERSION = 3; + + constructor() { + this.client = new HttpClient(); + } + + /** + * + * @param isProductMode + * @param country + * @param currency + * @param shopSlug + * @param withPhone + * @returns {ApplePaySession} + */ + create(isProductMode, country, currency, withPhone, shopSlug) { + + const me = this; + var shippingFields = [ + 'name', + 'email', + 'postalAddress', + ]; + + if (withPhone === 1) { + shippingFields.push('phone'); + } + + var request = { + countryCode: country, + currencyCode: currency, + requiredShippingContactFields: shippingFields, + supportedNetworks: [ + 'amex', + 'maestro', + 'masterCard', + 'visa', + 'vPay', + ], + merchantCapabilities: ['supports3DS'], + total: { + label: '', + amount: 0, + }, + }; + + // eslint-disable-next-line no-undef + const session = new ApplePaySession(this.APPLE_PAY_VERSION, request); + + session.onvalidatemerchant = function (event) { + me.client.post( + shopSlug + '/mollie/apple-pay/validate', + JSON.stringify({ + validationUrl: event.validationURL, + }), + (validationData) => { + if (validationData.success === false) { + throw new Error('Validation failed for URL: ' + event.validationURL); + } + const data = JSON.parse(validationData.session); + session.completeMerchantValidation(data); + }, + () => { + session.abort(); + } + ); + }; + + session.onshippingcontactselected = function (event) { + + var countryCode = ''; + + if (event.shippingContact.countryCode !== undefined) { + countryCode = event.shippingContact.countryCode; + } + + me.client.post( + shopSlug + '/mollie/apple-pay/shipping-methods', + JSON.stringify({ + countryCode: countryCode, + }), + (data) => { + if (data.success) { + session.completeShippingContactSelection( + // eslint-disable-next-line no-undef + ApplePaySession.STATUS_SUCCESS, + data.shippingmethods, + data.cart.total, + data.cart.items + ); + } else { + session.completeShippingContactSelection( + // eslint-disable-next-line no-undef + ApplePaySession.STATUS_FAILURE, + [], + { + label: '', + amount: 0, + pending: true, + }, + [] + ); + } + }, + () => { + session.abort(); + } + ); + }; + + session.onshippingmethodselected = function (event) { + + me.client.post( + shopSlug + '/mollie/apple-pay/set-shipping', + JSON.stringify({ + identifier: event.shippingMethod.identifier, + }), + (data) => { + if (data.success) { + session.completeShippingMethodSelection( + // eslint-disable-next-line no-undef + ApplePaySession.STATUS_SUCCESS, + data.cart.total, + data.cart.items + ); + } else { + session.completeShippingMethodSelection( + // eslint-disable-next-line no-undef + ApplePaySession.STATUS_FAILURE, + { + label: '', + amount: 0, + pending: true, + }, + [] + ); + } + }, + () => { + session.abort(); + } + ); + }; + + session.onpaymentauthorized = function (event) { + var paymentToken = event.payment.token; + paymentToken = JSON.stringify(paymentToken); + + // complete the session and notify the + // devices and the system that everything worked + // eslint-disable-next-line no-undef + session.completePayment(ApplePaySession.STATUS_SUCCESS); + + // now finish our payment by filling a form + // and submitting it along with our payment token + me.finishPayment(shopSlug + '/mollie/apple-pay/start-payment', paymentToken, event.payment); + }; + + session.oncancel = function () { + + // if we are in product mode + // we should restore our original cart + if (isProductMode) { + me.client.post(shopSlug + '/mollie/apple-pay/restore-cart'); + } + }; + + return session; + } + + /** + * + * @param checkoutURL + * @param paymentToken + * @param payment + */ + finishPayment(checkoutURL, paymentToken, payment) { + const createInput = function (name, val) { + const input = document.createElement('input'); + input.type = 'hidden'; + input.name = name; + input.value = val; + + return input; + } + + const form = document.createElement('form'); + form.action = checkoutURL; + form.method = 'POST'; + + let street = payment.shippingContact.addressLines[0]; + + if (payment.shippingContact.addressLines.length > 1) { + street += ' ' + payment.shippingContact.addressLines[1]; + } + + // add billing data + form.insertAdjacentElement('beforeend', createInput('email', payment.shippingContact.emailAddress)); + form.insertAdjacentElement('beforeend', createInput('lastname', payment.shippingContact.familyName)); + form.insertAdjacentElement('beforeend', createInput('firstname', payment.shippingContact.givenName)); + form.insertAdjacentElement('beforeend', createInput('street', street)); + form.insertAdjacentElement('beforeend', createInput('postalCode', payment.shippingContact.postalCode)); + form.insertAdjacentElement('beforeend', createInput('city', payment.shippingContact.locality)); + + if (payment.shippingContact.phoneNumber !== undefined && payment.shippingContact.phoneNumber.length > 0) { + form.insertAdjacentElement('beforeend', createInput('phone', payment.shippingContact.phoneNumber)); + } + + form.insertAdjacentElement('beforeend', createInput('countryCode', payment.shippingContact.countryCode)); + // also add our payment token + form.insertAdjacentElement('beforeend', createInput('paymentToken', paymentToken)); + + document.body.insertAdjacentElement('beforeend', form); + + form.submit(); + } +} \ No newline at end of file diff --git a/src/Resources/config/services.xml b/src/Resources/config/services.xml index f0638fa31..0d4d82a60 100644 --- a/src/Resources/config/services.xml +++ b/src/Resources/config/services.xml @@ -231,6 +231,12 @@ + + + + + + diff --git a/src/Resources/config/services/controller.xml b/src/Resources/config/services/controller.xml index eae98a8e7..a31ed2697 100644 --- a/src/Resources/config/services/controller.xml +++ b/src/Resources/config/services/controller.xml @@ -181,8 +181,6 @@ - - diff --git a/src/Resources/config/services/subscriber.xml b/src/Resources/config/services/subscriber.xml index fad289545..641937bc9 100644 --- a/src/Resources/config/services/subscriber.xml +++ b/src/Resources/config/services/subscriber.xml @@ -71,5 +71,11 @@ + + + + + + diff --git a/src/Subscriber/ExpressCartRestoreSubscriber.php b/src/Subscriber/ExpressCartRestoreSubscriber.php new file mode 100644 index 000000000..3f63233da --- /dev/null +++ b/src/Subscriber/ExpressCartRestoreSubscriber.php @@ -0,0 +1,43 @@ +cartBackupService = $cartBackupService; + } + + public static function getSubscribedEvents(): array + { + return [ + CheckoutFinishPageLoadedEvent::class => 'onRestoreBackup', + CheckoutConfirmPageLoadedEvent::class => 'onRestoreBackup', + CheckoutRegisterPageLoadedEvent::class => 'onRestoreBackup' + ]; + } + + public function onRestoreBackup(PageLoadedEvent $event): void + { + $context = $event->getSalesChannelContext(); + + if ($this->cartBackupService->isBackupExisting($context)) { + $this->cartBackupService->restoreCart($context); + $this->cartBackupService->clearBackup($context); + } + } +}