mirror of
https://github.com/woocommerce/woocommerce-paypal-payments.git
synced 2025-09-01 07:02:48 +08:00
Inject either hosted fields or card fields depending on context
This commit is contained in:
parent
9b58eca1b0
commit
32f7b4940b
3 changed files with 274 additions and 134 deletions
|
@ -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,
|
||||
|
|
|
@ -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;
|
|
@ -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;
|
Loading…
Add table
Add a link
Reference in a new issue