mirror of
https://github.com/woocommerce/woocommerce-paypal-payments.git
synced 2025-09-04 08:47:23 +08:00
Merge branch 'trunk' into PCP-3171-several-issues-on-backwpup-com
This commit is contained in:
commit
39b6f8a1aa
67 changed files with 1716 additions and 490 deletions
|
@ -1,6 +1,20 @@
|
|||
*** Changelog ***
|
||||
|
||||
= 2.7.1 - xxxx-xx-xx =
|
||||
= 2.8.0 - 2024-06-11 =
|
||||
* 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
|
||||
* Fix - Critical error on pay for order page when we try to pay with ACDC gateway #2321
|
||||
* 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
|
||||
* Fix - Allow PUI Gateway for refund processor #2192
|
||||
|
|
10
modules.php
10
modules.php
|
@ -18,8 +18,8 @@ return function ( string $root_dir ): iterable {
|
|||
( require "$modules_dir/woocommerce-logging/module.php" )(),
|
||||
( require "$modules_dir/ppcp-admin-notices/module.php" )(),
|
||||
( require "$modules_dir/ppcp-api-client/module.php" )(),
|
||||
( require "$modules_dir/ppcp-button/module.php" )(),
|
||||
( require "$modules_dir/ppcp-compat/module.php" )(),
|
||||
( require "$modules_dir/ppcp-button/module.php" )(),
|
||||
( require "$modules_dir/ppcp-onboarding/module.php" )(),
|
||||
( require "$modules_dir/ppcp-session/module.php" )(),
|
||||
( require "$modules_dir/ppcp-status-report/module.php" )(),
|
||||
|
@ -73,12 +73,12 @@ return function ( string $root_dir ): iterable {
|
|||
$modules[] = ( require "$modules_dir/ppcp-paylater-block/module.php" )();
|
||||
}
|
||||
|
||||
if ( PayLaterWCBlocksModule::is_module_loading_required() ) {
|
||||
$modules[] = ( require "$modules_dir/ppcp-paylater-wc-blocks/module.php" )();
|
||||
}
|
||||
|
||||
if ( PayLaterConfiguratorModule::is_enabled() ) {
|
||||
$modules[] = ( require "$modules_dir/ppcp-paylater-configurator/module.php" )();
|
||||
|
||||
if ( PayLaterWCBlocksModule::is_module_loading_required() ) {
|
||||
$modules[] = ( require "$modules_dir/ppcp-paylater-wc-blocks/module.php" )();
|
||||
}
|
||||
}
|
||||
|
||||
if ( apply_filters(
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -964,7 +964,7 @@ return array(
|
|||
: $container->get( 'applepay.enable-url-sandbox' );
|
||||
|
||||
$button_url = $enabled
|
||||
? admin_url( 'admin.php?page=wc-settings&tab=checkout§ion=ppcp-gateway#field-alternative_payment_methods' )
|
||||
? admin_url( 'admin.php?page=wc-settings&tab=checkout§ion=ppcp-gateway&ppcp-tab=ppcp-credit-card-gateway#ppcp-applepay_button_enabled' )
|
||||
: $enable_url;
|
||||
|
||||
return sprintf(
|
||||
|
|
|
@ -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,48 @@
|
|||
.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-billing-email-field-wrapper {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
#ppcp-axo-billing-email-submit-button {
|
||||
margin-top: 0;
|
||||
position: relative;
|
||||
transition: opacity 0.3s ease;
|
||||
flex: 0 1 auto;
|
||||
align-self: flex-start;
|
||||
|
||||
.loader:before {
|
||||
display: inline;
|
||||
height: 12px;
|
||||
width: 12px;
|
||||
margin-left: -6px;
|
||||
margin-top: -6px;
|
||||
left: auto;
|
||||
right: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.ppcp-axo-billing-email-submit-button {
|
||||
&-hidden {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
&-loaded:not([disabled]) {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.ppcp-axo-payment-container {
|
||||
|
@ -28,6 +70,7 @@
|
|||
|
||||
.ppcp-axo-customer-details {
|
||||
margin-bottom: 40px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.axo-checkout-header-section {
|
||||
|
@ -44,6 +87,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;
|
||||
|
@ -54,3 +122,22 @@
|
|||
max-height: 25px;
|
||||
}
|
||||
}
|
||||
|
||||
.ppcp-axo-customer-details #billing_email_field .woocommerce-input-wrapper {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 719px) {
|
||||
#ppcp-axo-billing-email {
|
||||
&-field-wrapper {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
&-submit-button {
|
||||
align-self: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -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,18 +165,20 @@ 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}`);
|
||||
this.validateEmail(this.el.fieldBillingEmail.selector);
|
||||
if (this.emailInput && this.lastEmailCheckedIdentity !== this.emailInput.value) {
|
||||
await this.onChangeEmail();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.reEnableEmailInput();
|
||||
|
||||
// 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,15 +215,17 @@ class AxoManager {
|
|||
this.status.hasProfile
|
||||
);
|
||||
|
||||
log('Scenario', scenario);
|
||||
log(`Scenario: ${JSON.stringify(scenario)}`);
|
||||
|
||||
// Reset some elements to a default status.
|
||||
this.el.watermarkContainer.hide();
|
||||
|
||||
if (scenario.defaultSubmitButton) {
|
||||
this.el.defaultSubmitButton.show();
|
||||
this.el.billingEmailSubmitButton.hide();
|
||||
} else {
|
||||
this.el.defaultSubmitButton.hide();
|
||||
this.el.billingEmailSubmitButton.show();
|
||||
}
|
||||
|
||||
if (scenario.defaultEmailField) {
|
||||
|
@ -231,6 +236,7 @@ class AxoManager {
|
|||
|
||||
if (scenario.defaultFormFields) {
|
||||
this.el.customerDetails.show();
|
||||
this.toggleLoaderAndOverlay(this.el.customerDetails, 'loader', 'ppcp-axo-overlay');
|
||||
} else {
|
||||
this.el.customerDetails.hide();
|
||||
}
|
||||
|
@ -245,10 +251,9 @@ class AxoManager {
|
|||
this.el.watermarkContainer.show();
|
||||
|
||||
// Move watermark to after email.
|
||||
this.$(this.el.fieldBillingEmail.selector).append(
|
||||
this.$(this.el.watermarkContainer.selector)
|
||||
document.querySelector('#billing_email_field .woocommerce-input-wrapper').append(
|
||||
document.querySelector(this.el.watermarkContainer.selector)
|
||||
);
|
||||
|
||||
} else {
|
||||
this.el.emailWidgetContainer.hide();
|
||||
if (!scenario.defaultEmailField) {
|
||||
|
@ -257,12 +262,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)
|
||||
|
@ -278,6 +285,8 @@ class AxoManager {
|
|||
|
||||
if (scenario.axoPaymentContainer) {
|
||||
this.el.paymentContainer.show();
|
||||
this.el.gatewayDescription.hide();
|
||||
document.querySelector(this.el.billingEmailSubmitButton.selector).setAttribute('disabled', 'disabled');
|
||||
} else {
|
||||
this.el.paymentContainer.hide();
|
||||
}
|
||||
|
@ -372,7 +381,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 +393,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();
|
||||
}
|
||||
|
@ -424,10 +432,18 @@ class AxoManager {
|
|||
`);
|
||||
}
|
||||
|
||||
// billingEmailFieldWrapper
|
||||
const befw = this.el.billingEmailFieldWrapper;
|
||||
if (!document.querySelector(befw.selector)) {
|
||||
document.querySelector('#billing_email_field .woocommerce-input-wrapper').insertAdjacentHTML('afterend', `
|
||||
<div id="${befw.id}"></div>
|
||||
`);
|
||||
}
|
||||
|
||||
// Watermark container
|
||||
const wc = this.el.watermarkContainer;
|
||||
if (!document.querySelector(wc.selector)) {
|
||||
this.emailInput.insertAdjacentHTML('afterend', `
|
||||
document.querySelector(befw.selector).insertAdjacentHTML('beforeend', `
|
||||
<div class="${wc.className}" id="${wc.id}"></div>
|
||||
`);
|
||||
}
|
||||
|
@ -457,10 +473,10 @@ class AxoManager {
|
|||
}
|
||||
|
||||
} else {
|
||||
|
||||
// Move email to the AXO container.
|
||||
let emailRow = document.querySelector(this.el.fieldBillingEmail.selector);
|
||||
wrapperElement.prepend(emailRow);
|
||||
document.querySelector(this.el.billingEmailFieldWrapper.selector).prepend(document.querySelector('#billing_email_field .woocommerce-input-wrapper'));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -471,7 +487,8 @@ class AxoManager {
|
|||
this.initialized = true;
|
||||
|
||||
await this.connect();
|
||||
this.renderWatermark();
|
||||
await this.renderWatermark();
|
||||
this.renderEmailSubmitButton();
|
||||
this.watchEmail();
|
||||
}
|
||||
|
||||
|
@ -496,6 +513,26 @@ class AxoManager {
|
|||
(await this.fastlane.FastlaneWatermarkComponent({
|
||||
includeAdditionalInfo
|
||||
})).render(this.el.watermarkContainer.selector);
|
||||
|
||||
this.toggleWatermarkLoading(this.el.watermarkContainer, 'ppcp-axo-watermark-loading', 'loader');
|
||||
}
|
||||
|
||||
renderEmailSubmitButton() {
|
||||
const billingEmailSubmitButton = this.el.billingEmailSubmitButton;
|
||||
const billingEmailSubmitButtonSpinner = this.el.billingEmailSubmitButtonSpinner;
|
||||
|
||||
if (!document.querySelector(billingEmailSubmitButton.selector)) {
|
||||
document.querySelector(this.el.billingEmailFieldWrapper.selector).insertAdjacentHTML('beforeend', `
|
||||
<button type="button" id="${billingEmailSubmitButton.id}" class="${billingEmailSubmitButton.className}">
|
||||
${this.axoConfig.billing_email_button_text}
|
||||
<span id="${billingEmailSubmitButtonSpinner.id}"></span>
|
||||
</button>
|
||||
`);
|
||||
|
||||
document.querySelector(this.el.billingEmailSubmitButton.selector).offsetHeight;
|
||||
document.querySelector(this.el.billingEmailSubmitButton.selector).classList.remove('ppcp-axo-billing-email-submit-button-hidden');
|
||||
document.querySelector(this.el.billingEmailSubmitButton.selector).classList.add('ppcp-axo-billing-email-submit-button-loaded');
|
||||
}
|
||||
}
|
||||
|
||||
watchEmail() {
|
||||
|
@ -506,17 +543,16 @@ 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.validateEmail(this.el.fieldBillingEmail.selector);
|
||||
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 +572,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('');
|
||||
|
@ -548,7 +584,7 @@ class AxoManager {
|
|||
|
||||
this.lastEmailCheckedIdentity = this.emailInput.value;
|
||||
|
||||
if (!this.emailInput.value || !this.emailInput.checkValidity()) {
|
||||
if (!this.emailInput.value || !this.emailInput.checkValidity() || !this.validateEmailFormat(this.emailInput.value)) {
|
||||
log('The email address is not valid.');
|
||||
return;
|
||||
}
|
||||
|
@ -565,12 +601,19 @@ class AxoManager {
|
|||
page_type: 'checkout'
|
||||
});
|
||||
|
||||
|
||||
this.disableGatewaySelection();
|
||||
this.spinnerToggleLoaderAndOverlay(this.el.billingEmailSubmitButtonSpinner, 'loader', 'ppcp-axo-overlay');
|
||||
await this.lookupCustomerByEmail();
|
||||
this.spinnerToggleLoaderAndOverlay(this.el.billingEmailSubmitButtonSpinner, 'loader', 'ppcp-axo-overlay');
|
||||
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 +621,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 +657,7 @@ class AxoManager {
|
|||
|
||||
this.hideGatewaySelection = true;
|
||||
this.$('.wc_payment_methods label').hide();
|
||||
this.$('.wc_payment_methods input').hide();
|
||||
|
||||
await this.renderWatermark(false);
|
||||
|
||||
|
@ -644,6 +694,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 +730,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 +741,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 +775,7 @@ class AxoManager {
|
|||
|
||||
tokenizeData() {
|
||||
return {
|
||||
name: {
|
||||
cardholderName: {
|
||||
fullName: this.billingView.fullName()
|
||||
},
|
||||
billingAddress: {
|
||||
|
@ -776,7 +837,9 @@ class AxoManager {
|
|||
scrollTop: $notices.offset().top
|
||||
}, 500);
|
||||
}
|
||||
console.error('Failure:', responseData);
|
||||
|
||||
log(`Error sending checkout form. ${responseData}`, 'error');
|
||||
|
||||
this.hideLoading();
|
||||
return;
|
||||
}
|
||||
|
@ -785,7 +848,8 @@ class AxoManager {
|
|||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
log(`Error sending checkout form. ${error.message}`, 'error');
|
||||
|
||||
this.hideLoading();
|
||||
});
|
||||
|
||||
|
@ -840,6 +904,69 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
spinnerToggleLoaderAndOverlay(element, loaderClass, overlayClass) {
|
||||
const spinner = document.querySelector(`${element.selector}`);
|
||||
if (spinner) {
|
||||
spinner.classList.toggle(loaderClass);
|
||||
spinner.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);
|
||||
}
|
||||
}
|
||||
|
||||
validateEmailFormat(value) {
|
||||
const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
return emailPattern.test(value);
|
||||
}
|
||||
|
||||
validateEmail(billingEmail) {
|
||||
const billingEmailSelector = document.querySelector(billingEmail);
|
||||
const value = document.querySelector(billingEmail + ' input').value;
|
||||
|
||||
if (this.validateEmailFormat(value)) {
|
||||
billingEmailSelector.classList.remove('woocommerce-invalid');
|
||||
billingEmailSelector.classList.add('woocommerce-validated');
|
||||
this.setStatus('validEmail', true);
|
||||
} else {
|
||||
billingEmailSelector.classList.remove('woocommerce-validated');
|
||||
billingEmailSelector.classList.add('woocommerce-invalid');
|
||||
this.setStatus('validEmail', false);
|
||||
}
|
||||
}
|
||||
|
||||
reEnableEmailInput() {
|
||||
const reEnableInput = (ev) => {
|
||||
const submitButton = document.querySelector(this.el.billingEmailSubmitButton.selector);
|
||||
if (submitButton.hasAttribute('disabled')) {
|
||||
submitButton.removeAttribute('disabled');
|
||||
}
|
||||
};
|
||||
|
||||
this.$('#billing_email_field input').on('focus', reEnableInput);
|
||||
this.$('#billing_email_field input').on('input', reEnableInput);
|
||||
this.$('#billing_email_field input').on('click', reEnableInput);
|
||||
}
|
||||
}
|
||||
|
||||
export default AxoManager;
|
||||
|
|
|
@ -7,6 +7,10 @@ class DomElementCollection {
|
|||
selector: '#payment_method_ppcp-axo-gateway',
|
||||
});
|
||||
|
||||
this.gatewayDescription = new DomElement({
|
||||
selector: '.payment_box.payment_method_ppcp-axo-gateway',
|
||||
});
|
||||
|
||||
this.defaultSubmitButton = new DomElement({
|
||||
selector: '#place_order',
|
||||
});
|
||||
|
@ -20,7 +24,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({
|
||||
|
@ -56,6 +60,23 @@ class DomElementCollection {
|
|||
selector: '#billing_email_field'
|
||||
});
|
||||
|
||||
this.billingEmailFieldWrapper = new DomElement({
|
||||
id: 'ppcp-axo-billing-email-field-wrapper',
|
||||
selector: '#ppcp-axo-billing-email-field-wrapper',
|
||||
});
|
||||
|
||||
this.billingEmailSubmitButton = new DomElement({
|
||||
id: 'ppcp-axo-billing-email-submit-button',
|
||||
selector: '#ppcp-axo-billing-email-submit-button',
|
||||
className: 'ppcp-axo-billing-email-submit-button-hidden button alt wp-element-button wc-block-components-button'
|
||||
});
|
||||
|
||||
this.billingEmailSubmitButtonSpinner = new DomElement({
|
||||
id: 'ppcp-axo-billing-email-submit-button-spinner',
|
||||
selector: '#ppcp-axo-billing-email-submit-button-spinner',
|
||||
className: 'loader ppcp-axo-overlay'
|
||||
});
|
||||
|
||||
this.submitButtonContainer = new DomElement({
|
||||
selector: '#ppcp-axo-submit-button-container',
|
||||
});
|
||||
|
|
|
@ -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' )
|
||||
);
|
||||
},
|
||||
|
||||
|
@ -144,6 +148,11 @@ return array(
|
|||
'woocommerce_paypal_payments_axo_supported_country_currency_matrix',
|
||||
array(
|
||||
'US' => array(
|
||||
'AUD',
|
||||
'CAD',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'JPY',
|
||||
'USD',
|
||||
),
|
||||
)
|
||||
|
@ -191,6 +200,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,22 @@ 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() ),
|
||||
),
|
||||
),
|
||||
'billing_email_button_text' => __( 'Continue', 'woocommerce-paypal-payments' ),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -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',
|
||||
|
@ -74,6 +74,10 @@ class AxoModule implements ModuleInterface {
|
|||
return $methods;
|
||||
}
|
||||
|
||||
if ( $this->is_excluded_endpoint() ) {
|
||||
return $methods;
|
||||
}
|
||||
|
||||
$methods[] = $gateway;
|
||||
return $methods;
|
||||
},
|
||||
|
@ -108,9 +112,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 +149,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 +247,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 );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -290,6 +332,60 @@ class AxoModule implements ModuleInterface {
|
|||
|
||||
return ! is_user_logged_in()
|
||||
&& CartCheckoutDetector::has_classic_checkout()
|
||||
&& $is_axo_enabled;
|
||||
&& $is_axo_enabled
|
||||
&& ! $this->is_excluded_endpoint();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Condition to evaluate if the current endpoint is excluded.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function is_excluded_endpoint(): bool {
|
||||
// Exclude the Order Pay endpoint.
|
||||
return is_wc_endpoint_url( 'order-pay' );
|
||||
}
|
||||
}
|
||||
|
|
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;
|
||||
}
|
||||
}
|
|
@ -168,7 +168,7 @@ class AxoGateway extends WC_Payment_Gateway {
|
|||
? $this->ppcp_settings->get( 'axo_gateway_title' )
|
||||
: $this->get_option( 'title', $this->method_title );
|
||||
|
||||
$this->description = $this->get_option( 'description', '' );
|
||||
$this->description = __( 'Enter your email address to continue.', 'woocommerce-paypal-payments' );
|
||||
|
||||
$this->init_form_fields();
|
||||
$this->init_settings();
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
"Edge >= 14"
|
||||
],
|
||||
"dependencies": {
|
||||
"@paypal/react-paypal-js": "^8.2.0",
|
||||
"@paypal/react-paypal-js": "^8.3.0",
|
||||
"core-js": "^3.25.0",
|
||||
"react": "^17.0.0",
|
||||
"react-dom": "^17.0.0"
|
||||
|
|
87
modules/ppcp-blocks/resources/js/Components/card-fields.js
Normal file
87
modules/ppcp-blocks/resources/js/Components/card-fields.js
Normal file
|
@ -0,0 +1,87 @@
|
|||
import {useEffect, useState} from '@wordpress/element';
|
||||
|
||||
import {
|
||||
PayPalScriptProvider,
|
||||
PayPalCardFieldsProvider,
|
||||
PayPalCardFieldsForm,
|
||||
} from "@paypal/react-paypal-js";
|
||||
|
||||
import {CheckoutHandler} from "./checkout-handler";
|
||||
import {createOrder, onApprove} from "../card-fields-config";
|
||||
import {cartHasSubscriptionProducts} from "../Helper/Subscription";
|
||||
|
||||
export function CardFields({config, eventRegistration, emitResponse, components}) {
|
||||
const {onPaymentSetup} = eventRegistration;
|
||||
const {responseTypes} = emitResponse;
|
||||
const { PaymentMethodIcons } = components;
|
||||
|
||||
const [cardFieldsForm, setCardFieldsForm] = useState();
|
||||
const getCardFieldsForm = (cardFieldsForm) => {
|
||||
setCardFieldsForm(cardFieldsForm)
|
||||
}
|
||||
|
||||
const getSavePayment = (savePayment) => {
|
||||
localStorage.setItem('ppcp-save-card-payment', savePayment);
|
||||
}
|
||||
|
||||
const hasSubscriptionProducts = cartHasSubscriptionProducts(config.scriptData);
|
||||
useEffect(() => {
|
||||
localStorage.removeItem('ppcp-save-card-payment');
|
||||
|
||||
if(hasSubscriptionProducts) {
|
||||
localStorage.setItem('ppcp-save-card-payment', 'true');
|
||||
}
|
||||
|
||||
}, [hasSubscriptionProducts])
|
||||
|
||||
useEffect(
|
||||
() =>
|
||||
onPaymentSetup(() => {
|
||||
async function handlePaymentProcessing() {
|
||||
await cardFieldsForm.submit()
|
||||
.catch((error) => {
|
||||
return {
|
||||
type: responseTypes.ERROR,
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
type: responseTypes.SUCCESS,
|
||||
}
|
||||
}
|
||||
|
||||
return handlePaymentProcessing();
|
||||
}),
|
||||
[onPaymentSetup, cardFieldsForm]
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<PayPalScriptProvider
|
||||
options={{
|
||||
clientId: config.scriptData.client_id,
|
||||
components: "card-fields",
|
||||
dataNamespace: 'ppcp-block-card-fields',
|
||||
}}
|
||||
>
|
||||
<PayPalCardFieldsProvider
|
||||
createOrder={createOrder}
|
||||
onApprove={onApprove}
|
||||
onError={(err) => {
|
||||
console.error(err);
|
||||
}}
|
||||
>
|
||||
<PayPalCardFieldsForm/>
|
||||
<PaymentMethodIcons icons={config.card_icons} align="left" />
|
||||
<CheckoutHandler
|
||||
getCardFieldsForm={getCardFieldsForm}
|
||||
getSavePayment={getSavePayment}
|
||||
hasSubscriptionProducts={hasSubscriptionProducts}
|
||||
saveCardText={config.save_card_text}
|
||||
is_vaulting_enabled={config.is_vaulting_enabled}
|
||||
/>
|
||||
</PayPalCardFieldsProvider>
|
||||
</PayPalScriptProvider>
|
||||
</>
|
||||
)
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
import {useEffect} from '@wordpress/element';
|
||||
import {usePayPalCardFields} from "@paypal/react-paypal-js";
|
||||
|
||||
export const CheckoutHandler = ({getCardFieldsForm, getSavePayment, hasSubscriptionProducts, saveCardText, is_vaulting_enabled}) => {
|
||||
const {cardFieldsForm} = usePayPalCardFields();
|
||||
|
||||
useEffect(() => {
|
||||
getCardFieldsForm(cardFieldsForm)
|
||||
}, []);
|
||||
|
||||
if (!is_vaulting_enabled) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<input
|
||||
type="checkbox"
|
||||
id="save"
|
||||
name="save"
|
||||
onChange={(e) => getSavePayment(e.target.checked)}
|
||||
defaultChecked={hasSubscriptionProducts}
|
||||
disabled={hasSubscriptionProducts}
|
||||
/>
|
||||
<label htmlFor="save">{saveCardText}</label>
|
||||
</>
|
||||
)
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
import { registerPaymentMethod } from '@woocommerce/blocks-registry';
|
||||
import {CardFields} from "./Components/card-fields";
|
||||
|
||||
const config = wc.wcSettings.getSetting('ppcp-credit-card-gateway_data');
|
||||
|
||||
registerPaymentMethod({
|
||||
name: config.id,
|
||||
label: <div dangerouslySetInnerHTML={{__html: config.title}}/>,
|
||||
content: <CardFields config={config}/>,
|
||||
edit: <div></div>,
|
||||
ariaLabel: config.title,
|
||||
canMakePayment: () => {return true},
|
||||
supports: {
|
||||
showSavedCards: true,
|
||||
features: config.supports
|
||||
}
|
||||
})
|
43
modules/ppcp-blocks/resources/js/card-fields-config.js
Normal file
43
modules/ppcp-blocks/resources/js/card-fields-config.js
Normal file
|
@ -0,0 +1,43 @@
|
|||
const config = wc.wcSettings.getSetting('ppcp-credit-card-gateway_data');
|
||||
|
||||
export async function createOrder() {
|
||||
return fetch(config.scriptData.ajax.create_order.endpoint, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
nonce: config.scriptData.ajax.create_order.nonce,
|
||||
context: config.scriptData.context,
|
||||
payment_method: 'ppcp-credit-card-gateway',
|
||||
save_payment_method: localStorage.getItem('ppcp-save-card-payment') === 'true',
|
||||
}),
|
||||
})
|
||||
.then((response) => response.json())
|
||||
.then((order) => {
|
||||
return order.data.id;
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
});
|
||||
}
|
||||
|
||||
export async function onApprove(data) {
|
||||
return fetch(config.scriptData.ajax.approve_order.endpoint, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
order_id: data.orderID,
|
||||
nonce: config.scriptData.ajax.approve_order.nonce,
|
||||
}),
|
||||
})
|
||||
.then((response) => response.json())
|
||||
.then((data) => {
|
||||
localStorage.removeItem('ppcp-save-card-payment');
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
});
|
||||
}
|
|
@ -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;
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -568,7 +606,7 @@ if(cartHasSubscriptionProducts(config.scriptData)) {
|
|||
features.push('subscriptions');
|
||||
}
|
||||
|
||||
if (block_enabled) {
|
||||
if (block_enabled && config.enabled) {
|
||||
if ((config.addPlaceOrderMethod || config.usePlaceOrder) && !config.scriptData.continuation) {
|
||||
let descriptionElement = <div dangerouslySetInnerHTML={{__html: config.description}}></div>;
|
||||
if (config.placeOrderButtonDescription) {
|
||||
|
|
|
@ -9,6 +9,7 @@ declare(strict_types=1);
|
|||
|
||||
namespace WooCommerce\PayPalCommerce\Blocks;
|
||||
|
||||
use WooCommerce\PayPalCommerce\Blocks\Endpoint\GetPayPalOrderFromSession;
|
||||
use WooCommerce\PayPalCommerce\Blocks\Endpoint\UpdateShippingEndpoint;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
|
||||
use WooCommerce\PayPalCommerce\Button\Assets\SmartButtonInterface;
|
||||
|
@ -45,6 +46,17 @@ return array(
|
|||
$container->get( 'wcgateway.all-funding-sources' )
|
||||
);
|
||||
},
|
||||
'blocks.advanced-card-method' => static function( ContainerInterface $container ): AdvancedCardPaymentMethod {
|
||||
return new AdvancedCardPaymentMethod(
|
||||
$container->get( 'blocks.url' ),
|
||||
$container->get( 'ppcp.asset-version' ),
|
||||
$container->get( 'wcgateway.credit-card-gateway' ),
|
||||
function () use ( $container ): SmartButtonInterface {
|
||||
return $container->get( 'button.smart-button' );
|
||||
},
|
||||
$container->get( 'wcgateway.settings' )
|
||||
);
|
||||
},
|
||||
'blocks.settings.final_review_enabled' => static function ( ContainerInterface $container ): bool {
|
||||
$settings = $container->get( 'wcgateway.settings' );
|
||||
assert( $settings instanceof ContainerInterface );
|
||||
|
|
142
modules/ppcp-blocks/src/AdvancedCardPaymentMethod.php
Normal file
142
modules/ppcp-blocks/src/AdvancedCardPaymentMethod.php
Normal file
|
@ -0,0 +1,142 @@
|
|||
<?php
|
||||
/**
|
||||
* Advanced card payment method.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\Blocks
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\Blocks;
|
||||
|
||||
use Automattic\WooCommerce\Blocks\Payments\Integrations\AbstractPaymentMethodType;
|
||||
use WooCommerce\PayPalCommerce\Button\Assets\SmartButtonInterface;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\CreditCardGateway;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
|
||||
|
||||
/**
|
||||
* Class AdvancedCardPaymentMethod
|
||||
*/
|
||||
class AdvancedCardPaymentMethod extends AbstractPaymentMethodType {
|
||||
|
||||
/**
|
||||
* The URL of this module.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $module_url;
|
||||
|
||||
/**
|
||||
* The assets version.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $version;
|
||||
|
||||
/**
|
||||
* Credit card gateway.
|
||||
*
|
||||
* @var CreditCardGateway
|
||||
*/
|
||||
private $gateway;
|
||||
|
||||
/**
|
||||
* The smart button script loading handler.
|
||||
*
|
||||
* @var SmartButtonInterface|callable
|
||||
*/
|
||||
private $smart_button;
|
||||
|
||||
/**
|
||||
* The settings.
|
||||
*
|
||||
* @var Settings
|
||||
*/
|
||||
protected $settings;
|
||||
|
||||
/**
|
||||
* AdvancedCardPaymentMethod constructor.
|
||||
*
|
||||
* @param string $module_url The URL of this module.
|
||||
* @param string $version The assets version.
|
||||
* @param CreditCardGateway $gateway Credit card gateway.
|
||||
* @param SmartButtonInterface|callable $smart_button The smart button script loading handler.
|
||||
* @param Settings $settings The settings.
|
||||
*/
|
||||
public function __construct(
|
||||
string $module_url,
|
||||
string $version,
|
||||
CreditCardGateway $gateway,
|
||||
$smart_button,
|
||||
Settings $settings
|
||||
) {
|
||||
$this->name = CreditCardGateway::ID;
|
||||
$this->module_url = $module_url;
|
||||
$this->version = $version;
|
||||
$this->gateway = $gateway;
|
||||
$this->smart_button = $smart_button;
|
||||
$this->settings = $settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function initialize() {}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function is_active() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function get_payment_method_script_handles() {
|
||||
wp_register_script(
|
||||
'ppcp-advanced-card-checkout-block',
|
||||
trailingslashit( $this->module_url ) . 'assets/js/advanced-card-checkout-block.js',
|
||||
array(),
|
||||
$this->version,
|
||||
true
|
||||
);
|
||||
|
||||
return array( 'ppcp-advanced-card-checkout-block' );
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function get_payment_method_data() {
|
||||
$script_data = $this->smart_button_instance()->script_data();
|
||||
|
||||
return array(
|
||||
'id' => $this->name,
|
||||
'title' => $this->gateway->title,
|
||||
'description' => $this->gateway->description,
|
||||
'scriptData' => $script_data,
|
||||
'supports' => $this->gateway->supports,
|
||||
'save_card_text' => esc_html__( 'Save your card', 'woocommerce-paypal-payments' ),
|
||||
'is_vaulting_enabled' => $this->settings->has( 'vault_enabled_dcc' ) && $this->settings->get( 'vault_enabled_dcc' ),
|
||||
'card_icons' => $this->settings->has( 'card_icons' ) ? (array) $this->settings->get( 'card_icons' ) : array(),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* The smart button.
|
||||
*
|
||||
* @return SmartButtonInterface
|
||||
*/
|
||||
private function smart_button_instance(): SmartButtonInterface {
|
||||
if ( $this->smart_button instanceof SmartButtonInterface ) {
|
||||
return $this->smart_button;
|
||||
}
|
||||
|
||||
if ( is_callable( $this->smart_button ) ) {
|
||||
$this->smart_button = ( $this->smart_button )();
|
||||
}
|
||||
|
||||
return $this->smart_button;
|
||||
}
|
||||
}
|
|
@ -61,6 +61,7 @@ class BlocksModule implements ModuleInterface {
|
|||
'woocommerce_blocks_payment_method_type_registration',
|
||||
function( PaymentMethodRegistry $payment_method_registry ) use ( $c ): void {
|
||||
$payment_method_registry->register( $c->get( 'blocks.method' ) );
|
||||
$payment_method_registry->register( $c->get( 'blocks.advanced-card-method' ) );
|
||||
}
|
||||
);
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ module.exports = {
|
|||
plugins: [ new DependencyExtractionWebpackPlugin() ],
|
||||
entry: {
|
||||
'checkout-block': path.resolve('./resources/js/checkout-block.js'),
|
||||
'advanced-card-checkout-block': path.resolve('./resources/js/advanced-card-checkout-block.js'),
|
||||
"gateway": path.resolve('./resources/css/gateway.scss')
|
||||
},
|
||||
output: {
|
||||
|
|
|
@ -1005,19 +1005,19 @@
|
|||
"@jridgewell/resolve-uri" "3.1.0"
|
||||
"@jridgewell/sourcemap-codec" "1.4.14"
|
||||
|
||||
"@paypal/paypal-js@^8.0.4":
|
||||
version "8.0.4"
|
||||
resolved "https://registry.yarnpkg.com/@paypal/paypal-js/-/paypal-js-8.0.4.tgz#abe9f40f519b1d2c306adddfbe733be03eb26ce5"
|
||||
integrity sha512-91g5fhRBHGEBoikDzQT6uBn3PzlJQ75g0c3MvqVJqN0XRm5kHa9wz+6+Uaq8QQuxRzz5C2x55Zg057CW6EuwpQ==
|
||||
"@paypal/paypal-js@^8.0.5":
|
||||
version "8.0.5"
|
||||
resolved "https://registry.yarnpkg.com/@paypal/paypal-js/-/paypal-js-8.0.5.tgz#77bc461b4d1e5a2c6f081269e3ef0b2e3331a68c"
|
||||
integrity sha512-yQNV7rOILeaVCNU4aVDRPqEnbIlzfxgQfFsxzsBuZW1ouqRD/4kYBWJDzczCiscSr2xOeA/Pkm7e3a9fRfnuMQ==
|
||||
dependencies:
|
||||
promise-polyfill "^8.3.0"
|
||||
|
||||
"@paypal/react-paypal-js@^8.2.0":
|
||||
version "8.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@paypal/react-paypal-js/-/react-paypal-js-8.2.0.tgz#4b1a142bbb68e62dca4a92da4a6b5568f54901f0"
|
||||
integrity sha512-SworUfu0BNNcqoh0O53Ke4MFpx2m3qJRu3hayXvlluEEXJpKqGSV5aaSGFhbsZqi8hnbsx/hZR7BQbmqsggiGQ==
|
||||
"@paypal/react-paypal-js@^8.3.0":
|
||||
version "8.3.0"
|
||||
resolved "https://registry.yarnpkg.com/@paypal/react-paypal-js/-/react-paypal-js-8.3.0.tgz#a103080b752766b8ff59b8620887abf802e1a01b"
|
||||
integrity sha512-SX17d2h1CMNFGI+wtjb329AEDaBR8Ziy2LCV076eDcY1Q0MFKRkfQ/v0HOAvZtk3sJoydRmYez2pq47BRblwqQ==
|
||||
dependencies:
|
||||
"@paypal/paypal-js" "^8.0.4"
|
||||
"@paypal/paypal-js" "^8.0.5"
|
||||
"@paypal/sdk-constants" "^1.0.122"
|
||||
|
||||
"@paypal/sdk-constants@^1.0.122":
|
||||
|
|
|
@ -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' )
|
||||
);
|
||||
},
|
||||
);
|
||||
|
|
|
@ -1421,7 +1421,7 @@ document.querySelector("#payment").before(document.querySelector(".ppcp-messages
|
|||
$disable_funding,
|
||||
array_diff(
|
||||
array_keys( $this->all_funding_sources ),
|
||||
array( 'venmo', 'paylater', 'paypal' )
|
||||
array( 'venmo', 'paylater', 'paypal', 'card' )
|
||||
)
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -95,47 +95,51 @@ trait ContextTrait {
|
|||
* @return string
|
||||
*/
|
||||
protected function context(): string {
|
||||
if ( is_product() || wc_post_content_has_shortcode( 'product_page' ) ) {
|
||||
// Default context.
|
||||
$context = 'mini-cart';
|
||||
|
||||
// Do this check here instead of reordering outside conditions.
|
||||
// In order to have more control over the context.
|
||||
if ( $this->is_checkout() && ! $this->is_paypal_continuation() ) {
|
||||
return 'checkout';
|
||||
}
|
||||
switch ( true ) {
|
||||
case is_product() || wc_post_content_has_shortcode( 'product_page' ):
|
||||
// Do this check here instead of reordering outside conditions.
|
||||
// In order to have more control over the context.
|
||||
if ( $this->is_checkout() && ! $this->is_paypal_continuation() ) {
|
||||
$context = 'checkout';
|
||||
} else {
|
||||
$context = 'product';
|
||||
}
|
||||
break;
|
||||
|
||||
return 'product';
|
||||
// has_block may not work if called too early, such as during the block registration.
|
||||
case has_block( 'woocommerce/cart' ):
|
||||
$context = 'cart-block';
|
||||
break;
|
||||
|
||||
case $this->is_cart():
|
||||
$context = 'cart';
|
||||
break;
|
||||
|
||||
case is_checkout_pay_page():
|
||||
$context = 'pay-now';
|
||||
break;
|
||||
|
||||
case has_block( 'woocommerce/checkout' ):
|
||||
$context = 'checkout-block';
|
||||
break;
|
||||
|
||||
case $this->is_checkout() && ! $this->is_paypal_continuation():
|
||||
$context = 'checkout';
|
||||
break;
|
||||
|
||||
case $this->is_add_payment_method_page():
|
||||
$context = 'add-payment-method';
|
||||
break;
|
||||
|
||||
case $this->is_block_editor():
|
||||
$context = 'block-editor';
|
||||
break;
|
||||
}
|
||||
|
||||
// has_block may not work if called too early, such as during the block registration.
|
||||
if ( has_block( 'woocommerce/cart' ) ) {
|
||||
return 'cart-block';
|
||||
}
|
||||
|
||||
if ( $this->is_cart() ) {
|
||||
return 'cart';
|
||||
}
|
||||
|
||||
if ( is_checkout_pay_page() ) {
|
||||
return 'pay-now';
|
||||
}
|
||||
|
||||
if ( has_block( 'woocommerce/checkout' ) ) {
|
||||
return 'checkout-block';
|
||||
}
|
||||
|
||||
if ( $this->is_checkout() && ! $this->is_paypal_continuation() ) {
|
||||
return 'checkout';
|
||||
}
|
||||
|
||||
if ( $this->is_add_payment_method_page() ) {
|
||||
return 'add-payment-method';
|
||||
}
|
||||
|
||||
if ( $this->is_block_editor() ) {
|
||||
return 'block-editor';
|
||||
}
|
||||
|
||||
return 'mini-cart';
|
||||
return apply_filters( 'woocommerce_paypal_payments_context', $context );
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -105,9 +105,8 @@ class CardFieldsModule implements ModuleInterface {
|
|||
|
||||
add_filter(
|
||||
'ppcp_create_order_request_body_data',
|
||||
function( array $data ) use ( $c ): array {
|
||||
function( array $data, string $payment_method ) use ( $c ): array {
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Missing
|
||||
$payment_method = wc_clean( wp_unslash( $_POST['payment_method'] ?? '' ) );
|
||||
if ( $payment_method !== CreditCardGateway::ID ) {
|
||||
return $data;
|
||||
}
|
||||
|
@ -134,7 +133,9 @@ class CardFieldsModule implements ModuleInterface {
|
|||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
},
|
||||
10,
|
||||
2
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -54,6 +54,18 @@ return array(
|
|||
);
|
||||
},
|
||||
|
||||
'compat.plugin-script-file-names' => static function( ContainerInterface $container ) : array {
|
||||
return array(
|
||||
'button.js',
|
||||
'gateway-settings.js',
|
||||
'status-page.js',
|
||||
'order-edit-page.js',
|
||||
'fraudnet.js',
|
||||
'tracking-compat.js',
|
||||
'ppcp-clear-db.js',
|
||||
);
|
||||
},
|
||||
|
||||
'compat.gzd.is_supported_plugin_version_active' => function (): bool {
|
||||
return function_exists( 'wc_gzd_get_shipments_by_order' ); // 3.0+
|
||||
},
|
||||
|
|
|
@ -15,6 +15,7 @@ use WooCommerce\PayPalCommerce\Vendor\Interop\Container\ServiceProviderInterface
|
|||
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
|
||||
use WooCommerce\PayPalCommerce\Compat\Assets\CompatAssets;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Exception\NotFoundException;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Helper\CartCheckoutDetector;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
|
||||
|
||||
/**
|
||||
|
@ -41,7 +42,6 @@ class CompatModule implements ModuleInterface {
|
|||
public function run( ContainerInterface $c ): void {
|
||||
|
||||
$this->initialize_ppec_compat_layer( $c );
|
||||
$this->fix_site_ground_optimizer_compatibility( $c );
|
||||
$this->initialize_tracking_compat_layer( $c );
|
||||
|
||||
$asset_loader = $c->get( 'compat.assets' );
|
||||
|
@ -54,6 +54,8 @@ class CompatModule implements ModuleInterface {
|
|||
$this->migrate_smart_button_settings( $c );
|
||||
|
||||
$this->fix_page_builders();
|
||||
$this->exclude_cache_plugins_js_minification( $c );
|
||||
$this->set_elementor_checkout_context();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -88,24 +90,6 @@ class CompatModule implements ModuleInterface {
|
|||
}
|
||||
}
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Fixes the compatibility issue for <a href="https://wordpress.org/plugins/sg-cachepress/">SiteGround Optimizer plugin</a>.
|
||||
*
|
||||
* @link https://wordpress.org/plugins/sg-cachepress/
|
||||
*
|
||||
* @param ContainerInterface $c The Container.
|
||||
*/
|
||||
protected function fix_site_ground_optimizer_compatibility( ContainerInterface $c ): void {
|
||||
$ppcp_script_names = $c->get( 'compat.plugin-script-names' );
|
||||
add_filter(
|
||||
'sgo_js_minify_exclude',
|
||||
function ( array $scripts ) use ( $ppcp_script_names ) {
|
||||
return array_merge( $scripts, $ppcp_script_names );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -329,4 +313,69 @@ class CompatModule implements ModuleInterface {
|
|||
$parent = $theme->parent();
|
||||
return ( $parent && $parent->get( 'Name' ) === 'Divi' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the context for the Elementor checkout page.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function set_elementor_checkout_context(): void {
|
||||
add_action(
|
||||
'wp',
|
||||
function() {
|
||||
$page_id = get_the_ID();
|
||||
if ( ! $page_id || ! CartCheckoutDetector::has_elementor_checkout( $page_id ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
add_filter(
|
||||
'woocommerce_paypal_payments_context',
|
||||
function ( string $context ): string {
|
||||
// Default context.
|
||||
return ( 'mini-cart' === $context ) ? 'checkout' : $context;
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Excludes PayPal scripts from being minified by cache plugins.
|
||||
*
|
||||
* @param ContainerInterface $c The Container.
|
||||
* @return void
|
||||
*/
|
||||
protected function exclude_cache_plugins_js_minification( ContainerInterface $c ): void {
|
||||
$ppcp_script_names = $c->get( 'compat.plugin-script-names' );
|
||||
$ppcp_script_file_names = $c->get( 'compat.plugin-script-file-names' );
|
||||
|
||||
// Siteground SG Optimize.
|
||||
add_filter(
|
||||
'sgo_js_minify_exclude',
|
||||
function( array $scripts ) use ( $ppcp_script_names ) {
|
||||
return array_merge( $scripts, $ppcp_script_names );
|
||||
}
|
||||
);
|
||||
|
||||
// LiteSpeed Cache.
|
||||
add_filter(
|
||||
'litespeed_optimize_js_excludes',
|
||||
function( array $excluded_js ) use ( $ppcp_script_file_names ) {
|
||||
return array_merge( $excluded_js, $ppcp_script_file_names );
|
||||
}
|
||||
);
|
||||
|
||||
// W3 Total Cache.
|
||||
add_filter(
|
||||
'w3tc_minify_js_do_tag_minification',
|
||||
function( bool $do_tag_minification, string $script_tag, string $file ) {
|
||||
if ( $file && strpos( $file, 'ppcp' ) !== false ) {
|
||||
return false;
|
||||
}
|
||||
return $do_tag_minification;
|
||||
},
|
||||
10,
|
||||
3
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -926,7 +926,7 @@ return array(
|
|||
: $container->get( 'googlepay.enable-url-sandbox' );
|
||||
|
||||
$button_url = $enabled
|
||||
? admin_url( 'admin.php?page=wc-settings&tab=checkout§ion=ppcp-gateway#field-alternative_payment_methods' )
|
||||
? admin_url( 'admin.php?page=wc-settings&tab=checkout§ion=ppcp-gateway&ppcp-tab=ppcp-credit-card-gateway#ppcp-googlepay_button_enabled' )
|
||||
: $enable_url;
|
||||
|
||||
return sprintf(
|
||||
|
|
|
@ -73,34 +73,34 @@ class GermanizedShipmentIntegration implements Integration {
|
|||
add_action(
|
||||
'woocommerce_gzd_shipment_status_shipped',
|
||||
function( int $shipment_id, Shipment $shipment ) {
|
||||
if ( ! apply_filters( 'woocommerce_paypal_payments_sync_gzd_tracking', true ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$wc_order = $shipment->get_order();
|
||||
|
||||
if ( ! is_a( $wc_order, WC_Order::class ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$paypal_order = ppcp_get_paypal_order( $wc_order );
|
||||
$capture_id = $this->get_paypal_order_transaction_id( $paypal_order );
|
||||
$wc_order_id = $wc_order->get_id();
|
||||
$tracking_number = $shipment->get_tracking_id();
|
||||
$carrier = $shipment->get_shipping_provider();
|
||||
|
||||
$items = array_map(
|
||||
function ( ShipmentItem $item ): int {
|
||||
return $item->get_order_item_id();
|
||||
},
|
||||
$shipment->get_items()
|
||||
);
|
||||
|
||||
if ( ! $tracking_number || ! $carrier || ! $capture_id ) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if ( ! apply_filters( 'woocommerce_paypal_payments_sync_gzd_tracking', true ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$wc_order = $shipment->get_order();
|
||||
|
||||
if ( ! is_a( $wc_order, WC_Order::class ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$paypal_order = ppcp_get_paypal_order( $wc_order );
|
||||
$capture_id = $this->get_paypal_order_transaction_id( $paypal_order );
|
||||
$wc_order_id = $wc_order->get_id();
|
||||
$tracking_number = $shipment->get_tracking_id();
|
||||
$carrier = $shipment->get_shipping_provider();
|
||||
|
||||
$items = array_map(
|
||||
function ( ShipmentItem $item ): int {
|
||||
return $item->get_order_item_id();
|
||||
},
|
||||
$shipment->get_items()
|
||||
);
|
||||
|
||||
if ( ! $tracking_number || ! $carrier || ! $capture_id ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$ppcp_shipment = $this->shipment_factory->create_shipment(
|
||||
$wc_order_id,
|
||||
$capture_id,
|
||||
|
@ -118,7 +118,7 @@ class GermanizedShipmentIntegration implements Integration {
|
|||
: $this->endpoint->add_tracking_information( $ppcp_shipment, $wc_order_id );
|
||||
|
||||
} catch ( Exception $exception ) {
|
||||
$this->logger->error( "Couldn't sync tracking information: " . $exception->getMessage() );
|
||||
return;
|
||||
}
|
||||
},
|
||||
500,
|
||||
|
|
|
@ -76,25 +76,25 @@ class ShipStationIntegration implements Integration {
|
|||
* @psalm-suppress MissingClosureParamType
|
||||
*/
|
||||
function( $wc_order, array $data ) {
|
||||
if ( ! apply_filters( 'woocommerce_paypal_payments_sync_ship_station_tracking', true ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! is_a( $wc_order, WC_Order::class ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$paypal_order = ppcp_get_paypal_order( $wc_order );
|
||||
$capture_id = $this->get_paypal_order_transaction_id( $paypal_order );
|
||||
$order_id = $wc_order->get_id();
|
||||
$tracking_number = $data['tracking_number'] ?? '';
|
||||
$carrier = $data['carrier'] ?? '';
|
||||
|
||||
if ( ! $tracking_number || ! is_string( $tracking_number ) || ! $carrier || ! is_string( $carrier ) || ! $capture_id ) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if ( ! apply_filters( 'woocommerce_paypal_payments_sync_ship_station_tracking', true ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! is_a( $wc_order, WC_Order::class ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$paypal_order = ppcp_get_paypal_order( $wc_order );
|
||||
$capture_id = $this->get_paypal_order_transaction_id( $paypal_order );
|
||||
$order_id = $wc_order->get_id();
|
||||
$tracking_number = $data['tracking_number'] ?? '';
|
||||
$carrier = $data['carrier'] ?? '';
|
||||
|
||||
if ( ! $tracking_number || ! is_string( $tracking_number ) || ! $carrier || ! is_string( $carrier ) || ! $capture_id ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$ppcp_shipment = $this->shipment_factory->create_shipment(
|
||||
$order_id,
|
||||
$capture_id,
|
||||
|
@ -112,7 +112,7 @@ class ShipStationIntegration implements Integration {
|
|||
: $this->endpoint->add_tracking_information( $ppcp_shipment, $order_id );
|
||||
|
||||
} catch ( Exception $exception ) {
|
||||
$this->logger->error( "Couldn't sync tracking information: " . $exception->getMessage() );
|
||||
return;
|
||||
}
|
||||
},
|
||||
500,
|
||||
|
|
|
@ -73,30 +73,35 @@ class ShipmentTrackingIntegration implements Integration {
|
|||
add_action(
|
||||
'wp_ajax_wc_shipment_tracking_save_form',
|
||||
function() {
|
||||
check_ajax_referer( 'create-tracking-item', 'security', true );
|
||||
try {
|
||||
check_ajax_referer( 'create-tracking-item', 'security', true );
|
||||
|
||||
if ( ! apply_filters( 'woocommerce_paypal_payments_sync_wc_shipment_tracking', true ) ) {
|
||||
if ( ! apply_filters( 'woocommerce_paypal_payments_sync_wc_shipment_tracking', true ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$order_id = (int) wc_clean( wp_unslash( $_POST['order_id'] ?? '' ) );
|
||||
$wc_order = wc_get_order( $order_id );
|
||||
if ( ! is_a( $wc_order, WC_Order::class ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$paypal_order = ppcp_get_paypal_order( $wc_order );
|
||||
$capture_id = $this->get_paypal_order_transaction_id( $paypal_order );
|
||||
$tracking_number = wc_clean( wp_unslash( $_POST['tracking_number'] ?? '' ) );
|
||||
$carrier = wc_clean( wp_unslash( $_POST['tracking_provider'] ?? '' ) );
|
||||
$carrier_other = wc_clean( wp_unslash( $_POST['custom_tracking_provider'] ?? '' ) );
|
||||
$carrier = $carrier ?: $carrier_other ?: '';
|
||||
|
||||
if ( ! $tracking_number || ! is_string( $tracking_number ) || ! $carrier || ! is_string( $carrier ) || ! $capture_id ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->sync_tracking( $order_id, $capture_id, $tracking_number, $carrier );
|
||||
|
||||
} catch ( Exception $exception ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$order_id = (int) wc_clean( wp_unslash( $_POST['order_id'] ?? '' ) );
|
||||
$wc_order = wc_get_order( $order_id );
|
||||
if ( ! is_a( $wc_order, WC_Order::class ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$paypal_order = ppcp_get_paypal_order( $wc_order );
|
||||
$capture_id = $this->get_paypal_order_transaction_id( $paypal_order );
|
||||
$tracking_number = wc_clean( wp_unslash( $_POST['tracking_number'] ?? '' ) );
|
||||
$carrier = wc_clean( wp_unslash( $_POST['tracking_provider'] ?? '' ) );
|
||||
$carrier_other = wc_clean( wp_unslash( $_POST['custom_tracking_provider'] ?? '' ) );
|
||||
$carrier = $carrier ?: $carrier_other ?: '';
|
||||
|
||||
if ( ! $tracking_number || ! is_string( $tracking_number ) || ! $carrier || ! is_string( $carrier ) || ! $capture_id ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->sync_tracking( $order_id, $capture_id, $tracking_number, $carrier );
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -106,34 +111,39 @@ class ShipmentTrackingIntegration implements Integration {
|
|||
add_filter(
|
||||
'woocommerce_rest_prepare_order_shipment_tracking',
|
||||
function( WP_REST_Response $response, array $tracking_item, WP_REST_Request $request ): WP_REST_Response {
|
||||
if ( ! apply_filters( 'woocommerce_paypal_payments_sync_wc_shipment_tracking', true ) ) {
|
||||
try {
|
||||
if ( ! apply_filters( 'woocommerce_paypal_payments_sync_wc_shipment_tracking', true ) ) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
$callback = $request->get_attributes()['callback']['1'] ?? '';
|
||||
if ( $callback !== 'create_item' ) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
$order_id = $tracking_item['order_id'] ?? 0;
|
||||
$wc_order = wc_get_order( $order_id );
|
||||
if ( ! is_a( $wc_order, WC_Order::class ) ) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
$paypal_order = ppcp_get_paypal_order( $wc_order );
|
||||
$capture_id = $this->get_paypal_order_transaction_id( $paypal_order );
|
||||
$tracking_number = $tracking_item['tracking_number'] ?? '';
|
||||
$carrier = $tracking_item['tracking_provider'] ?? '';
|
||||
$carrier_other = $tracking_item['custom_tracking_provider'] ?? '';
|
||||
$carrier = $carrier ?: $carrier_other ?: '';
|
||||
|
||||
if ( ! $tracking_number || ! $carrier || ! $capture_id ) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
$this->sync_tracking( $order_id, $capture_id, $tracking_number, $carrier );
|
||||
|
||||
} catch ( Exception $exception ) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
$callback = $request->get_attributes()['callback']['1'] ?? '';
|
||||
if ( $callback !== 'create_item' ) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
$order_id = $tracking_item['order_id'] ?? 0;
|
||||
$wc_order = wc_get_order( $order_id );
|
||||
if ( ! is_a( $wc_order, WC_Order::class ) ) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
$paypal_order = ppcp_get_paypal_order( $wc_order );
|
||||
$capture_id = $this->get_paypal_order_transaction_id( $paypal_order );
|
||||
$tracking_number = $tracking_item['tracking_number'] ?? '';
|
||||
$carrier = $tracking_item['tracking_provider'] ?? '';
|
||||
$carrier_other = $tracking_item['custom_tracking_provider'] ?? '';
|
||||
$carrier = $carrier ?: $carrier_other ?: '';
|
||||
|
||||
if ( ! $tracking_number || ! $carrier || ! $capture_id ) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
$this->sync_tracking( $order_id, $capture_id, $tracking_number, $carrier );
|
||||
|
||||
return $response;
|
||||
},
|
||||
10,
|
||||
|
|
|
@ -75,44 +75,48 @@ class WcShippingTaxIntegration implements Integration {
|
|||
add_filter(
|
||||
'rest_post_dispatch',
|
||||
function( WP_HTTP_Response $response, WP_REST_Server $server, WP_REST_Request $request ): WP_HTTP_Response {
|
||||
if ( ! apply_filters( 'woocommerce_paypal_payments_sync_wc_shipping_tax', true ) ) {
|
||||
try {
|
||||
if ( ! apply_filters( 'woocommerce_paypal_payments_sync_wc_shipping_tax', true ) ) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
$params = $request->get_params();
|
||||
$order_id = (int) ( $params['order_id'] ?? 0 );
|
||||
$label_id = (int) ( $params['label_ids'] ?? 0 );
|
||||
|
||||
if ( ! $order_id || "/wc/v1/connect/label/{$order_id}/{$label_id}" !== $request->get_route() ) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
$data = $response->get_data() ?? array();
|
||||
$labels = $data['labels'] ?? array();
|
||||
|
||||
foreach ( $labels as $label ) {
|
||||
$tracking_number = $label['tracking'] ?? '';
|
||||
if ( ! $tracking_number ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$wc_order = wc_get_order( $order_id );
|
||||
if ( ! is_a( $wc_order, WC_Order::class ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$paypal_order = ppcp_get_paypal_order( $wc_order );
|
||||
$capture_id = $this->get_paypal_order_transaction_id( $paypal_order );
|
||||
$carrier = $label['carrier_id'] ?? $label['service_name'] ?? '';
|
||||
$items = array_map( 'intval', $label['product_ids'] ?? array() );
|
||||
|
||||
if ( ! $carrier || ! $capture_id ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->sync_tracking( $order_id, $capture_id, $tracking_number, $carrier, $items );
|
||||
}
|
||||
} catch ( Exception $exception ) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
$params = $request->get_params();
|
||||
$order_id = (int) ( $params['order_id'] ?? 0 );
|
||||
$label_id = (int) ( $params['label_ids'] ?? 0 );
|
||||
|
||||
if ( ! $order_id || "/wc/v1/connect/label/{$order_id}/{$label_id}" !== $request->get_route() ) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
$data = $response->get_data() ?? array();
|
||||
$labels = $data['labels'] ?? array();
|
||||
|
||||
foreach ( $labels as $label ) {
|
||||
$tracking_number = $label['tracking'] ?? '';
|
||||
if ( ! $tracking_number ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$wc_order = wc_get_order( $order_id );
|
||||
if ( ! is_a( $wc_order, WC_Order::class ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$paypal_order = ppcp_get_paypal_order( $wc_order );
|
||||
$capture_id = $this->get_paypal_order_transaction_id( $paypal_order );
|
||||
$carrier = $label['carrier_id'] ?? $label['service_name'] ?? '';
|
||||
$items = array_map( 'intval', $label['product_ids'] ?? array() );
|
||||
|
||||
if ( ! $carrier || ! $capture_id ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->sync_tracking( $order_id, $capture_id, $tracking_number, $carrier, $items );
|
||||
}
|
||||
|
||||
return $response;
|
||||
},
|
||||
10,
|
||||
|
|
|
@ -71,27 +71,27 @@ class YithShipmentIntegration implements Integration {
|
|||
add_action(
|
||||
'woocommerce_process_shop_order_meta',
|
||||
function( int $order_id ) {
|
||||
if ( ! apply_filters( 'woocommerce_paypal_payments_sync_ywot_tracking', true ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$wc_order = wc_get_order( $order_id );
|
||||
if ( ! is_a( $wc_order, WC_Order::class ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$paypal_order = ppcp_get_paypal_order( $wc_order );
|
||||
$capture_id = $this->get_paypal_order_transaction_id( $paypal_order );
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Missing
|
||||
$tracking_number = wc_clean( wp_unslash( $_POST['ywot_tracking_code'] ?? '' ) );
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Missing
|
||||
$carrier = wc_clean( wp_unslash( $_POST['ywot_carrier_name'] ?? '' ) );
|
||||
|
||||
if ( ! $tracking_number || ! is_string( $tracking_number ) || ! $carrier || ! is_string( $carrier ) || ! $capture_id ) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if ( ! apply_filters( 'woocommerce_paypal_payments_sync_ywot_tracking', true ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$wc_order = wc_get_order( $order_id );
|
||||
if ( ! is_a( $wc_order, WC_Order::class ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$paypal_order = ppcp_get_paypal_order( $wc_order );
|
||||
$capture_id = $this->get_paypal_order_transaction_id( $paypal_order );
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Missing
|
||||
$tracking_number = wc_clean( wp_unslash( $_POST['ywot_tracking_code'] ?? '' ) );
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Missing
|
||||
$carrier = wc_clean( wp_unslash( $_POST['ywot_carrier_name'] ?? '' ) );
|
||||
|
||||
if ( ! $tracking_number || ! is_string( $tracking_number ) || ! $carrier || ! is_string( $carrier ) || ! $capture_id ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$ppcp_shipment = $this->shipment_factory->create_shipment(
|
||||
$order_id,
|
||||
$capture_id,
|
||||
|
@ -109,7 +109,7 @@ class YithShipmentIntegration implements Integration {
|
|||
: $this->endpoint->add_tracking_information( $ppcp_shipment, $order_id );
|
||||
|
||||
} catch ( Exception $exception ) {
|
||||
$this->logger->error( "Couldn't sync tracking information: " . $exception->getMessage() );
|
||||
return;
|
||||
}
|
||||
},
|
||||
500,
|
||||
|
|
|
@ -17,12 +17,14 @@ use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
|
|||
return array(
|
||||
'paylater-configurator.url' => static function ( ContainerInterface $container ): string {
|
||||
/**
|
||||
* The return value must not contain a trailing slash.
|
||||
*
|
||||
* Cannot return false for this path.
|
||||
*
|
||||
* @psalm-suppress PossiblyFalseArgument
|
||||
*/
|
||||
return plugins_url(
|
||||
'/modules/ppcp-paylater-configurator/',
|
||||
'/modules/ppcp-paylater-configurator',
|
||||
dirname( realpath( __FILE__ ), 3 ) . '/woocommerce-paypal-payments.php'
|
||||
);
|
||||
},
|
||||
|
|
|
@ -38,6 +38,9 @@ document.addEventListener(
|
|||
|
||||
const subscriptionTrial = document.querySelector('._subscription_trial_length_field');
|
||||
subscriptionTrial.style.display = 'none';
|
||||
|
||||
const soldIndividually = document.querySelector( '#_sold_individually' );
|
||||
soldIndividually.setAttribute( 'disabled', 'disabled' );
|
||||
}
|
||||
|
||||
const setupProducts = () => {
|
||||
|
|
|
@ -87,6 +87,44 @@ class PayPalSubscriptionsModule implements ModuleInterface {
|
|||
12
|
||||
);
|
||||
|
||||
add_filter(
|
||||
'woocommerce_add_to_cart_validation',
|
||||
/**
|
||||
* Param types removed to avoid third-party issues.
|
||||
*
|
||||
* @psalm-suppress MissingClosureParamType
|
||||
*/
|
||||
static function ( $passed_validation, $product_id ) {
|
||||
if ( WC()->cart->is_empty() ) {
|
||||
return $passed_validation;
|
||||
}
|
||||
|
||||
$product = wc_get_product( $product_id );
|
||||
|
||||
if ( $product && $product->get_meta( '_ppcp_enable_subscription_product', true ) === 'yes' ) {
|
||||
if ( ! $product->get_sold_individually() ) {
|
||||
$product->set_sold_individually( true );
|
||||
$product->save();
|
||||
}
|
||||
|
||||
wc_add_notice( __( 'You cannot add a subscription product to a cart with other items.', 'woocommerce-paypal-payments' ), 'error' );
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
|
||||
$cart_product = wc_get_product( $cart_item['product_id'] );
|
||||
if ( $cart_product && $cart_product->get_meta( '_ppcp_enable_subscription_product', true ) === 'yes' ) {
|
||||
wc_add_notice( __( 'You can only have one subscription product in your cart.', 'woocommerce-paypal-payments' ), 'error' );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return $passed_validation;
|
||||
},
|
||||
10,
|
||||
2
|
||||
);
|
||||
|
||||
add_action(
|
||||
'woocommerce_save_product_variation',
|
||||
/**
|
||||
|
@ -654,12 +692,18 @@ class PayPalSubscriptionsModule implements ModuleInterface {
|
|||
// phpcs:ignore WordPress.Security.NonceVerification
|
||||
$enable_subscription_product = wc_clean( wp_unslash( $_POST['_ppcp_enable_subscription_product'] ?? '' ) );
|
||||
$product->update_meta_data( '_ppcp_enable_subscription_product', $enable_subscription_product );
|
||||
|
||||
if ( ! $product->get_sold_individually() ) {
|
||||
$product->set_sold_individually( true );
|
||||
}
|
||||
|
||||
$product->save();
|
||||
|
||||
if ( ( $product->get_type() === 'subscription' || $product->get_type() === 'subscription_variation' ) && $enable_subscription_product === 'yes' ) {
|
||||
if ( $product->meta_exists( 'ppcp_subscription_product' ) && $product->meta_exists( 'ppcp_subscription_plan' ) ) {
|
||||
$subscriptions_api_handler->update_product( $product );
|
||||
$subscriptions_api_handler->update_plan( $product );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -347,7 +347,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
|
||||
);
|
||||
},
|
||||
|
|
|
@ -12,6 +12,7 @@ namespace WooCommerce\PayPalCommerce\WcGateway\Endpoint;
|
|||
use Psr\Log\LoggerInterface;
|
||||
use RuntimeException;
|
||||
use stdClass;
|
||||
use WC_Order;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Authentication\Bearer;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\OrderEndpoint;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\RequestTrait;
|
||||
|
@ -131,16 +132,25 @@ class CaptureCardPayment {
|
|||
/**
|
||||
* Creates PayPal order from the given card vault id.
|
||||
*
|
||||
* @param string $vault_id Vault id.
|
||||
* @param string $custom_id Custom id.
|
||||
* @param string $invoice_id Invoice id.
|
||||
* @param string $vault_id Vault id.
|
||||
* @param string $custom_id Custom id.
|
||||
* @param string $invoice_id Invoice id.
|
||||
* @param WC_Order $wc_order The WC order.
|
||||
* @return stdClass
|
||||
* @throws RuntimeException When request fails.
|
||||
*/
|
||||
public function create_order( string $vault_id, string $custom_id, string $invoice_id ): stdClass {
|
||||
public function create_order( string $vault_id, string $custom_id, string $invoice_id, WC_Order $wc_order ): stdClass {
|
||||
$intent = $this->settings->has( 'intent' ) && strtoupper( (string) $this->settings->get( 'intent' ) ) === 'AUTHORIZE' ? 'AUTHORIZE' : 'CAPTURE';
|
||||
$items = array( $this->purchase_unit_factory->from_wc_cart() );
|
||||
|
||||
// phpcs:disable WordPress.Security.NonceVerification
|
||||
$pay_for_order = wc_clean( wp_unslash( $_GET['pay_for_order'] ?? '' ) );
|
||||
$order_key = wc_clean( wp_unslash( $_GET['key'] ?? '' ) );
|
||||
// phpcs:enable
|
||||
if ( $pay_for_order && $order_key === $wc_order->get_order_key() ) {
|
||||
$items = array( $this->purchase_unit_factory->from_wc_order( $wc_order ) );
|
||||
}
|
||||
|
||||
$data = array(
|
||||
'intent' => $intent,
|
||||
'purchase_units' => array_map(
|
||||
|
|
|
@ -490,7 +490,7 @@ class CreditCardGateway extends \WC_Payment_Gateway_CC {
|
|||
|
||||
$custom_id = $wc_order->get_order_number();
|
||||
$invoice_id = $this->prefix . $wc_order->get_order_number();
|
||||
$create_order = $this->capture_card_payment->create_order( $token->get_token(), $custom_id, $invoice_id );
|
||||
$create_order = $this->capture_card_payment->create_order( $token->get_token(), $custom_id, $invoice_id, $wc_order );
|
||||
|
||||
$order = $this->order_endpoint->order( $create_order->id );
|
||||
$wc_order->update_meta_data( PayPalGateway::INTENT_META_KEY, $order->intent() );
|
||||
|
|
|
@ -57,12 +57,19 @@ class CartCheckoutDetector {
|
|||
/**
|
||||
* Check if the Checkout page is using Elementor.
|
||||
*
|
||||
* @param int $page_id The ID of the page.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function has_elementor_checkout(): bool {
|
||||
public static function has_elementor_checkout( int $page_id = 0 ): bool {
|
||||
// Check if Elementor is installed and activated.
|
||||
if ( did_action( 'elementor/loaded' ) ) {
|
||||
$elementor_widgets = self::get_elementor_widgets( wc_get_page_id( 'checkout' ) );
|
||||
if ( $page_id ) {
|
||||
$elementor_widgets = self::get_elementor_widgets( $page_id );
|
||||
} else {
|
||||
// Check the WooCommerce checkout page.
|
||||
$elementor_widgets = self::get_elementor_widgets( wc_get_page_id( 'checkout' ) );
|
||||
}
|
||||
|
||||
if ( $elementor_widgets ) {
|
||||
return in_array( 'woocommerce-checkout-page', $elementor_widgets, true );
|
||||
|
@ -114,7 +121,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 +131,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,8 +220,8 @@ class OrderProcessor {
|
|||
);
|
||||
|
||||
throw new PayPalOrderMissingException(
|
||||
__(
|
||||
'Could not retrieve order. Maybe it was already completed or this browser is not supported. Please check your email or try again with a different browser.',
|
||||
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"
|
||||
}
|
||||
}
|
||||
|
|
18
readme.txt
18
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,7 +179,21 @@ If you encounter issues with the PayPal buttons not appearing after an update, p
|
|||
|
||||
== Changelog ==
|
||||
|
||||
= 2.7.1 - xxxx-xx-xx =
|
||||
= 2.8.0 - 2024-06-11 =
|
||||
* 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
|
||||
* Fix - Critical error on pay for order page when we try to pay with ACDC gateway #2321
|
||||
* 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
|
||||
* Fix - Allow PUI Gateway for refund processor #2192
|
||||
|
|
|
@ -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