Inject either hosted fields or card fields depending on context

This commit is contained in:
Emili Castells Guasch 2023-11-20 13:23:10 +01:00
parent 9b58eca1b0
commit 32f7b4940b
3 changed files with 274 additions and 134 deletions

View file

@ -5,7 +5,8 @@ import CheckoutBootstap from './modules/ContextBootstrap/CheckoutBootstap';
import PayNowBootstrap from "./modules/ContextBootstrap/PayNowBootstrap";
import Renderer from './modules/Renderer/Renderer';
import ErrorHandler from './modules/ErrorHandler';
import CreditCardRenderer from "./modules/Renderer/CreditCardRenderer";
import HostedFieldsRenderer from "./modules/Renderer/HostedFieldsRenderer";
import CardFieldsRenderer from "./modules/Renderer/CardFieldsRenderer";
import MessageRenderer from "./modules/Renderer/MessageRenderer";
import Spinner from "./modules/Helper/Spinner";
import {
@ -37,7 +38,11 @@ const bootstrap = () => {
document.querySelector(checkoutFormSelector) ?? document.querySelector('.woocommerce-notices-wrapper')
);
const spinner = new Spinner();
const creditCardRenderer = new CreditCardRenderer(PayPalCommerceGateway, errorHandler, spinner);
let creditCardRenderer = new HostedFieldsRenderer(PayPalCommerceGateway, errorHandler, spinner);
if (typeof paypal.CardFields !== 'undefined') {
creditCardRenderer = new CardFieldsRenderer(PayPalCommerceGateway, errorHandler, spinner);
}
const formSaver = new FormSaver(
PayPalCommerceGateway.ajax.save_checkout_form.endpoint,

View file

@ -0,0 +1,265 @@
import {show} from "../Helper/Hiding";
class CardFieldsRenderer {
constructor(defaultConfig, errorHandler, spinner) {
this.defaultConfig = defaultConfig;
this.errorHandler = errorHandler;
this.spinner = spinner;
this.cardValid = false;
this.formValid = false;
this.emptyFields = new Set(['number', 'cvv', 'expirationDate']);
this.currentHostedFieldsInstance = null;
}
render(wrapper, contextConfig) {
if (
(
this.defaultConfig.context !== 'checkout'
&& this.defaultConfig.context !== 'pay-now'
)
|| wrapper === null
|| document.querySelector(wrapper) === null
) {
return;
}
const buttonSelector = wrapper + ' button';
const gateWayBox = document.querySelector('.payment_box.payment_method_ppcp-credit-card-gateway');
if (!gateWayBox) {
return
}
const oldDisplayStyle = gateWayBox.style.display;
gateWayBox.style.display = 'block';
const hideDccGateway = document.querySelector('#ppcp-hide-dcc');
if (hideDccGateway) {
hideDccGateway.parentNode.removeChild(hideDccGateway);
}
const cardField = paypal.CardFields({
createOrder: contextConfig.createOrder,
onApprove: function (data) {
return contextConfig.onApprove(data);
},
onError: function (error) {
console.error(error)
this.spinner.unblock();
}
});
if (cardField.isEligible()) {
const nameField = document.getElementById('ppcp-credit-card-gateway-card-name');
if (nameField) {
let styles = this.cardFieldStyles(nameField);
cardField.NameField({style: {'input': styles}}).render(nameField.parentNode);
nameField.remove();
}
const numberField = document.getElementById('ppcp-credit-card-gateway-card-number');
if (numberField) {
let styles = this.cardFieldStyles(numberField);
cardField.NumberField({style: {'input': styles}}).render(numberField.parentNode);
numberField.remove();
}
const expiryField = document.getElementById('ppcp-credit-card-gateway-card-expiry');
if (expiryField) {
let styles = this.cardFieldStyles(expiryField);
cardField.ExpiryField({style: {'input': styles}}).render(expiryField.parentNode);
expiryField.remove();
}
const cvvField = document.getElementById('ppcp-credit-card-gateway-card-cvc');
if (cvvField) {
let styles = this.cardFieldStyles(cvvField);
cardField.CVVField({style: {'input': styles}}).render(cvvField.parentNode);
cvvField.remove();
}
document.dispatchEvent(new CustomEvent("hosted_fields_loaded"));
}
gateWayBox.style.display = oldDisplayStyle;
show(buttonSelector);
document.querySelector(buttonSelector).addEventListener("click", (event) => {
event.preventDefault();
this.spinner.block();
this.errorHandler.clear();
cardField.submit()
.catch((error) => {
this.spinner.unblock();
console.error(error)
this.errorHandler.message(this.defaultConfig.hosted_fields.labels.fields_not_valid);
})
});
}
cardFieldStyles(field) {
const allowedProperties = [
'appearance',
'color',
'direction',
'font',
'font-family',
'font-size',
'font-size-adjust',
'font-stretch',
'font-style',
'font-variant',
'font-variant-alternates',
'font-variant-caps',
'font-variant-east-asian',
'font-variant-ligatures',
'font-variant-numeric',
'font-weight',
'letter-spacing',
'line-height',
'opacity',
'outline',
'padding',
'padding-bottom',
'padding-left',
'padding-right',
'padding-top',
'text-shadow',
'transition',
'-moz-appearance',
'-moz-osx-font-smoothing',
'-moz-tap-highlight-color',
'-moz-transition',
'-webkit-appearance',
'-webkit-osx-font-smoothing',
'-webkit-tap-highlight-color',
'-webkit-transition',
];
const stylesRaw = window.getComputedStyle(field);
const styles = {};
Object.values(stylesRaw).forEach((prop) => {
if (!stylesRaw[prop] || !allowedProperties.includes(prop)) {
return;
}
styles[prop] = '' + stylesRaw[prop];
});
return styles;
}
disableFields() {
if (this.currentHostedFieldsInstance) {
this.currentHostedFieldsInstance.setAttribute({
field: 'number',
attribute: 'disabled'
})
this.currentHostedFieldsInstance.setAttribute({
field: 'cvv',
attribute: 'disabled'
})
this.currentHostedFieldsInstance.setAttribute({
field: 'expirationDate',
attribute: 'disabled'
})
}
}
enableFields() {
if (this.currentHostedFieldsInstance) {
this.currentHostedFieldsInstance.removeAttribute({
field: 'number',
attribute: 'disabled'
})
this.currentHostedFieldsInstance.removeAttribute({
field: 'cvv',
attribute: 'disabled'
})
this.currentHostedFieldsInstance.removeAttribute({
field: 'expirationDate',
attribute: 'disabled'
})
}
}
_submit(contextConfig) {
this.spinner.block();
this.errorHandler.clear();
if (this.formValid && this.cardValid) {
const save_card = this.defaultConfig.can_save_vault_token ? true : false;
let vault = document.getElementById('ppcp-credit-card-vault') ?
document.getElementById('ppcp-credit-card-vault').checked : save_card;
if (this.defaultConfig.enforce_vault) {
vault = true;
}
const contingency = this.defaultConfig.hosted_fields.contingency;
const hostedFieldsData = {
vault: vault
};
if (contingency !== 'NO_3D_SECURE') {
hostedFieldsData.contingencies = [contingency];
}
if (this.defaultConfig.payer) {
hostedFieldsData.cardholderName = this.defaultConfig.payer.name.given_name + ' ' + this.defaultConfig.payer.name.surname;
}
if (!hostedFieldsData.cardholderName) {
const firstName = document.getElementById('billing_first_name') ? document.getElementById('billing_first_name').value : '';
const lastName = document.getElementById('billing_last_name') ? document.getElementById('billing_last_name').value : '';
hostedFieldsData.cardholderName = firstName + ' ' + lastName;
}
this.currentHostedFieldsInstance.submit(hostedFieldsData).then((payload) => {
payload.orderID = payload.orderId;
this.spinner.unblock();
return contextConfig.onApprove(payload);
}).catch(err => {
this.spinner.unblock();
this.errorHandler.clear();
if (err.data?.details?.length) {
this.errorHandler.message(err.data.details.map(d => `${d.issue} ${d.description}`).join('<br/>'));
} else if (err.details?.length) {
this.errorHandler.message(err.details.map(d => `${d.issue} ${d.description}`).join('<br/>'));
} else if (err.data?.errors?.length > 0) {
this.errorHandler.messages(err.data.errors);
} else if (err.data?.message) {
this.errorHandler.message(err.data.message);
} else if (err.message) {
this.errorHandler.message(err.message);
} else {
this.errorHandler.genericError();
}
});
} else {
this.spinner.unblock();
let message = this.defaultConfig.labels.error.generic;
if (this.emptyFields.size > 0) {
message = this.defaultConfig.hosted_fields.labels.fields_empty;
} else if (!this.cardValid) {
message = this.defaultConfig.hosted_fields.labels.card_not_supported;
} else if (!this.formValid) {
message = this.defaultConfig.hosted_fields.labels.fields_not_valid;
}
this.errorHandler.message(message);
}
}
_cardNumberFiledCLassNameByCardType(cardType) {
return cardType === 'american-express' ? 'amex' : cardType.replace('-', '');
}
_recreateElementClassAttribute(element, newClassName) {
element.removeAttribute('class')
element.setAttribute('class', newClassName);
}
}
export default CardFieldsRenderer;

View file

@ -1,7 +1,7 @@
import dccInputFactory from "../Helper/DccInputFactory";
import {show} from "../Helper/Hiding";
class CreditCardRenderer {
class HostedFieldsRenderer {
constructor(defaultConfig, errorHandler, spinner) {
this.defaultConfig = defaultConfig;
@ -156,140 +156,10 @@ class CreditCardRenderer {
return;
}
if (typeof paypal.CardFields !== 'undefined') {
const buttonSelector = wrapper + ' button';
const gateWayBox = document.querySelector('.payment_box.payment_method_ppcp-credit-card-gateway');
if (!gateWayBox) {
return
}
const oldDisplayStyle = gateWayBox.style.display;
gateWayBox.style.display = 'block';
const hideDccGateway = document.querySelector('#ppcp-hide-dcc');
if (hideDccGateway) {
hideDccGateway.parentNode.removeChild(hideDccGateway);
}
const cardField = paypal.CardFields({
createOrder: contextConfig.createOrder,
onApprove: function (data) {
return contextConfig.onApprove(data);
},
onError: function (error) {
console.error(error)
this.spinner.unblock();
}
});
if (cardField.isEligible()) {
const nameField = document.getElementById('ppcp-credit-card-gateway-card-name');
if(nameField) {
let styles = this.cardFieldStyles(nameField);
cardField.NameField({style: {'input': styles}}).render(nameField.parentNode);
nameField.remove();
}
const numberField = document.getElementById('ppcp-credit-card-gateway-card-number');
if(numberField) {
let styles = this.cardFieldStyles(numberField);
cardField.NumberField({style: {'input': styles}}).render(numberField.parentNode);
numberField.remove();
}
const expiryField = document.getElementById('ppcp-credit-card-gateway-card-expiry');
if(expiryField) {
let styles = this.cardFieldStyles(expiryField);
cardField.ExpiryField({style: {'input': styles}}).render(expiryField.parentNode);
expiryField.remove();
}
const cvvField = document.getElementById('ppcp-credit-card-gateway-card-cvc');
if(cvvField) {
let styles = this.cardFieldStyles(cvvField);
cardField.CVVField({style: {'input': styles}}).render(cvvField.parentNode);
cvvField.remove();
}
document.dispatchEvent(new CustomEvent("hosted_fields_loaded"));
}
gateWayBox.style.display = oldDisplayStyle;
show(buttonSelector);
document.querySelector(buttonSelector).addEventListener("click", (event) => {
event.preventDefault();
this.spinner.block();
this.errorHandler.clear();
cardField.submit()
.catch((error) => {
this.spinner.unblock();
console.error(error)
this.errorHandler.message(this.defaultConfig.hosted_fields.labels.fields_not_valid);
})
});
return;
}
const wrapperElement = document.querySelector(wrapper);
wrapperElement.parentNode.removeChild(wrapperElement);
}
cardFieldStyles(field) {
const allowedProperties = [
'appearance',
'color',
'direction',
'font',
'font-family',
'font-size',
'font-size-adjust',
'font-stretch',
'font-style',
'font-variant',
'font-variant-alternates',
'font-variant-caps',
'font-variant-east-asian',
'font-variant-ligatures',
'font-variant-numeric',
'font-weight',
'letter-spacing',
'line-height',
'opacity',
'outline',
'padding',
'padding-bottom',
'padding-left',
'padding-right',
'padding-top',
'text-shadow',
'transition',
'-moz-appearance',
'-moz-osx-font-smoothing',
'-moz-tap-highlight-color',
'-moz-transition',
'-webkit-appearance',
'-webkit-osx-font-smoothing',
'-webkit-tap-highlight-color',
'-webkit-transition',
];
const stylesRaw = window.getComputedStyle(field);
const styles = {};
Object.values(stylesRaw).forEach((prop) => {
if (!stylesRaw[prop] || !allowedProperties.includes(prop)) {
return;
}
styles[prop] = '' + stylesRaw[prop];
});
return styles;
}
disableFields() {
if (this.currentHostedFieldsInstance) {
this.currentHostedFieldsInstance.setAttribute({
@ -401,4 +271,4 @@ class CreditCardRenderer {
}
}
export default CreditCardRenderer;
export default HostedFieldsRenderer;