mirror of
https://github.com/woocommerce/woocommerce-paypal-payments.git
synced 2025-09-06 09:08:09 +08:00
Merge trunk
This commit is contained in:
commit
9be6601ba9
13 changed files with 769 additions and 291 deletions
|
@ -53,6 +53,14 @@ return function ( string $root_dir ): iterable {
|
||||||
$modules[] = ( require "$modules_dir/ppcp-saved-payment-checker/module.php" )();
|
$modules[] = ( require "$modules_dir/ppcp-saved-payment-checker/module.php" )();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( apply_filters(
|
||||||
|
//phpcs:disable WordPress.NamingConventions.ValidHookName.UseUnderscores
|
||||||
|
'woocommerce.feature-flags.woocommerce_paypal_payments.card_fields_enabled',
|
||||||
|
getenv( 'PCP_CARD_FIELDS_ENABLED' ) === '1'
|
||||||
|
) ) {
|
||||||
|
$modules[] = ( require "$modules_dir/ppcp-card-fields/module.php" )();
|
||||||
|
}
|
||||||
|
|
||||||
if ( apply_filters(
|
if ( apply_filters(
|
||||||
//phpcs:disable WordPress.NamingConventions.ValidHookName.UseUnderscores
|
//phpcs:disable WordPress.NamingConventions.ValidHookName.UseUnderscores
|
||||||
'woocommerce.feature-flags.woocommerce_paypal_payments.save_payment_methods_enabled',
|
'woocommerce.feature-flags.woocommerce_paypal_payments.save_payment_methods_enabled',
|
||||||
|
|
|
@ -5,7 +5,8 @@ import CheckoutBootstap from './modules/ContextBootstrap/CheckoutBootstap';
|
||||||
import PayNowBootstrap from "./modules/ContextBootstrap/PayNowBootstrap";
|
import PayNowBootstrap from "./modules/ContextBootstrap/PayNowBootstrap";
|
||||||
import Renderer from './modules/Renderer/Renderer';
|
import Renderer from './modules/Renderer/Renderer';
|
||||||
import ErrorHandler from './modules/ErrorHandler';
|
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 MessageRenderer from "./modules/Renderer/MessageRenderer";
|
||||||
import Spinner from "./modules/Helper/Spinner";
|
import Spinner from "./modules/Helper/Spinner";
|
||||||
import {
|
import {
|
||||||
|
@ -37,7 +38,11 @@ const bootstrap = () => {
|
||||||
document.querySelector(checkoutFormSelector) ?? document.querySelector('.woocommerce-notices-wrapper')
|
document.querySelector(checkoutFormSelector) ?? document.querySelector('.woocommerce-notices-wrapper')
|
||||||
);
|
);
|
||||||
const spinner = new Spinner();
|
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(
|
const formSaver = new FormSaver(
|
||||||
PayPalCommerceGateway.ajax.save_checkout_form.endpoint,
|
PayPalCommerceGateway.ajax.save_checkout_form.endpoint,
|
||||||
|
|
|
@ -0,0 +1,155 @@
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CardFieldsRenderer;
|
|
@ -1,275 +0,0 @@
|
||||||
import dccInputFactory from "../Helper/DccInputFactory";
|
|
||||||
import {show} from "../Helper/Hiding";
|
|
||||||
import Product from "../Entity/Product";
|
|
||||||
|
|
||||||
class CreditCardRenderer {
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
typeof paypal.HostedFields === 'undefined'
|
|
||||||
|| ! paypal.HostedFields.isEligible()
|
|
||||||
) {
|
|
||||||
const wrapperElement = document.querySelector(wrapper);
|
|
||||||
wrapperElement.parentNode.removeChild(wrapperElement);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const buttonSelector = wrapper + ' button';
|
|
||||||
|
|
||||||
if (this.currentHostedFieldsInstance) {
|
|
||||||
this.currentHostedFieldsInstance.teardown()
|
|
||||||
.catch(err => console.error(`Hosted fields teardown error: ${err}`));
|
|
||||||
this.currentHostedFieldsInstance = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 cardNumberField = document.querySelector('#ppcp-credit-card-gateway-card-number');
|
|
||||||
|
|
||||||
const stylesRaw = window.getComputedStyle(cardNumberField);
|
|
||||||
let styles = {};
|
|
||||||
Object.values(stylesRaw).forEach( (prop) => {
|
|
||||||
if (! stylesRaw[prop]) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
styles[prop] = '' + stylesRaw[prop];
|
|
||||||
});
|
|
||||||
|
|
||||||
const cardNumber = dccInputFactory(cardNumberField);
|
|
||||||
cardNumberField.parentNode.replaceChild(cardNumber, cardNumberField);
|
|
||||||
|
|
||||||
const cardExpiryField = document.querySelector('#ppcp-credit-card-gateway-card-expiry');
|
|
||||||
const cardExpiry = dccInputFactory(cardExpiryField);
|
|
||||||
cardExpiryField.parentNode.replaceChild(cardExpiry, cardExpiryField);
|
|
||||||
|
|
||||||
const cardCodeField = document.querySelector('#ppcp-credit-card-gateway-card-cvc');
|
|
||||||
const cardCode = dccInputFactory(cardCodeField);
|
|
||||||
cardCodeField.parentNode.replaceChild(cardCode, cardCodeField);
|
|
||||||
|
|
||||||
gateWayBox.style.display = oldDisplayStyle;
|
|
||||||
|
|
||||||
const formWrapper = '.payment_box payment_method_ppcp-credit-card-gateway';
|
|
||||||
if (
|
|
||||||
this.defaultConfig.enforce_vault
|
|
||||||
&& document.querySelector(formWrapper + ' .ppcp-credit-card-vault')
|
|
||||||
) {
|
|
||||||
document.querySelector(formWrapper + ' .ppcp-credit-card-vault').checked = true;
|
|
||||||
document.querySelector(formWrapper + ' .ppcp-credit-card-vault').setAttribute('disabled', true);
|
|
||||||
}
|
|
||||||
paypal.HostedFields.render({
|
|
||||||
createOrder: contextConfig.createOrder,
|
|
||||||
styles: {
|
|
||||||
'input': styles
|
|
||||||
},
|
|
||||||
fields: {
|
|
||||||
number: {
|
|
||||||
selector: '#ppcp-credit-card-gateway-card-number',
|
|
||||||
placeholder: this.defaultConfig.hosted_fields.labels.credit_card_number,
|
|
||||||
},
|
|
||||||
cvv: {
|
|
||||||
selector: '#ppcp-credit-card-gateway-card-cvc',
|
|
||||||
placeholder: this.defaultConfig.hosted_fields.labels.cvv,
|
|
||||||
},
|
|
||||||
expirationDate: {
|
|
||||||
selector: '#ppcp-credit-card-gateway-card-expiry',
|
|
||||||
placeholder: this.defaultConfig.hosted_fields.labels.mm_yy,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}).then(hostedFields => {
|
|
||||||
document.dispatchEvent(new CustomEvent("hosted_fields_loaded"));
|
|
||||||
this.currentHostedFieldsInstance = hostedFields;
|
|
||||||
|
|
||||||
hostedFields.on('inputSubmitRequest', () => {
|
|
||||||
this._submit(contextConfig);
|
|
||||||
});
|
|
||||||
hostedFields.on('cardTypeChange', (event) => {
|
|
||||||
if ( ! event.cards.length ) {
|
|
||||||
this.cardValid = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const validCards = this.defaultConfig.hosted_fields.valid_cards;
|
|
||||||
this.cardValid = validCards.indexOf(event.cards[0].type) !== -1;
|
|
||||||
|
|
||||||
const className = this._cardNumberFiledCLassNameByCardType(event.cards[0].type);
|
|
||||||
this._recreateElementClassAttribute(cardNumber, cardNumberField.className);
|
|
||||||
if (event.cards.length === 1) {
|
|
||||||
cardNumber.classList.add(className);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
hostedFields.on('validityChange', (event) => {
|
|
||||||
this.formValid = Object.keys(event.fields).every(function (key) {
|
|
||||||
return event.fields[key].isValid;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
hostedFields.on('empty', (event) => {
|
|
||||||
this._recreateElementClassAttribute(cardNumber, cardNumberField.className);
|
|
||||||
this.emptyFields.add(event.emittedBy);
|
|
||||||
});
|
|
||||||
hostedFields.on('notEmpty', (event) => {
|
|
||||||
this.emptyFields.delete(event.emittedBy);
|
|
||||||
});
|
|
||||||
|
|
||||||
show(buttonSelector);
|
|
||||||
|
|
||||||
if (document.querySelector(wrapper).getAttribute('data-ppcp-subscribed') !== true) {
|
|
||||||
document.querySelector(buttonSelector).addEventListener(
|
|
||||||
'click',
|
|
||||||
event => {
|
|
||||||
event.preventDefault();
|
|
||||||
this._submit(contextConfig);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
document.querySelector(wrapper).setAttribute('data-ppcp-subscribed', true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
document.querySelector('#payment_method_ppcp-credit-card-gateway').addEventListener(
|
|
||||||
'click',
|
|
||||||
() => {
|
|
||||||
document.querySelector('label[for=ppcp-credit-card-gateway-card-number]').click();
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
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 CreditCardRenderer;
|
|
|
@ -0,0 +1,274 @@
|
||||||
|
import dccInputFactory from "../Helper/DccInputFactory";
|
||||||
|
import {show} from "../Helper/Hiding";
|
||||||
|
|
||||||
|
class HostedFieldsRenderer {
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof paypal.HostedFields !== 'undefined' && paypal.HostedFields.isEligible()) {
|
||||||
|
const buttonSelector = wrapper + ' button';
|
||||||
|
|
||||||
|
if (this.currentHostedFieldsInstance) {
|
||||||
|
this.currentHostedFieldsInstance.teardown()
|
||||||
|
.catch(err => console.error(`Hosted fields teardown error: ${err}`));
|
||||||
|
this.currentHostedFieldsInstance = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 cardNumberField = document.querySelector('#ppcp-credit-card-gateway-card-number');
|
||||||
|
|
||||||
|
const stylesRaw = window.getComputedStyle(cardNumberField);
|
||||||
|
let styles = {};
|
||||||
|
Object.values(stylesRaw).forEach((prop) => {
|
||||||
|
if (!stylesRaw[prop]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
styles[prop] = '' + stylesRaw[prop];
|
||||||
|
});
|
||||||
|
|
||||||
|
const cardNumber = dccInputFactory(cardNumberField);
|
||||||
|
cardNumberField.parentNode.replaceChild(cardNumber, cardNumberField);
|
||||||
|
|
||||||
|
const cardExpiryField = document.querySelector('#ppcp-credit-card-gateway-card-expiry');
|
||||||
|
const cardExpiry = dccInputFactory(cardExpiryField);
|
||||||
|
cardExpiryField.parentNode.replaceChild(cardExpiry, cardExpiryField);
|
||||||
|
|
||||||
|
const cardCodeField = document.querySelector('#ppcp-credit-card-gateway-card-cvc');
|
||||||
|
const cardCode = dccInputFactory(cardCodeField);
|
||||||
|
cardCodeField.parentNode.replaceChild(cardCode, cardCodeField);
|
||||||
|
|
||||||
|
gateWayBox.style.display = oldDisplayStyle;
|
||||||
|
|
||||||
|
const formWrapper = '.payment_box payment_method_ppcp-credit-card-gateway';
|
||||||
|
if (
|
||||||
|
this.defaultConfig.enforce_vault
|
||||||
|
&& document.querySelector(formWrapper + ' .ppcp-credit-card-vault')
|
||||||
|
) {
|
||||||
|
document.querySelector(formWrapper + ' .ppcp-credit-card-vault').checked = true;
|
||||||
|
document.querySelector(formWrapper + ' .ppcp-credit-card-vault').setAttribute('disabled', true);
|
||||||
|
}
|
||||||
|
paypal.HostedFields.render({
|
||||||
|
createOrder: contextConfig.createOrder,
|
||||||
|
styles: {
|
||||||
|
'input': styles
|
||||||
|
},
|
||||||
|
fields: {
|
||||||
|
number: {
|
||||||
|
selector: '#ppcp-credit-card-gateway-card-number',
|
||||||
|
placeholder: this.defaultConfig.hosted_fields.labels.credit_card_number,
|
||||||
|
},
|
||||||
|
cvv: {
|
||||||
|
selector: '#ppcp-credit-card-gateway-card-cvc',
|
||||||
|
placeholder: this.defaultConfig.hosted_fields.labels.cvv,
|
||||||
|
},
|
||||||
|
expirationDate: {
|
||||||
|
selector: '#ppcp-credit-card-gateway-card-expiry',
|
||||||
|
placeholder: this.defaultConfig.hosted_fields.labels.mm_yy,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).then(hostedFields => {
|
||||||
|
document.dispatchEvent(new CustomEvent("hosted_fields_loaded"));
|
||||||
|
this.currentHostedFieldsInstance = hostedFields;
|
||||||
|
|
||||||
|
hostedFields.on('inputSubmitRequest', () => {
|
||||||
|
this._submit(contextConfig);
|
||||||
|
});
|
||||||
|
hostedFields.on('cardTypeChange', (event) => {
|
||||||
|
if (!event.cards.length) {
|
||||||
|
this.cardValid = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const validCards = this.defaultConfig.hosted_fields.valid_cards;
|
||||||
|
this.cardValid = validCards.indexOf(event.cards[0].type) !== -1;
|
||||||
|
|
||||||
|
const className = this._cardNumberFiledCLassNameByCardType(event.cards[0].type);
|
||||||
|
this._recreateElementClassAttribute(cardNumber, cardNumberField.className);
|
||||||
|
if (event.cards.length === 1) {
|
||||||
|
cardNumber.classList.add(className);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
hostedFields.on('validityChange', (event) => {
|
||||||
|
this.formValid = Object.keys(event.fields).every(function (key) {
|
||||||
|
return event.fields[key].isValid;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
hostedFields.on('empty', (event) => {
|
||||||
|
this._recreateElementClassAttribute(cardNumber, cardNumberField.className);
|
||||||
|
this.emptyFields.add(event.emittedBy);
|
||||||
|
});
|
||||||
|
hostedFields.on('notEmpty', (event) => {
|
||||||
|
this.emptyFields.delete(event.emittedBy);
|
||||||
|
});
|
||||||
|
|
||||||
|
show(buttonSelector);
|
||||||
|
|
||||||
|
if (document.querySelector(wrapper).getAttribute('data-ppcp-subscribed') !== true) {
|
||||||
|
document.querySelector(buttonSelector).addEventListener(
|
||||||
|
'click',
|
||||||
|
event => {
|
||||||
|
event.preventDefault();
|
||||||
|
this._submit(contextConfig);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
document.querySelector(wrapper).setAttribute('data-ppcp-subscribed', true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
document.querySelector('#payment_method_ppcp-credit-card-gateway').addEventListener(
|
||||||
|
'click',
|
||||||
|
() => {
|
||||||
|
document.querySelector('label[for=ppcp-credit-card-gateway-card-number]').click();
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const wrapperElement = document.querySelector(wrapper);
|
||||||
|
wrapperElement.parentNode.removeChild(wrapperElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 HostedFieldsRenderer;
|
17
modules/ppcp-card-fields/composer.json
Normal file
17
modules/ppcp-card-fields/composer.json
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
{
|
||||||
|
"name": "woocommerce/ppcp-card-fields",
|
||||||
|
"type": "dhii-mod",
|
||||||
|
"description": "Advanced Checkout Card Fields module",
|
||||||
|
"license": "GPL-2.0",
|
||||||
|
"require": {
|
||||||
|
"php": "^7.2 | ^8.0",
|
||||||
|
"dhii/module-interface": "^0.3.0-alpha1"
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"WooCommerce\\PayPalCommerce\\CardFields\\": "src"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"minimum-stability": "dev",
|
||||||
|
"prefer-stable": true
|
||||||
|
}
|
12
modules/ppcp-card-fields/extensions.php
Normal file
12
modules/ppcp-card-fields/extensions.php
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* The Card Fields module extensions.
|
||||||
|
*
|
||||||
|
* @package WooCommerce\PayPalCommerce\CardFields
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace WooCommerce\PayPalCommerce\CardFields;
|
||||||
|
|
||||||
|
return array();
|
16
modules/ppcp-card-fields/module.php
Normal file
16
modules/ppcp-card-fields/module.php
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* The Card Fields module.
|
||||||
|
*
|
||||||
|
* @package WooCommerce\PayPalCommerce\CardFields
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace WooCommerce\PayPalCommerce\CardFields;
|
||||||
|
|
||||||
|
use WooCommerce\PayPalCommerce\Vendor\Dhii\Modular\Module\ModuleInterface;
|
||||||
|
|
||||||
|
return static function (): ModuleInterface {
|
||||||
|
return new CardFieldsModule();
|
||||||
|
};
|
44
modules/ppcp-card-fields/services.php
Normal file
44
modules/ppcp-card-fields/services.php
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* The Card Fields module services.
|
||||||
|
*
|
||||||
|
* @package WooCommerce\PayPalCommerce\CardFields
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace WooCommerce\PayPalCommerce\CardFields;
|
||||||
|
|
||||||
|
use WooCommerce\PayPalCommerce\CardFields\Helper\CardFieldsApplies;
|
||||||
|
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
|
||||||
|
|
||||||
|
return array(
|
||||||
|
'card-fields.eligible' => static function ( ContainerInterface $container ): bool {
|
||||||
|
$save_payment_methods_applies = $container->get( 'card-fields.helpers.save-payment-methods-applies' );
|
||||||
|
assert( $save_payment_methods_applies instanceof CardFieldsApplies );
|
||||||
|
|
||||||
|
return $save_payment_methods_applies->for_country_currency();
|
||||||
|
},
|
||||||
|
'card-fields.helpers.save-payment-methods-applies' => static function ( ContainerInterface $container ) : CardFieldsApplies {
|
||||||
|
return new CardFieldsApplies(
|
||||||
|
$container->get( 'card-fields.supported-country-currency-matrix' ),
|
||||||
|
$container->get( 'api.shop.currency' ),
|
||||||
|
$container->get( 'api.shop.country' )
|
||||||
|
);
|
||||||
|
},
|
||||||
|
'card-fields.supported-country-currency-matrix' => static function ( ContainerInterface $container ) : array {
|
||||||
|
return apply_filters(
|
||||||
|
'woocommerce_paypal_payments_card_fields_supported_country_currency_matrix',
|
||||||
|
array(
|
||||||
|
'US' => array(
|
||||||
|
'AUD',
|
||||||
|
'CAD',
|
||||||
|
'EUR',
|
||||||
|
'GBP',
|
||||||
|
'JPY',
|
||||||
|
'USD',
|
||||||
|
),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
115
modules/ppcp-card-fields/src/CardFieldsModule.php
Normal file
115
modules/ppcp-card-fields/src/CardFieldsModule.php
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* The Card Fields module.
|
||||||
|
*
|
||||||
|
* @package WooCommerce\PayPalCommerce\CardFields
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace WooCommerce\PayPalCommerce\CardFields;
|
||||||
|
|
||||||
|
use WooCommerce\PayPalCommerce\Vendor\Dhii\Container\ServiceProvider;
|
||||||
|
use WooCommerce\PayPalCommerce\Vendor\Dhii\Modular\Module\ModuleInterface;
|
||||||
|
use WooCommerce\PayPalCommerce\Vendor\Interop\Container\ServiceProviderInterface;
|
||||||
|
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
|
||||||
|
use WooCommerce\PayPalCommerce\WcGateway\Gateway\CreditCardGateway;
|
||||||
|
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class CardFieldsModule
|
||||||
|
*/
|
||||||
|
class CardFieldsModule implements ModuleInterface {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public function setup(): ServiceProviderInterface {
|
||||||
|
return new ServiceProvider(
|
||||||
|
require __DIR__ . '/../services.php',
|
||||||
|
require __DIR__ . '/../extensions.php'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public function run( ContainerInterface $c ): void {
|
||||||
|
if ( ! $c->get( 'card-fields.eligible' ) ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Param types removed to avoid third-party issues.
|
||||||
|
*
|
||||||
|
* @psalm-suppress MissingClosureParamType
|
||||||
|
*/
|
||||||
|
add_filter(
|
||||||
|
'woocommerce_paypal_payments_sdk_components_hook',
|
||||||
|
function( $components ) {
|
||||||
|
if ( in_array( 'hosted-fields', $components, true ) ) {
|
||||||
|
$key = array_search( 'hosted-fields', $components, true );
|
||||||
|
if ( $key !== false ) {
|
||||||
|
unset( $components[ $key ] );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$components[] = 'card-fields';
|
||||||
|
|
||||||
|
return $components;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
add_filter(
|
||||||
|
'woocommerce_credit_card_form_fields',
|
||||||
|
/**
|
||||||
|
* Return/Param types removed to avoid third-party issues.
|
||||||
|
*
|
||||||
|
* @psalm-suppress MissingClosureReturnType
|
||||||
|
* @psalm-suppress MissingClosureParamType
|
||||||
|
*/
|
||||||
|
function( $default_fields, $id ) {
|
||||||
|
if ( CreditCardGateway::ID === $id && apply_filters( 'woocommerce_paypal_payments_enable_cardholder_name_field', false ) ) {
|
||||||
|
$default_fields['card-name-field'] = '<p class="form-row form-row-wide">
|
||||||
|
<label for="ppcp-credit-card-gateway-card-name">' . esc_attr__( 'Cardholder Name', 'woocommerce-paypal-payments' ) . '</label>
|
||||||
|
<input id="ppcp-credit-card-gateway-card-name" class="input-text wc-credit-card-form-card-expiry" type="text" placeholder="' . esc_attr__( 'Cardholder Name (optional)', 'woocommerce-paypal-payments' ) . '" name="ppcp-credit-card-gateway-card-name">
|
||||||
|
</p>';
|
||||||
|
|
||||||
|
// Moves new item to first position.
|
||||||
|
$new_field = $default_fields['card-name-field'];
|
||||||
|
unset( $default_fields['card-name-field'] );
|
||||||
|
array_unshift( $default_fields, $new_field );
|
||||||
|
}
|
||||||
|
|
||||||
|
return $default_fields;
|
||||||
|
},
|
||||||
|
10,
|
||||||
|
2
|
||||||
|
);
|
||||||
|
|
||||||
|
add_filter(
|
||||||
|
'ppcp_create_order_request_body_data',
|
||||||
|
function( array $data ) use ( $c ): array {
|
||||||
|
$settings = $c->get( 'wcgateway.settings' );
|
||||||
|
assert( $settings instanceof Settings );
|
||||||
|
|
||||||
|
if (
|
||||||
|
$settings->has( '3d_secure_contingency' )
|
||||||
|
&& (
|
||||||
|
$settings->get( '3d_secure_contingency' ) === 'SCA_ALWAYS'
|
||||||
|
|| $settings->get( '3d_secure_contingency' ) === 'SCA_WHEN_REQUIRED'
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
$data['payment_source']['card'] = array(
|
||||||
|
'attributes' => array(
|
||||||
|
'verification' => array(
|
||||||
|
'method' => $settings->get( '3d_secure_contingency' ),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
66
modules/ppcp-card-fields/src/Helper/CardFieldsApplies.php
Normal file
66
modules/ppcp-card-fields/src/Helper/CardFieldsApplies.php
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Service for checking whether Card Fields can be used in the current country and the current currency.
|
||||||
|
*
|
||||||
|
* @package WooCommerce\PayPalCommerce\CardFields\Helper
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace WooCommerce\PayPalCommerce\CardFields\Helper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class CardFieldsApplies
|
||||||
|
*/
|
||||||
|
class CardFieldsApplies {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The matrix which countries and currency combinations can be used.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private $allowed_country_currency_matrix;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 3-letter currency code of the shop.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $currency;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 2-letter country code of the shop.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $country;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CardFieldsApplies constructor.
|
||||||
|
*
|
||||||
|
* @param array $allowed_country_currency_matrix The matrix which countries and currency combinations can be used.
|
||||||
|
* @param string $currency 3-letter currency code of the shop.
|
||||||
|
* @param string $country 2-letter country code of the shop.
|
||||||
|
*/
|
||||||
|
public function __construct(
|
||||||
|
array $allowed_country_currency_matrix,
|
||||||
|
string $currency,
|
||||||
|
string $country
|
||||||
|
) {
|
||||||
|
$this->allowed_country_currency_matrix = $allowed_country_currency_matrix;
|
||||||
|
$this->currency = $currency;
|
||||||
|
$this->country = $country;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether Card Fields can be used in the current country and the current currency.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function for_country_currency(): bool {
|
||||||
|
if ( ! in_array( $this->country, array_keys( $this->allowed_country_currency_matrix ), true ) ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return in_array( $this->currency, $this->allowed_country_currency_matrix[ $this->country ], true );
|
||||||
|
}
|
||||||
|
}
|
|
@ -273,7 +273,12 @@ class CompatModule implements ModuleInterface {
|
||||||
add_action(
|
add_action(
|
||||||
'init',
|
'init',
|
||||||
function() {
|
function() {
|
||||||
if ( $this->is_elementor_pro_active() || $this->is_divi_theme_active() ) {
|
if (
|
||||||
|
$this->is_block_theme_active()
|
||||||
|
|| $this->is_elementor_pro_active()
|
||||||
|
|| $this->is_divi_theme_active()
|
||||||
|
|| $this->is_divi_child_theme_active()
|
||||||
|
) {
|
||||||
add_filter(
|
add_filter(
|
||||||
'woocommerce_paypal_payments_single_product_renderer_hook',
|
'woocommerce_paypal_payments_single_product_renderer_hook',
|
||||||
function(): string {
|
function(): string {
|
||||||
|
@ -286,6 +291,15 @@ class CompatModule implements ModuleInterface {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether the current theme is a blocks theme.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
protected function is_block_theme_active(): bool {
|
||||||
|
return function_exists( 'wp_is_block_theme' ) && wp_is_block_theme();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks whether the Elementor Pro plugins (allowing integrations with WC) is active.
|
* Checks whether the Elementor Pro plugins (allowing integrations with WC) is active.
|
||||||
*
|
*
|
||||||
|
@ -304,4 +318,15 @@ class CompatModule implements ModuleInterface {
|
||||||
$theme = wp_get_theme();
|
$theme = wp_get_theme();
|
||||||
return $theme->get( 'Name' ) === 'Divi';
|
return $theme->get( 'Name' ) === 'Divi';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether a Divi child theme is currently used.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
protected function is_divi_child_theme_active(): bool {
|
||||||
|
$theme = wp_get_theme();
|
||||||
|
$parent = $theme->parent();
|
||||||
|
return ( $parent && $parent->get( 'Name' ) === 'Divi' );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,7 +38,7 @@ return array(
|
||||||
$container->get( 'button.request-data' ),
|
$container->get( 'button.request-data' ),
|
||||||
$container->get( 'order-tracking.shipment.factory' ),
|
$container->get( 'order-tracking.shipment.factory' ),
|
||||||
$container->get( 'order-tracking.allowed-shipping-statuses' ),
|
$container->get( 'order-tracking.allowed-shipping-statuses' ),
|
||||||
$container->get( 'order-tracking.is-merchant-country-us' )
|
$container->get( 'order-tracking.should-use-second-version-of-api' )
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
'order-tracking.module.url' => static function ( ContainerInterface $container ): string {
|
'order-tracking.module.url' => static function ( ContainerInterface $container ): string {
|
||||||
|
@ -57,7 +57,7 @@ return array(
|
||||||
$container->get( 'order-tracking.allowed-shipping-statuses' ),
|
$container->get( 'order-tracking.allowed-shipping-statuses' ),
|
||||||
$container->get( 'order-tracking.available-carriers' ),
|
$container->get( 'order-tracking.available-carriers' ),
|
||||||
$container->get( 'order-tracking.endpoint.controller' ),
|
$container->get( 'order-tracking.endpoint.controller' ),
|
||||||
$container->get( 'order-tracking.is-merchant-country-us' )
|
$container->get( 'order-tracking.should-use-second-version-of-api' )
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
'order-tracking.allowed-shipping-statuses' => static function ( ContainerInterface $container ): array {
|
'order-tracking.allowed-shipping-statuses' => static function ( ContainerInterface $container ): array {
|
||||||
|
@ -90,8 +90,24 @@ return array(
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
'order-tracking.is-merchant-country-us' => static function ( ContainerInterface $container ): bool {
|
|
||||||
return $container->get( 'api.shop.country' ) === 'US';
|
/**
|
||||||
|
* The list of country codes, for which the 2nd version of PayPal tracking API is supported.
|
||||||
|
*/
|
||||||
|
'order-tracking.second-version-api-supported-countries' => static function (): array {
|
||||||
|
/**
|
||||||
|
* Returns codes of countries, for which the 2nd version of PayPal tracking API is supported.
|
||||||
|
*/
|
||||||
|
return apply_filters(
|
||||||
|
'woocommerce_paypal_payments_supported_country_codes_for_second_version_of_tracking_api',
|
||||||
|
array( 'US', 'AU', 'CA', 'FR', 'DE', 'IT', 'ES' )
|
||||||
|
);
|
||||||
|
},
|
||||||
|
'order-tracking.should-use-second-version-of-api' => static function ( ContainerInterface $container ): bool {
|
||||||
|
$supported_county_codes = $container->get( 'order-tracking.second-version-api-supported-countries' );
|
||||||
|
$selected_country_code = $container->get( 'api.shop.country' );
|
||||||
|
|
||||||
|
return in_array( $selected_country_code, $supported_county_codes, true );
|
||||||
},
|
},
|
||||||
'order-tracking.integrations' => static function ( ContainerInterface $container ): array {
|
'order-tracking.integrations' => static function ( ContainerInterface $container ): array {
|
||||||
$shipment_factory = $container->get( 'order-tracking.shipment.factory' );
|
$shipment_factory = $container->get( 'order-tracking.shipment.factory' );
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue