mirror of
https://github.com/woocommerce/woocommerce-paypal-payments.git
synced 2025-09-06 16:24:33 +08:00
Merge branch 'trunk' into PCP-2049-place-order-redirect
This commit is contained in:
commit
ad715bcfb6
47 changed files with 1591 additions and 553 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,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;
|
|
@ -973,6 +973,7 @@ document.querySelector("#payment").before(document.querySelector("#ppcp-messages
|
|||
'subscription_plan_id' => $this->subscription_helper->paypal_subscription_id(),
|
||||
'variable_paypal_subscription_variations' => $this->subscription_helper->variable_paypal_subscription_variations(),
|
||||
'subscription_product_allowed' => $this->subscription_helper->checkout_subscription_product_allowed(),
|
||||
'locations_with_subscription_product' => $this->subscription_helper->locations_with_subscription_product(),
|
||||
'enforce_vault' => $this->has_subscriptions(),
|
||||
'can_save_vault_token' => $this->can_save_vault_token(),
|
||||
'is_free_trial_cart' => $is_free_trial_cart,
|
||||
|
@ -1006,8 +1007,9 @@ document.querySelector("#payment").before(document.querySelector("#ppcp-messages
|
|||
'id' => CardButtonGateway::ID,
|
||||
'wrapper' => '#ppc-button-' . CardButtonGateway::ID,
|
||||
'style' => array(
|
||||
'shape' => $this->style_for_context( 'shape', $this->context() ),
|
||||
// TODO: color black, white from the gateway settings.
|
||||
'shape' => $this->style_for_apm( 'shape', 'card' ),
|
||||
'color' => $this->style_for_apm( 'color', 'card', 'black' ),
|
||||
'layout' => $this->style_for_apm( 'poweredby_tagline', 'card', false ) === $this->normalize_style_value( true ) ? 'vertical' : 'horizontal',
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -1341,9 +1343,9 @@ document.querySelector("#payment").before(document.querySelector("#ppcp-messages
|
|||
}
|
||||
|
||||
/**
|
||||
* Determines the style for a given indicator in a given context.
|
||||
* Determines the style for a given property in a given context.
|
||||
*
|
||||
* @param string $style The style.
|
||||
* @param string $style The name of the style property.
|
||||
* @param string $context The context.
|
||||
*
|
||||
* @return string
|
||||
|
@ -1366,13 +1368,46 @@ document.querySelector("#payment").before(document.querySelector("#ppcp-messages
|
|||
$context = 'general';
|
||||
}
|
||||
|
||||
$value = isset( $defaults[ $style ] ) ?
|
||||
$defaults[ $style ] : '';
|
||||
$value = $this->settings->has( 'button_' . $style ) ?
|
||||
$this->settings->get( 'button_' . $style ) : $value;
|
||||
$value = $this->settings->has( 'button_' . $context . '_' . $style ) ?
|
||||
$this->settings->get( 'button_' . $context . '_' . $style ) : $value;
|
||||
return $this->get_style_value( "button_{$context}_${style}" )
|
||||
?? $this->get_style_value( "button_${style}" )
|
||||
?? $this->normalize_style_value( $defaults[ $style ] ?? '' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the style for a given property in a given APM.
|
||||
*
|
||||
* @param string $style The name of the style property.
|
||||
* @param string $apm The APM name, such as 'card'.
|
||||
* @param ?mixed $default The default value.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function style_for_apm( string $style, string $apm, $default = null ): string {
|
||||
return $this->get_style_value( "${apm}_button_${style}" )
|
||||
?? ( $default ? $this->normalize_style_value( $default ) : null )
|
||||
?? $this->style_for_context( $style, 'checkout' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the style property value or null.
|
||||
*
|
||||
* @param string $key The style property key in the settings.
|
||||
* @return string|null
|
||||
*/
|
||||
private function get_style_value( string $key ): ?string {
|
||||
if ( ! $this->settings->has( $key ) ) {
|
||||
return null;
|
||||
}
|
||||
return $this->normalize_style_value( $this->settings->get( $key ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the style property value to string.
|
||||
*
|
||||
* @param mixed $value The style property value.
|
||||
* @return string
|
||||
*/
|
||||
private function normalize_style_value( $value ): string {
|
||||
if ( is_bool( $value ) ) {
|
||||
$value = $value ? 'true' : 'false';
|
||||
}
|
||||
|
|
|
@ -70,6 +70,8 @@ class CartScriptParamsEndpoint implements EndpointInterface {
|
|||
wc_maybe_define_constant( 'WOOCOMMERCE_CART', true );
|
||||
}
|
||||
|
||||
$include_shipping = (bool) wc_clean( wp_unslash( $_GET['shipping'] ?? '' ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||
|
||||
$script_data = $this->smart_button->script_data();
|
||||
|
||||
$total = (float) WC()->cart->get_total( 'numeric' );
|
||||
|
@ -79,20 +81,23 @@ class CartScriptParamsEndpoint implements EndpointInterface {
|
|||
$shop_country_code = $base_location['country'] ?? '';
|
||||
$currency_code = get_woocommerce_currency();
|
||||
|
||||
wp_send_json_success(
|
||||
array(
|
||||
'url_params' => $script_data['url_params'],
|
||||
'button' => $script_data['button'],
|
||||
'messages' => $script_data['messages'],
|
||||
'amount' => WC()->cart->get_total( 'raw' ),
|
||||
$response = array(
|
||||
'url_params' => $script_data['url_params'],
|
||||
'button' => $script_data['button'],
|
||||
'messages' => $script_data['messages'],
|
||||
'amount' => WC()->cart->get_total( 'raw' ),
|
||||
|
||||
'total' => $total,
|
||||
'total_str' => ( new Money( $total, $currency_code ) )->value_str(),
|
||||
'currency_code' => $currency_code,
|
||||
'country_code' => $shop_country_code,
|
||||
)
|
||||
'total' => $total,
|
||||
'total_str' => ( new Money( $total, $currency_code ) )->value_str(),
|
||||
'currency_code' => $currency_code,
|
||||
'country_code' => $shop_country_code,
|
||||
);
|
||||
|
||||
if ( $include_shipping ) {
|
||||
$response = $this->append_shipping_data( $response, $currency_code );
|
||||
}
|
||||
|
||||
wp_send_json_success( $response );
|
||||
return true;
|
||||
} catch ( Throwable $error ) {
|
||||
$this->logger->error( "CartScriptParamsEndpoint execution failed. {$error->getMessage()} {$error->getFile()}:{$error->getLine()}" );
|
||||
|
@ -101,4 +106,45 @@ class CartScriptParamsEndpoint implements EndpointInterface {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends shipping data to response.
|
||||
*
|
||||
* @param array $response The response array.
|
||||
* @param string $currency_code The currency code.
|
||||
* @return array
|
||||
*/
|
||||
private function append_shipping_data( array $response, string $currency_code ): array {
|
||||
$calculated_packages = WC()->shipping->calculate_shipping(
|
||||
WC()->cart->get_shipping_packages()
|
||||
);
|
||||
|
||||
$shipping_packages = array();
|
||||
|
||||
foreach ( $calculated_packages[0]['rates'] as $rate ) {
|
||||
$rate_cost = $rate->get_cost();
|
||||
|
||||
/**
|
||||
* The shipping rate.
|
||||
*
|
||||
* @var \WC_Shipping_Rate $rate
|
||||
*/
|
||||
$shipping_packages[] = array(
|
||||
'id' => $rate->get_id(),
|
||||
'label' => $rate->get_label(),
|
||||
'cost' => (float) $rate_cost,
|
||||
'cost_str' => ( new Money( (float) $rate_cost, $currency_code ) )->value_str(),
|
||||
'description' => html_entity_decode(
|
||||
wp_strip_all_tags(
|
||||
wc_price( (float) $rate->get_cost(), array( 'currency' => get_woocommerce_currency() ) )
|
||||
)
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
$response['chosen_shipping_methods'] = WC()->session->get( 'chosen_shipping_methods' );
|
||||
$response['shipping_packages'] = $shipping_packages;
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue