woocommerce-paypal-payments/modules/ppcp-axo/resources/js/AxoManager.js

1065 lines
36 KiB
JavaScript
Raw Normal View History

2024-03-08 17:41:21 +00:00
import Fastlane from "./Connection/Fastlane";
import { log } from "./Helper/Debug";
2024-03-08 17:41:21 +00:00
import DomElementCollection from "./Components/DomElementCollection";
import ShippingView from "./Views/ShippingView";
import BillingView from "./Views/BillingView";
import CardView from "./Views/CardView";
2024-04-11 17:14:22 +01:00
import PayPalInsights from "./Insights/PayPalInsights";
import { disable, enable } from "../../../ppcp-button/resources/js/modules/Helper/ButtonDisabler";
import { getCurrentPaymentMethod } from "../../../ppcp-button/resources/js/modules/Helper/CheckoutMethodState";
2024-02-09 17:49:45 +00:00
class AxoManager {
constructor(axoConfig, ppcpConfig) {
this.axoConfig = axoConfig;
this.ppcpConfig = ppcpConfig;
2024-02-09 17:49:45 +00:00
this.initialized = false;
this.fastlane = new Fastlane();
2024-02-12 18:06:48 +00:00
this.$ = jQuery;
2024-03-08 14:39:50 +00:00
this.hideGatewaySelection = false;
2024-03-14 10:54:15 +00:00
this.status = {
active: false,
validEmail: false,
hasProfile: false,
useEmailWidget: this.useEmailWidget(),
hasCard: false,
2024-03-14 10:54:15 +00:00
};
2024-03-08 14:39:50 +00:00
this.data = {
2024-04-08 11:31:12 +01:00
email: null,
2024-03-08 14:39:50 +00:00
billing: null,
shipping: null,
card: null,
2024-03-14 10:54:15 +00:00
};
2024-03-08 14:39:50 +00:00
this.states = this.axoConfig.woocommerce.states;
2024-03-08 17:41:21 +00:00
this.el = new DomElementCollection();
2024-02-09 17:49:45 +00:00
2024-05-08 00:30:59 +02:00
this.emailInput = document.querySelector(this.el.fieldBillingEmail.selector + ' input');
2024-02-12 18:06:48 +00:00
this.styles = {
root: {
backgroundColorPrimary: '#ffffff'
}
2024-03-14 10:54:15 +00:00
};
2024-02-09 17:49:45 +00:00
2024-02-12 18:06:48 +00:00
this.locale = 'en_us';
2024-02-09 17:49:45 +00:00
2024-03-08 14:39:50 +00:00
this.registerEventHandlers();
this.shippingView = new ShippingView(this.el.shippingAddressContainer.selector, this.el, this.states);
2024-03-12 09:23:42 +00:00
this.billingView = new BillingView(this.el.billingAddressContainer.selector, this.el);
2024-03-14 10:54:15 +00:00
this.cardView = new CardView(this.el.paymentContainer.selector + '-details', this.el, this);
document.axoDebugSetStatus = (key, value) => {
2024-03-14 10:54:15 +00:00
this.setStatus(key, value);
}
2024-04-10 18:27:21 +01:00
document.axoDebugObject = () => {
return this;
}
2024-04-11 17:14:22 +01:00
if (
this.axoConfig?.insights?.enabled &&
this.axoConfig?.insights?.client_id &&
this.axoConfig?.insights?.session_id
2024-04-11 17:14:22 +01:00
) {
PayPalInsights.config(this.axoConfig?.insights?.client_id, { debug: true });
PayPalInsights.setSessionId(this.axoConfig?.insights?.session_id);
PayPalInsights.trackJsLoad();
if (document.querySelector('.woocommerce-checkout')) {
PayPalInsights.trackBeginCheckout({
amount: this.axoConfig?.insights?.amount,
page_type: 'checkout',
user_data: {
country: 'US',
is_store_member: false,
}
});
}
}
2024-04-17 14:51:30 +01:00
this.triggerGatewayChange();
2024-03-08 14:39:50 +00:00
}
registerEventHandlers() {
2024-04-17 10:14:27 +01:00
this.$(document).on('change', 'input[name=payment_method]', (ev) => {
2024-04-11 17:14:22 +01:00
const map = {
'ppcp-axo-gateway': 'card',
'ppcp-gateway': 'paypal',
}
PayPalInsights.trackSelectPaymentMethod({
payment_method_selected: map[ev.target.value] || 'other',
page_type: 'checkout'
});
});
2024-02-12 18:06:48 +00:00
// Listen to Gateway Radio button changes.
2024-03-08 17:41:21 +00:00
this.el.gatewayRadioButton.on('change', (ev) => {
2024-02-12 18:06:48 +00:00
if (ev.target.checked) {
2024-03-14 10:54:15 +00:00
this.activateAxo();
2024-02-09 17:49:45 +00:00
} else {
2024-03-14 10:54:15 +00:00
this.deactivateAxo();
2024-02-09 17:49:45 +00:00
}
});
2024-02-12 18:06:48 +00:00
this.$(document).on('updated_checkout payment_method_selected', () => {
this.triggerGatewayChange();
2024-02-09 17:49:45 +00:00
});
2024-03-08 14:39:50 +00:00
// On checkout form submitted.
2024-03-12 09:23:42 +00:00
this.el.submitButton.on('click', () => {
2024-02-12 18:06:48 +00:00
this.onClickSubmitButton();
2024-02-09 17:49:45 +00:00
return false;
2024-03-12 09:23:42 +00:00
})
2024-02-09 17:49:45 +00:00
2024-03-08 14:39:50 +00:00
// Click change shipping address link.
2024-03-12 09:23:42 +00:00
this.el.changeShippingAddressLink.on('click', async () => {
2024-03-14 10:54:15 +00:00
if (this.status.hasProfile) {
2024-03-08 14:39:50 +00:00
const { selectionChanged, selectedAddress } = await this.fastlane.profile.showShippingAddressSelector();
if (selectionChanged) {
this.setShipping(selectedAddress);
2024-03-14 10:54:15 +00:00
this.shippingView.refresh();
2024-03-08 14:39:50 +00:00
}
}
});
// Click change billing address link.
2024-03-12 09:23:42 +00:00
this.el.changeBillingAddressLink.on('click', async () => {
2024-03-14 10:54:15 +00:00
if (this.status.hasProfile) {
2024-03-12 09:23:42 +00:00
this.el.changeCardLink.trigger('click');
2024-03-08 14:39:50 +00:00
}
});
// Click change card link.
2024-03-12 09:23:42 +00:00
this.el.changeCardLink.on('click', async () => {
2024-03-08 14:39:50 +00:00
const response = await this.fastlane.profile.showCardSelector();
if (response.selectionChanged) {
this.setCard(response.selectedCard);
this.setBilling({
address: response.selectedCard.paymentSource.card.billingAddress
});
}
});
// Cancel "continuation" mode.
2024-03-12 09:23:42 +00:00
this.el.showGatewaySelectionLink.on('click', async () => {
2024-03-08 14:39:50 +00:00
this.hideGatewaySelection = false;
this.$('.wc_payment_methods label').show();
this.$('.wc_payment_methods input').show();
2024-03-08 17:41:21 +00:00
this.cardView.refresh();
2024-03-08 14:39:50 +00:00
});
// Prevents sending checkout form when pressing Enter key on input field
// and triggers customer lookup
this.$('form.woocommerce-checkout input').on('keydown', async (ev) => {
if (ev.key === 'Enter' && getCurrentPaymentMethod() === 'ppcp-axo-gateway') {
ev.preventDefault();
2024-05-29 14:53:58 +02:00
log(`Enter key attempt - emailInput: ${this.emailInput.value}`);
log(`this.lastEmailCheckedIdentity: ${this.lastEmailCheckedIdentity}`);
2024-05-08 00:30:59 +02:00
if (this.emailInput && this.lastEmailCheckedIdentity !== this.emailInput.value) {
await this.onChangeEmail();
}
}
});
2024-05-08 00:30:59 +02:00
// Clear last email checked identity when email field is focused.
this.$('#billing_email_field input').on('focus', (ev) => {
2024-05-29 14:53:58 +02:00
log(`Clear the last email checked: ${this.lastEmailCheckedIdentity}`);
2024-05-08 00:30:59 +02:00
this.lastEmailCheckedIdentity = '';
});
// Listening to status update event
document.addEventListener('axo_status_updated', (ev) => {
const termsField = document.querySelector("[name='terms-field']");
if (termsField) {
const status = ev.detail;
2024-05-07 09:55:48 +02:00
const shouldHide = status.active && status.validEmail === false && status.hasProfile === false;
2024-05-07 09:55:48 +02:00
termsField.parentElement.style.display = shouldHide ? 'none' : 'block';
}
});
2024-02-09 17:49:45 +00:00
}
2024-03-14 10:54:15 +00:00
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
);
2024-05-29 15:21:21 +02:00
log(`Scenario: ${JSON.stringify(scenario)}`);
2024-03-14 10:54:15 +00:00
// Reset some elements to a default status.
this.el.watermarkContainer.hide();
2024-03-08 14:39:50 +00:00
2024-03-14 10:54:15 +00:00
if (scenario.defaultSubmitButton) {
this.el.defaultSubmitButton.show();
} else {
this.el.defaultSubmitButton.hide();
2024-03-12 09:23:42 +00:00
}
2024-03-14 10:54:15 +00:00
if (scenario.defaultEmailField) {
this.el.fieldBillingEmail.show();
} else {
2024-03-12 09:23:42 +00:00
this.el.fieldBillingEmail.hide();
2024-03-14 10:54:15 +00:00
}
if (scenario.defaultFormFields) {
this.el.customerDetails.show();
this.toggleLoaderAndOverlay(this.el.customerDetails, 'loader', 'ppcp-axo-overlay');
2024-03-14 10:54:15 +00:00
} 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)
);
// Add the submit button to the email field container.
this.renderEmailSubmitNew();
2024-03-12 09:23:42 +00:00
} else {
this.el.emailWidgetContainer.hide();
2024-03-14 10:54:15 +00:00
if (!scenario.defaultEmailField) {
this.el.fieldBillingEmail.hide();
}
2024-03-12 09:23:42 +00:00
}
2024-03-14 10:54:15 +00:00
if (scenario.axoProfileViews) {
2024-03-12 09:23:42 +00:00
this.shippingView.activate();
this.cardView.activate();
if (this.status.hasCard) {
this.billingView.activate();
}
2024-03-12 09:23:42 +00:00
2024-03-14 10:54:15 +00:00
// 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();
this.el.gatewayDescription.hide();
2024-03-14 10:54:15 +00:00
} else {
this.el.paymentContainer.hide();
}
if (scenario.axoSubmitButton) {
this.el.submitButtonContainer.show();
} else {
this.el.submitButtonContainer.hide();
2024-03-12 09:23:42 +00:00
}
2024-02-12 18:06:48 +00:00
2024-03-14 10:54:15 +00:00
this.ensureBillingFieldsConsistency();
this.ensureShippingFieldsConsistency();
2024-02-12 18:06:48 +00:00
}
2024-03-14 10:54:15 +00:00
identifyScenario(active, validEmail, hasProfile) {
let response = {
defaultSubmitButton: false,
defaultEmailField: false,
defaultFormFields: false,
extraFormFields: false,
axoEmailField: false,
axoProfileViews: false,
axoPaymentContainer: false,
axoSubmitButton: false,
}
2024-03-12 09:23:42 +00:00
2024-03-14 10:54:15 +00:00
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.');
}
2024-03-08 14:39:50 +00:00
2024-03-14 10:54:15 +00:00
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();
}
}
2024-03-14 10:54:15 +00:00
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();
}
}
setStatus(key, value) {
this.status[key] = value;
2024-05-29 15:21:21 +02:00
log(`Status updated: ${JSON.stringify(this.status)}`);
2024-03-14 10:54:15 +00:00
document.dispatchEvent(new CustomEvent("axo_status_updated", { detail: this.status }));
2024-03-14 10:54:15 +00:00
this.rerender();
}
activateAxo() {
this.initPlacements();
this.initFastlane();
this.setStatus('active', true);
2024-05-29 14:53:58 +02:00
log(`Attempt on activation - emailInput: ${this.emailInput.value}`);
log(`this.lastEmailCheckedIdentity: ${this.lastEmailCheckedIdentity}`);
2024-05-08 00:30:59 +02:00
if (this.emailInput && this.lastEmailCheckedIdentity !== this.emailInput.value) {
2024-03-14 10:54:15 +00:00
this.onChangeEmail();
}
}
deactivateAxo() {
this.setStatus('active', false);
2024-02-12 18:06:48 +00:00
}
2024-03-08 14:39:50 +00:00
initPlacements() {
2024-03-14 10:54:15 +00:00
const wrapper = this.el.axoCustomerDetails;
2024-03-14 10:54:15 +00:00
// Customer details container.
if (!document.querySelector(wrapper.selector)) {
document.querySelector(wrapper.anchorSelector).insertAdjacentHTML('afterbegin', `
<div id="${wrapper.id}" class="${wrapper.className}"></div>
`);
}
const wrapperElement = document.querySelector(wrapper.selector);
2024-03-08 14:39:50 +00:00
2024-03-14 10:54:15 +00:00
// Billing view container.
const bc = this.el.billingAddressContainer;
2024-03-08 14:39:50 +00:00
if (!document.querySelector(bc.selector)) {
2024-03-14 10:54:15 +00:00
wrapperElement.insertAdjacentHTML('beforeend', `
2024-03-08 14:39:50 +00:00
<div id="${bc.id}" class="${bc.className}"></div>
`);
}
2024-03-14 10:54:15 +00:00
// Shipping view container.
const sc = this.el.shippingAddressContainer;
2024-03-08 14:39:50 +00:00
if (!document.querySelector(sc.selector)) {
2024-03-14 10:54:15 +00:00
wrapperElement.insertAdjacentHTML('beforeend', `
2024-03-08 14:39:50 +00:00
<div id="${sc.id}" class="${sc.className}"></div>
`);
}
2024-04-10 18:27:21 +01:00
// Watermark container
const wc = this.el.watermarkContainer;
if (!document.querySelector(wc.selector)) {
this.emailInput.insertAdjacentHTML('afterend', `
<div class="${wc.className}" id="${wc.id}"></div>
`);
}
// Payment container
const pc = this.el.paymentContainer;
if (!document.querySelector(pc.selector)) {
const gatewayPaymentContainer = document.querySelector('.payment_method_ppcp-axo-gateway');
gatewayPaymentContainer.insertAdjacentHTML('beforeend', `
<div id="${pc.id}" class="${pc.className} axo-hidden">
2024-04-10 18:27:21 +01:00
<div id="${pc.id}-form" class="${pc.className}-form"></div>
<div id="${pc.id}-details" class="${pc.className}-details"></div>
</div>
`);
}
if (this.useEmailWidget()) {
// Display email widget.
2024-03-14 10:54:15 +00:00
const ec = this.el.emailWidgetContainer;
2024-03-08 14:39:50 +00:00
if (!document.querySelector(ec.selector)) {
2024-03-14 10:54:15 +00:00
wrapperElement.insertAdjacentHTML('afterbegin', `
2024-03-08 14:39:50 +00:00
<div id="${ec.id}" class="${ec.className}">
--- EMAIL WIDGET PLACEHOLDER ---
</div>
`);
}
} else {
2024-03-14 10:54:15 +00:00
// Move email to the AXO container.
let emailRow = document.querySelector(this.el.fieldBillingEmail.selector);
wrapperElement.prepend(emailRow);
}
2024-02-12 18:06:48 +00:00
}
2024-03-08 14:39:50 +00:00
async initFastlane() {
2024-02-09 17:49:45 +00:00
if (this.initialized) {
return;
}
this.initialized = true;
2024-02-12 18:06:48 +00:00
await this.connect();
await this.renderWatermark(true, () => {
this.renderEmailSubmit();
});
2024-02-12 18:06:48 +00:00
this.watchEmail();
2024-02-09 17:49:45 +00:00
}
2024-02-12 18:06:48 +00:00
async connect() {
if (this.axoConfig.environment.is_sandbox) {
window.localStorage.setItem('axoEnv', 'sandbox');
}
2024-02-09 17:49:45 +00:00
await this.fastlane.connect({
2024-02-12 18:06:48 +00:00
locale: this.locale,
styles: this.styles
2024-02-09 17:49:45 +00:00
});
this.fastlane.setLocale('en_us');
2024-02-12 18:06:48 +00:00
}
2024-02-09 17:49:45 +00:00
2024-02-12 18:06:48 +00:00
triggerGatewayChange() {
2024-03-08 17:41:21 +00:00
this.el.gatewayRadioButton.trigger('change');
2024-02-12 18:06:48 +00:00
}
async renderWatermark(includeAdditionalInfo = true, callback) {
2024-04-16 16:37:12 +01:00
(await this.fastlane.FastlaneWatermarkComponent({
includeAdditionalInfo
2024-04-16 16:37:12 +01:00
})).render(this.el.watermarkContainer.selector);
this.toggleWatermarkLoading(this.el.watermarkContainer, 'ppcp-axo-watermark-loading', 'loader');
// Call the callback if provided
if (callback) {
callback();
}
}
async renderEmailSubmitNew() {
const billingEmailSubmitButton = this.el.billingEmailSubmitButton;
if (!document.querySelector(billingEmailSubmitButton.selector)) {
}
const submitButton = this.el.billingEmailSubmitButton.selector;
// submitButton.innerText = this.axoConfig.billing_email_button_text;
const submitButtonSpinner = this.el.billingEmailSubmitButtonSpinner.selector;
console.log(submitButton);
}
async renderEmailSubmit() {
// Create the submit button element
const submitButton = document.createElement('button');
submitButton.type = 'button';
submitButton.innerText = this.axoConfig.billing_email_button_text;
submitButton.className = 'email-submit-button'; // Add a class for styling if needed
// Create the spinner element
const spinner = document.createElement('span');
spinner.className = 'loader ppcp-axo-overlay'; // Use the native loader class
spinner.style.display = 'none'; // Initially hidden
// Append the spinner to the button
submitButton.appendChild(spinner);
// Add an event listener to handle the button click
submitButton.addEventListener('click', async () => {
const emailInput = document.querySelector(this.el.fieldBillingEmail.selector + ' input');
if (emailInput && emailInput.checkValidity()) {
if (this.lastEmailCheckedIdentity !== emailInput.value) {
log(`Submit button clicked - emailInput: ${emailInput.value}`);
// Show the spinner, add the class to adjust padding and disable the button
spinner.style.display = 'inline-block';
submitButton.disabled = true;
try {
await this.onChangeEmail();
} finally {
// Hide the spinner and re-enable the button after the lookup is complete
spinner.style.display = 'none';
submitButton.disabled = false;
}
}
} else {
emailInput.reportValidity(); // Trigger the HTML5 validation message
log('Invalid or empty email input.');
}
});
// Append the button inside the wrapper of the billing email input field
const emailFieldContainer = document.querySelector(this.el.fieldBillingEmail.selector);
if (emailFieldContainer) {
const inputWrapper = emailFieldContainer.querySelector('.woocommerce-input-wrapper');
if (inputWrapper) {
// Ensure the email input has the required attribute
const emailInput = inputWrapper.querySelector('input');
emailInput.setAttribute('required', 'required');
emailInput.style.flex = '1'; // Make the input take the remaining space
emailInput.style.marginRight = '10px'; // Ensure the spacing is consistent
// Remove any existing loader if present
const existingLoader = inputWrapper.querySelector('.loader');
if (existingLoader) {
existingLoader.remove();
}
// Append the submit button to the input wrapper
inputWrapper.appendChild(submitButton);
// Force a reflow to apply the transition
submitButton.offsetHeight;
// Add the class to trigger the animation
inputWrapper.classList.add('show-button');
}
}
2024-02-12 18:06:48 +00:00
}
2024-02-09 17:49:45 +00:00
async onChangeEmail() {
this.clearData();
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 : '<empty>'}`);
this.$(this.el.paymentContainer.selector + '-detail').html('');
this.$(this.el.paymentContainer.selector + '-form').html('');
2024-02-09 17:49:45 +00:00
this.setStatus('validEmail', false);
this.setStatus('hasProfile', false);
this.hideGatewaySelection = false;
this.lastEmailCheckedIdentity = this.emailInput.value;
if (!this.emailInput.value || !this.emailInput.checkValidity()) {
log('The email address is not valid.');
return;
}
this.data.email = this.emailInput.value;
this.billingView.setData(this.data);
if (!this.fastlane.identity) {
log('Not initialized.');
return;
}
PayPalInsights.trackSubmitCheckoutEmail({
page_type: 'checkout'
});
this.disableGatewaySelection();
const submitButton = document.querySelector('.email-submit-button');
const spinner = submitButton.querySelector('.loader');
if (submitButton && spinner) {
// Show the spinner and disable the button
spinner.style.display = 'inline-block';
submitButton.classList.add('show-spinner');
submitButton.disabled = true;
}
setTimeout(async () => {
console.log("Delayed for 1 milisecond.");
await this.lookupCustomerByEmail();
// Hide the spinner and re-enable the button after the lookup is complete
if (submitButton && spinner) {
spinner.style.display = 'none';
submitButton.classList.remove('show-spinner');
submitButton.disabled = false;
}
this.enableGatewaySelection();
}, 1);
}
watchEmail() {
if (this.useEmailWidget()) {
// TODO
} else {
this.emailInput.addEventListener('change', async () => {
2024-05-29 14:53:58 +02:00
log(`Change event attempt - emailInput: ${this.emailInput.value}`);
log(`this.lastEmailCheckedIdentity: ${this.lastEmailCheckedIdentity}`);
2024-05-08 00:30:59 +02:00
if (this.emailInput && this.lastEmailCheckedIdentity !== this.emailInput.value) {
this.onChangeEmail();
}
});
2024-05-29 14:53:58 +02:00
log(`Last, this.emailInput.value attempt - emailInput: ${this.emailInput.value}`);
log(`this.lastEmailCheckedIdentity: ${this.lastEmailCheckedIdentity}`);
if (this.emailInput.value) {
this.onChangeEmail();
}
2024-02-09 17:49:45 +00:00
}
}
async onChangeEmail() {
2024-04-08 11:31:12 +01:00
this.clearData();
2024-03-14 10:54:15 +00:00
if (!this.status.active) {
log('Email checking skipped, AXO not active.');
return;
}
if (!this.emailInput) {
log('Email field not initialized.');
return;
}
2024-05-29 14:53:58 +02:00
log(`Email changed: ${this.emailInput ? this.emailInput.value : '<empty>'}`);
2024-03-14 10:54:15 +00:00
this.$(this.el.paymentContainer.selector + '-detail').html('');
this.$(this.el.paymentContainer.selector + '-form').html('');
this.setStatus('validEmail', false);
this.setStatus('hasProfile', false);
2024-02-09 17:49:45 +00:00
2024-03-08 14:39:50 +00:00
this.hideGatewaySelection = false;
2024-03-14 10:54:15 +00:00
this.lastEmailCheckedIdentity = this.emailInput.value;
2024-03-12 09:23:42 +00:00
2024-03-14 10:54:15 +00:00
if (!this.emailInput.value || !this.emailInput.checkValidity()) {
2024-02-12 18:06:48 +00:00
log('The email address is not valid.');
2024-02-09 17:49:45 +00:00
return;
}
2024-03-12 09:23:42 +00:00
this.data.email = this.emailInput.value;
this.billingView.setData(this.data);
2024-02-09 17:49:45 +00:00
2024-04-11 17:14:22 +01:00
if (!this.fastlane.identity) {
log('Not initialized.');
return;
}
PayPalInsights.trackSubmitCheckoutEmail({
page_type: 'checkout'
});
this.disableGatewaySelection();
const submitButton = document.querySelector('.email-submit-button');
const spinner = submitButton.querySelector('.loader');
if (submitButton && spinner) {
// Show the spinner and disable the button
spinner.style.display = 'inline-block';
submitButton.disabled = true;
}
setTimeout(async () => {
console.log("Delayed for 1 milisecond.");
await this.lookupCustomerByEmail();
// Hide the spinner and re-enable the button after the lookup is complete
if (submitButton && spinner) {
spinner.style.display = 'none';
submitButton.disabled = false;
}
this.enableGatewaySelection();
}, 1);
}
async lookupCustomerByEmail() {
2024-03-12 09:23:42 +00:00
const lookupResponse = await this.fastlane.identity.lookupCustomerByEmail(this.emailInput.value);
2024-03-08 14:39:50 +00:00
2024-05-29 14:53:58 +02:00
log(`lookupCustomerByEmail: ${JSON.stringify(lookupResponse)}`);
2024-05-27 12:21:38 +02:00
2024-03-08 14:39:50 +00:00
if (lookupResponse.customerContextId) {
2024-02-12 18:06:48 +00:00
// Email is associated with a Connect profile or a PayPal member.
// Authenticate the customer to get access to their profile.
log('Email is associated with a Connect profile or a PayPal member');
2024-03-08 14:39:50 +00:00
const authResponse = await this.fastlane.identity.triggerAuthenticationFlow(lookupResponse.customerContextId);
2024-05-29 14:53:58 +02:00
log(`AuthResponse - triggerAuthenticationFlow: ${JSON.stringify(authResponse)}`);
2024-03-08 14:39:50 +00:00
if (authResponse.authenticationState === 'succeeded') {
2024-05-09 16:14:37 +02:00
const shippingData = authResponse.profileData.shippingAddress;
if (shippingData) {
2024-05-09 16:14:37 +02:00
this.setShipping(shippingData);
}
if (authResponse.profileData.card) {
this.setStatus('hasCard', true);
} else {
this.cardComponent = (await this.fastlane.FastlaneCardComponent(
this.cardComponentData()
)).render(this.el.paymentContainer.selector + '-form');
}
2024-05-09 16:14:37 +02:00
const cardBillingAddress = authResponse.profileData?.card?.paymentSource?.card?.billingAddress;
if (cardBillingAddress) {
this.setCard(authResponse.profileData.card);
2024-05-09 16:14:37 +02:00
const billingData = {
address: cardBillingAddress,
};
const phoneNumber = authResponse.profileData?.shippingAddress?.phoneNumber?.nationalNumber ?? '';
if (phoneNumber) {
2024-05-09 16:14:37 +02:00
billingData.phoneNumber = phoneNumber
}
this.setBilling(billingData);
}
2024-03-08 14:39:50 +00:00
2024-03-14 10:54:15 +00:00
this.setStatus('validEmail', true);
this.setStatus('hasProfile', true);
2024-03-08 14:39:50 +00:00
this.hideGatewaySelection = true;
this.$('.wc_payment_methods label').hide();
this.$('.wc_payment_methods input').hide();
2024-03-08 14:39:50 +00:00
await this.renderWatermark(false);
2024-03-14 10:54:15 +00:00
this.rerender();
2024-03-08 14:39:50 +00:00
} else {
// authentication failed or canceled by the customer
// set status as guest customer
2024-03-08 14:39:50 +00:00
log("Authentication Failed")
this.setStatus('validEmail', true);
this.setStatus('hasProfile', false);
await this.renderWatermark(true);
this.cardComponent = (await this.fastlane.FastlaneCardComponent(
this.cardComponentData()
)).render(this.el.paymentContainer.selector + '-form');
2024-03-08 14:39:50 +00:00
}
2024-02-09 17:49:45 +00:00
} else {
// No profile found with this email address.
// This is a guest customer.
2024-02-12 18:06:48 +00:00
log('No profile found with this email address.');
2024-02-09 17:49:45 +00:00
2024-03-14 10:54:15 +00:00
this.setStatus('validEmail', true);
this.setStatus('hasProfile', false);
2024-03-12 09:23:42 +00:00
await this.renderWatermark(true);
2024-04-17 16:32:38 +01:00
this.cardComponent = (await this.fastlane.FastlaneCardComponent(
2024-04-17 15:24:07 +01:00
this.cardComponentData()
2024-04-17 16:32:38 +01:00
)).render(this.el.paymentContainer.selector + '-form');
2024-02-12 18:06:48 +00:00
}
}
2024-02-09 17:49:45 +00:00
disableGatewaySelection() {
this.$('.wc_payment_methods input').prop('disabled', true);
}
enableGatewaySelection() {
this.$('.wc_payment_methods input').prop('disabled', false);
}
2024-04-08 11:31:12 +01:00
clearData() {
this.data = {
email: null,
billing: null,
shipping: null,
card: null,
};
}
2024-03-08 14:39:50 +00:00
setShipping(shipping) {
this.data.shipping = shipping;
2024-03-08 17:41:21 +00:00
this.shippingView.setData(this.data);
2024-03-08 14:39:50 +00:00
}
setBilling(billing) {
this.data.billing = billing;
2024-03-08 17:41:21 +00:00
this.billingView.setData(this.data);
2024-03-08 14:39:50 +00:00
}
setCard(card) {
this.data.card = card;
2024-03-08 17:41:21 +00:00
this.cardView.setData(this.data);
2024-03-08 14:39:50 +00:00
}
2024-02-12 18:06:48 +00:00
onClickSubmitButton() {
2024-04-10 18:27:21 +01:00
// TODO: validate data.
2024-04-08 11:31:12 +01:00
if (this.data.card) { // Ryan flow
2024-05-29 14:53:58 +02:00
log('Starting Ryan flow.');
2024-04-10 18:27:21 +01:00
2024-04-17 10:14:27 +01:00
this.$('#ship-to-different-address-checkbox').prop('checked', 'checked');
2024-04-10 18:27:21 +01:00
2024-04-15 17:20:02 +01:00
let data = {};
this.billingView.toSubmitData(data);
this.shippingView.toSubmitData(data);
this.cardView.toSubmitData(data);
this.ensureBillingPhoneNumber(data);
log(`Ryan flow - submitted nonce: ${this.data.card.id}`)
2024-05-24 17:13:43 +02:00
2024-04-15 17:20:02 +01:00
this.submit(this.data.card.id, data);
2024-04-08 11:31:12 +01:00
} else { // Gary flow
2024-05-29 14:53:58 +02:00
log('Starting Gary flow.');
2024-04-08 11:31:12 +01:00
try {
2024-04-17 16:32:38 +01:00
this.cardComponent.getPaymentToken(
2024-04-08 11:31:12 +01:00
this.tokenizeData()
).then((response) => {
log(`Gary flow - submitted nonce: ${response.id}`)
2024-04-17 16:32:38 +01:00
this.submit(response.id);
2024-04-08 11:31:12 +01:00
});
} catch (e) {
2024-04-17 16:32:38 +01:00
alert('Error tokenizing data.');
2024-05-29 14:53:58 +02:00
log(`Error tokenizing data. ${e.message}`, 'error');
2024-04-08 11:31:12 +01:00
}
}
}
cardComponentData() {
return {
2024-05-13 16:23:17 +02:00
fields: {
cardholderName: {
enabled: this.axoConfig.name_on_card === '1'
}
},
2024-05-07 09:55:48 +02:00
styles: this.deleteKeysWithEmptyString(this.axoConfig.style_options)
2024-04-08 11:31:12 +01:00
}
}
tokenizeData() {
return {
2024-05-24 11:38:48 +02:00
cardholderName: {
2024-04-08 11:31:12 +01:00
fullName: this.billingView.fullName()
},
billingAddress: {
addressLine1: this.billingView.inputValue('street1'),
addressLine2: this.billingView.inputValue('street2'),
adminArea1: this.billingView.inputValue('stateCode'),
adminArea2: this.billingView.inputValue('city'),
2024-04-08 11:31:12 +01:00
postalCode: this.billingView.inputValue('postCode'),
countryCode: this.billingView.inputValue('countryCode'),
}
2024-02-09 17:49:45 +00:00
}
}
2024-04-15 17:20:02 +01:00
submit(nonce, data) {
2024-02-12 18:06:48 +00:00
// Send the nonce and previously captured device data to server to complete checkout
2024-04-08 11:31:12 +01:00
if (!this.el.axoNonceInput.get()) {
2024-04-15 17:20:02 +01:00
this.$('form.woocommerce-checkout').append(`<input type="hidden" id="${this.el.axoNonceInput.id}" name="axo_nonce" value="" />`);
2024-04-08 11:31:12 +01:00
}
this.el.axoNonceInput.get().value = nonce;
2024-04-11 17:14:22 +01:00
PayPalInsights.trackEndCheckout({
amount: this.axoConfig?.insights?.amount,
page_type: 'checkout',
payment_method_selected: 'card',
user_data: {
country: 'US',
is_store_member: false,
}
});
2024-04-15 17:20:02 +01:00
if (data) {
// Ryan flow.
const form = document.querySelector('form.woocommerce-checkout');
const formData = new FormData(form);
2024-04-17 16:32:38 +01:00
this.showLoading();
2024-04-15 17:20:02 +01:00
// Fill in form data.
Object.keys(data).forEach((key) => {
formData.set(key, data[key]);
});
// Set type of user (Ryan) to be received on WC gateway process payment request.
formData.set('fastlane_member', true);
2024-04-15 17:20:02 +01:00
fetch(wc_checkout_params.checkout_url, { // TODO: maybe create a new endpoint to process_payment.
method: "POST",
body: formData
})
.then(response => response.json())
.then(responseData => {
2024-04-17 10:14:27 +01:00
if (responseData.result === 'failure') {
if (responseData.messages) {
const $notices = this.$('.woocommerce-notices-wrapper').eq(0);
$notices.html(responseData.messages);
this.$('html, body').animate({
scrollTop: $notices.offset().top
}, 500);
}
2024-05-27 12:21:38 +02:00
2024-05-29 14:53:58 +02:00
log(`Error sending checkout form. ${responseData}`, 'error');
2024-05-27 12:21:38 +02:00
2024-04-17 16:32:38 +01:00
this.hideLoading();
2024-04-17 10:14:27 +01:00
return;
}
2024-04-15 17:20:02 +01:00
if (responseData.redirect) {
window.location.href = responseData.redirect;
}
})
.catch(error => {
2024-05-29 14:53:58 +02:00
log(`Error sending checkout form. ${error.message}`, 'error');
2024-05-27 12:21:38 +02:00
2024-04-17 16:32:38 +01:00
this.hideLoading();
2024-04-15 17:20:02 +01:00
});
} else {
// Gary flow.
this.el.defaultSubmitButton.click();
}
2024-02-09 17:49:45 +00:00
}
2024-04-17 16:32:38 +01:00
showLoading() {
const submitContainerSelector = '.woocommerce-checkout-payment';
jQuery('form.woocommerce-checkout').append('<div class="blockUI blockOverlay" style="z-index: 1000; border: medium; margin: 0px; padding: 0px; width: 100%; height: 100%; top: 0px; left: 0px; background: rgb(255, 255, 255); opacity: 0.6; cursor: default; position: absolute;"></div>');
disable(submitContainerSelector);
}
hideLoading() {
const submitContainerSelector = '.woocommerce-checkout-payment';
jQuery('form.woocommerce-checkout .blockOverlay').remove();
enable(submitContainerSelector);
}
useEmailWidget() {
return this.axoConfig?.widgets?.email === 'use_widget';
}
2024-05-07 09:55:48 +02:00
deleteKeysWithEmptyString = (obj) => {
for (let key of Object.keys(obj)) {
if (obj[key] === '') {
2024-05-01 14:54:59 +02:00
delete obj[key];
}
else if (typeof obj[key] === 'object') {
2024-05-07 09:55:48 +02:00
obj[key] = this.deleteKeysWithEmptyString(obj[key]);
if (Object.keys(obj[key]).length === 0) delete obj[key];
2024-05-01 14:54:59 +02:00
}
}
return Array.isArray(obj) ? obj.filter(val => val) : obj;
}
ensureBillingPhoneNumber(data) {
if (data.billing_phone === '') {
let phone = '';
const cc = this.data?.shipping?.phoneNumber?.countryCode;
const number = this.data?.shipping?.phoneNumber?.nationalNumber;
if (cc) {
phone = `+${cc} `;
}
phone += number;
data.billing_phone = phone;
}
}
toggleLoaderAndOverlay(element, loaderClass, overlayClass) {
const loader = document.querySelector(`${element.selector} .${loaderClass}`);
const overlay = document.querySelector(`${element.selector} .${overlayClass}`);
if (loader) {
loader.classList.toggle(loaderClass);
}
if (overlay) {
overlay.classList.toggle(overlayClass);
}
}
toggleWatermarkLoading(container, loadingClass, loaderClass) {
const watermarkLoading = document.querySelector(`${container.selector}.${loadingClass}`);
const watermarkLoader = document.querySelector(`${container.selector}.${loaderClass}`);
if (watermarkLoading) {
watermarkLoading.classList.toggle(loadingClass);
}
if (watermarkLoader) {
watermarkLoader.classList.toggle(loaderClass);
}
}
2024-02-09 17:49:45 +00:00
}
export default AxoManager;