Skip to content

Commit

Permalink
NTR: refactor apple pay
Browse files Browse the repository at this point in the history
  • Loading branch information
Vitalij Mik committed Oct 22, 2024
1 parent 8fa9706 commit 8afe095
Show file tree
Hide file tree
Showing 16 changed files with 132 additions and 247 deletions.
16 changes: 13 additions & 3 deletions makefile
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,18 @@ stryker: ##2 Starts the Stryker Jest Mutation Tests
@# cd ./src/Resources/app/storefront && ./node_modules/.bin/stryker run .stryker.conf.json

eslint: ##2 Starts the ESLinter
ifndef mode
cd ./src/Resources/app/administration && ./node_modules/.bin/eslint --config ./.eslintrc.json ./src
cd ./src/Resources/app/storefront && ./node_modules/.bin/eslint --config ./.eslintrc.json ./src
endif
ifeq ($(mode), no-dry-run)
cd ./src/Resources/app/administration && ./node_modules/.bin/eslint --config ./.eslintrc.json ./src --fix
cd ./src/Resources/app/storefront && ./node_modules/.bin/eslint --config ./.eslintrc.json ./src --fix
endif





stylelint: ##2 Starts the Stylelinter
cd ./src/Resources/app/administration && ./node_modules/.bin/stylelint --allow-empty-input ./src/**/*.scss
Expand Down Expand Up @@ -141,13 +151,13 @@ pr: ##2 Prepares everything for a Pull Request
@make phpmin -B
@make stan -B
@make phpunit -B
@make infection -B
@make jest -B
@make stryker -B
@make eslint -B
@make eslint mode=no-dry-run -B
@make stylelint -B
@make configcheck -B
@make snippetcheck -B
@make stryker -B
@make infection -B

# -------------------------------------------------------------------------------------------------

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,9 +106,6 @@ public function startCheckout(Request $request, SalesChannelContext $context): R

$sessionRedirect = $session->getRedirectUrl();
if ($sessionRedirect !== null) {
$this->logger->error('Paypal Express redirect URL is empty', [
'sessionId' => $session->id,
]);
$redirectUrl = $sessionRedirect;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,230 +1,81 @@
import HttpClient from '../services/HttpClient';
import Plugin from '../Plugin';
import ApplePaySessionFactory from '../services/ApplePaySessionFactory';
import ExpressButtonsRepository from '../repository/ExpressButtonsRepository';
import BuyElementRepository from '../repository/BuyElementRepository';
import {MOLLIE_BIND_EXPRESS_EVENTS} from './mollie-express-actions.plugin';

export default class MollieApplePayDirect extends Plugin {

/**
*
* @type {number}
*/
APPLE_PAY_VERSION = 3;


/**
*
*/
init() {
const me = this;

me.client = new HttpClient();

// register our off-canvas listener
// 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');
if (pluginOffCanvasInstances.length > 0) {
const pluginOffCanvas = pluginOffCanvasInstances[0];
pluginOffCanvas.$emitter.subscribe('offCanvasOpened', me.initCurrentPage.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();
pluginOffCanvasInstances.forEach((pluginOffCanvas) => {
pluginOffCanvas.$emitter.subscribe('offCanvasOpened', this.bindEvents.bind(this));
});
}

// now update our current page
this.initCurrentPage();

this.bindEvents();
}


/**
*
*/
initCurrentPage() {

const me = this;

// we might have wrapping containers
// that also need to be hidden -> they might have different margins or other things
const applePayContainers = document.querySelectorAll('.js-apple-pay-container');
// of course, also grab our real buttons
const applePayButtons = document.querySelectorAll('.js-apple-pay');

bindEvents() {

if (!window.ApplePaySession || !window.ApplePaySession.canMakePayments()) {
// hide our wrapping Apple Pay containers
// to avoid any wrong margins being displayed

if (applePayContainers) {
applePayContainers.forEach(function (container) {
container.style.display = 'none';
container.classList.add('d-none');
});
}
return;
}
const expressButtonsRepository = new ExpressButtonsRepository();
const expressButtons = expressButtonsRepository.findAll('.js-apple-pay');
const applePayContainers = document.querySelectorAll('.js-apple-pay-container');


if (applePayButtons.length <= 0) {
if (expressButtons.length === 0 && applePayContainers.length === 0) {
return;
}

// we start by fetching the shop url from the data attribute.
// we need this as prefix for our ajax calls, so that we always
// call the correct sales channel and its controllers.

const shopUrl = me.getShopUrl(applePayButtons[0]);


// verify if apple pay is even allowed
// in our current sales channel
me.client.get(
shopUrl + '/mollie/apple-pay/available',
data => {
if (data.available === undefined || data.available === false) {
return;
}

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);
// add click event handlers
button.addEventListener('click', me.onButtonClick);
});
}
);
}
document.dispatchEvent(new CustomEvent(MOLLIE_BIND_EXPRESS_EVENTS, {detail: expressButtons}));

checkSubmitButton(form) {
const buyButton = form.querySelector('.btn-buy');
applePayContainers.forEach((container) => {
container.classList.remove('d-none');
})

if (buyButton === null) {
return;
}
expressButtons.forEach((button) => {
button.classList.remove('d-none');
button.addEventListener('click', this.onExpressCheckout)
});
}

const expressButtons = form.querySelectorAll('.mollie-express-button');
onExpressCheckout(event) {
const clickedButton = event.target;

if (expressButtons.length === 0) {
if (!clickedButton.classList.contains('processed')) {
return;
}

expressButtons.each(function(expressButton){
if (expressButton.hasAttribute('disabled')) {
expressButton.removeAttribute('disabled');
}
if (buyButton.hasAttribute('disabled')) {
expressButton.setAttribute('disabled', 'disabled');
}
})
}

/**
*
* @param event
*/
onButtonClick(event) {

event.preventDefault();
const buyElementRepository = new BuyElementRepository();
const buyElement = buyElementRepository.find(clickedButton);

const button = event.target;
const form = button.parentNode;

// 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 countryCode = form.querySelector('input[name="countryCode"]').value;
const currency = form.querySelector('input[name="currency"]').value;
const mode = form.querySelector('input[name="mode"]').value;
const withPhone = parseInt(form.querySelector('input[name="withPhone"]').value);
const dataProtection = form.querySelector('input[name="acceptedDataProtection"]');
const countryCode = buyElement.querySelector('input[name="countryCode"]').value;
const currency = buyElement.querySelector('input[name="currency"]').value;
const mode = buyElement.querySelector('input[name="mode"]').value;
const withPhone = parseInt(buyElement.querySelector('input[name="withPhone"]').value);
const dataProtection = buyElement.querySelector('input[name="acceptedDataProtection"]');
const isProductMode = mode === 'productMode';

form.classList.remove('was-validated');
let shopSlug = clickedButton.getAttribute('data-shop-url');

if (dataProtection !== null) {
const dataProtectionValue = dataProtection.checked ? 1: 0;
form.classList.add('was-validated');

dataProtection.classList.remove('is-invalid');
if (dataProtectionValue === 0) {
dataProtection.classList.add('is-invalid');
return;
}
if (shopSlug.slice(-1) === '/') {
shopSlug = shopSlug.slice(0, -1);
}

// this helps us to figure out if we are in
// "product" mode to purchase a single product, or in "cart" mode
// to just purchase the current cart with Apple Pay Direct.
const isProductMode = (mode === 'productMode');

if (isProductMode) {

let productForm = document.querySelector('#productDetailPageBuyProductForm');
if (productForm === null) {
productForm = button.closest('.product-box').querySelector('form');
}


const formData = new FormData(productForm);
formData.delete('redirectTo');
formData.append('isExpressCheckout', '1');


fetch(productForm.action, {
method: productForm.method,
body: formData,
});


}
const applePaySessionFactory = new ApplePaySessionFactory();
const session = applePaySessionFactory.create(isProductMode, countryCode, currency, withPhone, shopSlug, dataProtection);
session.begin();

}

/**
*
* @param button
* @returns string
*/
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);
}

return shopSlug;
}

}
Loading

0 comments on commit 8afe095

Please sign in to comment.