mirror of
https://github.com/woocommerce/woocommerce-paypal-payments.git
synced 2025-09-05 08:59:14 +08:00
🔀 Merge branch 'trunk' into PCP-3179-apple-pay-google-pay-buttons-no-longer-visible-in-standard-payments-button-previews-after-moving-them-to-advanced-card-processing-tab
This commit is contained in:
commit
202f308850
38 changed files with 820 additions and 251 deletions
|
@ -1,5 +1,18 @@
|
|||
*** Changelog ***
|
||||
|
||||
= 2.8.0 - xxxx-xx-xx =
|
||||
* Fix - Calculate totals after adding shipping to include taxes #2296
|
||||
* Fix - Package tracking integration throws error in 2.7.1 #2289
|
||||
* Fix - Make PayPal Subscription products unique in cart #2265
|
||||
* Fix - PayPal declares subscription support when merchant not enabled for Reference Transactions #2282
|
||||
* Fix - Google Pay and Apple Pay Settings button from Connection tab have wrong links #2273
|
||||
* Fix - Smart Buttons in Block Checkout not respecting the location setting (2830) #2278
|
||||
* Fix - Disable Pay Upon Invoice if billing/shipping country not set #2281
|
||||
* Enhancement - Enable shipping callback for WC subscriptions #2259
|
||||
* Enhancement - Disable the shipping callback for "venmo" when vaulting is active #2269
|
||||
* Enhancement - Improve "Could not retrieve order" error message #2271
|
||||
* Enhancement - Add block Checkout compatibility to Advanced Card Processing #2246
|
||||
|
||||
= 2.7.1 - 2024-05-28 =
|
||||
* Fix - Ensure package tracking data is sent to original PayPal transaction #2180
|
||||
* Fix - Set the 'Woo_PPCP' as a default value for data-partner-attribution-id #2188
|
||||
|
|
|
@ -73,6 +73,7 @@ class SdkClientToken {
|
|||
|
||||
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
|
||||
$domain = wp_unslash( $_SERVER['HTTP_HOST'] ?? '' );
|
||||
$domain = preg_replace( '/^www\./', '', $domain );
|
||||
|
||||
$url = trailingslashit( $this->host ) . 'v1/oauth2/token?grant_type=client_credentials&response_type=client_token&intent=sdk_init&domains[]=' . $domain;
|
||||
|
||||
|
|
|
@ -50,9 +50,7 @@ class ShippingOptionFactory {
|
|||
$cart->calculate_shipping();
|
||||
|
||||
$chosen_shipping_methods = WC()->session->get( 'chosen_shipping_methods', array() );
|
||||
if ( ! is_array( $chosen_shipping_methods ) ) {
|
||||
$chosen_shipping_methods = array();
|
||||
}
|
||||
$chosen_shipping_method = $chosen_shipping_methods[0] ?? false;
|
||||
|
||||
$packages = WC()->shipping()->get_packages();
|
||||
$options = array();
|
||||
|
@ -62,11 +60,10 @@ class ShippingOptionFactory {
|
|||
if ( ! $rate instanceof \WC_Shipping_Rate ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$options[] = new ShippingOption(
|
||||
$rate->get_id(),
|
||||
$rate->get_label(),
|
||||
in_array( $rate->get_id(), $chosen_shipping_methods, true ),
|
||||
$rate->get_id() === $chosen_shipping_method,
|
||||
new Money(
|
||||
(float) $rate->get_cost(),
|
||||
get_woocommerce_currency()
|
||||
|
|
|
@ -148,14 +148,8 @@ return array(
|
|||
'axo_privacy' => array(
|
||||
'title' => __( 'Privacy', 'woocommerce-paypal-payments' ),
|
||||
'type' => 'select',
|
||||
'label' => __(
|
||||
'This setting will control whether Fastlane branding is shown by email field.
|
||||
<p class="description">PayPal powers this accelerated checkout solution from Fastlane. Since you\'ll share consumers\' email addresses with PayPal, please consult your legal advisors on the apropriate privacy setting for your business.</p>',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
'desc_tip' => true,
|
||||
'description' => __(
|
||||
'This setting will control whether Fastlane branding is shown by email field.',
|
||||
'PayPal powers this accelerated checkout solution from Fastlane. Since you\'ll share consumers\' email address with PayPal, please consult your legal advisors on the appropriate privacy setting for your business.',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
'classes' => array( 'ppcp-field-indent' ),
|
||||
|
@ -168,12 +162,14 @@ return array(
|
|||
'requirements' => array( 'axo' ),
|
||||
),
|
||||
'axo_name_on_card' => array(
|
||||
'title' => __( 'Display Name on Card', 'woocommerce-paypal-payments' ),
|
||||
'type' => 'checkbox',
|
||||
'title' => __( 'Cardholder Name', 'woocommerce-paypal-payments' ),
|
||||
'type' => 'select',
|
||||
'default' => 'yes',
|
||||
'options' => PropertiesDictionary::cardholder_name_options(),
|
||||
'classes' => array( 'ppcp-field-indent' ),
|
||||
'class' => array(),
|
||||
'label' => __( 'Enable this to display the "Name on Card" field for new Fastlane buyers.', 'woocommerce-paypal-payments' ),
|
||||
'input_class' => array( 'wc-enhanced-select' ),
|
||||
'description' => __( 'This setting will control whether or not the cardholder name is displayed in the card field\'s UI.', 'woocommerce-paypal-payments' ),
|
||||
'screens' => array( State::STATE_ONBOARDED ),
|
||||
'gateway' => array( 'dcc', 'axo' ),
|
||||
'requirements' => array( 'axo' ),
|
||||
|
@ -196,7 +192,7 @@ return array(
|
|||
sprintf(
|
||||
// translators: %1$s and %2$s is a link tag.
|
||||
__(
|
||||
'Leave the default styling, or customize how Fastlane looks on your website. %1$sSee PayPal\'s developer docs%2$s for info',
|
||||
'Leave the default styling, or customize how Fastlane looks on your website. Styles that don\'t meet accessibility guidelines will revert to the defaults. See %1$sPayPal\'s developer docs%2$s for info.',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
'<a href="https://www.paypal.com/us/fastlane" target="_blank">',
|
||||
|
@ -236,18 +232,6 @@ return array(
|
|||
'requirements' => array( 'axo' ),
|
||||
'gateway' => array( 'dcc', 'axo' ),
|
||||
),
|
||||
'axo_style_root_primary_color' => array(
|
||||
'title' => __( 'Primary Color', 'woocommerce-paypal-payments' ),
|
||||
'type' => 'text',
|
||||
'placeholder' => '#0057F',
|
||||
'classes' => array( 'ppcp-field-indent' ),
|
||||
'default' => '',
|
||||
'screens' => array(
|
||||
State::STATE_ONBOARDED,
|
||||
),
|
||||
'requirements' => array( 'axo' ),
|
||||
'gateway' => array( 'dcc', 'axo' ),
|
||||
),
|
||||
'axo_style_root_error_color' => array(
|
||||
'title' => __( 'Error Color', 'woocommerce-paypal-payments' ),
|
||||
'type' => 'text',
|
||||
|
@ -308,6 +292,18 @@ return array(
|
|||
'requirements' => array( 'axo' ),
|
||||
'gateway' => array( 'dcc', 'axo' ),
|
||||
),
|
||||
'axo_style_root_primary_color' => array(
|
||||
'title' => __( 'Primary Color', 'woocommerce-paypal-payments' ),
|
||||
'type' => 'text',
|
||||
'placeholder' => '#0057FF',
|
||||
'classes' => array( 'ppcp-field-indent' ),
|
||||
'default' => '',
|
||||
'screens' => array(
|
||||
State::STATE_ONBOARDED,
|
||||
),
|
||||
'requirements' => array( 'axo' ),
|
||||
'gateway' => array( 'dcc', 'axo' ),
|
||||
),
|
||||
'axo_style_input_heading' => array(
|
||||
'heading' => __( 'Input Settings', 'woocommerce-paypal-payments' ),
|
||||
'type' => 'ppcp-heading',
|
||||
|
|
|
@ -1,6 +1,15 @@
|
|||
.ppcp-axo-watermark-container {
|
||||
max-width: 200px;
|
||||
margin-top: 10px;
|
||||
position: relative;
|
||||
|
||||
&.loader:before {
|
||||
height: 12px;
|
||||
width: 12px;
|
||||
margin-left: -6px;
|
||||
margin-top: -6px;
|
||||
left: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.ppcp-axo-payment-container {
|
||||
|
@ -28,6 +37,7 @@
|
|||
|
||||
.ppcp-axo-customer-details {
|
||||
margin-bottom: 40px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.axo-checkout-header-section {
|
||||
|
@ -44,6 +54,31 @@
|
|||
padding: 0.6em 1em;
|
||||
}
|
||||
|
||||
.ppcp-axo-watermark-loading {
|
||||
min-height: 12px;
|
||||
}
|
||||
|
||||
.ppcp-axo-overlay,
|
||||
.ppcp-axo-watermark-loading:after {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 999;
|
||||
content: '';
|
||||
}
|
||||
|
||||
.ppcp-axo-loading .col-1 {
|
||||
position: relative;
|
||||
opacity: 0.9;
|
||||
transition: opacity 0.5s ease;
|
||||
}
|
||||
|
||||
#payment .payment_methods li label[for="payment_method_ppcp-axo-gateway"] {
|
||||
img {
|
||||
float: none;
|
||||
|
|
|
@ -24,7 +24,8 @@ class AxoManager {
|
|||
active: false,
|
||||
validEmail: false,
|
||||
hasProfile: false,
|
||||
useEmailWidget: this.useEmailWidget()
|
||||
useEmailWidget: this.useEmailWidget(),
|
||||
hasCard: false,
|
||||
};
|
||||
|
||||
this.data = {
|
||||
|
@ -59,7 +60,6 @@ class AxoManager {
|
|||
}
|
||||
|
||||
document.axoDebugObject = () => {
|
||||
console.log(this);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -156,6 +156,7 @@ class AxoManager {
|
|||
this.el.showGatewaySelectionLink.on('click', async () => {
|
||||
this.hideGatewaySelection = false;
|
||||
this.$('.wc_payment_methods label').show();
|
||||
this.$('.wc_payment_methods input').show();
|
||||
this.cardView.refresh();
|
||||
});
|
||||
|
||||
|
@ -164,9 +165,8 @@ class AxoManager {
|
|||
this.$('form.woocommerce-checkout input').on('keydown', async (ev) => {
|
||||
if(ev.key === 'Enter' && getCurrentPaymentMethod() === 'ppcp-axo-gateway' ) {
|
||||
ev.preventDefault();
|
||||
log('Enter key attempt');
|
||||
log('emailInput', this.emailInput.value);
|
||||
log('this.lastEmailCheckedIdentity', this.lastEmailCheckedIdentity);
|
||||
log(`Enter key attempt - emailInput: ${this.emailInput.value}`);
|
||||
log(`this.lastEmailCheckedIdentity: ${this.lastEmailCheckedIdentity}`);
|
||||
if (this.emailInput && this.lastEmailCheckedIdentity !== this.emailInput.value) {
|
||||
await this.onChangeEmail();
|
||||
}
|
||||
|
@ -175,7 +175,7 @@ class AxoManager {
|
|||
|
||||
// Clear last email checked identity when email field is focused.
|
||||
this.$('#billing_email_field input').on('focus', (ev) => {
|
||||
log('Clear the last email checked:', this.lastEmailCheckedIdentity);
|
||||
log(`Clear the last email checked: ${this.lastEmailCheckedIdentity}`);
|
||||
this.lastEmailCheckedIdentity = '';
|
||||
});
|
||||
|
||||
|
@ -212,7 +212,7 @@ class AxoManager {
|
|||
this.status.hasProfile
|
||||
);
|
||||
|
||||
log('Scenario', scenario);
|
||||
log(`Scenario: ${JSON.stringify(scenario)}`);
|
||||
|
||||
// Reset some elements to a default status.
|
||||
this.el.watermarkContainer.hide();
|
||||
|
@ -231,6 +231,7 @@ class AxoManager {
|
|||
|
||||
if (scenario.defaultFormFields) {
|
||||
this.el.customerDetails.show();
|
||||
this.toggleLoaderAndOverlay(this.el.customerDetails, 'loader', 'ppcp-axo-overlay');
|
||||
} else {
|
||||
this.el.customerDetails.hide();
|
||||
}
|
||||
|
@ -248,7 +249,6 @@ class AxoManager {
|
|||
this.$(this.el.fieldBillingEmail.selector).append(
|
||||
this.$(this.el.watermarkContainer.selector)
|
||||
);
|
||||
|
||||
} else {
|
||||
this.el.emailWidgetContainer.hide();
|
||||
if (!scenario.defaultEmailField) {
|
||||
|
@ -257,12 +257,14 @@ class AxoManager {
|
|||
}
|
||||
|
||||
if (scenario.axoProfileViews) {
|
||||
this.el.billingAddressContainer.hide();
|
||||
|
||||
this.shippingView.activate();
|
||||
this.billingView.activate();
|
||||
this.cardView.activate();
|
||||
|
||||
if (this.status.hasCard) {
|
||||
this.billingView.activate();
|
||||
}
|
||||
|
||||
// Move watermark to after shipping.
|
||||
this.$(this.el.shippingAddressContainer.selector).after(
|
||||
this.$(this.el.watermarkContainer.selector)
|
||||
|
@ -372,7 +374,7 @@ class AxoManager {
|
|||
setStatus(key, value) {
|
||||
this.status[key] = value;
|
||||
|
||||
log('Status updated', JSON.parse(JSON.stringify(this.status)));
|
||||
log(`Status updated: ${JSON.stringify(this.status)}`);
|
||||
|
||||
document.dispatchEvent(new CustomEvent("axo_status_updated", {detail: this.status}));
|
||||
|
||||
|
@ -384,9 +386,8 @@ class AxoManager {
|
|||
this.initFastlane();
|
||||
this.setStatus('active', true);
|
||||
|
||||
log('Attempt on activation');
|
||||
log('emailInput', this.emailInput.value);
|
||||
log('this.lastEmailCheckedIdentity', this.lastEmailCheckedIdentity);
|
||||
log(`Attempt on activation - emailInput: ${this.emailInput.value}`);
|
||||
log(`this.lastEmailCheckedIdentity: ${this.lastEmailCheckedIdentity}`);
|
||||
if (this.emailInput && this.lastEmailCheckedIdentity !== this.emailInput.value) {
|
||||
this.onChangeEmail();
|
||||
}
|
||||
|
@ -496,6 +497,8 @@ class AxoManager {
|
|||
(await this.fastlane.FastlaneWatermarkComponent({
|
||||
includeAdditionalInfo
|
||||
})).render(this.el.watermarkContainer.selector);
|
||||
|
||||
this.toggleWatermarkLoading(this.el.watermarkContainer, 'ppcp-axo-watermark-loading', 'loader');
|
||||
}
|
||||
|
||||
watchEmail() {
|
||||
|
@ -506,17 +509,15 @@ class AxoManager {
|
|||
|
||||
} else {
|
||||
this.emailInput.addEventListener('change', async ()=> {
|
||||
log('Change event attempt');
|
||||
log('emailInput', this.emailInput.value);
|
||||
log('this.lastEmailCheckedIdentity', this.lastEmailCheckedIdentity);
|
||||
log(`Change event attempt - emailInput: ${this.emailInput.value}`);
|
||||
log(`this.lastEmailCheckedIdentity: ${this.lastEmailCheckedIdentity}`);
|
||||
if (this.emailInput && this.lastEmailCheckedIdentity !== this.emailInput.value) {
|
||||
this.onChangeEmail();
|
||||
}
|
||||
});
|
||||
|
||||
log('Last, this.emailInput.value attempt');
|
||||
log('emailInput', this.emailInput.value);
|
||||
log('this.lastEmailCheckedIdentity', this.lastEmailCheckedIdentity);
|
||||
log(`Last, this.emailInput.value attempt - emailInput: ${this.emailInput.value}`);
|
||||
log(`this.lastEmailCheckedIdentity: ${this.lastEmailCheckedIdentity}`);
|
||||
if (this.emailInput.value) {
|
||||
this.onChangeEmail();
|
||||
}
|
||||
|
@ -536,7 +537,7 @@ class AxoManager {
|
|||
return;
|
||||
}
|
||||
|
||||
log('Email changed: ' + (this.emailInput ? this.emailInput.value : '<empty>'));
|
||||
log(`Email changed: ${this.emailInput ? this.emailInput.value : '<empty>'}`);
|
||||
|
||||
this.$(this.el.paymentContainer.selector + '-detail').html('');
|
||||
this.$(this.el.paymentContainer.selector + '-form').html('');
|
||||
|
@ -565,12 +566,16 @@ class AxoManager {
|
|||
page_type: 'checkout'
|
||||
});
|
||||
|
||||
this.disableGatewaySelection();
|
||||
await this.lookupCustomerByEmail();
|
||||
this.enableGatewaySelection();
|
||||
}
|
||||
|
||||
async lookupCustomerByEmail() {
|
||||
const lookupResponse = await this.fastlane.identity.lookupCustomerByEmail(this.emailInput.value);
|
||||
|
||||
log(`lookupCustomerByEmail: ${JSON.stringify(lookupResponse)}`);
|
||||
|
||||
if (lookupResponse.customerContextId) {
|
||||
// Email is associated with a Connect profile or a PayPal member.
|
||||
// Authenticate the customer to get access to their profile.
|
||||
|
@ -578,18 +583,24 @@ class AxoManager {
|
|||
|
||||
const authResponse = await this.fastlane.identity.triggerAuthenticationFlow(lookupResponse.customerContextId);
|
||||
|
||||
log('AuthResponse', authResponse);
|
||||
log(`AuthResponse - triggerAuthenticationFlow: ${JSON.stringify(authResponse)}`);
|
||||
|
||||
if (authResponse.authenticationState === 'succeeded') {
|
||||
log(JSON.stringify(authResponse));
|
||||
|
||||
const shippingData = authResponse.profileData.shippingAddress;
|
||||
if(shippingData) {
|
||||
if (shippingData) {
|
||||
this.setShipping(shippingData);
|
||||
}
|
||||
|
||||
if (authResponse.profileData.card) {
|
||||
this.setStatus('hasCard', true);
|
||||
} else {
|
||||
this.cardComponent = (await this.fastlane.FastlaneCardComponent(
|
||||
this.cardComponentData()
|
||||
)).render(this.el.paymentContainer.selector + '-form');
|
||||
}
|
||||
|
||||
const cardBillingAddress = authResponse.profileData?.card?.paymentSource?.card?.billingAddress;
|
||||
if(cardBillingAddress) {
|
||||
if (cardBillingAddress) {
|
||||
this.setCard(authResponse.profileData.card);
|
||||
|
||||
const billingData = {
|
||||
|
@ -608,6 +619,7 @@ class AxoManager {
|
|||
|
||||
this.hideGatewaySelection = true;
|
||||
this.$('.wc_payment_methods label').hide();
|
||||
this.$('.wc_payment_methods input').hide();
|
||||
|
||||
await this.renderWatermark(false);
|
||||
|
||||
|
@ -644,6 +656,14 @@ class AxoManager {
|
|||
}
|
||||
}
|
||||
|
||||
disableGatewaySelection() {
|
||||
this.$('.wc_payment_methods input').prop('disabled', true);
|
||||
}
|
||||
|
||||
enableGatewaySelection() {
|
||||
this.$('.wc_payment_methods input').prop('disabled', false);
|
||||
}
|
||||
|
||||
clearData() {
|
||||
this.data = {
|
||||
email: null,
|
||||
|
@ -672,7 +692,7 @@ class AxoManager {
|
|||
// TODO: validate data.
|
||||
|
||||
if (this.data.card) { // Ryan flow
|
||||
log('Ryan flow.');
|
||||
log('Starting Ryan flow.');
|
||||
|
||||
this.$('#ship-to-different-address-checkbox').prop('checked', 'checked');
|
||||
|
||||
|
@ -683,20 +703,23 @@ class AxoManager {
|
|||
|
||||
this.ensureBillingPhoneNumber(data);
|
||||
|
||||
log(`Ryan flow - submitted nonce: ${this.data.card.id}` )
|
||||
|
||||
this.submit(this.data.card.id, data);
|
||||
|
||||
} else { // Gary flow
|
||||
log('Gary flow.');
|
||||
log('Starting Gary flow.');
|
||||
|
||||
try {
|
||||
this.cardComponent.getPaymentToken(
|
||||
this.tokenizeData()
|
||||
).then((response) => {
|
||||
log(`Gary flow - submitted nonce: ${response.id}` )
|
||||
this.submit(response.id);
|
||||
});
|
||||
} catch (e) {
|
||||
log('Error tokenizing.');
|
||||
alert('Error tokenizing data.');
|
||||
log(`Error tokenizing data. ${e.message}`, 'error');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -714,7 +737,7 @@ class AxoManager {
|
|||
|
||||
tokenizeData() {
|
||||
return {
|
||||
name: {
|
||||
cardholderName: {
|
||||
fullName: this.billingView.fullName()
|
||||
},
|
||||
billingAddress: {
|
||||
|
@ -776,7 +799,9 @@ class AxoManager {
|
|||
scrollTop: $notices.offset().top
|
||||
}, 500);
|
||||
}
|
||||
console.error('Failure:', responseData);
|
||||
|
||||
log(`Error sending checkout form. ${responseData}`, 'error');
|
||||
|
||||
this.hideLoading();
|
||||
return;
|
||||
}
|
||||
|
@ -785,7 +810,8 @@ class AxoManager {
|
|||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
log(`Error sending checkout form. ${error.message}`, 'error');
|
||||
|
||||
this.hideLoading();
|
||||
});
|
||||
|
||||
|
@ -840,6 +866,28 @@ class AxoManager {
|
|||
data.billing_phone = phone;
|
||||
}
|
||||
}
|
||||
|
||||
toggleLoaderAndOverlay(element, loaderClass, overlayClass) {
|
||||
const loader = document.querySelector(`${element.selector} .${loaderClass}`);
|
||||
const overlay = document.querySelector(`${element.selector} .${overlayClass}`);
|
||||
if (loader) {
|
||||
loader.classList.toggle(loaderClass);
|
||||
}
|
||||
if (overlay) {
|
||||
overlay.classList.toggle(overlayClass);
|
||||
}
|
||||
}
|
||||
|
||||
toggleWatermarkLoading(container, loadingClass, loaderClass) {
|
||||
const watermarkLoading = document.querySelector(`${container.selector}.${loadingClass}`);
|
||||
const watermarkLoader = document.querySelector(`${container.selector}.${loaderClass}`);
|
||||
if (watermarkLoading) {
|
||||
watermarkLoading.classList.toggle(loadingClass);
|
||||
}
|
||||
if (watermarkLoader) {
|
||||
watermarkLoader.classList.toggle(loaderClass);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default AxoManager;
|
||||
|
|
|
@ -20,7 +20,7 @@ class DomElementCollection {
|
|||
this.watermarkContainer = new DomElement({
|
||||
id: 'ppcp-axo-watermark-container',
|
||||
selector: '#ppcp-axo-watermark-container',
|
||||
className: 'ppcp-axo-watermark-container'
|
||||
className: 'ppcp-axo-watermark-container ppcp-axo-watermark-loading loader'
|
||||
});
|
||||
|
||||
this.customerDetails = new DomElement({
|
||||
|
|
|
@ -1,4 +1,26 @@
|
|||
export function log(message, level = 'info') {
|
||||
const endpoint = window.wc_ppcp_axo?.ajax?.frontend_logger?.endpoint;
|
||||
if(!endpoint) {
|
||||
return;
|
||||
}
|
||||
|
||||
export function log(...args) {
|
||||
//console.log('[AXO] ', ...args);
|
||||
fetch(endpoint, {
|
||||
method: 'POST',
|
||||
credentials: 'same-origin',
|
||||
body: JSON.stringify({
|
||||
nonce: window.wc_ppcp_axo.ajax.frontend_logger.nonce,
|
||||
log: {
|
||||
message,
|
||||
level,
|
||||
}
|
||||
})
|
||||
}).then(() => {
|
||||
switch (level) {
|
||||
case 'error':
|
||||
console.error(`[AXO] ${message}`);
|
||||
break;
|
||||
default:
|
||||
console.log(`[AXO] ${message}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -34,22 +34,7 @@ class BillingView {
|
|||
</div>
|
||||
`;
|
||||
}
|
||||
return `
|
||||
<div style="margin-bottom: 20px;">
|
||||
<div class="axo-checkout-header-section">
|
||||
<h3>Billing</h3>
|
||||
<a href="javascript:void(0)" ${this.el.changeBillingAddressLink.attributes}>Edit</a>
|
||||
</div>
|
||||
<div>${data.value('email')}</div>
|
||||
<div>${data.value('company')}</div>
|
||||
<div>${data.value('firstName')} ${data.value('lastName')}</div>
|
||||
<div>${data.value('street1')}</div>
|
||||
<div>${data.value('street2')}</div>
|
||||
<div>${data.value('postCode')} ${data.value('city')}</div>
|
||||
<div>${valueOfSelect('#billing_state', data.value('stateCode'))}</div>
|
||||
<div>${valueOfSelect('#billing_country', data.value('countryCode'))}</div>
|
||||
</div>
|
||||
`;
|
||||
return '';
|
||||
},
|
||||
fields: {
|
||||
email: {
|
||||
|
|
|
@ -20,10 +20,6 @@ class CardView {
|
|||
if (data.isEmpty()) {
|
||||
return `
|
||||
<div style="margin-bottom: 20px; text-align: center;">
|
||||
<div style="border:2px solid #cccccc; border-radius: 10px; padding: 26px 20px; margin-bottom: 20px; background-color:#f6f6f6">
|
||||
<div>Please fill in your card details.</div>
|
||||
</div>
|
||||
<h4><a href="javascript:void(0)" ${this.el.changeCardLink.attributes}>Add card details</a></h4>
|
||||
${selectOtherPaymentMethod()}
|
||||
</div>
|
||||
`;
|
||||
|
@ -34,8 +30,8 @@ class CardView {
|
|||
const cardIcons = {
|
||||
'VISA': 'visa-light.svg',
|
||||
'MASTER_CARD': 'mastercard-light.svg',
|
||||
'AMEX': 'amex.svg',
|
||||
'DISCOVER': 'discover.svg',
|
||||
'AMEX': 'amex-light.svg',
|
||||
'DISCOVER': 'discover-light.svg',
|
||||
'DINERS': 'dinersclub-light.svg',
|
||||
'JCB': 'jcb-light.svg',
|
||||
'UNIONPAY': 'unionpay-light.svg',
|
||||
|
@ -52,7 +48,7 @@ class CardView {
|
|||
<img
|
||||
class="ppcp-card-icon"
|
||||
title="${data.value('brand')}"
|
||||
src="${window.wc_ppcp_axo.module_url}/assets/images/axo/${cardIcons[data.value('brand')]}"
|
||||
src="${window.wc_ppcp_axo.icons_directory}${cardIcons[data.value('brand')]}"
|
||||
alt="${data.value('brand')}"
|
||||
>
|
||||
</div>
|
||||
|
|
|
@ -13,7 +13,10 @@ use WooCommerce\PayPalCommerce\Axo\Assets\AxoManager;
|
|||
use WooCommerce\PayPalCommerce\Axo\Gateway\AxoGateway;
|
||||
use WooCommerce\PayPalCommerce\Axo\Helper\ApmApplies;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\CreditCardGateway;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Helper\CartCheckoutDetector;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
|
||||
|
||||
return array(
|
||||
|
||||
|
@ -58,7 +61,8 @@ return array(
|
|||
$container->get( 'onboarding.environment' ),
|
||||
$container->get( 'wcgateway.settings.status' ),
|
||||
$container->get( 'api.shop.currency' ),
|
||||
$container->get( 'woocommerce.logger.woocommerce' )
|
||||
$container->get( 'woocommerce.logger.woocommerce' ),
|
||||
$container->get( 'wcgateway.url' )
|
||||
);
|
||||
},
|
||||
|
||||
|
@ -191,6 +195,40 @@ return array(
|
|||
return '';
|
||||
}
|
||||
|
||||
return '<div class="ppcp-notice ppcp-notice-error"><p>' . $notice_content . '</p></div>';
|
||||
},
|
||||
|
||||
'axo.smart-button-location-notice' => static function ( ContainerInterface $container ) : string {
|
||||
$settings = $container->get( 'wcgateway.settings' );
|
||||
assert( $settings instanceof Settings );
|
||||
|
||||
if ( $settings->has( 'axo_enabled' ) && $settings->get( 'axo_enabled' ) ) {
|
||||
$fastlane_settings_url = admin_url(
|
||||
sprintf(
|
||||
'admin.php?page=wc-settings&tab=checkout§ion=%1$s&ppcp-tab=%2$s#field-axo_heading',
|
||||
PayPalGateway::ID,
|
||||
CreditCardGateway::ID
|
||||
)
|
||||
);
|
||||
|
||||
$notice_content = sprintf(
|
||||
/* translators: %1$s: URL to the Checkout edit page. */
|
||||
__(
|
||||
'<span class="highlight">Important:</span> The <code>Cart</code> & <code>Classic Cart</code> <strong>Smart Button Locations</strong> cannot be disabled while <a href="%1$s">Fastlane</a> is active.',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
esc_url( $fastlane_settings_url )
|
||||
);
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
|
||||
return '<div class="ppcp-notice ppcp-notice-warning"><p>' . $notice_content . '</p></div>';
|
||||
},
|
||||
'axo.endpoint.frontend-logger' => static function ( ContainerInterface $container ): FrontendLoggerEndpoint {
|
||||
return new FrontendLoggerEndpoint(
|
||||
$container->get( 'button.request-data' ),
|
||||
$container->get( 'woocommerce.logger.woocommerce' )
|
||||
);
|
||||
},
|
||||
);
|
||||
|
|
|
@ -10,6 +10,7 @@ declare(strict_types=1);
|
|||
namespace WooCommerce\PayPalCommerce\Axo\Assets;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use WooCommerce\PayPalCommerce\Axo\FrontendLoggerEndpoint;
|
||||
use WooCommerce\PayPalCommerce\Onboarding\Environment;
|
||||
use WooCommerce\PayPalCommerce\Session\SessionHandler;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Helper\SettingsStatus;
|
||||
|
@ -78,6 +79,13 @@ class AxoManager {
|
|||
*/
|
||||
private $session_handler;
|
||||
|
||||
/**
|
||||
* The WcGateway module URL.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $wcgateway_module_url;
|
||||
|
||||
/**
|
||||
* AxoManager constructor.
|
||||
*
|
||||
|
@ -89,6 +97,7 @@ class AxoManager {
|
|||
* @param SettingsStatus $settings_status The Settings status helper.
|
||||
* @param string $currency 3-letter currency code of the shop.
|
||||
* @param LoggerInterface $logger The logger.
|
||||
* @param string $wcgateway_module_url The WcGateway module URL.
|
||||
*/
|
||||
public function __construct(
|
||||
string $module_url,
|
||||
|
@ -98,17 +107,19 @@ class AxoManager {
|
|||
Environment $environment,
|
||||
SettingsStatus $settings_status,
|
||||
string $currency,
|
||||
LoggerInterface $logger
|
||||
LoggerInterface $logger,
|
||||
string $wcgateway_module_url
|
||||
) {
|
||||
|
||||
$this->module_url = $module_url;
|
||||
$this->version = $version;
|
||||
$this->session_handler = $session_handler;
|
||||
$this->settings = $settings;
|
||||
$this->environment = $environment;
|
||||
$this->settings_status = $settings_status;
|
||||
$this->currency = $currency;
|
||||
$this->logger = $logger;
|
||||
$this->module_url = $module_url;
|
||||
$this->version = $version;
|
||||
$this->session_handler = $session_handler;
|
||||
$this->settings = $settings;
|
||||
$this->environment = $environment;
|
||||
$this->settings_status = $settings_status;
|
||||
$this->currency = $currency;
|
||||
$this->logger = $logger;
|
||||
$this->wcgateway_module_url = $wcgateway_module_url;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -151,13 +162,13 @@ class AxoManager {
|
|||
*/
|
||||
private function script_data() {
|
||||
return array(
|
||||
'environment' => array(
|
||||
'environment' => array(
|
||||
'is_sandbox' => $this->environment->current_environment() === 'sandbox',
|
||||
),
|
||||
'widgets' => array(
|
||||
'widgets' => array(
|
||||
'email' => 'render',
|
||||
),
|
||||
'insights' => array(
|
||||
'insights' => array(
|
||||
'enabled' => true,
|
||||
'client_id' => ( $this->settings->has( 'client_id' ) ? $this->settings->get( 'client_id' ) : null ),
|
||||
'session_id' =>
|
||||
|
@ -171,7 +182,7 @@ class AxoManager {
|
|||
'value' => WC()->cart->get_total( 'numeric' ),
|
||||
),
|
||||
),
|
||||
'style_options' => array(
|
||||
'style_options' => array(
|
||||
'root' => array(
|
||||
'backgroundColor' => $this->settings->has( 'axo_style_root_bg_color' ) ? $this->settings->get( 'axo_style_root_bg_color' ) : '',
|
||||
'errorColor' => $this->settings->has( 'axo_style_root_error_color' ) ? $this->settings->get( 'axo_style_root_error_color' ) : '',
|
||||
|
@ -190,14 +201,21 @@ class AxoManager {
|
|||
'focusBorderColor' => $this->settings->has( 'axo_style_input_focus_border_color' ) ? $this->settings->get( 'axo_style_input_focus_border_color' ) : '',
|
||||
),
|
||||
),
|
||||
'name_on_card' => $this->settings->has( 'axo_name_on_card' ) ? $this->settings->get( 'axo_name_on_card' ) : '',
|
||||
'woocommerce' => array(
|
||||
'name_on_card' => $this->settings->has( 'axo_name_on_card' ) ? $this->settings->get( 'axo_name_on_card' ) : '',
|
||||
'woocommerce' => array(
|
||||
'states' => array(
|
||||
'US' => WC()->countries->get_states( 'US' ),
|
||||
'CA' => WC()->countries->get_states( 'CA' ),
|
||||
),
|
||||
),
|
||||
'module_url' => untrailingslashit( $this->module_url ),
|
||||
'icons_directory' => esc_url( $this->wcgateway_module_url ) . 'assets/images/axo/',
|
||||
'module_url' => untrailingslashit( $this->module_url ),
|
||||
'ajax' => array(
|
||||
'frontend_logger' => array(
|
||||
'endpoint' => \WC_AJAX::get_endpoint( FrontendLoggerEndpoint::ENDPOINT ),
|
||||
'nonce' => wp_create_nonce( FrontendLoggerEndpoint::nonce() ),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
|
|||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\CreditCardGateway;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Helper\CartCheckoutDetector;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Settings\SettingsListener;
|
||||
|
||||
/**
|
||||
* Class AxoModule
|
||||
|
@ -43,7 +44,6 @@ class AxoModule implements ModuleInterface {
|
|||
* {@inheritDoc}
|
||||
*/
|
||||
public function run( ContainerInterface $c ): void {
|
||||
$module = $this;
|
||||
|
||||
add_filter(
|
||||
'woocommerce_payment_gateways',
|
||||
|
@ -108,9 +108,31 @@ class AxoModule implements ModuleInterface {
|
|||
}
|
||||
);
|
||||
|
||||
// Force 'cart-block' and 'cart' Smart Button locations in the settings.
|
||||
add_action(
|
||||
'admin_init',
|
||||
static function () use ( $c ) {
|
||||
$listener = $c->get( 'wcgateway.settings.listener' );
|
||||
assert( $listener instanceof SettingsListener );
|
||||
|
||||
$settings = $c->get( 'wcgateway.settings' );
|
||||
assert( $settings instanceof Settings );
|
||||
|
||||
$listener->filter_settings(
|
||||
$settings->has( 'axo_enabled' ) && $settings->get( 'axo_enabled' ),
|
||||
'smart_button_locations',
|
||||
function( array $existing_setting_value ) {
|
||||
$axo_forced_locations = array( 'cart-block', 'cart' );
|
||||
return array_unique( array_merge( $existing_setting_value, $axo_forced_locations ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
add_action(
|
||||
'init',
|
||||
function () use ( $c, $module ) {
|
||||
function () use ( $c ) {
|
||||
$module = $this;
|
||||
|
||||
// Check if the module is applicable, correct country, currency, ... etc.
|
||||
if ( ! $c->get( 'axo.eligible' ) ) {
|
||||
|
@ -123,11 +145,15 @@ class AxoModule implements ModuleInterface {
|
|||
// Enqueue frontend scripts.
|
||||
add_action(
|
||||
'wp_enqueue_scripts',
|
||||
static function () use ( $c, $manager ) {
|
||||
static function () use ( $c, $manager, $module ) {
|
||||
|
||||
$settings = $c->get( 'wcgateway.settings' );
|
||||
assert( $settings instanceof Settings );
|
||||
|
||||
$smart_button = $c->get( 'button.smart-button' );
|
||||
assert( $smart_button instanceof SmartButtonInterface );
|
||||
|
||||
if ( $smart_button->should_load_ppcp_script() ) {
|
||||
if ( $module->should_render_fastlane( $settings ) && $smart_button->should_load_ppcp_script() ) {
|
||||
$manager->enqueue();
|
||||
}
|
||||
}
|
||||
|
@ -217,6 +243,18 @@ class AxoModule implements ModuleInterface {
|
|||
1
|
||||
);
|
||||
|
||||
add_action(
|
||||
'wc_ajax_' . FrontendLoggerEndpoint::ENDPOINT,
|
||||
static function () use ( $c ) {
|
||||
$endpoint = $c->get( 'axo.endpoint.frontend-logger' );
|
||||
assert( $endpoint instanceof FrontendLoggerEndpoint );
|
||||
|
||||
$endpoint->handle_request();
|
||||
}
|
||||
);
|
||||
|
||||
// Add the markup necessary for displaying overlays and loaders for Axo on the checkout page.
|
||||
$this->add_checkout_loader_markup( $c );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -292,4 +330,47 @@ class AxoModule implements ModuleInterface {
|
|||
&& CartCheckoutDetector::has_classic_checkout()
|
||||
&& $is_axo_enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the markup necessary for displaying overlays and loaders for Axo on the checkout page.
|
||||
*
|
||||
* @param ContainerInterface $c The container.
|
||||
* @return void
|
||||
*/
|
||||
private function add_checkout_loader_markup( ContainerInterface $c ): void {
|
||||
$settings = $c->get( 'wcgateway.settings' );
|
||||
assert( $settings instanceof Settings );
|
||||
|
||||
if ( $this->should_render_fastlane( $settings ) ) {
|
||||
add_action(
|
||||
'woocommerce_checkout_before_customer_details',
|
||||
function () {
|
||||
echo '<div class="ppcp-axo-loading">';
|
||||
}
|
||||
);
|
||||
|
||||
add_action(
|
||||
'woocommerce_checkout_after_customer_details',
|
||||
function () {
|
||||
echo '</div>';
|
||||
}
|
||||
);
|
||||
|
||||
add_action(
|
||||
'woocommerce_checkout_billing',
|
||||
function () {
|
||||
echo '<div class="loader"><div class="ppcp-axo-overlay"></div>';
|
||||
},
|
||||
8
|
||||
);
|
||||
|
||||
add_action(
|
||||
'woocommerce_checkout_billing',
|
||||
function () {
|
||||
echo '</div>';
|
||||
},
|
||||
12
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
80
modules/ppcp-axo/src/FrontendLoggerEndpoint.php
Normal file
80
modules/ppcp-axo/src/FrontendLoggerEndpoint.php
Normal file
|
@ -0,0 +1,80 @@
|
|||
<?php
|
||||
/**
|
||||
* The endpoint to log entries from frontend.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\Button\Endpoint
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\Axo;
|
||||
|
||||
use Exception;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use WooCommerce\PayPalCommerce\Button\Endpoint\EndpointInterface;
|
||||
use WooCommerce\PayPalCommerce\Button\Endpoint\RequestData;
|
||||
|
||||
/**
|
||||
* Class FrontendLoggerEndpoint
|
||||
*/
|
||||
class FrontendLoggerEndpoint implements EndpointInterface {
|
||||
|
||||
const ENDPOINT = 'ppc-frontend-logger';
|
||||
|
||||
/**
|
||||
* The request data helper.
|
||||
*
|
||||
* @var RequestData
|
||||
*/
|
||||
private $request_data;
|
||||
|
||||
/**
|
||||
* The logger.
|
||||
*
|
||||
* @var LoggerInterface
|
||||
*/
|
||||
private $logger;
|
||||
|
||||
/**
|
||||
* FrontendLoggerEndpoint constructor.
|
||||
*
|
||||
* @param RequestData $request_data The request data helper.
|
||||
* @param LoggerInterface $logger The logger.
|
||||
*/
|
||||
public function __construct( RequestData $request_data, LoggerInterface $logger ) {
|
||||
$this->request_data = $request_data;
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the nonce.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function nonce(): string {
|
||||
return self::ENDPOINT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the request.
|
||||
*
|
||||
* @return bool
|
||||
* @throws Exception On Error.
|
||||
*/
|
||||
public function handle_request(): bool {
|
||||
$data = $this->request_data->read_request( $this->nonce() );
|
||||
$level = $data['log']['level'] ?? 'info';
|
||||
|
||||
switch ( $level ) {
|
||||
case 'error':
|
||||
$this->logger->error( '[AXO] ' . $data['log']['message'] );
|
||||
break;
|
||||
default:
|
||||
$this->logger->info( '[AXO] ' . $data['log']['message'] );
|
||||
break;
|
||||
}
|
||||
|
||||
wp_send_json_success();
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -26,4 +26,16 @@ class PropertiesDictionary {
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of possible cardholder name options.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function cardholder_name_options(): array {
|
||||
return array(
|
||||
'yes' => __( 'Yes', 'woocommerce-paypal-payments' ),
|
||||
'no' => __( 'No', 'woocommerce-paypal-payments' ),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -51,16 +51,7 @@ return array(
|
|||
);
|
||||
}
|
||||
|
||||
$subscription_helper = $container->get( 'wc-subscriptions.helper' );
|
||||
|
||||
if ( $subscription_helper->plugin_is_active() ) {
|
||||
$label .= __(
|
||||
'<div class="ppcp-notice ppcp-notice-warning"><p><span class="highlight">Important:</span> Cannot be deactivated while the WooCommerce Subscriptions plugin is active.</p></div>',
|
||||
'woocommerce-paypal-payments'
|
||||
);
|
||||
}
|
||||
|
||||
$should_disable_checkbox = $subscription_helper->plugin_is_active() || apply_filters( 'woocommerce_paypal_payments_toggle_final_review_checkbox', false );
|
||||
$should_disable_checkbox = apply_filters( 'woocommerce_paypal_payments_toggle_final_review_checkbox', false );
|
||||
|
||||
return $insert_after(
|
||||
$fields,
|
||||
|
|
|
@ -23,6 +23,9 @@ import {
|
|||
import buttonModuleWatcher from "../../../ppcp-button/resources/js/modules/ButtonModuleWatcher";
|
||||
import BlockCheckoutMessagesBootstrap from "./Bootstrap/BlockCheckoutMessagesBootstrap";
|
||||
import {keysToCamelCase} from "../../../ppcp-button/resources/js/modules/Helper/Utils";
|
||||
import {
|
||||
handleShippingOptionsChange
|
||||
} from "../../../ppcp-button/resources/js/modules/Helper/ShippingHandler";
|
||||
const config = wc.wcSettings.getSetting('ppcp-gateway_data');
|
||||
|
||||
window.ppcpFundingSource = config.fundingSource;
|
||||
|
@ -146,7 +149,7 @@ const PayPalComponent = ({
|
|||
shipping_address: addresses.shippingAddress,
|
||||
}),
|
||||
];
|
||||
if (!config.finalReviewEnabled) {
|
||||
if (shouldHandleShippingInPayPal()) {
|
||||
// set address in UI
|
||||
promises.push(wp.data.dispatch('wc/store/cart').setBillingAddress(addresses.billingAddress));
|
||||
if (shippingData.needsShipping) {
|
||||
|
@ -181,7 +184,7 @@ const PayPalComponent = ({
|
|||
throw new Error(config.scriptData.labels.error.generic)
|
||||
}
|
||||
|
||||
if (config.finalReviewEnabled) {
|
||||
if (!shouldHandleShippingInPayPal()) {
|
||||
location.href = getCheckoutRedirectUrl();
|
||||
} else {
|
||||
setGotoContinuationOnError(true);
|
||||
|
@ -220,7 +223,7 @@ const PayPalComponent = ({
|
|||
shipping_address: addresses.shippingAddress,
|
||||
}),
|
||||
];
|
||||
if (!config.finalReviewEnabled) {
|
||||
if (shouldHandleShippingInPayPal()) {
|
||||
// set address in UI
|
||||
promises.push(wp.data.dispatch('wc/store/cart').setBillingAddress(addresses.billingAddress));
|
||||
if (shippingData.needsShipping) {
|
||||
|
@ -255,7 +258,7 @@ const PayPalComponent = ({
|
|||
throw new Error(config.scriptData.labels.error.generic)
|
||||
}
|
||||
|
||||
if (config.finalReviewEnabled) {
|
||||
if (!shouldHandleShippingInPayPal()) {
|
||||
location.href = getCheckoutRedirectUrl();
|
||||
} else {
|
||||
setGotoContinuationOnError(true);
|
||||
|
@ -297,8 +300,12 @@ const PayPalComponent = ({
|
|||
onClick();
|
||||
};
|
||||
|
||||
const isVenmoAndVaultingEnabled = () => {
|
||||
return window.ppcpFundingSource === 'venmo' && config.scriptData.vaultingEnabled;
|
||||
const shouldHandleShippingInPayPal = () => {
|
||||
if (config.finalReviewEnabled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return window.ppcpFundingSource !== 'venmo' || !config.scriptData.vaultingEnabled;
|
||||
}
|
||||
|
||||
let handleShippingOptionsChange = null;
|
||||
|
@ -306,7 +313,7 @@ const PayPalComponent = ({
|
|||
let handleSubscriptionShippingOptionsChange = null;
|
||||
let handleSubscriptionShippingAddressChange = null;
|
||||
|
||||
if (shippingData.needsShipping && !config.finalReviewEnabled) {
|
||||
if (shippingData.needsShipping && shouldHandleShippingInPayPal()) {
|
||||
handleShippingOptionsChange = async (data, actions) => {
|
||||
try {
|
||||
const shippingOptionId = data.selectedShippingOption?.id;
|
||||
|
@ -391,6 +398,21 @@ const PayPalComponent = ({
|
|||
|
||||
await shippingData.setShippingAddress(address);
|
||||
|
||||
const res = await fetch(config.ajax.update_shipping.endpoint, {
|
||||
method: 'POST',
|
||||
credentials: 'same-origin',
|
||||
body: JSON.stringify({
|
||||
nonce: config.ajax.update_shipping.nonce,
|
||||
order_id: data.orderID,
|
||||
})
|
||||
});
|
||||
|
||||
const json = await res.json();
|
||||
|
||||
if (!json.success) {
|
||||
throw new Error(json.data.message);
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
|
||||
|
@ -447,7 +469,7 @@ const PayPalComponent = ({
|
|||
if (config.scriptData.continuation) {
|
||||
return true;
|
||||
}
|
||||
if (!config.finalReviewEnabled) {
|
||||
if (shouldHandleShippingInPayPal()) {
|
||||
location.href = getCheckoutRedirectUrl();
|
||||
}
|
||||
return true;
|
||||
|
@ -493,8 +515,16 @@ const PayPalComponent = ({
|
|||
onError={onClose}
|
||||
createSubscription={createSubscription}
|
||||
onApprove={handleApproveSubscription}
|
||||
onShippingOptionsChange={handleSubscriptionShippingOptionsChange}
|
||||
onShippingAddressChange={handleSubscriptionShippingAddressChange}
|
||||
onShippingOptionsChange={(data, actions) => {
|
||||
shouldHandleShippingInPayPal()
|
||||
? handleSubscriptionShippingOptionsChange(data, actions)
|
||||
: null;
|
||||
}}
|
||||
onShippingAddressChange={(data, actions) => {
|
||||
shouldHandleShippingInPayPal()
|
||||
? handleSubscriptionShippingAddressChange(data, actions)
|
||||
: null;
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -508,8 +538,16 @@ const PayPalComponent = ({
|
|||
onError={onClose}
|
||||
createOrder={createOrder}
|
||||
onApprove={handleApprove}
|
||||
onShippingOptionsChange={handleShippingOptionsChange}
|
||||
onShippingAddressChange={handleShippingAddressChange}
|
||||
onShippingOptionsChange={(data, actions) => {
|
||||
shouldHandleShippingInPayPal()
|
||||
? handleShippingOptionsChange(data, actions)
|
||||
: null;
|
||||
}}
|
||||
onShippingAddressChange={(data, actions) => {
|
||||
shouldHandleShippingInPayPal()
|
||||
? handleShippingAddressChange(data, actions)
|
||||
: null;
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -23,7 +23,8 @@ class CartActionHandler {
|
|||
body: JSON.stringify({
|
||||
nonce: this.config.ajax.approve_subscription.nonce,
|
||||
order_id: data.orderID,
|
||||
subscription_id: data.subscriptionID
|
||||
subscription_id: data.subscriptionID,
|
||||
should_create_wc_order: !context.config.vaultingEnabled || data.paymentSource !== 'venmo'
|
||||
})
|
||||
}).then((res)=>{
|
||||
return res.json();
|
||||
|
@ -33,7 +34,9 @@ class CartActionHandler {
|
|||
throw Error(data.data.message);
|
||||
}
|
||||
|
||||
location.href = this.config.redirect;
|
||||
let orderReceivedUrl = data.data?.order_received_url
|
||||
|
||||
location.href = orderReceivedUrl ? orderReceivedUrl : context.config.redirect;
|
||||
});
|
||||
},
|
||||
onError: (err) => {
|
||||
|
@ -60,8 +63,7 @@ class CartActionHandler {
|
|||
funding_source: window.ppcpFundingSource,
|
||||
bn_code:bnCode,
|
||||
payer,
|
||||
context:this.config.context,
|
||||
payment_source: data.paymentSource
|
||||
context:this.config.context
|
||||
}),
|
||||
}).then(function(res) {
|
||||
return res.json();
|
||||
|
|
|
@ -4,6 +4,7 @@ import widgetBuilder from "../Renderer/WidgetBuilder";
|
|||
import merge from "deepmerge";
|
||||
import {keysToCamelCase} from "./Utils";
|
||||
import {getCurrentPaymentMethod} from "./CheckoutMethodState";
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
// This component may be used by multiple modules. This assures that options are shared between all instances.
|
||||
let options = window.ppcpWidgetBuilder = window.ppcpWidgetBuilder || {
|
||||
|
@ -63,9 +64,10 @@ export const loadPaypalScript = (config, onLoaded, onError = null) => {
|
|||
|
||||
// Axo SDK options
|
||||
const sdkClientToken = config?.axo?.sdk_client_token;
|
||||
const uuid = uuidv4().replace(/-/g, '');
|
||||
if(sdkClientToken) {
|
||||
scriptOptions['data-sdk-client-token'] = sdkClientToken;
|
||||
scriptOptions['data-client-metadata-id'] = 'ppcp-cm-id';
|
||||
scriptOptions['data-client-metadata-id'] = uuid;
|
||||
}
|
||||
|
||||
// Load PayPal script for special case with data-client-token
|
||||
|
|
|
@ -39,19 +39,21 @@ export const handleShippingOptionsChange = async (data, actions, config) => {
|
|||
})
|
||||
}
|
||||
|
||||
const res = await fetch(config.ajax.update_shipping.endpoint, {
|
||||
method: 'POST',
|
||||
credentials: 'same-origin',
|
||||
body: JSON.stringify({
|
||||
nonce: config.ajax.update_shipping.nonce,
|
||||
order_id: data.orderID,
|
||||
})
|
||||
});
|
||||
if (!config.data_client_id.has_subscriptions) {
|
||||
const res = await fetch(config.ajax.update_shipping.endpoint, {
|
||||
method: 'POST',
|
||||
credentials: 'same-origin',
|
||||
body: JSON.stringify({
|
||||
nonce: config.ajax.update_shipping.nonce,
|
||||
order_id: data.orderID,
|
||||
})
|
||||
});
|
||||
|
||||
const json = await res.json();
|
||||
const json = await res.json();
|
||||
|
||||
if (!json.success) {
|
||||
throw new Error(json.data.message);
|
||||
if (!json.success) {
|
||||
throw new Error(json.data.message);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
|
@ -104,20 +106,20 @@ export const handleShippingAddressChange = async (data, actions, config) => {
|
|||
})
|
||||
})
|
||||
|
||||
const res = await fetch(config.ajax.update_shipping.endpoint, {
|
||||
method: 'POST',
|
||||
credentials: 'same-origin',
|
||||
body: JSON.stringify({
|
||||
nonce: config.ajax.update_shipping.nonce,
|
||||
order_id: data.orderID,
|
||||
})
|
||||
});
|
||||
const res = await fetch(config.ajax.update_shipping.endpoint, {
|
||||
method: 'POST',
|
||||
credentials: 'same-origin',
|
||||
body: JSON.stringify({
|
||||
nonce: config.ajax.update_shipping.nonce,
|
||||
order_id: data.orderID,
|
||||
})
|
||||
});
|
||||
|
||||
const json = await res.json();
|
||||
const json = await res.json();
|
||||
|
||||
if (!json.success) {
|
||||
throw new Error(json.data.message);
|
||||
}
|
||||
if (!json.success) {
|
||||
throw new Error(json.data.message);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
|
||||
|
|
|
@ -68,14 +68,6 @@ class Renderer {
|
|||
}
|
||||
}
|
||||
|
||||
shouldHandleShippingInPaypal = (venmoButtonClicked) => {
|
||||
if (!this.defaultSettings.should_handle_shipping_in_paypal) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return !venmoButtonClicked || !this.defaultSettings.vaultingEnabled;
|
||||
}
|
||||
|
||||
renderButtons(wrapper, style, contextConfig, hasEnabledSeparateGateways, fundingSource = null) {
|
||||
if (! document.querySelector(wrapper) || this.isAlreadyRendered(wrapper, fundingSource, hasEnabledSeparateGateways) ) {
|
||||
// Try to render registered buttons again in case they were removed from the DOM by an external source.
|
||||
|
@ -93,7 +85,16 @@ class Renderer {
|
|||
const options = {
|
||||
style,
|
||||
...contextConfig,
|
||||
onClick: this.onSmartButtonClick,
|
||||
onClick: (data, actions) => {
|
||||
if (this.onSmartButtonClick) {
|
||||
this.onSmartButtonClick(data, actions);
|
||||
}
|
||||
|
||||
venmoButtonClicked = false;
|
||||
if (data.fundingSource === 'venmo') {
|
||||
venmoButtonClicked = true;
|
||||
}
|
||||
},
|
||||
onInit: (data, actions) => {
|
||||
if (this.onSmartButtonsInit) {
|
||||
this.onSmartButtonsInit(data, actions);
|
||||
|
@ -103,9 +104,17 @@ class Renderer {
|
|||
};
|
||||
|
||||
// Check the condition and add the handler if needed
|
||||
if (this.shouldHandleShippingInPaypal(venmoButtonClicked)) {
|
||||
options.onShippingOptionsChange = (data, actions) => handleShippingOptionsChange(data, actions, this.defaultSettings);
|
||||
options.onShippingAddressChange = (data, actions) => handleShippingAddressChange(data, actions, this.defaultSettings);
|
||||
if (this.defaultSettings.should_handle_shipping_in_paypal) {
|
||||
options.onShippingOptionsChange = (data, actions) => {
|
||||
!this.isVenmoButtonClickedWhenVaultingIsEnabled(venmoButtonClicked)
|
||||
? handleShippingOptionsChange(data, actions, this.defaultSettings)
|
||||
: null;
|
||||
}
|
||||
options.onShippingAddressChange = (data, actions) => {
|
||||
!this.isVenmoButtonClickedWhenVaultingIsEnabled(venmoButtonClicked)
|
||||
? handleShippingAddressChange(data, actions, this.defaultSettings)
|
||||
: null;
|
||||
}
|
||||
}
|
||||
|
||||
return options;
|
||||
|
@ -139,6 +148,10 @@ class Renderer {
|
|||
}
|
||||
}
|
||||
|
||||
isVenmoButtonClickedWhenVaultingIsEnabled = (venmoButtonClicked) => {
|
||||
return venmoButtonClicked && this.defaultSettings.vaultingEnabled;
|
||||
}
|
||||
|
||||
isAlreadyRendered(wrapper, fundingSource) {
|
||||
return this.renderedSources.has(wrapper + (fundingSource ?? ''));
|
||||
}
|
||||
|
|
|
@ -239,7 +239,6 @@ return array(
|
|||
$final_review_enabled = $container->get( 'blocks.settings.final_review_enabled' );
|
||||
$wc_order_creator = $container->get( 'button.helper.wc-order-creator' );
|
||||
$gateway = $container->get( 'wcgateway.paypal-gateway' );
|
||||
$subscription_helper = $container->get( 'wc-subscriptions.helper' );
|
||||
$logger = $container->get( 'woocommerce.logger.woocommerce' );
|
||||
return new ApproveOrderEndpoint(
|
||||
$request_data,
|
||||
|
@ -252,7 +251,6 @@ return array(
|
|||
$final_review_enabled,
|
||||
$gateway,
|
||||
$wc_order_creator,
|
||||
$subscription_helper,
|
||||
$logger
|
||||
);
|
||||
},
|
||||
|
@ -260,7 +258,10 @@ return array(
|
|||
return new ApproveSubscriptionEndpoint(
|
||||
$container->get( 'button.request-data' ),
|
||||
$container->get( 'api.endpoint.order' ),
|
||||
$container->get( 'session.handler' )
|
||||
$container->get( 'session.handler' ),
|
||||
$container->get( 'blocks.settings.final_review_enabled' ),
|
||||
$container->get( 'button.helper.wc-order-creator' ),
|
||||
$container->get( 'wcgateway.paypal-gateway' )
|
||||
);
|
||||
},
|
||||
'button.checkout-form-saver' => static function ( ContainerInterface $container ): CheckoutFormSaver {
|
||||
|
@ -362,6 +363,10 @@ return array(
|
|||
},
|
||||
|
||||
'button.helper.wc-order-creator' => static function ( ContainerInterface $container ): WooCommerceOrderCreator {
|
||||
return new WooCommerceOrderCreator( $container->get( 'wcgateway.funding-source.renderer' ), $container->get( 'session.handler' ) );
|
||||
return new WooCommerceOrderCreator(
|
||||
$container->get( 'wcgateway.funding-source.renderer' ),
|
||||
$container->get( 'session.handler' ),
|
||||
$container->get( 'wc-subscriptions.helper' )
|
||||
);
|
||||
},
|
||||
);
|
||||
|
|
|
@ -105,13 +105,6 @@ class ApproveOrderEndpoint implements EndpointInterface {
|
|||
*/
|
||||
protected $wc_order_creator;
|
||||
|
||||
/**
|
||||
* The Subscription Helper.
|
||||
*
|
||||
* @var SubscriptionHelper
|
||||
*/
|
||||
protected $subscription_helper;
|
||||
|
||||
/**
|
||||
* The logger.
|
||||
*
|
||||
|
@ -132,7 +125,6 @@ class ApproveOrderEndpoint implements EndpointInterface {
|
|||
* @param bool $final_review_enabled Whether the final review is enabled.
|
||||
* @param PayPalGateway $gateway The WC gateway.
|
||||
* @param WooCommerceOrderCreator $wc_order_creator The WooCommerce order creator.
|
||||
* @param SubscriptionHelper $subscription_helper The subscription helper.
|
||||
* @param LoggerInterface $logger The logger.
|
||||
*/
|
||||
public function __construct(
|
||||
|
@ -146,7 +138,6 @@ class ApproveOrderEndpoint implements EndpointInterface {
|
|||
bool $final_review_enabled,
|
||||
PayPalGateway $gateway,
|
||||
WooCommerceOrderCreator $wc_order_creator,
|
||||
SubscriptionHelper $subscription_helper,
|
||||
LoggerInterface $logger
|
||||
) {
|
||||
|
||||
|
@ -160,7 +151,6 @@ class ApproveOrderEndpoint implements EndpointInterface {
|
|||
$this->final_review_enabled = $final_review_enabled;
|
||||
$this->gateway = $gateway;
|
||||
$this->wc_order_creator = $wc_order_creator;
|
||||
$this->subscription_helper = $subscription_helper;
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
|
@ -247,7 +237,7 @@ class ApproveOrderEndpoint implements EndpointInterface {
|
|||
|
||||
$this->session_handler->replace_order( $order );
|
||||
|
||||
if ( ! $this->subscription_helper->plugin_is_active() && apply_filters( 'woocommerce_paypal_payments_toggle_final_review_checkbox', false ) ) {
|
||||
if ( apply_filters( 'woocommerce_paypal_payments_toggle_final_review_checkbox', false ) ) {
|
||||
$this->toggle_final_review_enabled_setting();
|
||||
}
|
||||
|
||||
|
|
|
@ -11,13 +11,18 @@ namespace WooCommerce\PayPalCommerce\Button\Endpoint;
|
|||
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\OrderEndpoint;
|
||||
use WooCommerce\PayPalCommerce\Button\Exception\RuntimeException;
|
||||
use WooCommerce\PayPalCommerce\Button\Helper\ContextTrait;
|
||||
use WooCommerce\PayPalCommerce\Button\Helper\WooCommerceOrderCreator;
|
||||
use WooCommerce\PayPalCommerce\Session\SessionHandler;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
|
||||
|
||||
/**
|
||||
* Class ApproveSubscriptionEndpoint
|
||||
*/
|
||||
class ApproveSubscriptionEndpoint implements EndpointInterface {
|
||||
|
||||
use ContextTrait;
|
||||
|
||||
const ENDPOINT = 'ppc-approve-subscription';
|
||||
|
||||
/**
|
||||
|
@ -41,21 +46,51 @@ class ApproveSubscriptionEndpoint implements EndpointInterface {
|
|||
*/
|
||||
private $session_handler;
|
||||
|
||||
/**
|
||||
* Whether the final review is enabled.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $final_review_enabled;
|
||||
|
||||
/**
|
||||
* The WooCommerce order creator.
|
||||
*
|
||||
* @var WooCommerceOrderCreator
|
||||
*/
|
||||
protected $wc_order_creator;
|
||||
|
||||
/**
|
||||
* The WC gateway.
|
||||
*
|
||||
* @var PayPalGateway
|
||||
*/
|
||||
protected $gateway;
|
||||
|
||||
/**
|
||||
* ApproveSubscriptionEndpoint constructor.
|
||||
*
|
||||
* @param RequestData $request_data The request data helper.
|
||||
* @param OrderEndpoint $order_endpoint The order endpoint.
|
||||
* @param SessionHandler $session_handler The session handler.
|
||||
* @param RequestData $request_data The request data helper.
|
||||
* @param OrderEndpoint $order_endpoint The order endpoint.
|
||||
* @param SessionHandler $session_handler The session handler.
|
||||
* @param bool $final_review_enabled Whether the final review is enabled.
|
||||
* @param WooCommerceOrderCreator $wc_order_creator The WooCommerce order creator.
|
||||
* @param PayPalGateway $gateway The WC gateway.
|
||||
*/
|
||||
public function __construct(
|
||||
RequestData $request_data,
|
||||
OrderEndpoint $order_endpoint,
|
||||
SessionHandler $session_handler
|
||||
SessionHandler $session_handler,
|
||||
bool $final_review_enabled,
|
||||
WooCommerceOrderCreator $wc_order_creator,
|
||||
PayPalGateway $gateway
|
||||
) {
|
||||
$this->request_data = $request_data;
|
||||
$this->order_endpoint = $order_endpoint;
|
||||
$this->session_handler = $session_handler;
|
||||
$this->request_data = $request_data;
|
||||
$this->order_endpoint = $order_endpoint;
|
||||
$this->session_handler = $session_handler;
|
||||
$this->final_review_enabled = $final_review_enabled;
|
||||
$this->wc_order_creator = $wc_order_creator;
|
||||
$this->gateway = $gateway;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -88,6 +123,15 @@ class ApproveSubscriptionEndpoint implements EndpointInterface {
|
|||
WC()->session->set( 'ppcp_subscription_id', $data['subscription_id'] );
|
||||
}
|
||||
|
||||
$should_create_wc_order = $data['should_create_wc_order'] ?? false;
|
||||
if ( ! $this->final_review_enabled && ! $this->is_checkout() && $should_create_wc_order ) {
|
||||
$wc_order = $this->wc_order_creator->create_from_paypal_order( $order, WC()->cart );
|
||||
$this->gateway->process_payment( $wc_order->get_id() );
|
||||
$order_received_url = $wc_order->get_checkout_order_received_url();
|
||||
|
||||
wp_send_json_success( array( 'order_received_url' => $order_received_url ) );
|
||||
}
|
||||
|
||||
wp_send_json_success();
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -246,7 +246,6 @@ class CreateOrderEndpoint implements EndpointInterface {
|
|||
$this->parsed_request_data = $data;
|
||||
$payment_method = $data['payment_method'] ?? '';
|
||||
$funding_source = $data['funding_source'] ?? '';
|
||||
$payment_source = $data['payment_source'] ?? '';
|
||||
$wc_order = null;
|
||||
if ( 'pay-now' === $data['context'] ) {
|
||||
$wc_order = wc_get_order( (int) $data['order_id'] );
|
||||
|
@ -262,7 +261,7 @@ class CreateOrderEndpoint implements EndpointInterface {
|
|||
}
|
||||
$this->purchase_unit = $this->purchase_unit_factory->from_wc_order( $wc_order );
|
||||
} else {
|
||||
$this->purchase_unit = $this->purchase_unit_factory->from_wc_cart( null, $this->should_handle_shipping_in_paypal( $payment_source ) );
|
||||
$this->purchase_unit = $this->purchase_unit_factory->from_wc_cart( null, $this->should_handle_shipping_in_paypal( $funding_source ) );
|
||||
|
||||
// Do not allow completion by webhooks when started via non-checkout buttons,
|
||||
// it is needed only for some APMs in checkout.
|
||||
|
@ -615,16 +614,16 @@ class CreateOrderEndpoint implements EndpointInterface {
|
|||
/**
|
||||
* Checks if the shipping should be handled in PayPal popup.
|
||||
*
|
||||
* @param string $payment_source The payment source.
|
||||
* @param string $funding_source The funding source.
|
||||
* @return bool true if the shipping should be handled in PayPal popup, otherwise false.
|
||||
*/
|
||||
protected function should_handle_shipping_in_paypal( string $payment_source ): bool {
|
||||
protected function should_handle_shipping_in_paypal( string $funding_source ): bool {
|
||||
$is_vaulting_enabled = $this->settings->has( 'vault_enabled' ) && $this->settings->get( 'vault_enabled' );
|
||||
|
||||
if ( ! $this->handle_shipping_in_paypal ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ! $is_vaulting_enabled || $payment_source !== 'venmo';
|
||||
return ! $is_vaulting_enabled || $funding_source !== 'venmo';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,12 +14,16 @@ use WC_Cart;
|
|||
use WC_Order;
|
||||
use WC_Order_Item_Product;
|
||||
use WC_Order_Item_Shipping;
|
||||
use WC_Subscription;
|
||||
use WC_Subscriptions_Product;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Order;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Payer;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Shipping;
|
||||
use WooCommerce\PayPalCommerce\Session\SessionHandler;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\FundingSource\FundingSourceRenderer;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
|
||||
use WooCommerce\PayPalCommerce\WcSubscriptions\Helper\SubscriptionHelper;
|
||||
use WP_Error;
|
||||
|
||||
/**
|
||||
* Class WooCommerceOrderCreator
|
||||
|
@ -40,18 +44,28 @@ class WooCommerceOrderCreator {
|
|||
*/
|
||||
protected $session_handler;
|
||||
|
||||
/**
|
||||
* The subscription helper
|
||||
*
|
||||
* @var SubscriptionHelper
|
||||
*/
|
||||
protected $subscription_helper;
|
||||
|
||||
/**
|
||||
* WooCommerceOrderCreator constructor.
|
||||
*
|
||||
* @param FundingSourceRenderer $funding_source_renderer The funding source renderer.
|
||||
* @param SessionHandler $session_handler The session handler.
|
||||
* @param SubscriptionHelper $subscription_helper The subscription helper.
|
||||
*/
|
||||
public function __construct(
|
||||
FundingSourceRenderer $funding_source_renderer,
|
||||
SessionHandler $session_handler
|
||||
SessionHandler $session_handler,
|
||||
SubscriptionHelper $subscription_helper
|
||||
) {
|
||||
$this->funding_source_renderer = $funding_source_renderer;
|
||||
$this->session_handler = $session_handler;
|
||||
$this->subscription_helper = $subscription_helper;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -69,10 +83,13 @@ class WooCommerceOrderCreator {
|
|||
throw new RuntimeException( 'Problem creating WC order.' );
|
||||
}
|
||||
|
||||
$this->configure_line_items( $wc_order, $wc_cart );
|
||||
$this->configure_shipping( $wc_order, $order->payer(), $order->purchase_units()[0]->shipping() );
|
||||
$payer = $order->payer();
|
||||
$shipping = $order->purchase_units()[0]->shipping();
|
||||
|
||||
$this->configure_payment_source( $wc_order );
|
||||
$this->configure_customer( $wc_order );
|
||||
$this->configure_line_items( $wc_order, $wc_cart, $payer, $shipping );
|
||||
$this->configure_shipping( $wc_order, $payer, $shipping );
|
||||
$this->configure_coupons( $wc_order, $wc_cart->get_applied_coupons() );
|
||||
|
||||
$wc_order->calculate_totals();
|
||||
|
@ -84,11 +101,13 @@ class WooCommerceOrderCreator {
|
|||
/**
|
||||
* Configures the line items.
|
||||
*
|
||||
* @param WC_Order $wc_order The WC order.
|
||||
* @param WC_Cart $wc_cart The Cart.
|
||||
* @param WC_Order $wc_order The WC order.
|
||||
* @param WC_Cart $wc_cart The Cart.
|
||||
* @param Payer|null $payer The payer.
|
||||
* @param Shipping|null $shipping The shipping.
|
||||
* @return void
|
||||
*/
|
||||
protected function configure_line_items( WC_Order $wc_order, WC_Cart $wc_cart ): void {
|
||||
protected function configure_line_items( WC_Order $wc_order, WC_Cart $wc_cart, ?Payer $payer, ?Shipping $shipping ): void {
|
||||
$cart_contents = $wc_cart->get_cart();
|
||||
|
||||
foreach ( $cart_contents as $cart_item ) {
|
||||
|
@ -111,9 +130,37 @@ class WooCommerceOrderCreator {
|
|||
return;
|
||||
}
|
||||
|
||||
$total = $product->get_price() * $quantity;
|
||||
|
||||
$item->set_name( $product->get_name() );
|
||||
$item->set_subtotal( $product->get_price() * $quantity );
|
||||
$item->set_total( $product->get_price() * $quantity );
|
||||
$item->set_subtotal( $total );
|
||||
$item->set_total( $total );
|
||||
|
||||
$product_id = $product->get_id();
|
||||
|
||||
if ( $this->is_subscription( $product_id ) ) {
|
||||
$subscription = $this->create_subscription( $wc_order, $product_id );
|
||||
$sign_up_fee = WC_Subscriptions_Product::get_sign_up_fee( $product );
|
||||
$subscription_total = $total + $sign_up_fee;
|
||||
|
||||
$item->set_subtotal( $subscription_total );
|
||||
$item->set_total( $subscription_total );
|
||||
|
||||
$subscription->add_product( $product );
|
||||
$this->configure_shipping( $subscription, $payer, $shipping );
|
||||
$this->configure_payment_source( $subscription );
|
||||
$this->configure_coupons( $subscription, $wc_cart->get_applied_coupons() );
|
||||
|
||||
$dates = array(
|
||||
'trial_end' => WC_Subscriptions_Product::get_trial_expiration_date( $product_id ),
|
||||
'next_payment' => WC_Subscriptions_Product::get_first_renewal_payment_date( $product_id ),
|
||||
'end' => WC_Subscriptions_Product::get_expiration_date( $product_id ),
|
||||
);
|
||||
|
||||
$subscription->update_dates( $dates );
|
||||
$subscription->calculate_totals();
|
||||
$subscription->payment_complete_for_order( $wc_order );
|
||||
}
|
||||
|
||||
$wc_order->add_item( $item );
|
||||
}
|
||||
|
@ -179,8 +226,18 @@ class WooCommerceOrderCreator {
|
|||
$shipping->set_method_id( $shipping_options->id() );
|
||||
$shipping->set_total( $shipping_options->amount()->value_str() );
|
||||
|
||||
$items = $wc_order->get_items();
|
||||
$items_in_package = array();
|
||||
foreach ( $items as $item ) {
|
||||
$items_in_package[] = $item->get_name() . ' × ' . (string) $item->get_quantity();
|
||||
}
|
||||
|
||||
$shipping->add_meta_data( __( 'Items', 'woocommerce-paypal-payments' ), implode( ', ', $items_in_package ) );
|
||||
|
||||
$wc_order->add_item( $shipping );
|
||||
}
|
||||
|
||||
$wc_order->calculate_totals();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -225,4 +282,43 @@ class WooCommerceOrderCreator {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the product with given ID is WC subscription.
|
||||
*
|
||||
* @param int $product_id The product ID.
|
||||
* @return bool true if the product is subscription, otherwise false.
|
||||
*/
|
||||
protected function is_subscription( int $product_id ): bool {
|
||||
if ( ! $this->subscription_helper->plugin_is_active() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return WC_Subscriptions_Product::is_subscription( $product_id );
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates WC subscription from given order and product ID.
|
||||
*
|
||||
* @param WC_Order $wc_order The WC order.
|
||||
* @param int $product_id The product ID.
|
||||
* @return WC_Subscription The subscription order
|
||||
* @throws RuntimeException If problem creating.
|
||||
*/
|
||||
protected function create_subscription( WC_Order $wc_order, int $product_id ): WC_Subscription {
|
||||
$subscription = wcs_create_subscription(
|
||||
array(
|
||||
'order_id' => $wc_order->get_id(),
|
||||
'status' => 'pending',
|
||||
'billing_period' => WC_Subscriptions_Product::get_period( $product_id ),
|
||||
'billing_interval' => WC_Subscriptions_Product::get_interval( $product_id ),
|
||||
'customer_id' => $wc_order->get_customer_id(),
|
||||
)
|
||||
);
|
||||
|
||||
if ( $subscription instanceof WP_Error ) {
|
||||
throw new RuntimeException( $subscription->get_error_message() );
|
||||
}
|
||||
|
||||
return $subscription;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -61,17 +61,33 @@ $background-ident-color: #fbfbfb;
|
|||
border: 1px solid #c3c4c7;
|
||||
border-left-width: 4px;
|
||||
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.04);
|
||||
margin: 5px 0px 2px;
|
||||
padding: 0px 12px 4px 12px;
|
||||
margin: 5px 15px 2px 0;
|
||||
padding: 1px 12px;
|
||||
|
||||
p, .form-table td & p {
|
||||
margin-top: 4px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.highlight {
|
||||
background: transparent;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
.ppcp-notice-warning {
|
||||
border-left-color: #dba617;
|
||||
|
||||
.highlight {
|
||||
background: transparent;
|
||||
color: #dba617;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
.ppcp-notice-error {
|
||||
border-left-color: #d63638;
|
||||
|
||||
.highlight {
|
||||
color: #d63638;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -360,7 +360,6 @@ return array(
|
|||
$container->get( 'api.partner_merchant_id-production' ),
|
||||
$container->get( 'api.partner_merchant_id-sandbox' ),
|
||||
$container->get( 'api.endpoint.billing-agreements' ),
|
||||
$container->get( 'wc-subscriptions.helper' ),
|
||||
$logger
|
||||
);
|
||||
},
|
||||
|
|
|
@ -114,7 +114,7 @@ class CartCheckoutDetector {
|
|||
*/
|
||||
public static function has_classic_checkout(): bool {
|
||||
$checkout_page_id = wc_get_page_id( 'checkout' );
|
||||
return $checkout_page_id && has_block( 'woocommerce/classic-shortcode', $checkout_page_id );
|
||||
return $checkout_page_id && ( has_block( 'woocommerce/classic-shortcode', $checkout_page_id ) || self::has_classic_shortcode( $checkout_page_id, 'woocommerce_checkout' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -124,6 +124,25 @@ class CartCheckoutDetector {
|
|||
*/
|
||||
public static function has_classic_cart(): bool {
|
||||
$cart_page_id = wc_get_page_id( 'cart' );
|
||||
return $cart_page_id && has_block( 'woocommerce/classic-shortcode', $cart_page_id );
|
||||
return $cart_page_id && ( has_block( 'woocommerce/classic-shortcode', $cart_page_id ) || self::has_classic_shortcode( $cart_page_id, 'woocommerce_cart' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a page has a specific shortcode.
|
||||
*
|
||||
* @param int $page_id The ID of the page.
|
||||
* @param string $shortcode The shortcode to check for.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private static function has_classic_shortcode( int $page_id, string $shortcode ): bool {
|
||||
if ( ! $page_id ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$page = get_post( $page_id );
|
||||
$page_content = is_object( $page ) ? $page->post_content : '';
|
||||
|
||||
return str_contains( $page_content, $shortcode );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -56,14 +56,14 @@ class PayUponInvoiceHelper {
|
|||
}
|
||||
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Missing
|
||||
$billing_country = wc_clean( wp_unslash( $_POST['country'] ?? '' ) );
|
||||
if ( $billing_country && 'DE' !== $billing_country ) {
|
||||
$billing_country = WC()->customer->get_billing_country();
|
||||
if ( empty( $billing_country ) || 'DE' !== $billing_country ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Missing
|
||||
$shipping_country = wc_clean( wp_unslash( $_POST['s_country'] ?? '' ) );
|
||||
if ( $shipping_country && 'DE' !== $shipping_country ) {
|
||||
$shipping_country = WC()->customer->get_shipping_country();
|
||||
if ( empty( $shipping_country ) || 'DE' !== $shipping_country ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -220,14 +220,9 @@ class OrderProcessor {
|
|||
);
|
||||
|
||||
throw new PayPalOrderMissingException(
|
||||
sprintf(
|
||||
// translators: %s: Order history URL on My Account section.
|
||||
esc_attr__(
|
||||
'There was an error processing your order. Please check for any charges in your payment method and review your <a href="%s">order history</a> before placing the order again.',
|
||||
// phpcs:ignore WordPress.WP.I18n.TextDomainMismatch -- Intentionally "woocommerce" to reflect the original message.
|
||||
'woocommerce'
|
||||
),
|
||||
esc_url( wc_get_account_endpoint_url( 'orders' ) )
|
||||
esc_attr__(
|
||||
'There was an error processing your order. Please check for any charges in your payment method and review your order history before placing the order again.',
|
||||
'woocommerce-paypal-payments'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
|
|
@ -36,6 +36,8 @@ return function ( ContainerInterface $container, array $fields ): array {
|
|||
</div>';
|
||||
};
|
||||
|
||||
$axo_smart_button_location_notice = $container->has( 'axo.smart-button-location-notice' ) ? $container->get( 'axo.smart-button-location-notice' ) : '';
|
||||
|
||||
$smart_button_fields = array(
|
||||
'button_style_heading' => array(
|
||||
'heading' => __( 'PayPal Smart Buttons', 'woocommerce-paypal-payments' ),
|
||||
|
@ -65,7 +67,7 @@ return function ( ContainerInterface $container, array $fields ): array {
|
|||
'type' => 'ppcp-multiselect',
|
||||
'input_class' => array( 'wc-enhanced-select' ),
|
||||
'default' => $container->get( 'wcgateway.button.default-locations' ),
|
||||
'description' => __( 'Select where the PayPal smart buttons should be displayed.', 'woocommerce-paypal-payments' ),
|
||||
'description' => __( 'Select where the PayPal smart buttons should be displayed.', 'woocommerce-paypal-payments' ) . $axo_smart_button_location_notice,
|
||||
'options' => $container->get( 'wcgateway.button.locations' ),
|
||||
'screens' => array( State::STATE_START, State::STATE_ONBOARDED ),
|
||||
'requirements' => array(),
|
||||
|
|
|
@ -10,8 +10,6 @@ declare(strict_types=1);
|
|||
namespace WooCommerce\PayPalCommerce\WcGateway\Settings;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use WooCommerce\PayPalCommerce\AdminNotices\Entity\Message;
|
||||
use WooCommerce\PayPalCommerce\AdminNotices\Repository\Repository;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Authentication\Bearer;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Authentication\PayPalBearer;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\BillingAgreementsEndpoint;
|
||||
|
@ -23,9 +21,7 @@ use WooCommerce\PayPalCommerce\Onboarding\Helper\OnboardingUrl;
|
|||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Helper\DCCProductStatus;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Helper\PayUponInvoiceProductStatus;
|
||||
use WooCommerce\PayPalCommerce\WcSubscriptions\Helper\SubscriptionHelper;
|
||||
use WooCommerce\PayPalCommerce\Webhooks\WebhookRegistrar;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Exception\NotFoundException;
|
||||
use WooCommerce\WooCommerce\Logging\Logger\NullLogger;
|
||||
|
||||
/**
|
||||
|
@ -161,13 +157,6 @@ class SettingsListener {
|
|||
*/
|
||||
private $billing_agreements_endpoint;
|
||||
|
||||
/**
|
||||
* The subscription helper
|
||||
*
|
||||
* @var SubscriptionHelper
|
||||
*/
|
||||
protected $subscription_helper;
|
||||
|
||||
/**
|
||||
* The logger.
|
||||
*
|
||||
|
@ -193,7 +182,6 @@ class SettingsListener {
|
|||
* @param string $partner_merchant_id_production Partner merchant ID production.
|
||||
* @param string $partner_merchant_id_sandbox Partner merchant ID sandbox.
|
||||
* @param BillingAgreementsEndpoint $billing_agreements_endpoint Billing Agreements endpoint.
|
||||
* @param SubscriptionHelper $subscription_helper The subscription helper.
|
||||
* @param ?LoggerInterface $logger The logger.
|
||||
*/
|
||||
public function __construct(
|
||||
|
@ -212,7 +200,6 @@ class SettingsListener {
|
|||
string $partner_merchant_id_production,
|
||||
string $partner_merchant_id_sandbox,
|
||||
BillingAgreementsEndpoint $billing_agreements_endpoint,
|
||||
SubscriptionHelper $subscription_helper,
|
||||
LoggerInterface $logger = null
|
||||
) {
|
||||
|
||||
|
@ -231,7 +218,6 @@ class SettingsListener {
|
|||
$this->partner_merchant_id_production = $partner_merchant_id_production;
|
||||
$this->partner_merchant_id_sandbox = $partner_merchant_id_sandbox;
|
||||
$this->billing_agreements_endpoint = $billing_agreements_endpoint;
|
||||
$this->subscription_helper = $subscription_helper;
|
||||
$this->logger = $logger ?: new NullLogger();
|
||||
}
|
||||
|
||||
|
@ -394,7 +380,18 @@ class SettingsListener {
|
|||
|
||||
if ( $reference_transaction_enabled !== true ) {
|
||||
$this->settings->set( 'vault_enabled', false );
|
||||
$this->settings->set( 'subscriptions_mode', 'subscriptions_api' );
|
||||
|
||||
/**
|
||||
* If Vaulting-API was previously enabled, then fall-back to the
|
||||
* PayPal subscription mode, to ensure subscriptions are still
|
||||
* possible on this shop.
|
||||
*
|
||||
* This can happen when switching to a different PayPal merchant account
|
||||
*/
|
||||
if ( 'vaulting_api' === $subscription_mode ) {
|
||||
$this->settings->set( 'subscriptions_mode', 'subscriptions_api' );
|
||||
}
|
||||
|
||||
$this->settings->persist();
|
||||
}
|
||||
|
||||
|
@ -403,11 +400,6 @@ class SettingsListener {
|
|||
$this->settings->persist();
|
||||
}
|
||||
|
||||
if ( $this->subscription_helper->plugin_is_active() ) {
|
||||
$this->settings->set( 'blocks_final_review_enabled', true );
|
||||
$this->settings->persist();
|
||||
}
|
||||
|
||||
if ( $subscription_mode === 'disable_paypal_subscriptions' && $vault_enabled === '1' ) {
|
||||
$this->settings->set( 'vault_enabled', false );
|
||||
$this->settings->persist();
|
||||
|
@ -745,4 +737,28 @@ class SettingsListener {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter settings based on a condition.
|
||||
*
|
||||
* @param bool $condition The condition.
|
||||
* @param string $setting_slug The setting slug.
|
||||
* @param callable $filter_function The filter function.
|
||||
* @param bool $persist Whether to persist the settings.
|
||||
*/
|
||||
public function filter_settings( bool $condition, string $setting_slug, callable $filter_function, bool $persist = true ): void {
|
||||
if ( ! $this->is_valid_site_request() || State::STATE_ONBOARDED !== $this->state->current_state() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$existing_setting_value = $this->settings->has( $setting_slug ) ? $this->settings->get( $setting_slug ) : null;
|
||||
|
||||
if ( $condition ) {
|
||||
$new_setting_value = $filter_function( $existing_setting_value );
|
||||
$this->settings->set( $setting_slug, $new_setting_value );
|
||||
|
||||
if ( $persist ) {
|
||||
$this->settings->persist();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "woocommerce-paypal-payments",
|
||||
"version": "2.7.1",
|
||||
"version": "2.8.0",
|
||||
"description": "WooCommerce PayPal Payments",
|
||||
"repository": "https://github.com/woocommerce/woocommerce-paypal-payments",
|
||||
"license": "GPL-2.0",
|
||||
|
@ -94,6 +94,7 @@
|
|||
"dotenv": "^16.0.3",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"playwright": "^1.43.0",
|
||||
"run-s": "^0.0.0"
|
||||
"run-s": "^0.0.0",
|
||||
"uuid": "^9.0.1"
|
||||
}
|
||||
}
|
||||
|
|
15
readme.txt
15
readme.txt
|
@ -4,7 +4,7 @@ Tags: woocommerce, paypal, payments, ecommerce, checkout, cart, pay later, apple
|
|||
Requires at least: 5.3
|
||||
Tested up to: 6.5
|
||||
Requires PHP: 7.2
|
||||
Stable tag: 2.7.1
|
||||
Stable tag: 2.8.0
|
||||
License: GPLv2
|
||||
License URI: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
|
||||
|
@ -179,6 +179,19 @@ If you encounter issues with the PayPal buttons not appearing after an update, p
|
|||
|
||||
== Changelog ==
|
||||
|
||||
= 2.8.0 - xxxx-xx-xx =
|
||||
* Fix - Calculate totals after adding shipping to include taxes #2296
|
||||
* Fix - Package tracking integration throws error in 2.7.1 #2289
|
||||
* Fix - Make PayPal Subscription products unique in cart #2265
|
||||
* Fix - PayPal declares subscription support when merchant not enabled for Reference Transactions #2282
|
||||
* Fix - Google Pay and Apple Pay Settings button from Connection tab have wrong links #2273
|
||||
* Fix - Smart Buttons in Block Checkout not respecting the location setting (2830) #2278
|
||||
* Fix - Disable Pay Upon Invoice if billing/shipping country not set #2281
|
||||
* Enhancement - Enable shipping callback for WC subscriptions #2259
|
||||
* Enhancement - Disable the shipping callback for "venmo" when vaulting is active #2269
|
||||
* Enhancement - Improve "Could not retrieve order" error message #2271
|
||||
* Enhancement - Add block Checkout compatibility to Advanced Card Processing #2246
|
||||
|
||||
= 2.7.1 - 2024-05-28 =
|
||||
* Fix - Ensure package tracking data is sent to original PayPal transaction #2180
|
||||
* Fix - Set the 'Woo_PPCP' as a default value for data-partner-attribution-id #2188
|
||||
|
|
|
@ -60,7 +60,6 @@ class SettingsListenerTest extends ModularTestCase
|
|||
'',
|
||||
'',
|
||||
$billing_agreement_endpoint,
|
||||
$subscription_helper,
|
||||
$logger
|
||||
);
|
||||
|
||||
|
|
|
@ -3,14 +3,14 @@
|
|||
* Plugin Name: WooCommerce PayPal Payments
|
||||
* Plugin URI: https://woocommerce.com/products/woocommerce-paypal-payments/
|
||||
* Description: PayPal's latest complete payments processing solution. Accept PayPal, Pay Later, credit/debit cards, alternative digital wallets local payment types and bank accounts. Turn on only PayPal options or process a full suite of payment methods. Enable global transaction with extensive currency and country coverage.
|
||||
* Version: 2.7.1
|
||||
* Version: 2.8.0
|
||||
* Author: WooCommerce
|
||||
* Author URI: https://woocommerce.com/
|
||||
* License: GPL-2.0
|
||||
* Requires PHP: 7.2
|
||||
* Requires Plugins: woocommerce
|
||||
* WC requires at least: 3.9
|
||||
* WC tested up to: 8.8
|
||||
* WC tested up to: 8.9
|
||||
* Text Domain: woocommerce-paypal-payments
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce
|
||||
|
@ -26,7 +26,7 @@ define( 'PAYPAL_API_URL', 'https://api-m.paypal.com' );
|
|||
define( 'PAYPAL_URL', 'https://www.paypal.com' );
|
||||
define( 'PAYPAL_SANDBOX_API_URL', 'https://api-m.sandbox.paypal.com' );
|
||||
define( 'PAYPAL_SANDBOX_URL', 'https://www.sandbox.paypal.com' );
|
||||
define( 'PAYPAL_INTEGRATION_DATE', '2024-05-13' );
|
||||
define( 'PAYPAL_INTEGRATION_DATE', '2024-06-03' );
|
||||
|
||||
! defined( 'CONNECT_WOO_CLIENT_ID' ) && define( 'CONNECT_WOO_CLIENT_ID', 'AcCAsWta_JTL__OfpjspNyH7c1GGHH332fLwonA5CwX4Y10mhybRZmHLA0GdRbwKwjQIhpDQy0pluX_P' );
|
||||
! defined( 'CONNECT_WOO_SANDBOX_CLIENT_ID' ) && define( 'CONNECT_WOO_SANDBOX_CLIENT_ID', 'AYmOHbt1VHg-OZ_oihPdzKEVbU3qg0qXonBcAztuzniQRaKE0w1Hr762cSFwd4n8wxOl-TCWohEa0XM_' );
|
||||
|
|
|
@ -598,6 +598,11 @@ unbox-primitive@^1.0.2:
|
|||
has-symbols "^1.0.3"
|
||||
which-boxed-primitive "^1.0.2"
|
||||
|
||||
uuid@^9.0.1:
|
||||
version "9.0.1"
|
||||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30"
|
||||
integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==
|
||||
|
||||
validate-npm-package-license@^3.0.1:
|
||||
version "3.0.4"
|
||||
resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue