diff --git a/modules/ppcp-axo/resources/js/AxoManager.js b/modules/ppcp-axo/resources/js/AxoManager.js index e02846b67..905270867 100644 --- a/modules/ppcp-axo/resources/js/AxoManager.js +++ b/modules/ppcp-axo/resources/js/AxoManager.js @@ -16,15 +16,20 @@ class AxoManager { this.fastlane = new Fastlane(); this.$ = jQuery; - this.isConnectProfile = false; - this.isNewProfile = false; this.hideGatewaySelection = false; + this.status = { + active: false, + validEmail: false, + hasProfile: false, + useEmailWidget: this.useEmailWidget() + }; + this.data = { billing: null, shipping: null, card: null, - } + }; this.el = new DomElementCollection(); @@ -32,7 +37,7 @@ class AxoManager { root: { backgroundColorPrimary: '#ffffff' } - } + }; this.locale = 'en_us'; @@ -40,7 +45,11 @@ class AxoManager { this.shippingView = new ShippingView(this.el.shippingAddressContainer.selector, this.el); this.billingView = new BillingView(this.el.billingAddressContainer.selector, this.el); - this.cardView = new CardView(this.el.paymentContainer.selector, this.el, this); + this.cardView = new CardView(this.el.paymentContainer.selector + '-details', this.el, this); + + document.testAxoStatus = (key, value) => { + this.setStatus(key, value); + } } registerEventHandlers() { @@ -48,9 +57,9 @@ class AxoManager { // Listen to Gateway Radio button changes. this.el.gatewayRadioButton.on('change', (ev) => { if (ev.target.checked) { - this.showAxo(); + this.activateAxo(); } else { - this.hideAxo(); + this.deactivateAxo(); } }); @@ -66,33 +75,27 @@ class AxoManager { // Click change shipping address link. this.el.changeShippingAddressLink.on('click', async () => { - if (this.isConnectProfile) { - console.log('profile', this.fastlane.profile); - - //this.shippingView.deactivate(); - + if (this.status.hasProfile) { const { selectionChanged, selectedAddress } = await this.fastlane.profile.showShippingAddressSelector(); console.log('selectedAddress', selectedAddress); if (selectionChanged) { this.setShipping(selectedAddress); - this.shippingView.activate(); + this.shippingView.refresh(); } } }); // Click change billing address link. this.el.changeBillingAddressLink.on('click', async () => { - if (this.isConnectProfile) { + if (this.status.hasProfile) { this.el.changeCardLink.trigger('click'); } }); // Click change card link. this.el.changeCardLink.on('click', async () => { - console.log('profile', this.fastlane.profile); - const response = await this.fastlane.profile.showCardSelector(); console.log('card response', response); @@ -114,67 +117,229 @@ class AxoManager { } - showAxo() { - this.initPlacements(); - this.initFastlane(); + rerender() { + /** + * active | 0 1 1 1 + * validEmail | * 0 1 1 + * hasProfile | * * 0 1 + * -------------------------------- + * defaultSubmitButton | 1 0 0 0 + * defaultEmailField | 1 0 0 0 + * defaultFormFields | 1 0 1 0 + * extraFormFields | 0 0 0 1 + * axoEmailField | 0 1 0 0 + * axoProfileViews | 0 0 0 1 + * axoPaymentContainer | 0 0 1 1 + * axoSubmitButton | 0 0 1 1 + */ + const scenario = this.identifyScenario( + this.status.active, + this.status.validEmail, + this.status.hasProfile + ); - if (!this.isNewProfile && !this.isConnectProfile) { - this.el.allFields.hide(); + log('Scenario', scenario); + + // Reset some elements to a default status. + this.el.watermarkContainer.hide(); + + if (scenario.defaultSubmitButton) { + this.el.defaultSubmitButton.show(); + } else { + this.el.defaultSubmitButton.hide(); } - if (this.useEmailWidget()) { + if (scenario.defaultEmailField) { + this.el.fieldBillingEmail.show(); + } else { + this.el.fieldBillingEmail.hide(); + } + + if (scenario.defaultFormFields) { + this.el.customerDetails.show(); + } else { + this.el.customerDetails.hide(); + } + + if (scenario.extraFormFields) { + this.el.customerDetails.show(); + // Hiding of unwanted will be handled by the axoProfileViews handler. + } + + if (scenario.axoEmailField) { + this.showAxoEmailField(); + this.el.watermarkContainer.show(); + + // Move watermark to after email. + this.$(this.el.fieldBillingEmail.selector).append( + this.$(this.el.watermarkContainer.selector) + ); + + } else { + this.el.emailWidgetContainer.hide(); + if (!scenario.defaultEmailField) { + this.el.fieldBillingEmail.hide(); + } + } + + if (scenario.axoProfileViews) { + this.shippingView.activate(); + this.billingView.activate(); + this.cardView.activate(); + + // Move watermark to after shipping. + this.$(this.el.shippingAddressContainer.selector).after( + this.$(this.el.watermarkContainer.selector) + ); + + this.el.watermarkContainer.show(); + + } else { + this.shippingView.deactivate(); + this.billingView.deactivate(); + this.cardView.deactivate(); + } + + if (scenario.axoPaymentContainer) { + this.el.paymentContainer.show(); + } else { + this.el.paymentContainer.hide(); + } + + if (scenario.axoSubmitButton) { + this.el.submitButtonContainer.show(); + } else { + this.el.submitButtonContainer.hide(); + } + + this.ensureBillingFieldsConsistency(); + this.ensureShippingFieldsConsistency(); + } + + identifyScenario(active, validEmail, hasProfile) { + let response = { + defaultSubmitButton: false, + defaultEmailField: false, + defaultFormFields: false, + extraFormFields: false, + axoEmailField: false, + axoProfileViews: false, + axoPaymentContainer: false, + axoSubmitButton: false, + } + + if (active && validEmail && hasProfile) { + response.extraFormFields = true; + response.axoProfileViews = true; + response.axoPaymentContainer = true; + response.axoSubmitButton = true; + return response; + } + if (active && validEmail && !hasProfile) { + response.defaultFormFields = true; + response.axoEmailField = true; + response.axoPaymentContainer = true; + response.axoSubmitButton = true; + return response; + } + if (active && !validEmail) { + response.axoEmailField = true; + return response; + } + if (!active) { + response.defaultSubmitButton = true; + response.defaultEmailField = true; + response.defaultFormFields = true; + return response; + } + throw new Error('Invalid scenario.'); + } + + ensureBillingFieldsConsistency() { + const $billingFields = this.$('.woocommerce-billing-fields .form-row:visible'); + const $billingHeaders = this.$('.woocommerce-billing-fields h3'); + if (this.billingView.isActive()) { + if ($billingFields.length) { + $billingHeaders.show(); + } else { + $billingHeaders.hide(); + } + } else { + $billingHeaders.show(); + } + } + + ensureShippingFieldsConsistency() { + const $shippingFields = this.$('.woocommerce-shipping-fields .form-row:visible'); + const $shippingHeaders = this.$('.woocommerce-shipping-fields h3'); + if (this.shippingView.isActive()) { + if ($shippingFields.length) { + $shippingHeaders.show(); + } else { + $shippingHeaders.hide(); + } + } else { + $shippingHeaders.show(); + } + } + + showAxoEmailField() { + if (this.status.useEmailWidget) { this.el.emailWidgetContainer.show(); this.el.fieldBillingEmail.hide(); } else { this.el.emailWidgetContainer.hide(); this.el.fieldBillingEmail.show(); } - - if (this.isConnectProfile) { - this.shippingView.activate(); - this.billingView.activate(); - - this.el.emailWidgetContainer.hide(); - this.el.fieldBillingEmail.hide(); - } - - this.el.watermarkContainer.show(); - this.el.paymentContainer.show(); - this.el.submitButtonContainer.show(); - this.el.defaultSubmitButton.hide(); } - hideAxo() { - this.el.allFields.show(); + setStatus(key, value) { + this.status[key] = value; - this.shippingView.deactivate(); - this.billingView.deactivate(); + log('Status updated', JSON.parse(JSON.stringify(this.status))); - this.el.emailWidgetContainer.hide(); - this.el.watermarkContainer.hide(); - this.el.paymentContainer.hide(); - this.el.submitButtonContainer.hide(); - this.el.defaultSubmitButton.show(); + this.rerender(); + } - this.el.emailWidgetContainer.hide(); - this.el.fieldBillingEmail.show(); + activateAxo() { + this.initPlacements(); + this.initFastlane(); + this.setStatus('active', true); + + const emailInput = document.querySelector(this.el.fieldBillingEmail.selector + ' input'); + if (emailInput && this.lastEmailCheckedIdentity !== emailInput.value) { + this.onChangeEmail(); + } + } + + deactivateAxo() { + this.setStatus('active', false); } initPlacements() { - let emailRow = document.querySelector(this.el.fieldBillingEmail.selector); + const wrapper = this.el.axoCustomerDetails; + // Customer details container. + if (!document.querySelector(wrapper.selector)) { + document.querySelector(wrapper.anchorSelector).insertAdjacentHTML('afterbegin', ` +
+ `); + } + + const wrapperElement = document.querySelector(wrapper.selector); + + // Billing view container. const bc = this.el.billingAddressContainer; - const sc = this.el.shippingAddressContainer; - const ec = this.el.emailWidgetContainer; - if (!document.querySelector(bc.selector)) { - document.querySelector(bc.anchorSelector).insertAdjacentHTML('beforeend', ` + wrapperElement.insertAdjacentHTML('beforeend', `
`); } + // Shipping view container. + const sc = this.el.shippingAddressContainer; if (!document.querySelector(sc.selector)) { - document.querySelector(sc.anchorSelector).insertAdjacentHTML('afterbegin', ` + wrapperElement.insertAdjacentHTML('beforeend', `
`); } @@ -182,8 +347,9 @@ class AxoManager { if (this.useEmailWidget()) { // Display email widget. + const ec = this.el.emailWidgetContainer; if (!document.querySelector(ec.selector)) { - emailRow.parentNode.insertAdjacentHTML('afterbegin', ` + wrapperElement.insertAdjacentHTML('afterbegin', `
--- EMAIL WIDGET PLACEHOLDER ---
@@ -192,8 +358,9 @@ class AxoManager { } else { - // Move email row to first place. - emailRow.parentNode.prepend(emailRow); + // Move email to the AXO container. + let emailRow = document.querySelector(this.el.fieldBillingEmail.selector); + wrapperElement.prepend(emailRow); emailRow.querySelector('input').focus(); } } @@ -229,7 +396,10 @@ class AxoManager { const gatewayPaymentContainer = document.querySelector('.payment_method_ppcp-axo-gateway'); gatewayPaymentContainer.insertAdjacentHTML('beforeend', ` - + `); } @@ -259,20 +429,33 @@ class AxoManager { if (this.emailInput.value) { this.onChangeEmail(); } - } } async onChangeEmail () { - log('Email changed: ' + this.emailInput.value); + if (!this.status.active) { + log('Email checking skipped, AXO not active.'); + return; + } + + if (!this.emailInput) { + log('Email field not initialized.'); + return; + } + + log('Email changed: ' + (this.emailInput ? this.emailInput.value : '')); + + this.$(this.el.paymentContainer.selector + '-detail').html(''); + this.$(this.el.paymentContainer.selector + '-form').html(''); + + this.setStatus('validEmail', false); + this.setStatus('hasProfile', false); - this.isConnectProfile = false; - this.isNewProfile = false; this.hideGatewaySelection = false; - this.el.allFields.hide(); + this.lastEmailCheckedIdentity = this.emailInput.value; - if (!this.emailInput.checkValidity()) { + if (!this.emailInput.value || !this.emailInput.checkValidity()) { log('The email address is not valid.'); return; } @@ -287,8 +470,6 @@ class AxoManager { // Authenticate the customer to get access to their profile. log('Email is associated with a Connect profile or a PayPal member'); - // TODO : enter hideOtherGateways mode - const authResponse = await this.fastlane.identity.triggerAuthenticationFlow(lookupResponse.customerContextId); log('AuthResponse', authResponse); @@ -296,25 +477,18 @@ class AxoManager { if (authResponse.authenticationState === 'succeeded') { log(JSON.stringify(authResponse)); - this.el.allFields.show(); - this.el.paymentContainer.show(); - - - // document.querySelector(this.el.paymentContainer.selector).innerHTML = - // 'Change card'; - // Add addresses this.setShipping(authResponse.profileData.shippingAddress); // TODO : set billing this.setCard(authResponse.profileData.card); - this.isConnectProfile = true; + this.setStatus('validEmail', true); + this.setStatus('hasProfile', true); + this.hideGatewaySelection = true; this.$('.wc_payment_methods label').hide(); - this.shippingView.activate(); - this.billingView.activate(); - this.cardView.activate(); + this.rerender(); } else { // authentication failed or canceled by the customer @@ -326,14 +500,12 @@ class AxoManager { // This is a guest customer. log('No profile found with this email address.'); - this.el.allFields.show(); - this.el.paymentContainer.show(); - - this.isNewProfile = true; + this.setStatus('validEmail', true); + this.setStatus('hasProfile', false); this.cardComponent = await this.fastlane .FastlaneCardComponent(MockData.cardComponent()) - .render(this.el.paymentContainer.selector); + .render(this.el.paymentContainer.selector + '-form'); } } diff --git a/modules/ppcp-axo/resources/js/Components/DomElementCollection.js b/modules/ppcp-axo/resources/js/Components/DomElementCollection.js index d6840242a..8cc1deb1a 100644 --- a/modules/ppcp-axo/resources/js/Components/DomElementCollection.js +++ b/modules/ppcp-axo/resources/js/Components/DomElementCollection.js @@ -23,8 +23,15 @@ class DomElementCollection { className: 'ppcp-axo-watermark-container' }); - this.allFields = new DomElement({ - selector: '#customer_details .form-row, #customer_details .woocommerce-shipping-fields' + this.customerDetails = new DomElement({ + selector: '#customer_details > *:not(#ppcp-axo-customer-details)' + }); + + this.axoCustomerDetails = new DomElement({ + id: 'ppcp-axo-customer-details', + selector: '#ppcp-axo-customer-details', + className: 'ppcp-axo-customer-details', + anchorSelector: '#customer_details' }); this.emailWidgetContainer = new DomElement({ @@ -36,15 +43,13 @@ class DomElementCollection { this.shippingAddressContainer = new DomElement({ id: 'ppcp-axo-shipping-address-container', selector: '#ppcp-axo-shipping-address-container', - className: 'ppcp-axo-shipping-address-container', - anchorSelector: '.woocommerce-shipping-fields' + className: 'ppcp-axo-shipping-address-container' }); this.billingAddressContainer = new DomElement({ id: 'ppcp-axo-billing-address-container', selector: '#ppcp-axo-billing-address-container', - className: 'ppcp-axo-billing-address-container', - anchorSelector: '.woocommerce-billing-fields__field-wrapper' + className: 'ppcp-axo-billing-address-container' }); this.fieldBillingEmail = new DomElement({ diff --git a/modules/ppcp-axo/resources/js/Components/FormFieldGroup.js b/modules/ppcp-axo/resources/js/Components/FormFieldGroup.js index d35b54349..c308bef3f 100644 --- a/modules/ppcp-axo/resources/js/Components/FormFieldGroup.js +++ b/modules/ppcp-axo/resources/js/Components/FormFieldGroup.js @@ -48,36 +48,40 @@ class MockData { content.innerHTML = ''; + if (!this.active) { + this.hideField(this.contentSelector); + } else { + this.showField(this.contentSelector); + } + Object.keys(this.fields).forEach((key) => { const field = this.fields[key]; if (this.active) { this.hideField(field.selector); - //this.showField(this.contentSelector); } else { this.showField(field.selector); - //this.hideField(this.contentSelector); } - - if (typeof this.template === 'function') { - content.innerHTML = this.template({ - value: (valueKey) => { - return this.getDataValue(this.fields[valueKey].valuePath); - }, - isEmpty: () => { - let isEmpty = true; - Object.values(this.fields).forEach((valueField) => { - if (this.getDataValue(valueField.valuePath)) { - isEmpty = false; - return false; - } - }); - return isEmpty; - } - }); - } - }); + + if (typeof this.template === 'function') { + content.innerHTML = this.template({ + value: (valueKey) => { + return this.getDataValue(this.fields[valueKey].valuePath); + }, + isEmpty: () => { + let isEmpty = true; + Object.values(this.fields).forEach((valueField) => { + if (this.getDataValue(valueField.valuePath)) { + isEmpty = false; + return false; + } + }); + return isEmpty; + } + }); + } + } showField(selector) { diff --git a/modules/ppcp-axo/resources/js/Views/BillingView.js b/modules/ppcp-axo/resources/js/Views/BillingView.js index 7e61388c3..4163c19c1 100644 --- a/modules/ppcp-axo/resources/js/Views/BillingView.js +++ b/modules/ppcp-axo/resources/js/Views/BillingView.js @@ -21,14 +21,14 @@ class BillingView { if (data.isEmpty()) { return `
-

Billing details Edit

+

Billing Edit

Please fill in your billing details.
`; } return `
-

Billing details Edit

+

Billing Edit

${data.value('email')}
${data.value('company')}
${data.value('firstName')} ${data.value('lastName')}
@@ -89,6 +89,10 @@ class BillingView { }); } + isActive() { + return this.billingFormFields.active; + } + activate() { this.billingFormFields.activate(); } @@ -97,6 +101,10 @@ class BillingView { this.billingFormFields.deactivate(); } + refresh() { + this.billingFormFields.refresh(); + } + setData(data) { this.billingFormFields.setData(data); } diff --git a/modules/ppcp-axo/resources/js/Views/CardView.js b/modules/ppcp-axo/resources/js/Views/CardView.js index fd188d7c7..84a6555d2 100644 --- a/modules/ppcp-axo/resources/js/Views/CardView.js +++ b/modules/ppcp-axo/resources/js/Views/CardView.js @@ -20,19 +20,40 @@ class CardView { if (data.isEmpty()) { return `
-
Please fill in your card details.
-

Edit

+
+
Please fill in your card details.
+
+

Add card details

${selectOtherPaymentMethod()}
`; } + + const expiry = data.value('expiry').split('-'); + + const cardIcons = { + 'VISA': 'visa-dark.svg', + 'MASTERCARD_CARD': 'mastercard-dark.svg', + 'AMEX': 'amex.svg', + 'DISCOVER': 'discover.svg', + }; + return `
-

Card Details Edit

-
${data.value('name')}
-
${data.value('brand')}
-
${data.value('lastDigits') ? '************' + data.value('lastDigits'): ''}
-
${data.value('expiry')}
+

Card Details Edit

+
+
+ ${data.value('brand')} +
+
${data.value('lastDigits') ? '**** **** **** ' + data.value('lastDigits'): ''}
+
${expiry[1]}/${expiry[0]}
+
${data.value('name')}
+
${selectOtherPaymentMethod()}
`; diff --git a/modules/ppcp-axo/resources/js/Views/ShippingView.js b/modules/ppcp-axo/resources/js/Views/ShippingView.js index ab154913a..24c95cf87 100644 --- a/modules/ppcp-axo/resources/js/Views/ShippingView.js +++ b/modules/ppcp-axo/resources/js/Views/ShippingView.js @@ -21,14 +21,14 @@ class ShippingView { if (data.isEmpty()) { return `
-

Shipping details Edit

+

Shipping Edit

Please fill in your shipping details.
`; } return `
-

Shipping details Edit

+

Shipping Edit

${data.value('company')}
${data.value('firstName')} ${data.value('lastName')}
${data.value('street1')}
@@ -85,6 +85,10 @@ class ShippingView { }); } + isActive() { + return this.shippingFormFields.active; + } + activate() { this.shippingFormFields.activate(); } @@ -93,6 +97,10 @@ class ShippingView { this.shippingFormFields.deactivate(); } + refresh() { + this.shippingFormFields.refresh(); + } + setData(data) { this.shippingFormFields.setData(data); } diff --git a/modules/ppcp-axo/src/Gateway/AxoGateway.php b/modules/ppcp-axo/src/Gateway/AxoGateway.php index 32f9f83e9..d3ef227ed 100644 --- a/modules/ppcp-axo/src/Gateway/AxoGateway.php +++ b/modules/ppcp-axo/src/Gateway/AxoGateway.php @@ -214,12 +214,10 @@ class AxoGateway extends WC_Payment_Gateway { //$this->add_paypal_meta( $wc_order, $order, $this->environment ); - // TODO: inject dependency + // TODO: inject dependency. PPCP::container()->get( 'session.handler' )->replace_order( $order ); PPCP::container()->get( 'wcgateway.order-processor' )->process( $wc_order ); - - // $payment_source = array( // 'oxxo' => array( // 'name' => $wc_order->get_billing_first_name() . ' ' . $wc_order->get_billing_last_name(),