From 7c90031c4f6f667c0a24793a470a5cbc3791aea8 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Tue, 16 Apr 2024 12:05:06 +0200 Subject: [PATCH 001/104] Add boilerplate for advanced card block integration --- .../js/advanced-card-checkout-block.js | 12 +++ modules/ppcp-blocks/services.php | 7 ++ .../src/AdvancedCardPaymentMethod.php | 82 +++++++++++++++++++ modules/ppcp-blocks/src/BlocksModule.php | 1 + modules/ppcp-blocks/webpack.config.js | 1 + 5 files changed, 103 insertions(+) create mode 100644 modules/ppcp-blocks/resources/js/advanced-card-checkout-block.js create mode 100644 modules/ppcp-blocks/src/AdvancedCardPaymentMethod.php diff --git a/modules/ppcp-blocks/resources/js/advanced-card-checkout-block.js b/modules/ppcp-blocks/resources/js/advanced-card-checkout-block.js new file mode 100644 index 000000000..1775c1182 --- /dev/null +++ b/modules/ppcp-blocks/resources/js/advanced-card-checkout-block.js @@ -0,0 +1,12 @@ +import { registerPaymentMethod } from '@woocommerce/blocks-registry'; + +const config = wc.wcSettings.getSetting('ppcp-credit-card-gateway_data'); + +registerPaymentMethod({ + name: config.id, + label:
, + content:

content

, + edit:

edit...

, + ariaLabel: config.title, + canMakePayment: () => {return true}, +}) diff --git a/modules/ppcp-blocks/services.php b/modules/ppcp-blocks/services.php index a841e97b9..c5a9dddc8 100644 --- a/modules/ppcp-blocks/services.php +++ b/modules/ppcp-blocks/services.php @@ -45,6 +45,13 @@ 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' ) + ); + }, 'blocks.settings.final_review_enabled' => static function ( ContainerInterface $container ): bool { $settings = $container->get( 'wcgateway.settings' ); assert( $settings instanceof ContainerInterface ); diff --git a/modules/ppcp-blocks/src/AdvancedCardPaymentMethod.php b/modules/ppcp-blocks/src/AdvancedCardPaymentMethod.php new file mode 100644 index 000000000..933af1954 --- /dev/null +++ b/modules/ppcp-blocks/src/AdvancedCardPaymentMethod.php @@ -0,0 +1,82 @@ +name = CreditCardGateway::ID; + $this->module_url = $module_url; + $this->version = $version; + $this->gateway = $gateway; + } + + 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() { + return array( + 'id' => $this->name, + 'title' => $this->gateway->title, + 'description' => $this->gateway->description, + ); + } +} diff --git a/modules/ppcp-blocks/src/BlocksModule.php b/modules/ppcp-blocks/src/BlocksModule.php index 9b00d61bc..c0f2174d4 100644 --- a/modules/ppcp-blocks/src/BlocksModule.php +++ b/modules/ppcp-blocks/src/BlocksModule.php @@ -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' ) ); } ); diff --git a/modules/ppcp-blocks/webpack.config.js b/modules/ppcp-blocks/webpack.config.js index bdf508fba..44ab3e68f 100644 --- a/modules/ppcp-blocks/webpack.config.js +++ b/modules/ppcp-blocks/webpack.config.js @@ -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: { From 061d30f57506c28643f535be782f63c7210d553c Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Wed, 17 Apr 2024 16:20:42 +0200 Subject: [PATCH 002/104] Add card fields component (WIP) --- .../resources/js/Components/CardFields.js | 32 +++++++++++++++ .../js/advanced-card-checkout-block.js | 3 +- modules/ppcp-blocks/services.php | 5 ++- .../src/AdvancedCardPaymentMethod.php | 40 ++++++++++++++++--- 4 files changed, 73 insertions(+), 7 deletions(-) create mode 100644 modules/ppcp-blocks/resources/js/Components/CardFields.js diff --git a/modules/ppcp-blocks/resources/js/Components/CardFields.js b/modules/ppcp-blocks/resources/js/Components/CardFields.js new file mode 100644 index 000000000..ca3798855 --- /dev/null +++ b/modules/ppcp-blocks/resources/js/Components/CardFields.js @@ -0,0 +1,32 @@ +import {useEffect} from '@wordpress/element'; + +export function CardFields({config}) { + const cardField = paypal.CardFields({ + createOrder: () => {}, + onApprove: () => {}, + onError: function (error) { + console.error(error) + } + }); + + useEffect(() => { + if (cardField.isEligible()) { + const numberField = cardField.NumberField(); + numberField.render("#card-number-field-container"); + + const cvvField = cardField.CVVField(); + cvvField.render("#card-cvv-field-container"); + + const expiryField = cardField.ExpiryField(); + expiryField.render("#card-expiry-field-container"); + } + }, []); + + return ( + <> +
+
+
+ + ) +} diff --git a/modules/ppcp-blocks/resources/js/advanced-card-checkout-block.js b/modules/ppcp-blocks/resources/js/advanced-card-checkout-block.js index 1775c1182..cdfa2a5e8 100644 --- a/modules/ppcp-blocks/resources/js/advanced-card-checkout-block.js +++ b/modules/ppcp-blocks/resources/js/advanced-card-checkout-block.js @@ -1,11 +1,12 @@ import { registerPaymentMethod } from '@woocommerce/blocks-registry'; +import {CardFields} from "./Components/CardFields"; const config = wc.wcSettings.getSetting('ppcp-credit-card-gateway_data'); registerPaymentMethod({ name: config.id, label:
, - content:

content

, + content: , edit:

edit...

, ariaLabel: config.title, canMakePayment: () => {return true}, diff --git a/modules/ppcp-blocks/services.php b/modules/ppcp-blocks/services.php index c5a9dddc8..051f6323d 100644 --- a/modules/ppcp-blocks/services.php +++ b/modules/ppcp-blocks/services.php @@ -49,7 +49,10 @@ return array( return new AdvancedCardPaymentMethod( $container->get( 'blocks.url' ), $container->get( 'ppcp.asset-version' ), - $container->get( 'wcgateway.credit-card-gateway' ) + $container->get( 'wcgateway.credit-card-gateway' ), + function () use ( $container ): SmartButtonInterface { + return $container->get( 'button.smart-button' ); + } ); }, 'blocks.settings.final_review_enabled' => static function ( ContainerInterface $container ): bool { diff --git a/modules/ppcp-blocks/src/AdvancedCardPaymentMethod.php b/modules/ppcp-blocks/src/AdvancedCardPaymentMethod.php index 933af1954..57b9d5b9a 100644 --- a/modules/ppcp-blocks/src/AdvancedCardPaymentMethod.php +++ b/modules/ppcp-blocks/src/AdvancedCardPaymentMethod.php @@ -10,6 +10,7 @@ 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; class AdvancedCardPaymentMethod extends AbstractPaymentMethodType { @@ -34,15 +35,24 @@ class AdvancedCardPaymentMethod extends AbstractPaymentMethodType { */ private $gateway; + /** + * The smart button script loading handler. + * + * @var SmartButtonInterface|callable + */ + private $smart_button; + public function __construct( string $module_url, string $version, - CreditCardGateway $gateway + CreditCardGateway $gateway, + $smart_button ) { - $this->name = CreditCardGateway::ID; - $this->module_url = $module_url; - $this->version = $version; - $this->gateway = $gateway; + $this->name = CreditCardGateway::ID; + $this->module_url = $module_url; + $this->version = $version; + $this->gateway = $gateway; + $this->smart_button = $smart_button; } public function initialize() {} @@ -73,10 +83,30 @@ class AdvancedCardPaymentMethod extends AbstractPaymentMethodType { * {@inheritDoc} */ public function get_payment_method_data() { + $script_data = $this->smart_button()->script_data(); + return array( 'id' => $this->name, 'title' => $this->gateway->title, 'description' => $this->gateway->description, + 'scriptData' => $script_data, ); } + + /** + * The smart button. + * + * @return SmartButtonInterface + */ + private function smart_button(): 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; + } } From 94379e4feb02b2ea5365ca28aa226a0cab6f9aac Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Wed, 17 Apr 2024 16:22:36 +0200 Subject: [PATCH 003/104] Add card fields component (WIP) --- modules/ppcp-button/src/Assets/SmartButton.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ppcp-button/src/Assets/SmartButton.php b/modules/ppcp-button/src/Assets/SmartButton.php index d795d4950..fcd519368 100644 --- a/modules/ppcp-button/src/Assets/SmartButton.php +++ b/modules/ppcp-button/src/Assets/SmartButton.php @@ -1365,7 +1365,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' ) ) ); } From 3f619608809f0ec1fcc196c52f3fd88ba02ac5c9 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Wed, 17 Apr 2024 17:35:30 +0200 Subject: [PATCH 004/104] Add card fields component (WIP) --- .../resources/js/Components/CardFields.js | 75 ++++++++++++++++++- 1 file changed, 72 insertions(+), 3 deletions(-) diff --git a/modules/ppcp-blocks/resources/js/Components/CardFields.js b/modules/ppcp-blocks/resources/js/Components/CardFields.js index ca3798855..074788da5 100644 --- a/modules/ppcp-blocks/resources/js/Components/CardFields.js +++ b/modules/ppcp-blocks/resources/js/Components/CardFields.js @@ -1,14 +1,83 @@ import {useEffect} from '@wordpress/element'; -export function CardFields({config}) { +export function CardFields({config, eventRegistration, emitResponse}) { + const {onPaymentSetup, onCheckoutFail, onCheckoutValidation} = eventRegistration; + const {responseTypes} = emitResponse; + + const createOrder = async () => { + try { + const res = await fetch(config.scriptData.ajax.create_order.endpoint, { + method: 'POST', + credentials: 'same-origin', + body: JSON.stringify({ + nonce: config.scriptData.ajax.create_order.nonce, + context: config.scriptData.context, + payment_method: 'ppcp-credit-card-gateway', + createaccount: false + }), + }); + + const json = await res.json(); + if (!json.success) { + console.error(json) + } + + console.log(json.data.id) + + return json.data.id; + } catch (err) { + console.error(err); + } + }; + + const handleApprove = async (data, actions) => { + try { + const res = await fetch(config.scriptData.ajax.approve_order.endpoint, { + method: 'POST', + credentials: 'same-origin', + body: JSON.stringify({ + nonce: config.scriptData.ajax.approve_order.nonce, + order_id: data.orderID, + }) + }); + + const json = await res.json(); + if (!json.success) { + console.error(json) + } + + console.log(json) + } catch (err) { + console.error(err); + } + }; + const cardField = paypal.CardFields({ - createOrder: () => {}, - onApprove: () => {}, + createOrder: () => { + return createOrder(); + }, + onApprove: (data, actions) => { + return handleApprove(data, actions); + }, onError: function (error) { console.error(error) } }); + useEffect(() => { + const unsubscribe = onPaymentSetup(() => { + + cardField.submit() + .catch((error) => { + console.error(error) + return {type: responseTypes.ERROR}; + }); + + return true; + }); + return unsubscribe; + }, [onPaymentSetup]); + useEffect(() => { if (cardField.isEligible()) { const numberField = cardField.NumberField(); From 2f2dc5feebcf86d706462788210c17f1bdda5264 Mon Sep 17 00:00:00 2001 From: Daniel Dudzic Date: Tue, 14 May 2024 13:51:20 +0200 Subject: [PATCH 005/104] AXO: Fix Ryan flow credit card icons --- modules/ppcp-axo/resources/js/Views/CardView.js | 6 +++--- modules/ppcp-axo/services.php | 3 ++- modules/ppcp-axo/src/Assets/AxoManager.php | 14 ++++++++++++-- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/modules/ppcp-axo/resources/js/Views/CardView.js b/modules/ppcp-axo/resources/js/Views/CardView.js index 9ca8670a2..a72a38ff3 100644 --- a/modules/ppcp-axo/resources/js/Views/CardView.js +++ b/modules/ppcp-axo/resources/js/Views/CardView.js @@ -34,8 +34,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 +52,7 @@ class CardView { ${data.value('brand')}
diff --git a/modules/ppcp-axo/services.php b/modules/ppcp-axo/services.php index 44253f22d..61db78977 100644 --- a/modules/ppcp-axo/services.php +++ b/modules/ppcp-axo/services.php @@ -58,7 +58,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' ) ); }, diff --git a/modules/ppcp-axo/src/Assets/AxoManager.php b/modules/ppcp-axo/src/Assets/AxoManager.php index 2ed5fc04c..d7042b6e5 100644 --- a/modules/ppcp-axo/src/Assets/AxoManager.php +++ b/modules/ppcp-axo/src/Assets/AxoManager.php @@ -78,6 +78,13 @@ class AxoManager { */ private $session_handler; + /** + * The WcGateway module URL. + * + * @var string + */ + private $wcgateway_module_url; + /** * AxoManager constructor. * @@ -89,6 +96,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,7 +106,8 @@ class AxoManager { Environment $environment, SettingsStatus $settings_status, string $currency, - LoggerInterface $logger + LoggerInterface $logger, + string $wcgateway_module_url ) { $this->module_url = $module_url; @@ -109,6 +118,7 @@ class AxoManager { $this->settings_status = $settings_status; $this->currency = $currency; $this->logger = $logger; + $this->wcgateway_module_url = $wcgateway_module_url; } /** @@ -197,7 +207,7 @@ class AxoManager { 'CA' => WC()->countries->get_states( 'CA' ), ), ), - 'module_url' => untrailingslashit( $this->module_url ), + 'icons_directory' => esc_url( $this->wcgateway_module_url ) . 'assets/images/axo/', ); } From 41ac4197eee1004d4ee2dddf1da800f9652d178d Mon Sep 17 00:00:00 2001 From: Daniel Dudzic Date: Wed, 15 May 2024 14:42:40 +0200 Subject: [PATCH 006/104] Force cart and block-cart button loctions when AXO is active (3091) --- modules/ppcp-axo/services.php | 31 +++++++++++++++++++ modules/ppcp-axo/src/Assets/AxoManager.php | 2 +- modules/ppcp-axo/src/AxoModule.php | 22 +++++++++++++ .../ppcp-wc-gateway/resources/css/common.scss | 7 ++++- .../Fields/paypal-smart-button-fields.php | 10 ++++++ .../src/Settings/SettingsListener.php | 24 ++++++++++++++ 6 files changed, 94 insertions(+), 2 deletions(-) diff --git a/modules/ppcp-axo/services.php b/modules/ppcp-axo/services.php index 44253f22d..088ecfacb 100644 --- a/modules/ppcp-axo/services.php +++ b/modules/ppcp-axo/services.php @@ -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( @@ -193,4 +196,32 @@ return array( return '

' . $notice_content . '

'; }, + + '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. */ + __( + 'Important: The Cart & Classic Cart Smart Button Locations cannot be disabled while Fastlane is active.', + 'woocommerce-paypal-payments' + ), + esc_url( $fastlane_settings_url ) + ); + } else { + return ''; + } + + return '

' . $notice_content . '

'; + }, ); diff --git a/modules/ppcp-axo/src/Assets/AxoManager.php b/modules/ppcp-axo/src/Assets/AxoManager.php index 2ed5fc04c..d9b4313fc 100644 --- a/modules/ppcp-axo/src/Assets/AxoManager.php +++ b/modules/ppcp-axo/src/Assets/AxoManager.php @@ -197,7 +197,7 @@ class AxoManager { 'CA' => WC()->countries->get_states( 'CA' ), ), ), - 'module_url' => untrailingslashit( $this->module_url ), + 'module_url' => untrailingslashit( $this->module_url ), ); } diff --git a/modules/ppcp-axo/src/AxoModule.php b/modules/ppcp-axo/src/AxoModule.php index dfb76ef60..bd1028ef5 100644 --- a/modules/ppcp-axo/src/AxoModule.php +++ b/modules/ppcp-axo/src/AxoModule.php @@ -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 @@ -108,6 +109,27 @@ 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 ) { diff --git a/modules/ppcp-wc-gateway/resources/css/common.scss b/modules/ppcp-wc-gateway/resources/css/common.scss index 1345cb810..d9f40345f 100644 --- a/modules/ppcp-wc-gateway/resources/css/common.scss +++ b/modules/ppcp-wc-gateway/resources/css/common.scss @@ -61,8 +61,13 @@ $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 15px 2px; + margin: 5px 15px 2px 0; padding: 1px 12px; + + p, .form-table td & p { + margin-top: 4px; + margin-bottom: 4px; + } } .ppcp-notice-warning { diff --git a/modules/ppcp-wc-gateway/src/Settings/Fields/paypal-smart-button-fields.php b/modules/ppcp-wc-gateway/src/Settings/Fields/paypal-smart-button-fields.php index 0d02ad679..9db7f2601 100644 --- a/modules/ppcp-wc-gateway/src/Settings/Fields/paypal-smart-button-fields.php +++ b/modules/ppcp-wc-gateway/src/Settings/Fields/paypal-smart-button-fields.php @@ -60,6 +60,16 @@ return function ( ContainerInterface $container, array $fields ): array { '' ), ), + 'smart_button_locations_notice' => array( + 'heading' => '', + 'html' => $container->get( 'axo.smart-button-location-notice' ), + 'type' => 'ppcp-html', + 'classes' => array(), + 'class' => array(), + 'screens' => array( State::STATE_START, State::STATE_ONBOARDED ), + 'requirements' => array(), + 'gateway' => 'paypal', + ), 'smart_button_locations' => array( 'title' => __( 'Smart Button Locations', 'woocommerce-paypal-payments' ), 'type' => 'ppcp-multiselect', diff --git a/modules/ppcp-wc-gateway/src/Settings/SettingsListener.php b/modules/ppcp-wc-gateway/src/Settings/SettingsListener.php index b7d0c39bd..c6d7e7231 100644 --- a/modules/ppcp-wc-gateway/src/Settings/SettingsListener.php +++ b/modules/ppcp-wc-gateway/src/Settings/SettingsListener.php @@ -729,4 +729,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(); + } + } + } } From e3ac1908499dda290d4edc3f82b4b910f36bb165 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Wed, 15 May 2024 17:37:02 +0200 Subject: [PATCH 007/104] Add card fields component (WIP) --- modules/ppcp-blocks/package.json | 2 +- .../resources/js/Components/CardFields.js | 71 +++++++------------ .../js/Components/checkout-handler.js | 33 +++++++++ modules/ppcp-blocks/yarn.lock | 18 ++--- 4 files changed, 70 insertions(+), 54 deletions(-) create mode 100644 modules/ppcp-blocks/resources/js/Components/checkout-handler.js diff --git a/modules/ppcp-blocks/package.json b/modules/ppcp-blocks/package.json index 4958d3648..993b115e3 100644 --- a/modules/ppcp-blocks/package.json +++ b/modules/ppcp-blocks/package.json @@ -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" diff --git a/modules/ppcp-blocks/resources/js/Components/CardFields.js b/modules/ppcp-blocks/resources/js/Components/CardFields.js index 074788da5..c73e32d2f 100644 --- a/modules/ppcp-blocks/resources/js/Components/CardFields.js +++ b/modules/ppcp-blocks/resources/js/Components/CardFields.js @@ -1,4 +1,10 @@ -import {useEffect} from '@wordpress/element'; +import { + PayPalScriptProvider, + PayPalCardFieldsProvider, + PayPalCardFieldsForm, +} from "@paypal/react-paypal-js"; + +import {CheckoutHandler} from "./checkout-handler"; export function CardFields({config, eventRegistration, emitResponse}) { const {onPaymentSetup, onCheckoutFail, onCheckoutValidation} = eventRegistration; @@ -30,7 +36,7 @@ export function CardFields({config, eventRegistration, emitResponse}) { } }; - const handleApprove = async (data, actions) => { + const onApprove = async (data, actions) => { try { const res = await fetch(config.scriptData.ajax.approve_order.endpoint, { method: 'POST', @@ -52,50 +58,27 @@ export function CardFields({config, eventRegistration, emitResponse}) { } }; - const cardField = paypal.CardFields({ - createOrder: () => { - return createOrder(); - }, - onApprove: (data, actions) => { - return handleApprove(data, actions); - }, - onError: function (error) { - console.error(error) - } - }); - - useEffect(() => { - const unsubscribe = onPaymentSetup(() => { - - cardField.submit() - .catch((error) => { - console.error(error) - return {type: responseTypes.ERROR}; - }); - - return true; - }); - return unsubscribe; - }, [onPaymentSetup]); - - useEffect(() => { - if (cardField.isEligible()) { - const numberField = cardField.NumberField(); - numberField.render("#card-number-field-container"); - - const cvvField = cardField.CVVField(); - cvvField.render("#card-cvv-field-container"); - - const expiryField = cardField.ExpiryField(); - expiryField.render("#card-expiry-field-container"); - } - }, []); - return ( <> -
-
-
+ + { + console.log(err); + }} + > + + + + ) } diff --git a/modules/ppcp-blocks/resources/js/Components/checkout-handler.js b/modules/ppcp-blocks/resources/js/Components/checkout-handler.js new file mode 100644 index 000000000..035c10609 --- /dev/null +++ b/modules/ppcp-blocks/resources/js/Components/checkout-handler.js @@ -0,0 +1,33 @@ +import {useEffect} from '@wordpress/element'; +import {usePayPalCardFields} from "@paypal/react-paypal-js"; + +export const CheckoutHandler = ({onPaymentSetup, responseTypes}) => { + const {cardFieldsForm} = usePayPalCardFields(); + + useEffect(() => { + const unsubscribe = onPaymentSetup(async () => { + + cardFieldsForm.submit() + .then(() => { + return { + type: responseTypes.SUCCESS, + meta: { + paymentMethodData: { + foo: 'bar', + } + } + }; + }) + .catch((err) => { + return { + type: responseTypes.ERROR, + message: err + } + }); + }) + + return unsubscribe + }, [onPaymentSetup, cardFieldsForm]); + + return null +} diff --git a/modules/ppcp-blocks/yarn.lock b/modules/ppcp-blocks/yarn.lock index ff731f5b7..1e812a4ae 100644 --- a/modules/ppcp-blocks/yarn.lock +++ b/modules/ppcp-blocks/yarn.lock @@ -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": From aec32512cedbaf3aafb5f8de5ee63270f1a8c496 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Thu, 16 May 2024 12:29:05 +0200 Subject: [PATCH 008/104] Add basic card payment flow --- .../resources/js/Components/CardFields.js | 92 +++++++++---------- .../js/Components/checkout-handler.js | 16 +--- 2 files changed, 47 insertions(+), 61 deletions(-) diff --git a/modules/ppcp-blocks/resources/js/Components/CardFields.js b/modules/ppcp-blocks/resources/js/Components/CardFields.js index c73e32d2f..952896396 100644 --- a/modules/ppcp-blocks/resources/js/Components/CardFields.js +++ b/modules/ppcp-blocks/resources/js/Components/CardFields.js @@ -10,69 +10,65 @@ export function CardFields({config, eventRegistration, emitResponse}) { const {onPaymentSetup, onCheckoutFail, onCheckoutValidation} = eventRegistration; const {responseTypes} = emitResponse; - const createOrder = async () => { - try { - const res = await fetch(config.scriptData.ajax.create_order.endpoint, { - method: 'POST', - credentials: 'same-origin', - body: JSON.stringify({ - nonce: config.scriptData.ajax.create_order.nonce, - context: config.scriptData.context, - payment_method: 'ppcp-credit-card-gateway', - createaccount: false - }), + 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', + createaccount: false + }), + }) + .then((response) => response.json()) + .then((order) => { + return order.data.id; + }) + .catch((err) => { + console.error(err); }); + } - const json = await res.json(); - if (!json.success) { - console.error(json) - } - - console.log(json.data.id) - - return json.data.id; - } catch (err) { - console.error(err); - } - }; - - const onApprove = async (data, actions) => { - try { - const res = await fetch(config.scriptData.ajax.approve_order.endpoint, { - method: 'POST', - credentials: 'same-origin', - body: JSON.stringify({ - nonce: config.scriptData.ajax.approve_order.nonce, - order_id: data.orderID, - }) + function onApprove(data) { + 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) => { + console.log(data) + return { + type: responseTypes.SUCCESS, + }; + }) + .catch((err) => { + console.error(err); }); - - const json = await res.json(); - if (!json.success) { - console.error(json) - } - - console.log(json) - } catch (err) { - console.error(err); - } - }; + } return ( <> { - console.log(err); + console.error(err); }} > diff --git a/modules/ppcp-blocks/resources/js/Components/checkout-handler.js b/modules/ppcp-blocks/resources/js/Components/checkout-handler.js index 035c10609..e4b2c9f8b 100644 --- a/modules/ppcp-blocks/resources/js/Components/checkout-handler.js +++ b/modules/ppcp-blocks/resources/js/Components/checkout-handler.js @@ -7,21 +7,11 @@ export const CheckoutHandler = ({onPaymentSetup, responseTypes}) => { useEffect(() => { const unsubscribe = onPaymentSetup(async () => { - cardFieldsForm.submit() - .then(() => { - return { - type: responseTypes.SUCCESS, - meta: { - paymentMethodData: { - foo: 'bar', - } - } - }; - }) - .catch((err) => { + await cardFieldsForm.submit() + .catch((error) => { + console.error(error) return { type: responseTypes.ERROR, - message: err } }); }) From a362fdc3ba5559e1344ac84ada7c6490c2e98f43 Mon Sep 17 00:00:00 2001 From: Daniel Dudzic Date: Thu, 16 May 2024 19:51:47 +0200 Subject: [PATCH 009/104] Ensure the classic cart and checkout check works for the regular WooCommerce shortcodes --- modules/ppcp-axo/src/Assets/AxoManager.php | 2 +- .../src/Helper/CartCheckoutDetector.php | 23 +++++++++++++++++-- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/modules/ppcp-axo/src/Assets/AxoManager.php b/modules/ppcp-axo/src/Assets/AxoManager.php index 2ed5fc04c..d9b4313fc 100644 --- a/modules/ppcp-axo/src/Assets/AxoManager.php +++ b/modules/ppcp-axo/src/Assets/AxoManager.php @@ -197,7 +197,7 @@ class AxoManager { 'CA' => WC()->countries->get_states( 'CA' ), ), ), - 'module_url' => untrailingslashit( $this->module_url ), + 'module_url' => untrailingslashit( $this->module_url ), ); } diff --git a/modules/ppcp-wc-gateway/src/Helper/CartCheckoutDetector.php b/modules/ppcp-wc-gateway/src/Helper/CartCheckoutDetector.php index faac7f257..253193758 100644 --- a/modules/ppcp-wc-gateway/src/Helper/CartCheckoutDetector.php +++ b/modules/ppcp-wc-gateway/src/Helper/CartCheckoutDetector.php @@ -114,7 +114,7 @@ class CartCheckoutDetector { */ public static function has_classic_checkout(): bool { $checkout_page_id = wc_get_page_id( 'checkout' ); - return $checkout_page_id && has_block( 'woocommerce/classic-shortcode', $checkout_page_id ); + return $checkout_page_id && ( has_block( 'woocommerce/classic-shortcode', $checkout_page_id ) || self::has_classic_shortcode( $checkout_page_id, 'woocommerce_checkout' ) ); } /** @@ -124,6 +124,25 @@ class CartCheckoutDetector { */ public static function has_classic_cart(): bool { $cart_page_id = wc_get_page_id( 'cart' ); - return $cart_page_id && has_block( 'woocommerce/classic-shortcode', $cart_page_id ); + return $cart_page_id && ( has_block( 'woocommerce/classic-shortcode', $cart_page_id ) || self::has_classic_shortcode( $cart_page_id, 'woocommerce_cart' ) ); + } + + /** + * Check if a page has a specific shortcode. + * + * @param int $page_id The ID of the page. + * @param string $shortcode The shortcode to check for. + * + * @return bool + */ + private static function has_classic_shortcode( int $page_id, string $shortcode ): bool { + if ( ! $page_id ) { + return false; + } + + $page = get_post( $page_id ); + $page_content = is_object($page) ? $page->post_content : ''; + + return str_contains( $page_content, $shortcode ); } } From 934da12d6070223f027b70af488ee27e822dfdad Mon Sep 17 00:00:00 2001 From: Daniel Dudzic Date: Thu, 16 May 2024 22:29:26 +0200 Subject: [PATCH 010/104] Fix PHPCS issues --- modules/ppcp-wc-gateway/src/Helper/CartCheckoutDetector.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ppcp-wc-gateway/src/Helper/CartCheckoutDetector.php b/modules/ppcp-wc-gateway/src/Helper/CartCheckoutDetector.php index 253193758..e18fb703e 100644 --- a/modules/ppcp-wc-gateway/src/Helper/CartCheckoutDetector.php +++ b/modules/ppcp-wc-gateway/src/Helper/CartCheckoutDetector.php @@ -141,7 +141,7 @@ class CartCheckoutDetector { } $page = get_post( $page_id ); - $page_content = is_object($page) ? $page->post_content : ''; + $page_content = is_object( $page ) ? $page->post_content : ''; return str_contains( $page_content, $shortcode ); } From c6a775298aa3dc6d9a645ac11261068a091fda21 Mon Sep 17 00:00:00 2001 From: Daniel Dudzic Date: Thu, 16 May 2024 23:59:03 +0200 Subject: [PATCH 011/104] Fastlane notice location and color changes --- modules/ppcp-axo/services.php | 4 ++-- modules/ppcp-wc-gateway/resources/css/common.scss | 15 +++++++++++++-- .../Fields/paypal-smart-button-fields.php | 12 +----------- 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/modules/ppcp-axo/services.php b/modules/ppcp-axo/services.php index 088ecfacb..27ad74501 100644 --- a/modules/ppcp-axo/services.php +++ b/modules/ppcp-axo/services.php @@ -194,7 +194,7 @@ return array( return ''; } - return '

' . $notice_content . '

'; + return '

' . $notice_content . '

'; }, 'axo.smart-button-location-notice' => static function ( ContainerInterface $container ) : string { @@ -222,6 +222,6 @@ return array( return ''; } - return '

' . $notice_content . '

'; + return '

' . $notice_content . '

'; }, ); diff --git a/modules/ppcp-wc-gateway/resources/css/common.scss b/modules/ppcp-wc-gateway/resources/css/common.scss index d9f40345f..edc9b4689 100644 --- a/modules/ppcp-wc-gateway/resources/css/common.scss +++ b/modules/ppcp-wc-gateway/resources/css/common.scss @@ -68,15 +68,26 @@ $background-ident-color: #fbfbfb; 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; } } diff --git a/modules/ppcp-wc-gateway/src/Settings/Fields/paypal-smart-button-fields.php b/modules/ppcp-wc-gateway/src/Settings/Fields/paypal-smart-button-fields.php index 9db7f2601..9e6038606 100644 --- a/modules/ppcp-wc-gateway/src/Settings/Fields/paypal-smart-button-fields.php +++ b/modules/ppcp-wc-gateway/src/Settings/Fields/paypal-smart-button-fields.php @@ -60,22 +60,12 @@ return function ( ContainerInterface $container, array $fields ): array { '' ), ), - 'smart_button_locations_notice' => array( - 'heading' => '', - 'html' => $container->get( 'axo.smart-button-location-notice' ), - 'type' => 'ppcp-html', - 'classes' => array(), - 'class' => array(), - 'screens' => array( State::STATE_START, State::STATE_ONBOARDED ), - 'requirements' => array(), - 'gateway' => 'paypal', - ), 'smart_button_locations' => array( 'title' => __( 'Smart Button Locations', 'woocommerce-paypal-payments' ), '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' ) . $container->get( 'axo.smart-button-location-notice' ), 'options' => $container->get( 'wcgateway.button.locations' ), 'screens' => array( State::STATE_START, State::STATE_ONBOARDED ), 'requirements' => array(), From 9a55140802d63ae8f3f287bf1f69f8ff28efa71a Mon Sep 17 00:00:00 2001 From: Narek Zakarian Date: Mon, 20 May 2024 15:29:56 +0400 Subject: [PATCH 012/104] Allow shipping callback with subscriptions --- modules/ppcp-blocks/extensions.php | 11 +---------- modules/ppcp-button/services.php | 2 -- .../src/Endpoint/ApproveOrderEndpoint.php | 12 +----------- modules/ppcp-wc-gateway/services.php | 1 - .../src/Settings/SettingsListener.php | 19 ------------------- 5 files changed, 2 insertions(+), 43 deletions(-) diff --git a/modules/ppcp-blocks/extensions.php b/modules/ppcp-blocks/extensions.php index c2b607780..b32f5bbc6 100644 --- a/modules/ppcp-blocks/extensions.php +++ b/modules/ppcp-blocks/extensions.php @@ -51,16 +51,7 @@ return array( ); } - $subscription_helper = $container->get( 'wc-subscriptions.helper' ); - - if ( $subscription_helper->plugin_is_active() ) { - $label .= __( - '

Important: Cannot be deactivated while the WooCommerce Subscriptions plugin is active.

', - '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, diff --git a/modules/ppcp-button/services.php b/modules/ppcp-button/services.php index 3d14c9a74..7c564d1ef 100644 --- a/modules/ppcp-button/services.php +++ b/modules/ppcp-button/services.php @@ -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 ); }, diff --git a/modules/ppcp-button/src/Endpoint/ApproveOrderEndpoint.php b/modules/ppcp-button/src/Endpoint/ApproveOrderEndpoint.php index 9e3d43c6f..775be302e 100644 --- a/modules/ppcp-button/src/Endpoint/ApproveOrderEndpoint.php +++ b/modules/ppcp-button/src/Endpoint/ApproveOrderEndpoint.php @@ -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(); } diff --git a/modules/ppcp-wc-gateway/services.php b/modules/ppcp-wc-gateway/services.php index 9ba0021b0..f2991d9ef 100644 --- a/modules/ppcp-wc-gateway/services.php +++ b/modules/ppcp-wc-gateway/services.php @@ -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 ); }, diff --git a/modules/ppcp-wc-gateway/src/Settings/SettingsListener.php b/modules/ppcp-wc-gateway/src/Settings/SettingsListener.php index 6966f97c0..68c3572c2 100644 --- a/modules/ppcp-wc-gateway/src/Settings/SettingsListener.php +++ b/modules/ppcp-wc-gateway/src/Settings/SettingsListener.php @@ -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(); } @@ -403,11 +389,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(); From 7a3579209564a6882e1400cc9d72f8e6fbfffeb4 Mon Sep 17 00:00:00 2001 From: Daniel Dudzic Date: Mon, 20 May 2024 16:17:55 +0200 Subject: [PATCH 013/104] AXO: Fix the Has Profile/No CC scenario --- modules/ppcp-axo/resources/js/AxoManager.js | 22 +++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/modules/ppcp-axo/resources/js/AxoManager.js b/modules/ppcp-axo/resources/js/AxoManager.js index a250c09b7..d6ab6a888 100644 --- a/modules/ppcp-axo/resources/js/AxoManager.js +++ b/modules/ppcp-axo/resources/js/AxoManager.js @@ -24,7 +24,8 @@ class AxoManager { active: false, validEmail: false, hasProfile: false, - useEmailWidget: this.useEmailWidget() + useEmailWidget: this.useEmailWidget(), + hasCard: false, }; this.data = { @@ -257,11 +258,12 @@ class AxoManager { } if (scenario.axoProfileViews) { - this.el.billingAddressContainer.hide(); this.shippingView.activate(); - this.billingView.activate(); - this.cardView.activate(); + + if (this.status.hasCard) { + this.cardView.activate(); + } // Move watermark to after shipping. this.$(this.el.shippingAddressContainer.selector).after( @@ -584,12 +586,20 @@ class AxoManager { 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 = { From e93e9db3be8b42d2a205c20011aa20a09fefd662 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Mon, 20 May 2024 16:37:11 +0200 Subject: [PATCH 014/104] Add place order logic (WIP) --- .../resources/js/Components/CardFields.js | 53 ++++++++++++++++--- .../js/Components/checkout-handler.js | 17 ++---- modules/ppcp-blocks/services.php | 6 +++ modules/ppcp-blocks/src/BlocksModule.php | 11 ++++ .../Endpoint/GetPayPalOrderFromSession.php | 43 +++++++++++++++ .../ppcp-button/src/Assets/SmartButton.php | 5 ++ 6 files changed, 115 insertions(+), 20 deletions(-) create mode 100644 modules/ppcp-blocks/src/Endpoint/GetPayPalOrderFromSession.php diff --git a/modules/ppcp-blocks/resources/js/Components/CardFields.js b/modules/ppcp-blocks/resources/js/Components/CardFields.js index 952896396..ad724eb42 100644 --- a/modules/ppcp-blocks/resources/js/Components/CardFields.js +++ b/modules/ppcp-blocks/resources/js/Components/CardFields.js @@ -1,3 +1,5 @@ +import {useEffect, useState} from '@wordpress/element'; + import { PayPalScriptProvider, PayPalCardFieldsProvider, @@ -7,9 +9,11 @@ import { import {CheckoutHandler} from "./checkout-handler"; export function CardFields({config, eventRegistration, emitResponse}) { - const {onPaymentSetup, onCheckoutFail, onCheckoutValidation} = eventRegistration; + const {onPaymentSetup} = eventRegistration; const {responseTypes} = emitResponse; + const [cardFieldsForm, setCardFieldsForm] = useState(); + async function createOrder() { return fetch(config.scriptData.ajax.create_order.endpoint, { method: "POST", @@ -20,7 +24,6 @@ export function CardFields({config, eventRegistration, emitResponse}) { nonce: config.scriptData.ajax.create_order.nonce, context: config.scriptData.context, payment_method: 'ppcp-credit-card-gateway', - createaccount: false }), }) .then((response) => response.json()) @@ -46,15 +49,53 @@ export function CardFields({config, eventRegistration, emitResponse}) { .then((response) => response.json()) .then((data) => { console.log(data) - return { - type: responseTypes.SUCCESS, - }; }) .catch((err) => { console.error(err); }); } + const getCardFieldsForm = (cardFieldsForm) => { + setCardFieldsForm(cardFieldsForm) + } + + const wait = (milliseconds) => { + return new Promise((resolve) => { + console.log('start...') + setTimeout(() => { + console.log('resolve') + resolve() + }, milliseconds) + }) + } + + useEffect( + () => + onPaymentSetup(() => { + async function handlePaymentProcessing() { + await cardFieldsForm.submit(); + + // TODO temporary workaround to wait for PayPal order in the session + await wait(3000) + + const response = await fetch(config.scriptData.ajax.get_paypal_order_from_session.endpoint) + const result = await response.json() + + return { + type: responseTypes.SUCCESS, + meta: { + paymentMethodData: { + 'paypal_order_id': result.data, + }, + }, + }; + } + + return handlePaymentProcessing(); + }), + [onPaymentSetup, cardFieldsForm] + ); + return ( <> - +
diff --git a/modules/ppcp-blocks/resources/js/Components/checkout-handler.js b/modules/ppcp-blocks/resources/js/Components/checkout-handler.js index e4b2c9f8b..08f4bef5a 100644 --- a/modules/ppcp-blocks/resources/js/Components/checkout-handler.js +++ b/modules/ppcp-blocks/resources/js/Components/checkout-handler.js @@ -1,23 +1,12 @@ import {useEffect} from '@wordpress/element'; import {usePayPalCardFields} from "@paypal/react-paypal-js"; -export const CheckoutHandler = ({onPaymentSetup, responseTypes}) => { +export const CheckoutHandler = ({getCardFieldsForm}) => { const {cardFieldsForm} = usePayPalCardFields(); useEffect(() => { - const unsubscribe = onPaymentSetup(async () => { - - await cardFieldsForm.submit() - .catch((error) => { - console.error(error) - return { - type: responseTypes.ERROR, - } - }); - }) - - return unsubscribe - }, [onPaymentSetup, cardFieldsForm]); + getCardFieldsForm(cardFieldsForm) + }, []); return null } diff --git a/modules/ppcp-blocks/services.php b/modules/ppcp-blocks/services.php index 051f6323d..7c26baf9c 100644 --- a/modules/ppcp-blocks/services.php +++ b/modules/ppcp-blocks/services.php @@ -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; @@ -72,6 +73,11 @@ return array( $container->get( 'woocommerce.logger.woocommerce' ) ); }, + 'blocks.endpoint.get-paypal-order-from-session' => static function ( ContainerInterface $container ): GetPayPalOrderFromSession { + return new GetPayPalOrderFromSession( + $container->get( 'session.handler' ) + ); + }, 'blocks.add-place-order-method' => function ( ContainerInterface $container ) : bool { /** diff --git a/modules/ppcp-blocks/src/BlocksModule.php b/modules/ppcp-blocks/src/BlocksModule.php index 1dd1560dd..765af5698 100644 --- a/modules/ppcp-blocks/src/BlocksModule.php +++ b/modules/ppcp-blocks/src/BlocksModule.php @@ -10,6 +10,7 @@ declare(strict_types=1); namespace WooCommerce\PayPalCommerce\Blocks; use Automattic\WooCommerce\Blocks\Payments\PaymentMethodRegistry; +use WooCommerce\PayPalCommerce\Blocks\Endpoint\GetPayPalOrderFromSession; use WooCommerce\PayPalCommerce\Blocks\Endpoint\UpdateShippingEndpoint; use WooCommerce\PayPalCommerce\Button\Assets\SmartButtonInterface; use WooCommerce\PayPalCommerce\Vendor\Dhii\Container\ServiceProvider; @@ -90,6 +91,16 @@ class BlocksModule implements ModuleInterface { } ); + add_action( + 'wc_ajax_' . GetPayPalOrderFromSession::ENDPOINT, + static function () use ( $c ) { + $endpoint = $c->get( 'blocks.endpoint.get-paypal-order-from-session' ); + assert( $endpoint instanceof GetPayPalOrderFromSession ); + + $endpoint->handle_request(); + } + ); + // Enqueue frontend scripts. add_action( 'wp_enqueue_scripts', diff --git a/modules/ppcp-blocks/src/Endpoint/GetPayPalOrderFromSession.php b/modules/ppcp-blocks/src/Endpoint/GetPayPalOrderFromSession.php new file mode 100644 index 000000000..d343a846d --- /dev/null +++ b/modules/ppcp-blocks/src/Endpoint/GetPayPalOrderFromSession.php @@ -0,0 +1,43 @@ +session_handler = $session_handler; + } + + public static function nonce(): string + { + return self::ENDPOINT; + } + + public function handle_request(): bool + { + $order = $this->session_handler->order(); + + wp_send_json_success($order->id()); + return true; + } +} diff --git a/modules/ppcp-button/src/Assets/SmartButton.php b/modules/ppcp-button/src/Assets/SmartButton.php index e3fa8c4ba..39b6f61cb 100644 --- a/modules/ppcp-button/src/Assets/SmartButton.php +++ b/modules/ppcp-button/src/Assets/SmartButton.php @@ -19,6 +19,7 @@ use WooCommerce\PayPalCommerce\ApiClient\Entity\Money; use WooCommerce\PayPalCommerce\ApiClient\Entity\PaymentToken; use WooCommerce\PayPalCommerce\ApiClient\Factory\PayerFactory; use WooCommerce\PayPalCommerce\ApiClient\Helper\DccApplies; +use WooCommerce\PayPalCommerce\Blocks\Endpoint\GetPayPalOrderFromSession; use WooCommerce\PayPalCommerce\Blocks\Endpoint\UpdateShippingEndpoint; use WooCommerce\PayPalCommerce\Button\Endpoint\ApproveOrderEndpoint; use WooCommerce\PayPalCommerce\Button\Endpoint\ApproveSubscriptionEndpoint; @@ -1159,6 +1160,10 @@ document.querySelector("#payment").before(document.querySelector(".ppcp-messages 'wp_rest_nonce' => wp_create_nonce( 'wc_store_api' ), 'update_shipping_method' => \WC_AJAX::get_endpoint( 'update_shipping_method' ), ), + 'get_paypal_order_from_session' => array( + 'endpoint' => \WC_AJAX::get_endpoint( GetPayPalOrderFromSession::ENDPOINT ), + 'nonce' => wp_create_nonce( GetPayPalOrderFromSession::nonce() ), + ), ), 'cart_contains_subscription' => $this->subscription_helper->cart_contains_subscription(), 'subscription_plan_id' => $this->subscription_helper->paypal_subscription_id(), From e566e376b98d85fc6aeafd3923c524484472ebd7 Mon Sep 17 00:00:00 2001 From: Daniel Dudzic Date: Mon, 20 May 2024 17:03:35 +0200 Subject: [PATCH 015/104] AXO: Fix hiding/showing of the alternative payment gatways with Twenty Twenty-Four theme --- modules/ppcp-axo/resources/js/AxoManager.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/ppcp-axo/resources/js/AxoManager.js b/modules/ppcp-axo/resources/js/AxoManager.js index a250c09b7..5780f7ccd 100644 --- a/modules/ppcp-axo/resources/js/AxoManager.js +++ b/modules/ppcp-axo/resources/js/AxoManager.js @@ -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(); }); @@ -608,6 +609,7 @@ class AxoManager { this.hideGatewaySelection = true; this.$('.wc_payment_methods label').hide(); + this.$('.wc_payment_methods input').hide(); await this.renderWatermark(false); From a42488b41d2db82ca7209b068f1929660e4f048b Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Mon, 20 May 2024 18:03:31 +0200 Subject: [PATCH 016/104] Do not use custom ajax endpoint for get order from session --- .../resources/js/Components/CardFields.js | 10 +---- modules/ppcp-blocks/src/BlocksModule.php | 11 ----- .../Endpoint/GetPayPalOrderFromSession.php | 43 ------------------- .../ppcp-button/src/Assets/SmartButton.php | 5 --- 4 files changed, 1 insertion(+), 68 deletions(-) delete mode 100644 modules/ppcp-blocks/src/Endpoint/GetPayPalOrderFromSession.php diff --git a/modules/ppcp-blocks/resources/js/Components/CardFields.js b/modules/ppcp-blocks/resources/js/Components/CardFields.js index ad724eb42..85c8ce139 100644 --- a/modules/ppcp-blocks/resources/js/Components/CardFields.js +++ b/modules/ppcp-blocks/resources/js/Components/CardFields.js @@ -78,17 +78,9 @@ export function CardFields({config, eventRegistration, emitResponse}) { // TODO temporary workaround to wait for PayPal order in the session await wait(3000) - const response = await fetch(config.scriptData.ajax.get_paypal_order_from_session.endpoint) - const result = await response.json() - return { type: responseTypes.SUCCESS, - meta: { - paymentMethodData: { - 'paypal_order_id': result.data, - }, - }, - }; + } } return handlePaymentProcessing(); diff --git a/modules/ppcp-blocks/src/BlocksModule.php b/modules/ppcp-blocks/src/BlocksModule.php index 765af5698..1dd1560dd 100644 --- a/modules/ppcp-blocks/src/BlocksModule.php +++ b/modules/ppcp-blocks/src/BlocksModule.php @@ -10,7 +10,6 @@ declare(strict_types=1); namespace WooCommerce\PayPalCommerce\Blocks; use Automattic\WooCommerce\Blocks\Payments\PaymentMethodRegistry; -use WooCommerce\PayPalCommerce\Blocks\Endpoint\GetPayPalOrderFromSession; use WooCommerce\PayPalCommerce\Blocks\Endpoint\UpdateShippingEndpoint; use WooCommerce\PayPalCommerce\Button\Assets\SmartButtonInterface; use WooCommerce\PayPalCommerce\Vendor\Dhii\Container\ServiceProvider; @@ -91,16 +90,6 @@ class BlocksModule implements ModuleInterface { } ); - add_action( - 'wc_ajax_' . GetPayPalOrderFromSession::ENDPOINT, - static function () use ( $c ) { - $endpoint = $c->get( 'blocks.endpoint.get-paypal-order-from-session' ); - assert( $endpoint instanceof GetPayPalOrderFromSession ); - - $endpoint->handle_request(); - } - ); - // Enqueue frontend scripts. add_action( 'wp_enqueue_scripts', diff --git a/modules/ppcp-blocks/src/Endpoint/GetPayPalOrderFromSession.php b/modules/ppcp-blocks/src/Endpoint/GetPayPalOrderFromSession.php deleted file mode 100644 index d343a846d..000000000 --- a/modules/ppcp-blocks/src/Endpoint/GetPayPalOrderFromSession.php +++ /dev/null @@ -1,43 +0,0 @@ -session_handler = $session_handler; - } - - public static function nonce(): string - { - return self::ENDPOINT; - } - - public function handle_request(): bool - { - $order = $this->session_handler->order(); - - wp_send_json_success($order->id()); - return true; - } -} diff --git a/modules/ppcp-button/src/Assets/SmartButton.php b/modules/ppcp-button/src/Assets/SmartButton.php index 39b6f61cb..e3fa8c4ba 100644 --- a/modules/ppcp-button/src/Assets/SmartButton.php +++ b/modules/ppcp-button/src/Assets/SmartButton.php @@ -19,7 +19,6 @@ use WooCommerce\PayPalCommerce\ApiClient\Entity\Money; use WooCommerce\PayPalCommerce\ApiClient\Entity\PaymentToken; use WooCommerce\PayPalCommerce\ApiClient\Factory\PayerFactory; use WooCommerce\PayPalCommerce\ApiClient\Helper\DccApplies; -use WooCommerce\PayPalCommerce\Blocks\Endpoint\GetPayPalOrderFromSession; use WooCommerce\PayPalCommerce\Blocks\Endpoint\UpdateShippingEndpoint; use WooCommerce\PayPalCommerce\Button\Endpoint\ApproveOrderEndpoint; use WooCommerce\PayPalCommerce\Button\Endpoint\ApproveSubscriptionEndpoint; @@ -1160,10 +1159,6 @@ document.querySelector("#payment").before(document.querySelector(".ppcp-messages 'wp_rest_nonce' => wp_create_nonce( 'wc_store_api' ), 'update_shipping_method' => \WC_AJAX::get_endpoint( 'update_shipping_method' ), ), - 'get_paypal_order_from_session' => array( - 'endpoint' => \WC_AJAX::get_endpoint( GetPayPalOrderFromSession::ENDPOINT ), - 'nonce' => wp_create_nonce( GetPayPalOrderFromSession::nonce() ), - ), ), 'cart_contains_subscription' => $this->subscription_helper->cart_contains_subscription(), 'subscription_plan_id' => $this->subscription_helper->paypal_subscription_id(), From de3fd75f691be97ac8f25e9b2afc49b1d5f02b5f Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Mon, 20 May 2024 18:46:44 +0200 Subject: [PATCH 017/104] Rename component filename --- .../{CardFields.js => card-fields.js} | 46 ++----------------- .../js/advanced-card-checkout-block.js | 4 +- .../resources/js/card-fields-config.js | 42 +++++++++++++++++ 3 files changed, 47 insertions(+), 45 deletions(-) rename modules/ppcp-blocks/resources/js/Components/{CardFields.js => card-fields.js} (59%) create mode 100644 modules/ppcp-blocks/resources/js/card-fields-config.js diff --git a/modules/ppcp-blocks/resources/js/Components/CardFields.js b/modules/ppcp-blocks/resources/js/Components/card-fields.js similarity index 59% rename from modules/ppcp-blocks/resources/js/Components/CardFields.js rename to modules/ppcp-blocks/resources/js/Components/card-fields.js index 85c8ce139..97cf83ab5 100644 --- a/modules/ppcp-blocks/resources/js/Components/CardFields.js +++ b/modules/ppcp-blocks/resources/js/Components/card-fields.js @@ -7,6 +7,7 @@ import { } from "@paypal/react-paypal-js"; import {CheckoutHandler} from "./checkout-handler"; +import {createOrder, onApprove} from "../card-fields-config"; export function CardFields({config, eventRegistration, emitResponse}) { const {onPaymentSetup} = eventRegistration; @@ -14,47 +15,6 @@ export function CardFields({config, eventRegistration, emitResponse}) { const [cardFieldsForm, setCardFieldsForm] = useState(); - 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', - }), - }) - .then((response) => response.json()) - .then((order) => { - return order.data.id; - }) - .catch((err) => { - console.error(err); - }); - } - - function onApprove(data) { - 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) => { - console.log(data) - }) - .catch((err) => { - console.error(err); - }); - } - const getCardFieldsForm = (cardFieldsForm) => { setCardFieldsForm(cardFieldsForm) } @@ -67,7 +27,7 @@ export function CardFields({config, eventRegistration, emitResponse}) { resolve() }, milliseconds) }) - } + }; useEffect( () => @@ -75,7 +35,7 @@ export function CardFields({config, eventRegistration, emitResponse}) { async function handlePaymentProcessing() { await cardFieldsForm.submit(); - // TODO temporary workaround to wait for PayPal order in the session + // TODO temporary workaround to wait for onApprove to resolve await wait(3000) return { diff --git a/modules/ppcp-blocks/resources/js/advanced-card-checkout-block.js b/modules/ppcp-blocks/resources/js/advanced-card-checkout-block.js index cdfa2a5e8..f8fb6f6e3 100644 --- a/modules/ppcp-blocks/resources/js/advanced-card-checkout-block.js +++ b/modules/ppcp-blocks/resources/js/advanced-card-checkout-block.js @@ -1,5 +1,5 @@ import { registerPaymentMethod } from '@woocommerce/blocks-registry'; -import {CardFields} from "./Components/CardFields"; +import {CardFields} from "./Components/card-fields"; const config = wc.wcSettings.getSetting('ppcp-credit-card-gateway_data'); @@ -7,7 +7,7 @@ registerPaymentMethod({ name: config.id, label:
, content: , - edit:

edit...

, + edit:
, ariaLabel: config.title, canMakePayment: () => {return true}, }) diff --git a/modules/ppcp-blocks/resources/js/card-fields-config.js b/modules/ppcp-blocks/resources/js/card-fields-config.js new file mode 100644 index 000000000..92700ac8a --- /dev/null +++ b/modules/ppcp-blocks/resources/js/card-fields-config.js @@ -0,0 +1,42 @@ +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', + }), + }) + .then((response) => response.json()) + .then((order) => { + return order.data.id; + }) + .catch((err) => { + console.error(err); + }); +} + +export function onApprove(data) { + 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) => { + console.log(data) + }) + .catch((err) => { + console.error(err); + }); +} From b7b19cfbc78404202306204cb11bb2b5ca908da0 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Mon, 20 May 2024 20:46:53 +0200 Subject: [PATCH 018/104] Add save payment checkbox --- .../resources/js/Components/checkout-handler.js | 7 ++++++- modules/ppcp-blocks/resources/js/card-fields-config.js | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/modules/ppcp-blocks/resources/js/Components/checkout-handler.js b/modules/ppcp-blocks/resources/js/Components/checkout-handler.js index 08f4bef5a..6ccb0eea4 100644 --- a/modules/ppcp-blocks/resources/js/Components/checkout-handler.js +++ b/modules/ppcp-blocks/resources/js/Components/checkout-handler.js @@ -8,5 +8,10 @@ export const CheckoutHandler = ({getCardFieldsForm}) => { getCardFieldsForm(cardFieldsForm) }, []); - return null + return ( + <> + + + + ) } diff --git a/modules/ppcp-blocks/resources/js/card-fields-config.js b/modules/ppcp-blocks/resources/js/card-fields-config.js index 92700ac8a..5dcd2a447 100644 --- a/modules/ppcp-blocks/resources/js/card-fields-config.js +++ b/modules/ppcp-blocks/resources/js/card-fields-config.js @@ -10,6 +10,7 @@ export async function createOrder() { nonce: config.scriptData.ajax.create_order.nonce, context: config.scriptData.context, payment_method: 'ppcp-credit-card-gateway', + save_payment_method: true, }), }) .then((response) => response.json()) From cf9f323e9b1c73237b0e820c4a2ef8ec01d3e1ac Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Tue, 21 May 2024 10:39:40 +0200 Subject: [PATCH 019/104] Use `showSaveOption` for save card checkbox --- .../resources/js/Components/checkout-handler.js | 7 +------ .../resources/js/advanced-card-checkout-block.js | 4 ++++ 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/modules/ppcp-blocks/resources/js/Components/checkout-handler.js b/modules/ppcp-blocks/resources/js/Components/checkout-handler.js index 6ccb0eea4..08f4bef5a 100644 --- a/modules/ppcp-blocks/resources/js/Components/checkout-handler.js +++ b/modules/ppcp-blocks/resources/js/Components/checkout-handler.js @@ -8,10 +8,5 @@ export const CheckoutHandler = ({getCardFieldsForm}) => { getCardFieldsForm(cardFieldsForm) }, []); - return ( - <> - - - - ) + return null } diff --git a/modules/ppcp-blocks/resources/js/advanced-card-checkout-block.js b/modules/ppcp-blocks/resources/js/advanced-card-checkout-block.js index f8fb6f6e3..923c88da8 100644 --- a/modules/ppcp-blocks/resources/js/advanced-card-checkout-block.js +++ b/modules/ppcp-blocks/resources/js/advanced-card-checkout-block.js @@ -10,4 +10,8 @@ registerPaymentMethod({ edit:
, ariaLabel: config.title, canMakePayment: () => {return true}, + supports: { + showSavedCards: true, + showSaveOption: true + } }) From d840296a25737853574c1277ffabeb64a89f12a3 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Tue, 21 May 2024 10:45:25 +0200 Subject: [PATCH 020/104] Remove unused service --- modules/ppcp-blocks/services.php | 5 ----- 1 file changed, 5 deletions(-) diff --git a/modules/ppcp-blocks/services.php b/modules/ppcp-blocks/services.php index 7c26baf9c..7f7fdf048 100644 --- a/modules/ppcp-blocks/services.php +++ b/modules/ppcp-blocks/services.php @@ -73,11 +73,6 @@ return array( $container->get( 'woocommerce.logger.woocommerce' ) ); }, - 'blocks.endpoint.get-paypal-order-from-session' => static function ( ContainerInterface $container ): GetPayPalOrderFromSession { - return new GetPayPalOrderFromSession( - $container->get( 'session.handler' ) - ); - }, 'blocks.add-place-order-method' => function ( ContainerInterface $container ) : bool { /** From 17fd6886ffc9ec95ea4f89a40a8ccec1103ba02d Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Tue, 21 May 2024 15:52:41 +0200 Subject: [PATCH 021/104] Add gateway features support --- .../resources/js/advanced-card-checkout-block.js | 3 ++- .../ppcp-blocks/src/AdvancedCardPaymentMethod.php | 15 +++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/modules/ppcp-blocks/resources/js/advanced-card-checkout-block.js b/modules/ppcp-blocks/resources/js/advanced-card-checkout-block.js index 923c88da8..9ddf3a6b1 100644 --- a/modules/ppcp-blocks/resources/js/advanced-card-checkout-block.js +++ b/modules/ppcp-blocks/resources/js/advanced-card-checkout-block.js @@ -12,6 +12,7 @@ registerPaymentMethod({ canMakePayment: () => {return true}, supports: { showSavedCards: true, - showSaveOption: true + showSaveOption: true, + features: config.supports } }) diff --git a/modules/ppcp-blocks/src/AdvancedCardPaymentMethod.php b/modules/ppcp-blocks/src/AdvancedCardPaymentMethod.php index 57b9d5b9a..d85555040 100644 --- a/modules/ppcp-blocks/src/AdvancedCardPaymentMethod.php +++ b/modules/ppcp-blocks/src/AdvancedCardPaymentMethod.php @@ -13,6 +13,9 @@ use Automattic\WooCommerce\Blocks\Payments\Integrations\AbstractPaymentMethodTyp use WooCommerce\PayPalCommerce\Button\Assets\SmartButtonInterface; use WooCommerce\PayPalCommerce\WcGateway\Gateway\CreditCardGateway; +/** + * Class AdvancedCardPaymentMethod + */ class AdvancedCardPaymentMethod extends AbstractPaymentMethodType { /** * The URL of this module. @@ -42,6 +45,14 @@ class AdvancedCardPaymentMethod extends AbstractPaymentMethodType { */ private $smart_button; + /** + * 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. + */ public function __construct( string $module_url, string $version, @@ -55,6 +66,9 @@ class AdvancedCardPaymentMethod extends AbstractPaymentMethodType { $this->smart_button = $smart_button; } + /** + * {@inheritDoc} + */ public function initialize() {} /** @@ -90,6 +104,7 @@ class AdvancedCardPaymentMethod extends AbstractPaymentMethodType { 'title' => $this->gateway->title, 'description' => $this->gateway->description, 'scriptData' => $script_data, + 'supports' => $this->gateway->supports, ); } From 1de72ed0bfd4fc67e696de9990bf87d90611929b Mon Sep 17 00:00:00 2001 From: Narek Zakarian Date: Tue, 21 May 2024 18:41:27 +0400 Subject: [PATCH 022/104] allow skipping confirmation page for subscriptions --- .../ActionHandler/CartActionHandler.js | 7 ++- modules/ppcp-button/services.php | 11 +++- .../Endpoint/ApproveSubscriptionEndpoint.php | 58 ++++++++++++++++--- 3 files changed, 65 insertions(+), 11 deletions(-) diff --git a/modules/ppcp-button/resources/js/modules/ActionHandler/CartActionHandler.js b/modules/ppcp-button/resources/js/modules/ActionHandler/CartActionHandler.js index 2b3066395..e5c0dbac1 100644 --- a/modules/ppcp-button/resources/js/modules/ActionHandler/CartActionHandler.js +++ b/modules/ppcp-button/resources/js/modules/ActionHandler/CartActionHandler.js @@ -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) => { diff --git a/modules/ppcp-button/services.php b/modules/ppcp-button/services.php index 7c564d1ef..aa518f13f 100644 --- a/modules/ppcp-button/services.php +++ b/modules/ppcp-button/services.php @@ -258,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 { @@ -360,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' ) + ); }, ); diff --git a/modules/ppcp-button/src/Endpoint/ApproveSubscriptionEndpoint.php b/modules/ppcp-button/src/Endpoint/ApproveSubscriptionEndpoint.php index d2ca73b83..d89a0a1af 100644 --- a/modules/ppcp-button/src/Endpoint/ApproveSubscriptionEndpoint.php +++ b/modules/ppcp-button/src/Endpoint/ApproveSubscriptionEndpoint.php @@ -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; } From feab77ed03b25a5d6cf21d937b7f83789f628064 Mon Sep 17 00:00:00 2001 From: Narek Zakarian Date: Tue, 21 May 2024 18:42:03 +0400 Subject: [PATCH 023/104] Create subscription order --- .../src/Helper/WooCommerceOrderCreator.php | 101 ++++++++++++++++-- 1 file changed, 93 insertions(+), 8 deletions(-) diff --git a/modules/ppcp-button/src/Helper/WooCommerceOrderCreator.php b/modules/ppcp-button/src/Helper/WooCommerceOrderCreator.php index 92f07c0fd..6f26626e3 100644 --- a/modules/ppcp-button/src/Helper/WooCommerceOrderCreator.php +++ b/modules/ppcp-button/src/Helper/WooCommerceOrderCreator.php @@ -14,12 +14,14 @@ use WC_Cart; use WC_Order; use WC_Order_Item_Product; use WC_Order_Item_Shipping; +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; /** * Class WooCommerceOrderCreator @@ -40,18 +42,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 +81,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 +99,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 +128,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,6 +224,14 @@ 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() . ' × ' . $item->get_quantity(); + } + + $shipping->add_meta_data( __( 'Items', 'woocommerce-paypal-payments' ), implode( ', ', $items_in_package ) ); + $wc_order->add_item( $shipping ); } } @@ -225,4 +278,36 @@ 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_Order The subscription order + */ + protected function create_subscription( WC_Order $wc_order, int $product_id ): WC_Order { + return 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(), + ) + ); + } } From 6e575acd2210503dcfa24b677c35697e2860e446 Mon Sep 17 00:00:00 2001 From: Daniel Dudzic Date: Wed, 22 May 2024 00:03:43 +0200 Subject: [PATCH 024/104] Fix AXO psalm errors --- modules/ppcp-axo/src/Assets/AxoManager.php | 2 +- modules/ppcp-blocks/extensions.php | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/modules/ppcp-axo/src/Assets/AxoManager.php b/modules/ppcp-axo/src/Assets/AxoManager.php index 2ed5fc04c..d9b4313fc 100644 --- a/modules/ppcp-axo/src/Assets/AxoManager.php +++ b/modules/ppcp-axo/src/Assets/AxoManager.php @@ -197,7 +197,7 @@ class AxoManager { 'CA' => WC()->countries->get_states( 'CA' ), ), ), - 'module_url' => untrailingslashit( $this->module_url ), + 'module_url' => untrailingslashit( $this->module_url ), ); } diff --git a/modules/ppcp-blocks/extensions.php b/modules/ppcp-blocks/extensions.php index c2b607780..2409d1bbc 100644 --- a/modules/ppcp-blocks/extensions.php +++ b/modules/ppcp-blocks/extensions.php @@ -54,10 +54,11 @@ return array( $subscription_helper = $container->get( 'wc-subscriptions.helper' ); if ( $subscription_helper->plugin_is_active() ) { - $label .= __( - '

Important: Cannot be deactivated while the WooCommerce Subscriptions plugin is active.

', + $notice_content = __( + 'Important: Cannot be deactivated while the WooCommerce Subscriptions plugin is active.', 'woocommerce-paypal-payments' ); + $label .= '

' . $notice_content . '

'; } $should_disable_checkbox = $subscription_helper->plugin_is_active() || apply_filters( 'woocommerce_paypal_payments_toggle_final_review_checkbox', false ); From 748fe6dfcf21e28013b9dc9a5c50518582b6140c Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Wed, 22 May 2024 12:06:15 +0200 Subject: [PATCH 025/104] Get payment method value from filter instead of request --- modules/ppcp-card-fields/src/CardFieldsModule.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/modules/ppcp-card-fields/src/CardFieldsModule.php b/modules/ppcp-card-fields/src/CardFieldsModule.php index 6835108c3..b499891a0 100644 --- a/modules/ppcp-card-fields/src/CardFieldsModule.php +++ b/modules/ppcp-card-fields/src/CardFieldsModule.php @@ -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 ); } } From 880bd48271cd03873ca06797e9f9d39053134f0d Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Wed, 22 May 2024 15:52:45 +0200 Subject: [PATCH 026/104] Use custom save card payment checkbox and show/hide it based on vaulting setting --- .../resources/js/Components/card-fields.js | 12 +++++++-- .../js/Components/checkout-handler.js | 13 ++++++++-- .../js/advanced-card-checkout-block.js | 1 - .../resources/js/card-fields-config.js | 3 ++- modules/ppcp-blocks/services.php | 3 ++- .../src/AdvancedCardPaymentMethod.php | 26 ++++++++++++++----- 6 files changed, 45 insertions(+), 13 deletions(-) diff --git a/modules/ppcp-blocks/resources/js/Components/card-fields.js b/modules/ppcp-blocks/resources/js/Components/card-fields.js index 97cf83ab5..dcd3ba2dd 100644 --- a/modules/ppcp-blocks/resources/js/Components/card-fields.js +++ b/modules/ppcp-blocks/resources/js/Components/card-fields.js @@ -14,11 +14,14 @@ export function CardFields({config, eventRegistration, emitResponse}) { const {responseTypes} = emitResponse; const [cardFieldsForm, setCardFieldsForm] = useState(); - const getCardFieldsForm = (cardFieldsForm) => { setCardFieldsForm(cardFieldsForm) } + const getSavePayment = (savePayment) => { + localStorage.setItem('ppcp-save-card-payment', savePayment); + } + const wait = (milliseconds) => { return new Promise((resolve) => { console.log('start...') @@ -65,7 +68,12 @@ export function CardFields({config, eventRegistration, emitResponse}) { }} > - + diff --git a/modules/ppcp-blocks/resources/js/Components/checkout-handler.js b/modules/ppcp-blocks/resources/js/Components/checkout-handler.js index 08f4bef5a..8e9658fc2 100644 --- a/modules/ppcp-blocks/resources/js/Components/checkout-handler.js +++ b/modules/ppcp-blocks/resources/js/Components/checkout-handler.js @@ -1,12 +1,21 @@ import {useEffect} from '@wordpress/element'; import {usePayPalCardFields} from "@paypal/react-paypal-js"; -export const CheckoutHandler = ({getCardFieldsForm}) => { +export const CheckoutHandler = ({getCardFieldsForm, getSavePayment, saveCardText, is_vaulting_enabled}) => { const {cardFieldsForm} = usePayPalCardFields(); useEffect(() => { getCardFieldsForm(cardFieldsForm) }, []); - return null + if (!is_vaulting_enabled) { + return null; + } + + return ( + <> + getSavePayment(e.target.checked)}/> + + + ) } diff --git a/modules/ppcp-blocks/resources/js/advanced-card-checkout-block.js b/modules/ppcp-blocks/resources/js/advanced-card-checkout-block.js index 9ddf3a6b1..6d268c122 100644 --- a/modules/ppcp-blocks/resources/js/advanced-card-checkout-block.js +++ b/modules/ppcp-blocks/resources/js/advanced-card-checkout-block.js @@ -12,7 +12,6 @@ registerPaymentMethod({ canMakePayment: () => {return true}, supports: { showSavedCards: true, - showSaveOption: true, features: config.supports } }) diff --git a/modules/ppcp-blocks/resources/js/card-fields-config.js b/modules/ppcp-blocks/resources/js/card-fields-config.js index 5dcd2a447..23b8859fb 100644 --- a/modules/ppcp-blocks/resources/js/card-fields-config.js +++ b/modules/ppcp-blocks/resources/js/card-fields-config.js @@ -10,7 +10,7 @@ export async function createOrder() { nonce: config.scriptData.ajax.create_order.nonce, context: config.scriptData.context, payment_method: 'ppcp-credit-card-gateway', - save_payment_method: true, + save_payment_method: localStorage.getItem('ppcp-save-card-payment') === 'true', }), }) .then((response) => response.json()) @@ -36,6 +36,7 @@ export function onApprove(data) { .then((response) => response.json()) .then((data) => { console.log(data) + localStorage.removeItem('ppcp-save-card-payment'); }) .catch((err) => { console.error(err); diff --git a/modules/ppcp-blocks/services.php b/modules/ppcp-blocks/services.php index 7f7fdf048..21a97f984 100644 --- a/modules/ppcp-blocks/services.php +++ b/modules/ppcp-blocks/services.php @@ -53,7 +53,8 @@ return array( $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 { diff --git a/modules/ppcp-blocks/src/AdvancedCardPaymentMethod.php b/modules/ppcp-blocks/src/AdvancedCardPaymentMethod.php index d85555040..55b5cfdc9 100644 --- a/modules/ppcp-blocks/src/AdvancedCardPaymentMethod.php +++ b/modules/ppcp-blocks/src/AdvancedCardPaymentMethod.php @@ -12,11 +12,13 @@ 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. * @@ -45,6 +47,13 @@ class AdvancedCardPaymentMethod extends AbstractPaymentMethodType { */ private $smart_button; + /** + * The settings. + * + * @var Settings + */ + protected $settings; + /** * AdvancedCardPaymentMethod constructor. * @@ -52,18 +61,21 @@ class AdvancedCardPaymentMethod extends AbstractPaymentMethodType { * @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 + $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; } /** @@ -100,11 +112,13 @@ class AdvancedCardPaymentMethod extends AbstractPaymentMethodType { $script_data = $this->smart_button()->script_data(); return array( - 'id' => $this->name, - 'title' => $this->gateway->title, - 'description' => $this->gateway->description, - 'scriptData' => $script_data, - 'supports' => $this->gateway->supports, + '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' ), ); } From 8ee4a524bb29ad33479df3d960c0027e94c36762 Mon Sep 17 00:00:00 2001 From: Daniel Dudzic Date: Thu, 23 May 2024 01:30:20 +0200 Subject: [PATCH 027/104] First attempt to optimize the checkout loading of the Fastlane experience to make it nice and smooth --- modules/ppcp-axo/resources/css/styles.scss | 35 +++++++++++++++ modules/ppcp-axo/resources/js/AxoManager.js | 26 ++++++++++- .../js/Components/DomElementCollection.js | 2 +- modules/ppcp-axo/src/Assets/AxoManager.php | 2 +- modules/ppcp-axo/src/AxoModule.php | 44 +++++++++++++++++++ 5 files changed, 106 insertions(+), 3 deletions(-) diff --git a/modules/ppcp-axo/resources/css/styles.scss b/modules/ppcp-axo/resources/css/styles.scss index 605ac18b2..9edd122fa 100644 --- a/modules/ppcp-axo/resources/css/styles.scss +++ b/modules/ppcp-axo/resources/css/styles.scss @@ -1,6 +1,15 @@ .ppcp-axo-watermark-container { max-width: 200px; margin-top: 10px; + position: relative; + + &.loader:before { + height: 12px; + width: 12px; + margin-left: -6px; + margin-top: -6px; + left: 12px; + } } .ppcp-axo-payment-container { @@ -28,6 +37,7 @@ .ppcp-axo-customer-details { margin-bottom: 40px; + position: relative; } .axo-checkout-header-section { @@ -44,6 +54,31 @@ padding: 0.6em 1em; } +.ppcp-axo-watermark-loading { + min-height: 12px; +} + +.ppcp-axo-overlay, +.ppcp-axo-watermark-loading:after { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(255, 255, 255, 0.8); + 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; diff --git a/modules/ppcp-axo/resources/js/AxoManager.js b/modules/ppcp-axo/resources/js/AxoManager.js index a250c09b7..aa94427a8 100644 --- a/modules/ppcp-axo/resources/js/AxoManager.js +++ b/modules/ppcp-axo/resources/js/AxoManager.js @@ -231,6 +231,7 @@ class AxoManager { if (scenario.defaultFormFields) { this.el.customerDetails.show(); + this.toggleLoaderAndOverlay(this.el.customerDetails, 'loader', 'ppcp-axo-overlay'); } else { this.el.customerDetails.hide(); } @@ -248,7 +249,6 @@ class AxoManager { this.$(this.el.fieldBillingEmail.selector).append( this.$(this.el.watermarkContainer.selector) ); - } else { this.el.emailWidgetContainer.hide(); if (!scenario.defaultEmailField) { @@ -496,6 +496,8 @@ class AxoManager { (await this.fastlane.FastlaneWatermarkComponent({ includeAdditionalInfo })).render(this.el.watermarkContainer.selector); + + this.toggleWatermarkLoading(this.el.watermarkContainer, 'ppcp-axo-watermark-loading', 'loader'); } watchEmail() { @@ -840,6 +842,28 @@ class AxoManager { data.billing_phone = phone; } } + + toggleLoaderAndOverlay(element, loaderClass, overlayClass) { + const loader = document.querySelector(`${element.selector} .${loaderClass}`); + const overlay = document.querySelector(`${element.selector} .${overlayClass}`); + if (loader) { + loader.classList.toggle(loaderClass); + } + if (overlay) { + overlay.classList.toggle(overlayClass); + } + } + + toggleWatermarkLoading(container, loadingClass, loaderClass) { + const watermarkLoading = document.querySelector(`${container.selector}.${loadingClass}`); + const watermarkLoader = document.querySelector(`${container.selector}.${loaderClass}`); + if (watermarkLoading) { + watermarkLoading.classList.toggle(loadingClass); + } + if (watermarkLoader) { + watermarkLoader.classList.toggle(loaderClass); + } + } } export default AxoManager; diff --git a/modules/ppcp-axo/resources/js/Components/DomElementCollection.js b/modules/ppcp-axo/resources/js/Components/DomElementCollection.js index fc44a2823..35f19ec6d 100644 --- a/modules/ppcp-axo/resources/js/Components/DomElementCollection.js +++ b/modules/ppcp-axo/resources/js/Components/DomElementCollection.js @@ -20,7 +20,7 @@ class DomElementCollection { this.watermarkContainer = new DomElement({ id: 'ppcp-axo-watermark-container', selector: '#ppcp-axo-watermark-container', - className: 'ppcp-axo-watermark-container' + className: 'ppcp-axo-watermark-container ppcp-axo-watermark-loading loader' }); this.customerDetails = new DomElement({ diff --git a/modules/ppcp-axo/src/Assets/AxoManager.php b/modules/ppcp-axo/src/Assets/AxoManager.php index 2ed5fc04c..d9b4313fc 100644 --- a/modules/ppcp-axo/src/Assets/AxoManager.php +++ b/modules/ppcp-axo/src/Assets/AxoManager.php @@ -197,7 +197,7 @@ class AxoManager { 'CA' => WC()->countries->get_states( 'CA' ), ), ), - 'module_url' => untrailingslashit( $this->module_url ), + 'module_url' => untrailingslashit( $this->module_url ), ); } diff --git a/modules/ppcp-axo/src/AxoModule.php b/modules/ppcp-axo/src/AxoModule.php index dfb76ef60..cdea50ee2 100644 --- a/modules/ppcp-axo/src/AxoModule.php +++ b/modules/ppcp-axo/src/AxoModule.php @@ -217,6 +217,8 @@ class AxoModule implements ModuleInterface { 1 ); + // Add the markup necessary for displaying overlays and loaders for Axo on the checkout page. + $this->add_checkout_loader_markup( $c ); } /** @@ -292,4 +294,46 @@ class AxoModule implements ModuleInterface { && CartCheckoutDetector::has_classic_checkout() && $is_axo_enabled; } + + /** + * Adds the markup necessary for displaying overlays and loaders for Axo on the checkout page. + * + * @param ContainerInterface $c The container. + */ + private function add_checkout_loader_markup( $c ) { + $settings = $c->get( 'wcgateway.settings' ); + assert( $settings instanceof Settings ); + + if ( $this->should_render_fastlane( $settings ) ) { + add_action( + 'woocommerce_checkout_before_customer_details', + function () { + echo '
'; + } + ); + + add_action( + 'woocommerce_checkout_after_customer_details', + function () { + echo '
'; + } + ); + + add_action( + 'woocommerce_checkout_billing', + function () { + echo '
'; + }, + 8 + ); + + add_action( + 'woocommerce_checkout_billing', + function () { + echo '
'; + }, + 12 + ); + } + } } From 97cd436efe36cc5651e9d65c9f898fe869ea239e Mon Sep 17 00:00:00 2001 From: Daniel Dudzic Date: Thu, 23 May 2024 10:51:27 +0200 Subject: [PATCH 028/104] Change opacity of the overlay color --- modules/ppcp-axo/resources/css/styles.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ppcp-axo/resources/css/styles.scss b/modules/ppcp-axo/resources/css/styles.scss index 9edd122fa..3513d0642 100644 --- a/modules/ppcp-axo/resources/css/styles.scss +++ b/modules/ppcp-axo/resources/css/styles.scss @@ -65,7 +65,7 @@ left: 0; width: 100%; height: 100%; - background: rgba(255, 255, 255, 0.8); + background: rgba(255, 255, 255, 0.5); display: flex; justify-content: center; align-items: center; From 451361dca03d1a75f783268f79cf593dd14394bc Mon Sep 17 00:00:00 2001 From: Daniel Dudzic Date: Thu, 23 May 2024 11:18:35 +0200 Subject: [PATCH 029/104] Fix Psalm errors --- modules/ppcp-axo/src/AxoModule.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/ppcp-axo/src/AxoModule.php b/modules/ppcp-axo/src/AxoModule.php index cdea50ee2..ba8d1d78b 100644 --- a/modules/ppcp-axo/src/AxoModule.php +++ b/modules/ppcp-axo/src/AxoModule.php @@ -299,8 +299,9 @@ class AxoModule implements ModuleInterface { * 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( $c ) { + private function add_checkout_loader_markup( ContainerInterface $c ): void { $settings = $c->get( 'wcgateway.settings' ); assert( $settings instanceof Settings ); From c011ce659f721edb57dad880d56bc16a8bdda013 Mon Sep 17 00:00:00 2001 From: Narek Zakarian Date: Thu, 23 May 2024 19:29:33 +0400 Subject: [PATCH 030/104] Fix more than 1 shipping option selected problem --- .../ppcp-api-client/src/Factory/ShippingOptionFactory.php | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/modules/ppcp-api-client/src/Factory/ShippingOptionFactory.php b/modules/ppcp-api-client/src/Factory/ShippingOptionFactory.php index 08af8c79e..3c21d4161 100644 --- a/modules/ppcp-api-client/src/Factory/ShippingOptionFactory.php +++ b/modules/ppcp-api-client/src/Factory/ShippingOptionFactory.php @@ -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() From da3fb6d8d64420f16a98407b3b06b82694971e6b Mon Sep 17 00:00:00 2001 From: Daniel Dudzic Date: Fri, 24 May 2024 09:38:40 +0200 Subject: [PATCH 031/104] Fix the two AXO bugs: Billing details missing and select another payment gateway missing --- modules/ppcp-axo/resources/js/AxoManager.js | 3 ++- modules/ppcp-axo/resources/js/Views/CardView.js | 4 ---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/modules/ppcp-axo/resources/js/AxoManager.js b/modules/ppcp-axo/resources/js/AxoManager.js index d3dd2fee7..79c2ac5a2 100644 --- a/modules/ppcp-axo/resources/js/AxoManager.js +++ b/modules/ppcp-axo/resources/js/AxoManager.js @@ -261,9 +261,10 @@ class AxoManager { if (scenario.axoProfileViews) { this.shippingView.activate(); + this.cardView.activate(); if (this.status.hasCard) { - this.cardView.activate(); + this.billingView.activate(); } // Move watermark to after shipping. diff --git a/modules/ppcp-axo/resources/js/Views/CardView.js b/modules/ppcp-axo/resources/js/Views/CardView.js index a72a38ff3..82c15079c 100644 --- a/modules/ppcp-axo/resources/js/Views/CardView.js +++ b/modules/ppcp-axo/resources/js/Views/CardView.js @@ -20,10 +20,6 @@ class CardView { if (data.isEmpty()) { return `
-
-
Please fill in your card details.
-
-

Add card details

${selectOtherPaymentMethod()}
`; From be9de3517580e8ed3d45339b516c5a0ac78127ca Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Fri, 24 May 2024 11:38:48 +0200 Subject: [PATCH 032/104] Rename `name` to `cardholderName` --- modules/ppcp-axo/resources/js/AxoManager.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ppcp-axo/resources/js/AxoManager.js b/modules/ppcp-axo/resources/js/AxoManager.js index d3dd2fee7..a6672d83e 100644 --- a/modules/ppcp-axo/resources/js/AxoManager.js +++ b/modules/ppcp-axo/resources/js/AxoManager.js @@ -728,7 +728,7 @@ class AxoManager { tokenizeData() { return { - name: { + cardholderName: { fullName: this.billingView.fullName() }, billingAddress: { From bd745cb7d4047266426196e25326e6d1175de73b Mon Sep 17 00:00:00 2001 From: Narek Zakarian Date: Fri, 24 May 2024 18:49:10 +0400 Subject: [PATCH 033/104] Do not update shipping when subscription --- .../js/modules/Helper/ShippingHandler.js | 48 ++++++++++--------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/modules/ppcp-button/resources/js/modules/Helper/ShippingHandler.js b/modules/ppcp-button/resources/js/modules/Helper/ShippingHandler.js index 49f45ab2d..311b8a949 100644 --- a/modules/ppcp-button/resources/js/modules/Helper/ShippingHandler.js +++ b/modules/ppcp-button/resources/js/modules/Helper/ShippingHandler.js @@ -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,19 +106,21 @@ 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, - }) - }); + 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); From 11244fa055cdd8402f535e0800e5552ea2344429 Mon Sep 17 00:00:00 2001 From: Narek Zakarian Date: Fri, 24 May 2024 18:54:44 +0400 Subject: [PATCH 034/104] Fix the tests --- tests/PHPUnit/WcGateway/Settings/SettingsListenerTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/PHPUnit/WcGateway/Settings/SettingsListenerTest.php b/tests/PHPUnit/WcGateway/Settings/SettingsListenerTest.php index 92d3f50d1..c34146f3e 100644 --- a/tests/PHPUnit/WcGateway/Settings/SettingsListenerTest.php +++ b/tests/PHPUnit/WcGateway/Settings/SettingsListenerTest.php @@ -60,7 +60,6 @@ class SettingsListenerTest extends ModularTestCase '', '', $billing_agreement_endpoint, - $subscription_helper, $logger ); From ac42b18592e1c94f9995ffeab30a46da8e6f9400 Mon Sep 17 00:00:00 2001 From: Narek Zakarian Date: Fri, 24 May 2024 19:08:44 +0400 Subject: [PATCH 035/104] Fix the Psalm errors --- .../src/Helper/WooCommerceOrderCreator.php | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/modules/ppcp-button/src/Helper/WooCommerceOrderCreator.php b/modules/ppcp-button/src/Helper/WooCommerceOrderCreator.php index 6f26626e3..313fb5e62 100644 --- a/modules/ppcp-button/src/Helper/WooCommerceOrderCreator.php +++ b/modules/ppcp-button/src/Helper/WooCommerceOrderCreator.php @@ -14,6 +14,7 @@ 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; @@ -22,6 +23,7 @@ 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 @@ -227,7 +229,7 @@ class WooCommerceOrderCreator { $items = $wc_order->get_items(); $items_in_package = array(); foreach ( $items as $item ) { - $items_in_package[] = $item->get_name() . ' × ' . $item->get_quantity(); + $items_in_package[] = $item->get_name() . ' × ' . (string) $item->get_quantity(); } $shipping->add_meta_data( __( 'Items', 'woocommerce-paypal-payments' ), implode( ', ', $items_in_package ) ); @@ -297,10 +299,11 @@ class WooCommerceOrderCreator { * * @param WC_Order $wc_order The WC order. * @param int $product_id The product ID. - * @return WC_Order The subscription order + * @return WC_Subscription The subscription order + * @throws RuntimeException If problem creating. */ - protected function create_subscription( WC_Order $wc_order, int $product_id ): WC_Order { - return wcs_create_subscription( + 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', @@ -309,5 +312,11 @@ class WooCommerceOrderCreator { 'customer_id' => $wc_order->get_customer_id(), ) ); + + if ( $subscription instanceof WP_Error ) { + throw new RuntimeException( $subscription->get_error_message() ); + } + + return $subscription; } } From f6d07d9225085d12304870bc3e34e83619179d87 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Fri, 24 May 2024 17:13:43 +0200 Subject: [PATCH 036/104] Adding frontend logger (WIP) --- modules/ppcp-axo/resources/js/AxoManager.js | 17 +++++ modules/ppcp-axo/services.php | 6 ++ modules/ppcp-axo/src/Assets/AxoManager.php | 7 +++ modules/ppcp-axo/src/AxoModule.php | 10 +++ .../ppcp-axo/src/FrontendLoggerEndpoint.php | 63 +++++++++++++++++++ 5 files changed, 103 insertions(+) create mode 100644 modules/ppcp-axo/src/FrontendLoggerEndpoint.php diff --git a/modules/ppcp-axo/resources/js/AxoManager.js b/modules/ppcp-axo/resources/js/AxoManager.js index a6672d83e..11605dbd6 100644 --- a/modules/ppcp-axo/resources/js/AxoManager.js +++ b/modules/ppcp-axo/resources/js/AxoManager.js @@ -88,6 +88,20 @@ class AxoManager { this.triggerGatewayChange(); } + async log(message, level = 'info') { + await fetch(axoConfig.ajax.frontend_logger.endpoint, { + method: 'POST', + credentials: 'same-origin', + body: JSON.stringify({ + nonce: axoConfig.ajax.frontend_logger.nonce, + log: { + message, + level, + } + }) + }); + } + registerEventHandlers() { this.$(document).on('change', 'input[name=payment_method]', (ev) => { @@ -697,6 +711,8 @@ class AxoManager { this.ensureBillingPhoneNumber(data); + this.log(`Ryan flow - submitted nonce: ${this.data.card.id}` ) + this.submit(this.data.card.id, data); } else { // Gary flow @@ -706,6 +722,7 @@ class AxoManager { this.cardComponent.getPaymentToken( this.tokenizeData() ).then((response) => { + this.log(`Gary flow - submitted nonce: ${response.id}` ) this.submit(response.id); }); } catch (e) { diff --git a/modules/ppcp-axo/services.php b/modules/ppcp-axo/services.php index ffed3f1e2..8af3df17d 100644 --- a/modules/ppcp-axo/services.php +++ b/modules/ppcp-axo/services.php @@ -225,4 +225,10 @@ return array( return '

' . $notice_content . '

'; }, + 'axo.endpoint.frontend-logger' => static function (ContainerInterface $container): FrontendLoggerEndpoint { + return new FrontendLoggerEndpoint( + $container->get( 'button.request-data' ), + $container->get( 'woocommerce.logger.woocommerce' ) + ); + }, ); diff --git a/modules/ppcp-axo/src/Assets/AxoManager.php b/modules/ppcp-axo/src/Assets/AxoManager.php index 24b574975..4e75f56fa 100644 --- a/modules/ppcp-axo/src/Assets/AxoManager.php +++ b/modules/ppcp-axo/src/Assets/AxoManager.php @@ -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; @@ -209,6 +210,12 @@ class AxoManager { ), '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() ), + ), + ), ); } diff --git a/modules/ppcp-axo/src/AxoModule.php b/modules/ppcp-axo/src/AxoModule.php index 3770117c6..7a10c34ad 100644 --- a/modules/ppcp-axo/src/AxoModule.php +++ b/modules/ppcp-axo/src/AxoModule.php @@ -239,6 +239,16 @@ 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 ); } diff --git a/modules/ppcp-axo/src/FrontendLoggerEndpoint.php b/modules/ppcp-axo/src/FrontendLoggerEndpoint.php new file mode 100644 index 000000000..34f8096f3 --- /dev/null +++ b/modules/ppcp-axo/src/FrontendLoggerEndpoint.php @@ -0,0 +1,63 @@ +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() ); + + $this->logger->info("[AXO] " . $data['log']['message']); + + wp_send_json_success(); + return true; + } +} From ff3091b3a6b6106c4559594b557e53faaafc15b8 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Fri, 24 May 2024 17:20:44 +0200 Subject: [PATCH 037/104] Use uuid for `data-client-metadata-id` --- .../ppcp-button/resources/js/modules/Helper/ScriptLoading.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/ppcp-button/resources/js/modules/Helper/ScriptLoading.js b/modules/ppcp-button/resources/js/modules/Helper/ScriptLoading.js index dce3136c8..6fbd419cb 100644 --- a/modules/ppcp-button/resources/js/modules/Helper/ScriptLoading.js +++ b/modules/ppcp-button/resources/js/modules/Helper/ScriptLoading.js @@ -63,9 +63,10 @@ export const loadPaypalScript = (config, onLoaded, onError = null) => { // Axo SDK options const sdkClientToken = config?.axo?.sdk_client_token; + const uuid = self.crypto.randomUUID(); 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 From a3c610bc1c56899da3888511bd8ff048674eacf7 Mon Sep 17 00:00:00 2001 From: Daniel Dudzic Date: Mon, 27 May 2024 11:33:34 +0200 Subject: [PATCH 038/104] Replace crypto.randomUUID with a dependency (3165) --- .../ppcp-button/resources/js/modules/Helper/ScriptLoading.js | 3 ++- package.json | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/modules/ppcp-button/resources/js/modules/Helper/ScriptLoading.js b/modules/ppcp-button/resources/js/modules/Helper/ScriptLoading.js index 6fbd419cb..55866fe9d 100644 --- a/modules/ppcp-button/resources/js/modules/Helper/ScriptLoading.js +++ b/modules/ppcp-button/resources/js/modules/Helper/ScriptLoading.js @@ -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,7 +64,7 @@ export const loadPaypalScript = (config, onLoaded, onError = null) => { // Axo SDK options const sdkClientToken = config?.axo?.sdk_client_token; - const uuid = self.crypto.randomUUID(); + const uuid = uuidv4().replace(/-/g, ''); if(sdkClientToken) { scriptOptions['data-sdk-client-token'] = sdkClientToken; scriptOptions['data-client-metadata-id'] = uuid; diff --git a/package.json b/package.json index bf52a8677..1e060a0ce 100644 --- a/package.json +++ b/package.json @@ -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" } } From cb190796ed25057b9116d98a4407dd037dbbd065 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Mon, 27 May 2024 12:21:38 +0200 Subject: [PATCH 039/104] Add error logs --- modules/ppcp-axo/resources/js/AxoManager.js | 42 ++++++++++++------- .../ppcp-axo/src/FrontendLoggerEndpoint.php | 23 ++++++++-- 2 files changed, 48 insertions(+), 17 deletions(-) diff --git a/modules/ppcp-axo/resources/js/AxoManager.js b/modules/ppcp-axo/resources/js/AxoManager.js index 11605dbd6..6659fbb2c 100644 --- a/modules/ppcp-axo/resources/js/AxoManager.js +++ b/modules/ppcp-axo/resources/js/AxoManager.js @@ -88,20 +88,6 @@ class AxoManager { this.triggerGatewayChange(); } - async log(message, level = 'info') { - await fetch(axoConfig.ajax.frontend_logger.endpoint, { - method: 'POST', - credentials: 'same-origin', - body: JSON.stringify({ - nonce: axoConfig.ajax.frontend_logger.nonce, - log: { - message, - level, - } - }) - }); - } - registerEventHandlers() { this.$(document).on('change', 'input[name=payment_method]', (ev) => { @@ -590,6 +576,8 @@ class AxoManager { async lookupCustomerByEmail() { const lookupResponse = await this.fastlane.identity.lookupCustomerByEmail(this.emailInput.value); + await this.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. @@ -598,6 +586,7 @@ class AxoManager { const authResponse = await this.fastlane.identity.triggerAuthenticationFlow(lookupResponse.customerContextId); log('AuthResponse', authResponse); + await this.log(`triggerAuthenticationFlow: ${JSON.stringify(authResponse)}`); if (authResponse.authenticationState === 'succeeded') { log(JSON.stringify(authResponse)); @@ -728,6 +717,7 @@ class AxoManager { } catch (e) { log('Error tokenizing.'); alert('Error tokenizing data.'); + this.log(`Error tokenizing data. ${e.message}`, 'error'); } } } @@ -807,7 +797,10 @@ class AxoManager { scrollTop: $notices.offset().top }, 500); } + console.error('Failure:', responseData); + this.log(`Error sending checkout form. ${responseData}`, 'error'); + this.hideLoading(); return; } @@ -817,6 +810,8 @@ class AxoManager { }) .catch(error => { console.error('Error:', error); + this.log(`Error sending checkout form. ${error.message}`, 'error'); + this.hideLoading(); }); @@ -893,6 +888,25 @@ class AxoManager { watermarkLoader.classList.toggle(loaderClass); } } + + async log(message, level = 'info') { + const endpoint = this.axoConfig?.ajax?.frontend_logger?.endpoint; + if(!endpoint) { + return; + } + + await fetch(endpoint, { + method: 'POST', + credentials: 'same-origin', + body: JSON.stringify({ + nonce: this.axoConfig.ajax.frontend_logger.nonce, + log: { + message, + level, + } + }) + }); + } } export default AxoManager; diff --git a/modules/ppcp-axo/src/FrontendLoggerEndpoint.php b/modules/ppcp-axo/src/FrontendLoggerEndpoint.php index 34f8096f3..ad7f01375 100644 --- a/modules/ppcp-axo/src/FrontendLoggerEndpoint.php +++ b/modules/ppcp-axo/src/FrontendLoggerEndpoint.php @@ -14,6 +14,9 @@ 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'; @@ -32,9 +35,15 @@ class FrontendLoggerEndpoint implements EndpointInterface { */ private $logger; - public function __construct(RequestData $request_data, LoggerInterface $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; + $this->logger = $logger; } /** @@ -54,8 +63,16 @@ class FrontendLoggerEndpoint implements EndpointInterface { */ public function handle_request(): bool { $data = $this->request_data->read_request( $this->nonce() ); + $level = $data['log']['level'] ?? 'info'; - $this->logger->info("[AXO] " . $data['log']['message']); + switch ( $level ) { + case 'error': + $this->logger->error( '[AXO] ' . esc_html( $data['log']['message'] ) ); + break; + default: + $this->logger->info( '[AXO] ' . esc_html( $data['log']['message'] ) ); + break; + } wp_send_json_success(); return true; From 5b8d9b352081cf642bf4172d50742a82ac0465fc Mon Sep 17 00:00:00 2001 From: Daniel Dudzic Date: Mon, 27 May 2024 12:27:59 +0200 Subject: [PATCH 040/104] AXO: Remove Billing Details from the Ryan flow (3173) --- .../ppcp-axo/resources/js/Views/BillingView.js | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/modules/ppcp-axo/resources/js/Views/BillingView.js b/modules/ppcp-axo/resources/js/Views/BillingView.js index aba44afcf..24f8a3682 100644 --- a/modules/ppcp-axo/resources/js/Views/BillingView.js +++ b/modules/ppcp-axo/resources/js/Views/BillingView.js @@ -34,22 +34,7 @@ class BillingView {
`; } - return ` -
-
-

Billing

- Edit -
-
${data.value('email')}
-
${data.value('company')}
-
${data.value('firstName')} ${data.value('lastName')}
-
${data.value('street1')}
-
${data.value('street2')}
-
${data.value('postCode')} ${data.value('city')}
-
${valueOfSelect('#billing_state', data.value('stateCode'))}
-
${valueOfSelect('#billing_country', data.value('countryCode'))}
-
- `; + return ``; }, fields: { email: { From 65a74d12a5a7c5157976ce9298bf942870bc08ae Mon Sep 17 00:00:00 2001 From: Daniel Dudzic Date: Mon, 27 May 2024 12:40:17 +0200 Subject: [PATCH 041/104] Replace backticks with single quotes --- modules/ppcp-axo/resources/js/Views/BillingView.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ppcp-axo/resources/js/Views/BillingView.js b/modules/ppcp-axo/resources/js/Views/BillingView.js index 24f8a3682..f2903d4ef 100644 --- a/modules/ppcp-axo/resources/js/Views/BillingView.js +++ b/modules/ppcp-axo/resources/js/Views/BillingView.js @@ -34,7 +34,7 @@ class BillingView {
`; } - return ``; + return ''; }, fields: { email: { From 90397b5dbd783fc108a16c20d1bc8dba15afff1b Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Mon, 27 May 2024 13:04:14 +0200 Subject: [PATCH 042/104] Fix phpcs --- modules/ppcp-axo/services.php | 2 +- modules/ppcp-axo/src/FrontendLoggerEndpoint.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/ppcp-axo/services.php b/modules/ppcp-axo/services.php index 8af3df17d..0020169a7 100644 --- a/modules/ppcp-axo/services.php +++ b/modules/ppcp-axo/services.php @@ -225,7 +225,7 @@ return array( return '

' . $notice_content . '

'; }, - 'axo.endpoint.frontend-logger' => static function (ContainerInterface $container): FrontendLoggerEndpoint { + 'axo.endpoint.frontend-logger' => static function ( ContainerInterface $container ): FrontendLoggerEndpoint { return new FrontendLoggerEndpoint( $container->get( 'button.request-data' ), $container->get( 'woocommerce.logger.woocommerce' ) diff --git a/modules/ppcp-axo/src/FrontendLoggerEndpoint.php b/modules/ppcp-axo/src/FrontendLoggerEndpoint.php index ad7f01375..2623051f8 100644 --- a/modules/ppcp-axo/src/FrontendLoggerEndpoint.php +++ b/modules/ppcp-axo/src/FrontendLoggerEndpoint.php @@ -38,7 +38,7 @@ class FrontendLoggerEndpoint implements EndpointInterface { /** * FrontendLoggerEndpoint constructor. * - * @param RequestData $request_data The request data helper. + * @param RequestData $request_data The request data helper. * @param LoggerInterface $logger The logger. */ public function __construct( RequestData $request_data, LoggerInterface $logger ) { @@ -62,7 +62,7 @@ class FrontendLoggerEndpoint implements EndpointInterface { * @throws Exception On Error. */ public function handle_request(): bool { - $data = $this->request_data->read_request( $this->nonce() ); + $data = $this->request_data->read_request( $this->nonce() ); $level = $data['log']['level'] ?? 'info'; switch ( $level ) { From 9b3febfdebdf67f005d7d07e4ac30eb480c78614 Mon Sep 17 00:00:00 2001 From: George Burduli Date: Mon, 27 May 2024 16:57:07 +0400 Subject: [PATCH 043/104] Make PayPal Subscription products unique in cart --- .../src/PayPalSubscriptionsModule.php | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/modules/ppcp-paypal-subscriptions/src/PayPalSubscriptionsModule.php b/modules/ppcp-paypal-subscriptions/src/PayPalSubscriptionsModule.php index d0aefecaa..79934074f 100644 --- a/modules/ppcp-paypal-subscriptions/src/PayPalSubscriptionsModule.php +++ b/modules/ppcp-paypal-subscriptions/src/PayPalSubscriptionsModule.php @@ -80,6 +80,8 @@ class PayPalSubscriptionsModule implements ModuleInterface { return; } + $foo = $product; + $subscriptions_api_handler = $c->get( 'paypal-subscriptions.api-handler' ); assert( $subscriptions_api_handler instanceof SubscriptionsApiHandler ); $this->update_subscription_product_meta( $product, $subscriptions_api_handler ); @@ -87,6 +89,30 @@ class PayPalSubscriptionsModule implements ModuleInterface { 12 ); + add_filter( 'woocommerce_add_to_cart_validation', function + ( + $passed_validation, + $product_id + ) { + if ( WC()->cart->is_empty() ) { + return $passed_validation; + } + + foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) { + if ( get_post_meta( $cart_item['product_id'], 'ppcp_subscription_product', true ) ) { + wc_add_notice(__('You can only have one subscription product in your cart.', 'woocommerce-paypal-payments'), 'error'); + return false; + } + + if ( get_post_meta( $product_id, 'ppcp_subscription_product', true ) ) { + wc_add_notice(__('You cannot add a subscription product to a cart with other items.', 'woocommerce-paypal-payments'), 'error'); + return false; + } + } + + return $passed_validation; + }, 10, 2); + add_action( 'woocommerce_save_product_variation', /** From f42814fc1d701c6f22b6f6f777f2198cfcf1c808 Mon Sep 17 00:00:00 2001 From: George Burduli Date: Mon, 27 May 2024 17:01:52 +0400 Subject: [PATCH 044/104] Remove test string --- .../ppcp-paypal-subscriptions/src/PayPalSubscriptionsModule.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/modules/ppcp-paypal-subscriptions/src/PayPalSubscriptionsModule.php b/modules/ppcp-paypal-subscriptions/src/PayPalSubscriptionsModule.php index 79934074f..27333072f 100644 --- a/modules/ppcp-paypal-subscriptions/src/PayPalSubscriptionsModule.php +++ b/modules/ppcp-paypal-subscriptions/src/PayPalSubscriptionsModule.php @@ -80,8 +80,6 @@ class PayPalSubscriptionsModule implements ModuleInterface { return; } - $foo = $product; - $subscriptions_api_handler = $c->get( 'paypal-subscriptions.api-handler' ); assert( $subscriptions_api_handler instanceof SubscriptionsApiHandler ); $this->update_subscription_product_meta( $product, $subscriptions_api_handler ); From 34acb8ce8020116a0efc407f6badd519e06cd4dd Mon Sep 17 00:00:00 2001 From: George Burduli Date: Mon, 27 May 2024 17:07:31 +0400 Subject: [PATCH 045/104] Fix Psalm, phpcs errors --- .../src/PayPalSubscriptionsModule.php | 50 +++++++++++-------- 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/modules/ppcp-paypal-subscriptions/src/PayPalSubscriptionsModule.php b/modules/ppcp-paypal-subscriptions/src/PayPalSubscriptionsModule.php index 27333072f..c5f0e7146 100644 --- a/modules/ppcp-paypal-subscriptions/src/PayPalSubscriptionsModule.php +++ b/modules/ppcp-paypal-subscriptions/src/PayPalSubscriptionsModule.php @@ -87,29 +87,35 @@ class PayPalSubscriptionsModule implements ModuleInterface { 12 ); - add_filter( 'woocommerce_add_to_cart_validation', function - ( - $passed_validation, - $product_id - ) { - if ( WC()->cart->is_empty() ) { + add_filter( + 'woocommerce_add_to_cart_validation', + /** + * Param types removed to avoid third-party issues. + * + * @psalm-suppress MissingClosureParamType + */ + function ( $passed_validation, $product_id ) { + if ( WC()->cart->is_empty() ) { + return $passed_validation; + } + + foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) { + if ( get_post_meta( $cart_item['product_id'], 'ppcp_subscription_product', true ) ) { + wc_add_notice( __( 'You can only have one subscription product in your cart.', 'woocommerce-paypal-payments' ), 'error' ); + return false; + } + + if ( get_post_meta( $product_id, 'ppcp_subscription_product', true ) ) { + wc_add_notice( __( 'You cannot add a subscription product to a cart with other items.', 'woocommerce-paypal-payments' ), 'error' ); + return false; + } + } + return $passed_validation; - } - - foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) { - if ( get_post_meta( $cart_item['product_id'], 'ppcp_subscription_product', true ) ) { - wc_add_notice(__('You can only have one subscription product in your cart.', 'woocommerce-paypal-payments'), 'error'); - return false; - } - - if ( get_post_meta( $product_id, 'ppcp_subscription_product', true ) ) { - wc_add_notice(__('You cannot add a subscription product to a cart with other items.', 'woocommerce-paypal-payments'), 'error'); - return false; - } - } - - return $passed_validation; - }, 10, 2); + }, + 10, + 2 + ); add_action( 'woocommerce_save_product_variation', From 64dee95a76b31c26943d3cfee84c21d3286f3197 Mon Sep 17 00:00:00 2001 From: Daniel Dudzic Date: Mon, 27 May 2024 15:59:46 +0200 Subject: [PATCH 046/104] AXO: Disable payment method selection during OTP interaction (3174) --- modules/ppcp-axo/resources/js/AxoManager.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/modules/ppcp-axo/resources/js/AxoManager.js b/modules/ppcp-axo/resources/js/AxoManager.js index 7c2a21964..5229573ae 100644 --- a/modules/ppcp-axo/resources/js/AxoManager.js +++ b/modules/ppcp-axo/resources/js/AxoManager.js @@ -571,7 +571,9 @@ class AxoManager { page_type: 'checkout' }); + this.disableGatewaySelection(); await this.lookupCustomerByEmail(); + this.enableGatewaySelection(); } async lookupCustomerByEmail() { @@ -659,6 +661,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, From 63300d941298b6f3e5da383258b8fb2fdc8a14e2 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Mon, 27 May 2024 16:41:37 +0200 Subject: [PATCH 047/104] Change error message to standard WooCommerce copy --- .../ppcp-wc-gateway/src/Processor/OrderProcessor.php | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/modules/ppcp-wc-gateway/src/Processor/OrderProcessor.php b/modules/ppcp-wc-gateway/src/Processor/OrderProcessor.php index d2b145b0b..4d1363361 100644 --- a/modules/ppcp-wc-gateway/src/Processor/OrderProcessor.php +++ b/modules/ppcp-wc-gateway/src/Processor/OrderProcessor.php @@ -220,9 +220,14 @@ 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.', - 'woocommerce-paypal-payments' + sprintf( + // translators: %s: Order history URL on My Account section. + esc_attr__( + 'There was an error processing your order. Please check for any charges in your payment method and review your order history before placing the order again.', + // phpcs:ignore WordPress.WP.I18n.TextDomainMismatch -- Intentionally "woocommerce" to reflect the original message. + 'woocommerce' + ), + esc_url( wc_get_account_endpoint_url( 'orders' ) ) ) ); } From 0245fa640bd163e7a4ed92fc50ae414498924f82 Mon Sep 17 00:00:00 2001 From: Narek Zakarian Date: Mon, 27 May 2024 19:57:00 +0400 Subject: [PATCH 048/104] Determine if venmo button is clicked and do not pass shipping callback handlers --- .../resources/js/modules/Renderer/Renderer.js | 37 +++++++++++++------ 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/modules/ppcp-button/resources/js/modules/Renderer/Renderer.js b/modules/ppcp-button/resources/js/modules/Renderer/Renderer.js index 895a7845b..a95621456 100644 --- a/modules/ppcp-button/resources/js/modules/Renderer/Renderer.js +++ b/modules/ppcp-button/resources/js/modules/Renderer/Renderer.js @@ -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 ?? '')); } From 49d943e25bb6cc7abf66e25842b12da87ae17f8d Mon Sep 17 00:00:00 2001 From: Narek Zakarian Date: Mon, 27 May 2024 19:57:30 +0400 Subject: [PATCH 049/104] use funding source instead of payment source as they are same --- modules/ppcp-button/src/Endpoint/CreateOrderEndpoint.php | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/modules/ppcp-button/src/Endpoint/CreateOrderEndpoint.php b/modules/ppcp-button/src/Endpoint/CreateOrderEndpoint.php index fbfb22309..868f9bf6c 100644 --- a/modules/ppcp-button/src/Endpoint/CreateOrderEndpoint.php +++ b/modules/ppcp-button/src/Endpoint/CreateOrderEndpoint.php @@ -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'; } } From 84616d07da5afbd0cc0f86c610eff15454f88757 Mon Sep 17 00:00:00 2001 From: Narek Zakarian Date: Mon, 27 May 2024 19:57:48 +0400 Subject: [PATCH 050/104] dont need to pass the payment source --- .../resources/js/modules/ActionHandler/CartActionHandler.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/modules/ppcp-button/resources/js/modules/ActionHandler/CartActionHandler.js b/modules/ppcp-button/resources/js/modules/ActionHandler/CartActionHandler.js index 2b3066395..0400c8013 100644 --- a/modules/ppcp-button/resources/js/modules/ActionHandler/CartActionHandler.js +++ b/modules/ppcp-button/resources/js/modules/ActionHandler/CartActionHandler.js @@ -60,8 +60,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(); From 94259b6bcae2472b49b92f73c1f97de4dc3d50f3 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Mon, 27 May 2024 18:28:05 +0200 Subject: [PATCH 051/104] Fix asset URL double-slash in PayLater settings Refactored service return value to align with plugin-wide convention: unslashed folder path with slash-prefixed relative file path. --- modules/ppcp-paylater-configurator/services.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/ppcp-paylater-configurator/services.php b/modules/ppcp-paylater-configurator/services.php index de282b4c1..8be798d24 100644 --- a/modules/ppcp-paylater-configurator/services.php +++ b/modules/ppcp-paylater-configurator/services.php @@ -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' ); }, From cf77c37d6958cb9e1947bda07e142147b61799d7 Mon Sep 17 00:00:00 2001 From: George Burduli Date: Tue, 28 May 2024 15:57:13 +0400 Subject: [PATCH 052/104] Replace get_post_meta() with get_meta() for future-proofing --- .../src/PayPalSubscriptionsModule.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/modules/ppcp-paypal-subscriptions/src/PayPalSubscriptionsModule.php b/modules/ppcp-paypal-subscriptions/src/PayPalSubscriptionsModule.php index c5f0e7146..76f924fed 100644 --- a/modules/ppcp-paypal-subscriptions/src/PayPalSubscriptionsModule.php +++ b/modules/ppcp-paypal-subscriptions/src/PayPalSubscriptionsModule.php @@ -94,18 +94,21 @@ class PayPalSubscriptionsModule implements ModuleInterface { * * @psalm-suppress MissingClosureParamType */ - function ( $passed_validation, $product_id ) { + static function ( $passed_validation, $product_id ) { if ( WC()->cart->is_empty() ) { return $passed_validation; } foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) { - if ( get_post_meta( $cart_item['product_id'], 'ppcp_subscription_product', true ) ) { + $cart_product = wc_get_product( $cart_item['product_id'] ); + if ( $cart_product && $cart_product->get_meta( 'ppcp_subscription_product', true ) ) { wc_add_notice( __( 'You can only have one subscription product in your cart.', 'woocommerce-paypal-payments' ), 'error' ); return false; } - if ( get_post_meta( $product_id, 'ppcp_subscription_product', true ) ) { + $product = wc_get_product( $product_id ); + + if ( $product && $product->get_meta( 'ppcp_subscription_product', true ) ) { wc_add_notice( __( 'You cannot add a subscription product to a cart with other items.', 'woocommerce-paypal-payments' ), 'error' ); return false; } From 47e6b8d281cf191bf4f2d3ed7934b4d4c15dc1e4 Mon Sep 17 00:00:00 2001 From: George Burduli Date: Tue, 28 May 2024 16:26:00 +0400 Subject: [PATCH 053/104] Check 'sold individually' option on product save if ppcp subscription is active --- .../src/PayPalSubscriptionsModule.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/modules/ppcp-paypal-subscriptions/src/PayPalSubscriptionsModule.php b/modules/ppcp-paypal-subscriptions/src/PayPalSubscriptionsModule.php index 76f924fed..301e631d1 100644 --- a/modules/ppcp-paypal-subscriptions/src/PayPalSubscriptionsModule.php +++ b/modules/ppcp-paypal-subscriptions/src/PayPalSubscriptionsModule.php @@ -80,6 +80,10 @@ class PayPalSubscriptionsModule implements ModuleInterface { return; } + if ( $product->get_meta( '_ppcp_enable_subscription_product', true ) === 'yes' ) { + update_metadata( 'post', $product_id, '_sold_individually', 'yes', 'no' ); + } + $subscriptions_api_handler = $c->get( 'paypal-subscriptions.api-handler' ); assert( $subscriptions_api_handler instanceof SubscriptionsApiHandler ); $this->update_subscription_product_meta( $product, $subscriptions_api_handler ); From 9db695af75de1d0a613607755c8185e3ec8a0650 Mon Sep 17 00:00:00 2001 From: George Burduli Date: Tue, 28 May 2024 16:49:41 +0400 Subject: [PATCH 054/104] Disable 'Sold Individually' checkbox --- .../resources/js/paypal-subscription.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/modules/ppcp-paypal-subscriptions/resources/js/paypal-subscription.js b/modules/ppcp-paypal-subscriptions/resources/js/paypal-subscription.js index 7d848091f..0c4466d8d 100644 --- a/modules/ppcp-paypal-subscriptions/resources/js/paypal-subscription.js +++ b/modules/ppcp-paypal-subscriptions/resources/js/paypal-subscription.js @@ -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 = () => { From 639ced5c9123fe57db0d437fbba5c966ef5ad217 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Tue, 28 May 2024 16:41:33 +0200 Subject: [PATCH 055/104] =?UTF-8?q?=F0=9F=A9=B9=20Fix=20incorrect=20links?= =?UTF-8?q?=20to=20AP/GP=20payment=20settings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The settings button for Apple Pay and Google Pay now directs users to the correct tab and section. --- modules/ppcp-applepay/services.php | 2 +- modules/ppcp-googlepay/services.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/ppcp-applepay/services.php b/modules/ppcp-applepay/services.php index 003c8226c..6079fcb40 100644 --- a/modules/ppcp-applepay/services.php +++ b/modules/ppcp-applepay/services.php @@ -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( diff --git a/modules/ppcp-googlepay/services.php b/modules/ppcp-googlepay/services.php index f7e195821..6f41db792 100644 --- a/modules/ppcp-googlepay/services.php +++ b/modules/ppcp-googlepay/services.php @@ -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( From 8e573918ef21a0c71f5bc166a2c575b4dccac51f Mon Sep 17 00:00:00 2001 From: Daniel Dudzic Date: Tue, 28 May 2024 18:32:49 +0200 Subject: [PATCH 056/104] AXO: Update Fastlane settings (3166) --- modules/ppcp-axo/extensions.php | 42 +++++++++---------- .../src/Helper/PropertiesDictionary.php | 12 ++++++ 2 files changed, 31 insertions(+), 23 deletions(-) diff --git a/modules/ppcp-axo/extensions.php b/modules/ppcp-axo/extensions.php index e6d0f4836..8d8bf4378 100644 --- a/modules/ppcp-axo/extensions.php +++ b/modules/ppcp-axo/extensions.php @@ -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. -

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.

', - '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' ), '', @@ -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', diff --git a/modules/ppcp-axo/src/Helper/PropertiesDictionary.php b/modules/ppcp-axo/src/Helper/PropertiesDictionary.php index 024c3649c..a07c4ace3 100644 --- a/modules/ppcp-axo/src/Helper/PropertiesDictionary.php +++ b/modules/ppcp-axo/src/Helper/PropertiesDictionary.php @@ -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' ), + ); + } + } From 0cc4e1f9ceb3004c29a2e3be8844d448bbf7dbe4 Mon Sep 17 00:00:00 2001 From: Danae Millan Date: Wed, 29 May 2024 01:55:24 +0200 Subject: [PATCH 057/104] woorelease: Product version bump update --- changelog.txt | 2 +- readme.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/changelog.txt b/changelog.txt index 31875da81..3ca38e0e2 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,6 +1,6 @@ *** Changelog *** -= 2.7.1 - xxxx-xx-xx = += 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 diff --git a/readme.txt b/readme.txt index 771a68552..0c6bdbb1a 100644 --- a/readme.txt +++ b/readme.txt @@ -179,7 +179,7 @@ If you encounter issues with the PayPal buttons not appearing after an update, p == Changelog == -= 2.7.1 - xxxx-xx-xx = += 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 From 28257e3e38971a3b42f0d0f59e636e09438f22e0 Mon Sep 17 00:00:00 2001 From: George Burduli Date: Wed, 29 May 2024 11:15:44 +0400 Subject: [PATCH 058/104] Move function to appropriate place --- .../src/PayPalSubscriptionsModule.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/modules/ppcp-paypal-subscriptions/src/PayPalSubscriptionsModule.php b/modules/ppcp-paypal-subscriptions/src/PayPalSubscriptionsModule.php index 301e631d1..c1242b7cc 100644 --- a/modules/ppcp-paypal-subscriptions/src/PayPalSubscriptionsModule.php +++ b/modules/ppcp-paypal-subscriptions/src/PayPalSubscriptionsModule.php @@ -80,9 +80,7 @@ class PayPalSubscriptionsModule implements ModuleInterface { return; } - if ( $product->get_meta( '_ppcp_enable_subscription_product', true ) === 'yes' ) { - update_metadata( 'post', $product_id, '_sold_individually', 'yes', 'no' ); - } + $subscriptions_api_handler = $c->get( 'paypal-subscriptions.api-handler' ); assert( $subscriptions_api_handler instanceof SubscriptionsApiHandler ); @@ -697,6 +695,11 @@ class PayPalSubscriptionsModule implements ModuleInterface { 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 ); + + if ( $product->get_meta( '_ppcp_enable_subscription_product', true ) === 'yes' ) { + update_metadata( 'post', $product->get_id(), '_sold_individually', 'yes', 'no' ); + } + return; } From cc2e121e2af50aa4ed8fadbbc3fb1a4c5c2a30f2 Mon Sep 17 00:00:00 2001 From: George Burduli Date: Wed, 29 May 2024 11:48:41 +0400 Subject: [PATCH 059/104] Remove whitespace --- .../ppcp-paypal-subscriptions/src/PayPalSubscriptionsModule.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/modules/ppcp-paypal-subscriptions/src/PayPalSubscriptionsModule.php b/modules/ppcp-paypal-subscriptions/src/PayPalSubscriptionsModule.php index c1242b7cc..ef0af81ed 100644 --- a/modules/ppcp-paypal-subscriptions/src/PayPalSubscriptionsModule.php +++ b/modules/ppcp-paypal-subscriptions/src/PayPalSubscriptionsModule.php @@ -80,8 +80,6 @@ class PayPalSubscriptionsModule implements ModuleInterface { return; } - - $subscriptions_api_handler = $c->get( 'paypal-subscriptions.api-handler' ); assert( $subscriptions_api_handler instanceof SubscriptionsApiHandler ); $this->update_subscription_product_meta( $product, $subscriptions_api_handler ); From 07d041fe1f0a16d83c326fce067c76ddda5c506e Mon Sep 17 00:00:00 2001 From: George Burduli Date: Wed, 29 May 2024 13:42:14 +0400 Subject: [PATCH 060/104] Replace update_metadata() with set_sold_individually() --- .../ppcp-paypal-subscriptions/src/PayPalSubscriptionsModule.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ppcp-paypal-subscriptions/src/PayPalSubscriptionsModule.php b/modules/ppcp-paypal-subscriptions/src/PayPalSubscriptionsModule.php index ef0af81ed..1e9234cf6 100644 --- a/modules/ppcp-paypal-subscriptions/src/PayPalSubscriptionsModule.php +++ b/modules/ppcp-paypal-subscriptions/src/PayPalSubscriptionsModule.php @@ -695,7 +695,7 @@ class PayPalSubscriptionsModule implements ModuleInterface { $subscriptions_api_handler->update_plan( $product ); if ( $product->get_meta( '_ppcp_enable_subscription_product', true ) === 'yes' ) { - update_metadata( 'post', $product->get_id(), '_sold_individually', 'yes', 'no' ); + $product->set_sold_individually( true ); } return; From 1524d03ba2b383ba413367d4eda2a7726ffad706 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Wed, 29 May 2024 14:53:58 +0200 Subject: [PATCH 061/104] Consolidate logging --- modules/ppcp-axo/resources/js/AxoManager.js | 72 ++++++------------- modules/ppcp-axo/resources/js/Helper/Debug.js | 26 ++++++- 2 files changed, 45 insertions(+), 53 deletions(-) diff --git a/modules/ppcp-axo/resources/js/AxoManager.js b/modules/ppcp-axo/resources/js/AxoManager.js index e868eb207..b93703ace 100644 --- a/modules/ppcp-axo/resources/js/AxoManager.js +++ b/modules/ppcp-axo/resources/js/AxoManager.js @@ -60,7 +60,6 @@ class AxoManager { } document.axoDebugObject = () => { - console.log(this); return this; } @@ -166,9 +165,8 @@ class AxoManager { this.$('form.woocommerce-checkout input').on('keydown', async (ev) => { if(ev.key === 'Enter' && getCurrentPaymentMethod() === 'ppcp-axo-gateway' ) { ev.preventDefault(); - log('Enter key attempt'); - log('emailInput', this.emailInput.value); - log('this.lastEmailCheckedIdentity', this.lastEmailCheckedIdentity); + log(`Enter key attempt - emailInput: ${this.emailInput.value}`); + log(`this.lastEmailCheckedIdentity: ${this.lastEmailCheckedIdentity}`); if (this.emailInput && this.lastEmailCheckedIdentity !== this.emailInput.value) { await this.onChangeEmail(); } @@ -177,7 +175,7 @@ class AxoManager { // Clear last email checked identity when email field is focused. this.$('#billing_email_field input').on('focus', (ev) => { - log('Clear the last email checked:', this.lastEmailCheckedIdentity); + log(`Clear the last email checked: ${this.lastEmailCheckedIdentity}`); this.lastEmailCheckedIdentity = ''; }); @@ -214,7 +212,7 @@ class AxoManager { this.status.hasProfile ); - log('Scenario', scenario); + log(`Scenario: ${scenario}`); // Reset some elements to a default status. this.el.watermarkContainer.hide(); @@ -376,7 +374,7 @@ class AxoManager { setStatus(key, value) { this.status[key] = value; - log('Status updated', JSON.parse(JSON.stringify(this.status))); + log(`Status updated: ${JSON.parse(JSON.stringify(this.status))}`); document.dispatchEvent(new CustomEvent("axo_status_updated", {detail: this.status})); @@ -388,9 +386,8 @@ class AxoManager { this.initFastlane(); this.setStatus('active', true); - log('Attempt on activation'); - log('emailInput', this.emailInput.value); - log('this.lastEmailCheckedIdentity', this.lastEmailCheckedIdentity); + log(`Attempt on activation - emailInput: ${this.emailInput.value}`); + log(`this.lastEmailCheckedIdentity: ${this.lastEmailCheckedIdentity}`); if (this.emailInput && this.lastEmailCheckedIdentity !== this.emailInput.value) { this.onChangeEmail(); } @@ -512,17 +509,15 @@ class AxoManager { } else { this.emailInput.addEventListener('change', async ()=> { - log('Change event attempt'); - log('emailInput', this.emailInput.value); - log('this.lastEmailCheckedIdentity', this.lastEmailCheckedIdentity); + log(`Change event attempt - emailInput: ${this.emailInput.value}`); + log(`this.lastEmailCheckedIdentity: ${this.lastEmailCheckedIdentity}`); if (this.emailInput && this.lastEmailCheckedIdentity !== this.emailInput.value) { this.onChangeEmail(); } }); - log('Last, this.emailInput.value attempt'); - log('emailInput', this.emailInput.value); - log('this.lastEmailCheckedIdentity', this.lastEmailCheckedIdentity); + log(`Last, this.emailInput.value attempt - emailInput: ${this.emailInput.value}`); + log(`this.lastEmailCheckedIdentity: ${this.lastEmailCheckedIdentity}`); if (this.emailInput.value) { this.onChangeEmail(); } @@ -542,7 +537,7 @@ class AxoManager { return; } - log('Email changed: ' + (this.emailInput ? this.emailInput.value : '')); + log(`Email changed: ${this.emailInput ? this.emailInput.value : ''}`); this.$(this.el.paymentContainer.selector + '-detail').html(''); this.$(this.el.paymentContainer.selector + '-form').html(''); @@ -579,7 +574,7 @@ class AxoManager { async lookupCustomerByEmail() { const lookupResponse = await this.fastlane.identity.lookupCustomerByEmail(this.emailInput.value); - await this.log(`lookupCustomerByEmail: ${JSON.stringify(lookupResponse)}`); + log(`lookupCustomerByEmail: ${JSON.stringify(lookupResponse)}`); if (lookupResponse.customerContextId) { // Email is associated with a Connect profile or a PayPal member. @@ -588,12 +583,9 @@ class AxoManager { const authResponse = await this.fastlane.identity.triggerAuthenticationFlow(lookupResponse.customerContextId); - log('AuthResponse', authResponse); - await this.log(`triggerAuthenticationFlow: ${JSON.stringify(authResponse)}`); + log(`AuthResponse - triggerAuthenticationFlow: ${JSON.stringify(authResponse)}`); if (authResponse.authenticationState === 'succeeded') { - log(JSON.stringify(authResponse)); - const shippingData = authResponse.profileData.shippingAddress; if (shippingData) { this.setShipping(shippingData); @@ -700,7 +692,7 @@ class AxoManager { // TODO: validate data. if (this.data.card) { // Ryan flow - log('Ryan flow.'); + log('Starting Ryan flow.'); this.$('#ship-to-different-address-checkbox').prop('checked', 'checked'); @@ -711,24 +703,23 @@ class AxoManager { this.ensureBillingPhoneNumber(data); - this.log(`Ryan flow - submitted nonce: ${this.data.card.id}` ) + 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) => { - this.log(`Gary flow - submitted nonce: ${response.id}` ) + log(`Gary flow - submitted nonce: ${response.id}` ) this.submit(response.id); }); } catch (e) { - log('Error tokenizing.'); alert('Error tokenizing data.'); - this.log(`Error tokenizing data. ${e.message}`, 'error'); + log(`Error tokenizing data. ${e.message}`, 'error'); } } } @@ -809,8 +800,7 @@ class AxoManager { }, 500); } - console.error('Failure:', responseData); - this.log(`Error sending checkout form. ${responseData}`, 'error'); + log(`Error sending checkout form. ${responseData}`, 'error'); this.hideLoading(); return; @@ -820,8 +810,7 @@ class AxoManager { } }) .catch(error => { - console.error('Error:', error); - this.log(`Error sending checkout form. ${error.message}`, 'error'); + log(`Error sending checkout form. ${error.message}`, 'error'); this.hideLoading(); }); @@ -899,25 +888,6 @@ class AxoManager { watermarkLoader.classList.toggle(loaderClass); } } - - async log(message, level = 'info') { - const endpoint = this.axoConfig?.ajax?.frontend_logger?.endpoint; - if(!endpoint) { - return; - } - - await fetch(endpoint, { - method: 'POST', - credentials: 'same-origin', - body: JSON.stringify({ - nonce: this.axoConfig.ajax.frontend_logger.nonce, - log: { - message, - level, - } - }) - }); - } } export default AxoManager; diff --git a/modules/ppcp-axo/resources/js/Helper/Debug.js b/modules/ppcp-axo/resources/js/Helper/Debug.js index e473d4a3b..8073c2683 100644 --- a/modules/ppcp-axo/resources/js/Helper/Debug.js +++ b/modules/ppcp-axo/resources/js/Helper/Debug.js @@ -1,4 +1,26 @@ +export function log(message, level = 'info') { + const endpoint = this.axoConfig?.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: this.axoConfig.ajax.frontend_logger.nonce, + log: { + message, + level, + } + }) + }).then(() => { + switch (level) { + case 'error': + console.error(`[AXO] ${message}`); + break; + default: + console.log(`[AXO] ${message}`); + } + }); } From b3ffa1030d311283323556e7595b64b4046c46d1 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Wed, 29 May 2024 15:21:21 +0200 Subject: [PATCH 062/104] Fix stringify objects --- modules/ppcp-axo/resources/js/AxoManager.js | 4 ++-- modules/ppcp-axo/resources/js/Helper/Debug.js | 4 ++-- yarn.lock | 5 +++++ 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/modules/ppcp-axo/resources/js/AxoManager.js b/modules/ppcp-axo/resources/js/AxoManager.js index b93703ace..960da7f26 100644 --- a/modules/ppcp-axo/resources/js/AxoManager.js +++ b/modules/ppcp-axo/resources/js/AxoManager.js @@ -212,7 +212,7 @@ class AxoManager { this.status.hasProfile ); - log(`Scenario: ${scenario}`); + log(`Scenario: ${JSON.stringify(scenario)}`); // Reset some elements to a default status. this.el.watermarkContainer.hide(); @@ -374,7 +374,7 @@ class AxoManager { setStatus(key, value) { this.status[key] = value; - log(`Status updated: ${JSON.parse(JSON.stringify(this.status))}`); + log(`Status updated: ${JSON.stringify(this.status)}`); document.dispatchEvent(new CustomEvent("axo_status_updated", {detail: this.status})); diff --git a/modules/ppcp-axo/resources/js/Helper/Debug.js b/modules/ppcp-axo/resources/js/Helper/Debug.js index 8073c2683..c08fa8087 100644 --- a/modules/ppcp-axo/resources/js/Helper/Debug.js +++ b/modules/ppcp-axo/resources/js/Helper/Debug.js @@ -1,5 +1,5 @@ export function log(message, level = 'info') { - const endpoint = this.axoConfig?.ajax?.frontend_logger?.endpoint; + const endpoint = window.wc_ppcp_axo?.ajax?.frontend_logger?.endpoint; if(!endpoint) { return; } @@ -8,7 +8,7 @@ export function log(message, level = 'info') { method: 'POST', credentials: 'same-origin', body: JSON.stringify({ - nonce: this.axoConfig.ajax.frontend_logger.nonce, + nonce: window.wc_ppcp_axo.ajax.frontend_logger.nonce, log: { message, level, diff --git a/yarn.lock b/yarn.lock index 722df19c5..81af54726 100644 --- a/yarn.lock +++ b/yarn.lock @@ -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" From f08d6c089cfff30e08859624416a2eee78c30ec1 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Wed, 29 May 2024 15:28:37 +0200 Subject: [PATCH 063/104] Fix stringify objects --- modules/ppcp-axo/src/FrontendLoggerEndpoint.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/ppcp-axo/src/FrontendLoggerEndpoint.php b/modules/ppcp-axo/src/FrontendLoggerEndpoint.php index 2623051f8..e17a819e4 100644 --- a/modules/ppcp-axo/src/FrontendLoggerEndpoint.php +++ b/modules/ppcp-axo/src/FrontendLoggerEndpoint.php @@ -67,10 +67,10 @@ class FrontendLoggerEndpoint implements EndpointInterface { switch ( $level ) { case 'error': - $this->logger->error( '[AXO] ' . esc_html( $data['log']['message'] ) ); + $this->logger->error( '[AXO] ' . $data['log']['message'] ); break; default: - $this->logger->info( '[AXO] ' . esc_html( $data['log']['message'] ) ); + $this->logger->info( '[AXO] ' . $data['log']['message'] ); break; } From b1bfa2940b0414af8c19d55617da0b4eb92ec0f3 Mon Sep 17 00:00:00 2001 From: George Burduli Date: Wed, 29 May 2024 17:44:28 +0400 Subject: [PATCH 064/104] Disable Pay Upon Invoice if billing/shipping country not set --- modules/ppcp-wc-gateway/src/Helper/PayUponInvoiceHelper.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/ppcp-wc-gateway/src/Helper/PayUponInvoiceHelper.php b/modules/ppcp-wc-gateway/src/Helper/PayUponInvoiceHelper.php index 823caf611..c140b0976 100644 --- a/modules/ppcp-wc-gateway/src/Helper/PayUponInvoiceHelper.php +++ b/modules/ppcp-wc-gateway/src/Helper/PayUponInvoiceHelper.php @@ -57,13 +57,13 @@ class PayUponInvoiceHelper { // phpcs:ignore WordPress.Security.NonceVerification.Missing $billing_country = wc_clean( wp_unslash( $_POST['country'] ?? '' ) ); - if ( $billing_country && 'DE' !== $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 ) { + if ( empty( $shipping_country ) || 'DE' !== $shipping_country ) { return false; } From 709871820ff0aed9d0918a709959c880aea93fb4 Mon Sep 17 00:00:00 2001 From: Daniel Dudzic Date: Wed, 29 May 2024 15:59:56 +0200 Subject: [PATCH 065/104] Fix Smart Buttons on Elementor checkout --- modules.php | 2 +- .../ppcp-button/src/Helper/ContextTrait.php | 78 ++++++++++--------- modules/ppcp-compat/src/CompatModule.php | 30 +++++++ .../src/Helper/CartCheckoutDetector.php | 11 ++- 4 files changed, 81 insertions(+), 40 deletions(-) diff --git a/modules.php b/modules.php index 06a9f482d..3672dc515 100644 --- a/modules.php +++ b/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" )(), diff --git a/modules/ppcp-button/src/Helper/ContextTrait.php b/modules/ppcp-button/src/Helper/ContextTrait.php index 9b5874bfa..a034eda8b 100644 --- a/modules/ppcp-button/src/Helper/ContextTrait.php +++ b/modules/ppcp-button/src/Helper/ContextTrait.php @@ -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 ); } /** diff --git a/modules/ppcp-compat/src/CompatModule.php b/modules/ppcp-compat/src/CompatModule.php index 44cfd1988..a5772dccd 100644 --- a/modules/ppcp-compat/src/CompatModule.php +++ b/modules/ppcp-compat/src/CompatModule.php @@ -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; /** @@ -54,6 +55,7 @@ class CompatModule implements ModuleInterface { $this->migrate_smart_button_settings( $c ); $this->fix_page_builders(); + $this->set_elementor_checkout_context(); } /** @@ -329,4 +331,32 @@ 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 ) { + if ( CartCheckoutDetector::has_elementor_checkout( $page_id ) ) { + add_filter( + 'woocommerce_paypal_payments_context', + function ( $context ): string { + // Default context. + if ( 'mini-cart' === $context ) { + return 'checkout'; + } + return $context; + } + ); + } + } + } + ); + } } diff --git a/modules/ppcp-wc-gateway/src/Helper/CartCheckoutDetector.php b/modules/ppcp-wc-gateway/src/Helper/CartCheckoutDetector.php index faac7f257..b66a8e8ff 100644 --- a/modules/ppcp-wc-gateway/src/Helper/CartCheckoutDetector.php +++ b/modules/ppcp-wc-gateway/src/Helper/CartCheckoutDetector.php @@ -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 ); From b753cf1ec907ba851d8b83415801ed285240a40e Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Wed, 29 May 2024 16:18:17 +0200 Subject: [PATCH 066/104] =?UTF-8?q?=F0=9F=90=9B=20Fix=20fallback=20logic?= =?UTF-8?q?=20when=20vaulting=20is=20disabled?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added condition to handle incorrect condition: hardcoded default setting did ignore the form's input value. --- .../src/Settings/SettingsListener.php | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/modules/ppcp-wc-gateway/src/Settings/SettingsListener.php b/modules/ppcp-wc-gateway/src/Settings/SettingsListener.php index 6966f97c0..97b1cd776 100644 --- a/modules/ppcp-wc-gateway/src/Settings/SettingsListener.php +++ b/modules/ppcp-wc-gateway/src/Settings/SettingsListener.php @@ -394,7 +394,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(); } From 14b20a696216eef15c8cd89040be4c28454c401e Mon Sep 17 00:00:00 2001 From: Daniel Dudzic Date: Wed, 29 May 2024 22:17:34 +0200 Subject: [PATCH 067/104] Reduce nested conditions --- modules/ppcp-compat/src/CompatModule.php | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/modules/ppcp-compat/src/CompatModule.php b/modules/ppcp-compat/src/CompatModule.php index a5772dccd..b2f31f052 100644 --- a/modules/ppcp-compat/src/CompatModule.php +++ b/modules/ppcp-compat/src/CompatModule.php @@ -342,20 +342,17 @@ class CompatModule implements ModuleInterface { 'wp', function() { $page_id = get_the_ID(); - if ( $page_id ) { - if ( CartCheckoutDetector::has_elementor_checkout( $page_id ) ) { - add_filter( - 'woocommerce_paypal_payments_context', - function ( $context ): string { - // Default context. - if ( 'mini-cart' === $context ) { - return 'checkout'; - } - return $context; - } - ); - } + if ( ! $page_id || ! CartCheckoutDetector::has_elementor_checkout( $page_id ) ) { + return; } + + add_filter( + 'woocommerce_paypal_payments_context', + function ( $context ): string { + // Default context. + return ( 'mini-cart' === $context ) ? 'checkout' : $context; + } + ); } ); } From e91e5e718ddcdc775994608ce9802d5309ea3fdd Mon Sep 17 00:00:00 2001 From: Daniel Dudzic Date: Wed, 29 May 2024 22:35:48 +0200 Subject: [PATCH 068/104] Fix Psalm error --- modules/ppcp-compat/src/CompatModule.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ppcp-compat/src/CompatModule.php b/modules/ppcp-compat/src/CompatModule.php index b2f31f052..4c23bd8c1 100644 --- a/modules/ppcp-compat/src/CompatModule.php +++ b/modules/ppcp-compat/src/CompatModule.php @@ -348,7 +348,7 @@ class CompatModule implements ModuleInterface { add_filter( 'woocommerce_paypal_payments_context', - function ( $context ): string { + function ( string $context ): string { // Default context. return ( 'mini-cart' === $context ) ? 'checkout' : $context; } From be95c3bb859dc66fc38def484bc62dca8d30959a Mon Sep 17 00:00:00 2001 From: George Burduli Date: Thu, 30 May 2024 15:25:18 +0400 Subject: [PATCH 069/104] Fix setting 'sold individually' option --- .../src/PayPalSubscriptionsModule.php | 30 +++++++++++-------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/modules/ppcp-paypal-subscriptions/src/PayPalSubscriptionsModule.php b/modules/ppcp-paypal-subscriptions/src/PayPalSubscriptionsModule.php index 1e9234cf6..bd4531174 100644 --- a/modules/ppcp-paypal-subscriptions/src/PayPalSubscriptionsModule.php +++ b/modules/ppcp-paypal-subscriptions/src/PayPalSubscriptionsModule.php @@ -99,17 +99,22 @@ class PayPalSubscriptionsModule implements ModuleInterface { return $passed_validation; } - 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_subscription_product', true ) ) { - wc_add_notice( __( 'You can only have one subscription product in your cart.', 'woocommerce-paypal-payments' ), 'error' ); - return false; + $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(); } - $product = wc_get_product( $product_id ); + wc_add_notice( __( 'You cannot add a subscription product to a cart with other items.', 'woocommerce-paypal-payments' ), 'error' ); + return false; + } - if ( $product && $product->get_meta( 'ppcp_subscription_product', true ) ) { - wc_add_notice( __( 'You cannot add a subscription product to a cart with other items.', 'woocommerce-paypal-payments' ), 'error' ); + 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; } } @@ -687,6 +692,11 @@ 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' ) { @@ -694,10 +704,6 @@ class PayPalSubscriptionsModule implements ModuleInterface { $subscriptions_api_handler->update_product( $product ); $subscriptions_api_handler->update_plan( $product ); - if ( $product->get_meta( '_ppcp_enable_subscription_product', true ) === 'yes' ) { - $product->set_sold_individually( true ); - } - return; } From 653a99a242889b1f19f1df4a71019e999a53ddd5 Mon Sep 17 00:00:00 2001 From: Daniel Dudzic Date: Thu, 30 May 2024 14:44:29 +0200 Subject: [PATCH 070/104] Fix the Smart Buttons in Block Checkout not respecting the location setting (2830) --- modules/ppcp-blocks/resources/js/checkout-block.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ppcp-blocks/resources/js/checkout-block.js b/modules/ppcp-blocks/resources/js/checkout-block.js index df003f494..240a0d709 100644 --- a/modules/ppcp-blocks/resources/js/checkout-block.js +++ b/modules/ppcp-blocks/resources/js/checkout-block.js @@ -568,7 +568,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 =
; if (config.placeOrderButtonDescription) { From 424b9ea91622dce8db62ce08fc1544712919a245 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Thu, 30 May 2024 14:47:50 +0200 Subject: [PATCH 071/104] Remove `www` in domain --- modules/ppcp-api-client/src/Authentication/SdkClientToken.php | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/ppcp-api-client/src/Authentication/SdkClientToken.php b/modules/ppcp-api-client/src/Authentication/SdkClientToken.php index 200ba2322..ba0f3a373 100644 --- a/modules/ppcp-api-client/src/Authentication/SdkClientToken.php +++ b/modules/ppcp-api-client/src/Authentication/SdkClientToken.php @@ -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; From 5bb240bb9af7a180a733dc78af1673bbf8e2066f Mon Sep 17 00:00:00 2001 From: Narek Zakarian Date: Thu, 30 May 2024 17:26:44 +0400 Subject: [PATCH 072/104] Surround with the integrations `try/catch` to avoid errors --- .../GermanizedShipmentIntegration.php | 56 +++++----- .../Integration/ShipStationIntegration.php | 38 +++---- .../ShipmentTrackingIntegration.php | 102 ++++++++++-------- .../Integration/WcShippingTaxIntegration.php | 74 +++++++------ .../Integration/YithShipmentIntegration.php | 42 ++++---- 5 files changed, 163 insertions(+), 149 deletions(-) diff --git a/modules/ppcp-order-tracking/src/Integration/GermanizedShipmentIntegration.php b/modules/ppcp-order-tracking/src/Integration/GermanizedShipmentIntegration.php index 3dfe5bf73..64f103676 100644 --- a/modules/ppcp-order-tracking/src/Integration/GermanizedShipmentIntegration.php +++ b/modules/ppcp-order-tracking/src/Integration/GermanizedShipmentIntegration.php @@ -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, diff --git a/modules/ppcp-order-tracking/src/Integration/ShipStationIntegration.php b/modules/ppcp-order-tracking/src/Integration/ShipStationIntegration.php index dbb95aabc..4ca54893b 100644 --- a/modules/ppcp-order-tracking/src/Integration/ShipStationIntegration.php +++ b/modules/ppcp-order-tracking/src/Integration/ShipStationIntegration.php @@ -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, diff --git a/modules/ppcp-order-tracking/src/Integration/ShipmentTrackingIntegration.php b/modules/ppcp-order-tracking/src/Integration/ShipmentTrackingIntegration.php index 9417da693..4ffe25b19 100644 --- a/modules/ppcp-order-tracking/src/Integration/ShipmentTrackingIntegration.php +++ b/modules/ppcp-order-tracking/src/Integration/ShipmentTrackingIntegration.php @@ -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, diff --git a/modules/ppcp-order-tracking/src/Integration/WcShippingTaxIntegration.php b/modules/ppcp-order-tracking/src/Integration/WcShippingTaxIntegration.php index 87cca24de..ebd8e4fdd 100644 --- a/modules/ppcp-order-tracking/src/Integration/WcShippingTaxIntegration.php +++ b/modules/ppcp-order-tracking/src/Integration/WcShippingTaxIntegration.php @@ -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, diff --git a/modules/ppcp-order-tracking/src/Integration/YithShipmentIntegration.php b/modules/ppcp-order-tracking/src/Integration/YithShipmentIntegration.php index 9b2be196c..db2672809 100644 --- a/modules/ppcp-order-tracking/src/Integration/YithShipmentIntegration.php +++ b/modules/ppcp-order-tracking/src/Integration/YithShipmentIntegration.php @@ -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, From 4a3345113b554b28e399a57c4a7920b5bb80086f Mon Sep 17 00:00:00 2001 From: George Burduli Date: Fri, 31 May 2024 13:07:41 +0400 Subject: [PATCH 073/104] Use WC Customer API to retrieve billing and shipping countries --- modules/ppcp-wc-gateway/src/Helper/PayUponInvoiceHelper.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/ppcp-wc-gateway/src/Helper/PayUponInvoiceHelper.php b/modules/ppcp-wc-gateway/src/Helper/PayUponInvoiceHelper.php index c140b0976..b91eca8a0 100644 --- a/modules/ppcp-wc-gateway/src/Helper/PayUponInvoiceHelper.php +++ b/modules/ppcp-wc-gateway/src/Helper/PayUponInvoiceHelper.php @@ -56,13 +56,13 @@ class PayUponInvoiceHelper { } // phpcs:ignore WordPress.Security.NonceVerification.Missing - $billing_country = wc_clean( wp_unslash( $_POST['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'] ?? '' ) ); + $shipping_country = WC()->customer->get_shipping_country(); if ( empty( $shipping_country ) || 'DE' !== $shipping_country ) { return false; } From 2cb76ed8f2fca7dea858a632c0f392460d2b5621 Mon Sep 17 00:00:00 2001 From: Narek Zakarian Date: Fri, 31 May 2024 15:36:32 +0400 Subject: [PATCH 074/104] Apply the Venmo + vaulting functionality on block pages --- .../resources/js/checkout-block.js | 47 ++++++++++++++----- 1 file changed, 35 insertions(+), 12 deletions(-) diff --git a/modules/ppcp-blocks/resources/js/checkout-block.js b/modules/ppcp-blocks/resources/js/checkout-block.js index df003f494..8a158828f 100644 --- a/modules/ppcp-blocks/resources/js/checkout-block.js +++ b/modules/ppcp-blocks/resources/js/checkout-block.js @@ -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; @@ -447,7 +454,7 @@ const PayPalComponent = ({ if (config.scriptData.continuation) { return true; } - if (!config.finalReviewEnabled) { + if (shouldHandleShippingInPayPal()) { location.href = getCheckoutRedirectUrl(); } return true; @@ -493,8 +500,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 +523,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; + }} /> ); } From bfb566162c6537a6b4665615c577368d9ef383e2 Mon Sep 17 00:00:00 2001 From: Daniel Dudzic Date: Fri, 31 May 2024 13:55:30 +0200 Subject: [PATCH 075/104] AXO: Only enqueue JS scripts on the WooCommerce checkout page --- modules/ppcp-axo/src/AxoModule.php | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/modules/ppcp-axo/src/AxoModule.php b/modules/ppcp-axo/src/AxoModule.php index 7a10c34ad..d1731b4fe 100644 --- a/modules/ppcp-axo/src/AxoModule.php +++ b/modules/ppcp-axo/src/AxoModule.php @@ -44,7 +44,6 @@ class AxoModule implements ModuleInterface { * {@inheritDoc} */ public function run( ContainerInterface $c ): void { - $module = $this; add_filter( 'woocommerce_payment_gateways', @@ -132,7 +131,8 @@ class AxoModule implements ModuleInterface { 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' ) ) { @@ -145,11 +145,15 @@ class AxoModule implements ModuleInterface { // Enqueue frontend scripts. add_action( 'wp_enqueue_scripts', - static function () use ( $c, $manager ) { + static function () use ( $c, $manager, $module ) { + + $settings = $c->get( 'wcgateway.settings' ); + assert( $settings instanceof Settings ); + $smart_button = $c->get( 'button.smart-button' ); assert( $smart_button instanceof SmartButtonInterface ); - if ( $smart_button->should_load_ppcp_script() ) { + if ( $module->should_render_fastlane( $settings ) && $smart_button->should_load_ppcp_script() ) { $manager->enqueue(); } } From dccb038e5e97efe168234e9032c07664f9668192 Mon Sep 17 00:00:00 2001 From: Narek Zakarian Date: Fri, 31 May 2024 18:46:21 +0400 Subject: [PATCH 076/104] Calculate totals after adding shipping to include taxes. --- modules/ppcp-button/src/Helper/WooCommerceOrderCreator.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/ppcp-button/src/Helper/WooCommerceOrderCreator.php b/modules/ppcp-button/src/Helper/WooCommerceOrderCreator.php index 92f07c0fd..4dd7a3293 100644 --- a/modules/ppcp-button/src/Helper/WooCommerceOrderCreator.php +++ b/modules/ppcp-button/src/Helper/WooCommerceOrderCreator.php @@ -181,6 +181,8 @@ class WooCommerceOrderCreator { $wc_order->add_item( $shipping ); } + + $wc_order->calculate_totals(); } /** From eaca9f6a41b38c18e6b0fc1063a86e211879ab5e Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Mon, 3 Jun 2024 11:29:04 +0200 Subject: [PATCH 077/104] Do not use timeout but return a promise on onApprove --- .../resources/js/Components/card-fields.js | 13 ------------- .../ppcp-blocks/resources/js/card-fields-config.js | 4 ++-- 2 files changed, 2 insertions(+), 15 deletions(-) diff --git a/modules/ppcp-blocks/resources/js/Components/card-fields.js b/modules/ppcp-blocks/resources/js/Components/card-fields.js index dcd3ba2dd..2d24bfce1 100644 --- a/modules/ppcp-blocks/resources/js/Components/card-fields.js +++ b/modules/ppcp-blocks/resources/js/Components/card-fields.js @@ -22,25 +22,12 @@ export function CardFields({config, eventRegistration, emitResponse}) { localStorage.setItem('ppcp-save-card-payment', savePayment); } - const wait = (milliseconds) => { - return new Promise((resolve) => { - console.log('start...') - setTimeout(() => { - console.log('resolve') - resolve() - }, milliseconds) - }) - }; - useEffect( () => onPaymentSetup(() => { async function handlePaymentProcessing() { await cardFieldsForm.submit(); - // TODO temporary workaround to wait for onApprove to resolve - await wait(3000) - return { type: responseTypes.SUCCESS, } diff --git a/modules/ppcp-blocks/resources/js/card-fields-config.js b/modules/ppcp-blocks/resources/js/card-fields-config.js index 23b8859fb..37d939a5f 100644 --- a/modules/ppcp-blocks/resources/js/card-fields-config.js +++ b/modules/ppcp-blocks/resources/js/card-fields-config.js @@ -22,8 +22,8 @@ export async function createOrder() { }); } -export function onApprove(data) { - fetch(config.scriptData.ajax.approve_order.endpoint, { +export async function onApprove(data) { + return fetch(config.scriptData.ajax.approve_order.endpoint, { method: "POST", headers: { "Content-Type": "application/json", From 4967c472e7b48399de8e05d5309f917ee377f68e Mon Sep 17 00:00:00 2001 From: Narek Zakarian Date: Mon, 3 Jun 2024 15:27:01 +0400 Subject: [PATCH 078/104] Update the shipping option for subscriptions on address change --- .../ppcp-blocks/resources/js/checkout-block.js | 15 +++++++++++++++ .../js/modules/Helper/ShippingHandler.js | 2 -- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/modules/ppcp-blocks/resources/js/checkout-block.js b/modules/ppcp-blocks/resources/js/checkout-block.js index df003f494..9ac471352 100644 --- a/modules/ppcp-blocks/resources/js/checkout-block.js +++ b/modules/ppcp-blocks/resources/js/checkout-block.js @@ -391,6 +391,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); diff --git a/modules/ppcp-button/resources/js/modules/Helper/ShippingHandler.js b/modules/ppcp-button/resources/js/modules/Helper/ShippingHandler.js index 311b8a949..c67bee5bb 100644 --- a/modules/ppcp-button/resources/js/modules/Helper/ShippingHandler.js +++ b/modules/ppcp-button/resources/js/modules/Helper/ShippingHandler.js @@ -106,7 +106,6 @@ export const handleShippingAddressChange = async (data, actions, config) => { }) }) - if (!config.data_client_id.has_subscriptions) { const res = await fetch(config.ajax.update_shipping.endpoint, { method: 'POST', credentials: 'same-origin', @@ -121,7 +120,6 @@ export const handleShippingAddressChange = async (data, actions, config) => { if (!json.success) { throw new Error(json.data.message); } - } } catch (e) { console.error(e); From 210d70a551bbf2a207763d6bee37799a83031805 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Mon, 3 Jun 2024 14:32:51 +0200 Subject: [PATCH 079/104] Return error type when catching submit eror --- modules/ppcp-blocks/resources/js/Components/card-fields.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/modules/ppcp-blocks/resources/js/Components/card-fields.js b/modules/ppcp-blocks/resources/js/Components/card-fields.js index 2d24bfce1..c74359be0 100644 --- a/modules/ppcp-blocks/resources/js/Components/card-fields.js +++ b/modules/ppcp-blocks/resources/js/Components/card-fields.js @@ -26,7 +26,12 @@ export function CardFields({config, eventRegistration, emitResponse}) { () => onPaymentSetup(() => { async function handlePaymentProcessing() { - await cardFieldsForm.submit(); + await cardFieldsForm.submit() + .catch((error) => { + return { + type: responseTypes.ERROR, + } + }); return { type: responseTypes.SUCCESS, From f4b34b6a3233fac9b8eabf030a3bcb5cd4b416c1 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Mon, 3 Jun 2024 14:47:00 +0200 Subject: [PATCH 080/104] Bump 2.7.1-rc1 version --- changelog.txt | 12 ++++++++++++ package.json | 2 +- readme.txt | 14 +++++++++++++- woocommerce-paypal-payments.php | 6 +++--- 4 files changed, 29 insertions(+), 5 deletions(-) diff --git a/changelog.txt b/changelog.txt index 3ca38e0e2..cad6f366e 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,17 @@ *** Changelog *** += 2.8.0 - xxxx-xx-xx = +* Fix - Calculate totals after adding shipping to include taxes #2296 +* Fix - Package tracking integration throws error in 2.7.1 #2289 +* Fix - Make PayPal Subscription products unique in cart #2265 +* Fix - PayPal declares subscription support when merchant not enabled for Reference Transactions #2282 +* Fix - Google Pay and Apple Pay Settings button from Connection tab have wrong links #2273 +* Fix - Smart Buttons in Block Checkout not respecting the location setting (2830) #2278 +* Fix - Disable Pay Upon Invoice if billing/shipping country not set #2281 +* Enhancement - Enable shipping callback for WC subscriptions #2259 +* Enhancement - Disable the shipping callback for "venmo" when vaulting is active #2269 +* Enhancement - Improve "Could not retrieve order" error message #2271 + = 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 diff --git a/package.json b/package.json index bf52a8677..2bc0582d6 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/readme.txt b/readme.txt index 0c6bdbb1a..111dc667c 100644 --- a/readme.txt +++ b/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 @@ -191,6 +191,18 @@ If you encounter issues with the PayPal buttons not appearing after an update, p * Fix - Enable the Shipping Callback handlers #2266 * Enhancement - Use admin theme color #1602 += 2.8.0 - xxxx-xx-xx = +* Fix - Calculate totals after adding shipping to include taxes #2296 +* Fix - Package tracking integration throws error in 2.7.1 #2289 +* Fix - Make PayPal Subscription products unique in cart #2265 +* Fix - PayPal declares subscription support when merchant not enabled for Reference Transactions #2282 +* Fix - Google Pay and Apple Pay Settings button from Connection tab have wrong links #2273 +* Fix - Smart Buttons in Block Checkout not respecting the location setting (2830) #2278 +* Fix - Disable Pay Upon Invoice if billing/shipping country not set #2281 +* Enhancement - Enable shipping callback for WC subscriptions #2259 +* Enhancement - Disable the shipping callback for "venmo" when vaulting is active #2269 +* Enhancement - Improve "Could not retrieve order" error message #2271 + = 2.7.0 - 2024-04-30 = * Fix - Zero sum subscriptions cause CANNOT_BE_ZERO_OR_NEGATIVE when using Vault v3 #2152 * Fix - Incorrect Pricing Issue with Variable Subscriptions in PayPal Subscriptions Mode #2156 diff --git a/woocommerce-paypal-payments.php b/woocommerce-paypal-payments.php index ce5883709..62d4359aa 100644 --- a/woocommerce-paypal-payments.php +++ b/woocommerce-paypal-payments.php @@ -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_' ); From b64523f2a333d097ad2d2d4881468dd29b663c2e Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Tue, 4 Jun 2024 16:10:33 +0200 Subject: [PATCH 081/104] Remove wc subscriptions plugin check --- modules/ppcp-blocks/extensions.php | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/modules/ppcp-blocks/extensions.php b/modules/ppcp-blocks/extensions.php index 2409d1bbc..b32f5bbc6 100644 --- a/modules/ppcp-blocks/extensions.php +++ b/modules/ppcp-blocks/extensions.php @@ -51,17 +51,7 @@ return array( ); } - $subscription_helper = $container->get( 'wc-subscriptions.helper' ); - - if ( $subscription_helper->plugin_is_active() ) { - $notice_content = __( - 'Important: Cannot be deactivated while the WooCommerce Subscriptions plugin is active.', - 'woocommerce-paypal-payments' - ); - $label .= '

' . $notice_content . '

'; - } - - $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, From cb96f888a0e804d028c08f57d5aa85915658f928 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Tue, 4 Jun 2024 17:33:28 +0200 Subject: [PATCH 082/104] Add card icons component --- modules/ppcp-blocks/resources/js/Components/card-fields.js | 4 +++- modules/ppcp-blocks/src/AdvancedCardPaymentMethod.php | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/modules/ppcp-blocks/resources/js/Components/card-fields.js b/modules/ppcp-blocks/resources/js/Components/card-fields.js index c74359be0..00517dfdb 100644 --- a/modules/ppcp-blocks/resources/js/Components/card-fields.js +++ b/modules/ppcp-blocks/resources/js/Components/card-fields.js @@ -9,9 +9,10 @@ import { import {CheckoutHandler} from "./checkout-handler"; import {createOrder, onApprove} from "../card-fields-config"; -export function CardFields({config, eventRegistration, emitResponse}) { +export function CardFields({config, eventRegistration, emitResponse, components}) { const {onPaymentSetup} = eventRegistration; const {responseTypes} = emitResponse; + const { PaymentMethodIcons } = components; const [cardFieldsForm, setCardFieldsForm] = useState(); const getCardFieldsForm = (cardFieldsForm) => { @@ -60,6 +61,7 @@ export function CardFields({config, eventRegistration, emitResponse}) { }} > + $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(), ); } From 559d0654a20963adf9d0dfe16b4bde76498c2b65 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Tue, 4 Jun 2024 18:17:33 +0200 Subject: [PATCH 083/104] Show save card checkbox checked and disabled if subscription in cart --- .../resources/js/Components/card-fields.js | 4 ++++ .../resources/js/Components/checkout-handler.js | 11 +++++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/modules/ppcp-blocks/resources/js/Components/card-fields.js b/modules/ppcp-blocks/resources/js/Components/card-fields.js index 00517dfdb..f7ff6c7e0 100644 --- a/modules/ppcp-blocks/resources/js/Components/card-fields.js +++ b/modules/ppcp-blocks/resources/js/Components/card-fields.js @@ -8,6 +8,7 @@ import { 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; @@ -23,6 +24,8 @@ export function CardFields({config, eventRegistration, emitResponse, components} localStorage.setItem('ppcp-save-card-payment', savePayment); } + const hasSubscriptionProducts = cartHasSubscriptionProducts(config.scriptData); + useEffect( () => onPaymentSetup(() => { @@ -65,6 +68,7 @@ export function CardFields({config, eventRegistration, emitResponse, components} diff --git a/modules/ppcp-blocks/resources/js/Components/checkout-handler.js b/modules/ppcp-blocks/resources/js/Components/checkout-handler.js index 8e9658fc2..48dd0602d 100644 --- a/modules/ppcp-blocks/resources/js/Components/checkout-handler.js +++ b/modules/ppcp-blocks/resources/js/Components/checkout-handler.js @@ -1,7 +1,7 @@ import {useEffect} from '@wordpress/element'; import {usePayPalCardFields} from "@paypal/react-paypal-js"; -export const CheckoutHandler = ({getCardFieldsForm, getSavePayment, saveCardText, is_vaulting_enabled}) => { +export const CheckoutHandler = ({getCardFieldsForm, getSavePayment, hasSubscriptionProducts, saveCardText, is_vaulting_enabled}) => { const {cardFieldsForm} = usePayPalCardFields(); useEffect(() => { @@ -14,7 +14,14 @@ export const CheckoutHandler = ({getCardFieldsForm, getSavePayment, saveCardText return ( <> - getSavePayment(e.target.checked)}/> + getSavePayment(e.target.checked)} + defaultChecked={hasSubscriptionProducts} + disabled={hasSubscriptionProducts} + /> ) From c8b2f99e658454a629ef0e155967528e0af4b750 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Wed, 5 Jun 2024 11:59:04 +0200 Subject: [PATCH 084/104] Fix axo.smart-button-location-notice service not found --- .../src/Settings/Fields/paypal-smart-button-fields.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/ppcp-wc-gateway/src/Settings/Fields/paypal-smart-button-fields.php b/modules/ppcp-wc-gateway/src/Settings/Fields/paypal-smart-button-fields.php index 9e6038606..460ff3592 100644 --- a/modules/ppcp-wc-gateway/src/Settings/Fields/paypal-smart-button-fields.php +++ b/modules/ppcp-wc-gateway/src/Settings/Fields/paypal-smart-button-fields.php @@ -36,6 +36,8 @@ return function ( ContainerInterface $container, array $fields ): array { '; }; + $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' ) . $container->get( 'axo.smart-button-location-notice' ), + '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(), From 3c8144de421b2563b08b7628f0e714a03d5d7dc7 Mon Sep 17 00:00:00 2001 From: Philipp Stracker Date: Wed, 5 Jun 2024 15:24:13 +0200 Subject: [PATCH 085/104] =?UTF-8?q?=F0=9F=A9=B9=20Remove=20HTML=20from=20t?= =?UTF-8?q?he=20error=20message?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As outlined in the linked issue: HTML code is escaped by the exception handler, instead of displaying a clickable link. --- .../ppcp-wc-gateway/src/Processor/OrderProcessor.php | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/modules/ppcp-wc-gateway/src/Processor/OrderProcessor.php b/modules/ppcp-wc-gateway/src/Processor/OrderProcessor.php index 4d1363361..82183e0d4 100644 --- a/modules/ppcp-wc-gateway/src/Processor/OrderProcessor.php +++ b/modules/ppcp-wc-gateway/src/Processor/OrderProcessor.php @@ -220,14 +220,9 @@ class OrderProcessor { ); throw new PayPalOrderMissingException( - sprintf( - // translators: %s: Order history URL on My Account section. - esc_attr__( - 'There was an error processing your order. Please check for any charges in your payment method and review your
order history before placing the order again.', - // phpcs:ignore WordPress.WP.I18n.TextDomainMismatch -- Intentionally "woocommerce" to reflect the original message. - 'woocommerce' - ), - esc_url( wc_get_account_endpoint_url( 'orders' ) ) + esc_attr__( + 'There was an error processing your order. Please check for any charges in your payment method and review your order history before placing the order again.', + 'woocommerce-paypal-payments' ) ); } From 76bcc99bc5222339b4140deaf7630be0302c4f6e Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Wed, 5 Jun 2024 15:35:47 +0200 Subject: [PATCH 086/104] Rename method --- modules/ppcp-blocks/src/AdvancedCardPaymentMethod.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/ppcp-blocks/src/AdvancedCardPaymentMethod.php b/modules/ppcp-blocks/src/AdvancedCardPaymentMethod.php index 51b45b495..2c6fd60f4 100644 --- a/modules/ppcp-blocks/src/AdvancedCardPaymentMethod.php +++ b/modules/ppcp-blocks/src/AdvancedCardPaymentMethod.php @@ -109,7 +109,7 @@ class AdvancedCardPaymentMethod extends AbstractPaymentMethodType { * {@inheritDoc} */ public function get_payment_method_data() { - $script_data = $this->smart_button()->script_data(); + $script_data = $this->smart_button_instance()->script_data(); return array( 'id' => $this->name, @@ -128,7 +128,7 @@ class AdvancedCardPaymentMethod extends AbstractPaymentMethodType { * * @return SmartButtonInterface */ - private function smart_button(): SmartButtonInterface { + private function smart_button_instance(): SmartButtonInterface { if ( $this->smart_button instanceof SmartButtonInterface ) { return $this->smart_button; } From 08f0b42f1c7b7983927a7148f433cbdf7423c718 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Wed, 5 Jun 2024 15:52:54 +0200 Subject: [PATCH 087/104] Update changelog --- changelog.txt | 1 + readme.txt | 25 +++++++++++++------------ 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/changelog.txt b/changelog.txt index cad6f366e..17c03cb83 100644 --- a/changelog.txt +++ b/changelog.txt @@ -11,6 +11,7 @@ * 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 diff --git a/readme.txt b/readme.txt index 111dc667c..ac1315805 100644 --- a/readme.txt +++ b/readme.txt @@ -179,18 +179,6 @@ If you encounter issues with the PayPal buttons not appearing after an update, p == Changelog == -= 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 -* Fix - Notice on newly created block cart checkout #2211 -* Fix - Apple Pay button in the editor #2177 -* Fix - Allow shipping callback and skipping confirmation page from any express button #2236 -* Fix - Pay Later messaging configurator sometimes displays old settings after saving #2249 -* Fix - Update the apple-developer-merchantid-domain-association validation strings for Apple Pay #2251 -* Fix - Enable the Shipping Callback handlers #2266 -* Enhancement - Use admin theme color #1602 - = 2.8.0 - xxxx-xx-xx = * Fix - Calculate totals after adding shipping to include taxes #2296 * Fix - Package tracking integration throws error in 2.7.1 #2289 @@ -202,6 +190,19 @@ If you encounter issues with the PayPal buttons not appearing after an update, p * 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 +* Fix - Notice on newly created block cart checkout #2211 +* Fix - Apple Pay button in the editor #2177 +* Fix - Allow shipping callback and skipping confirmation page from any express button #2236 +* Fix - Pay Later messaging configurator sometimes displays old settings after saving #2249 +* Fix - Update the apple-developer-merchantid-domain-association validation strings for Apple Pay #2251 +* Fix - Enable the Shipping Callback handlers #2266 +* Enhancement - Use admin theme color #1602 = 2.7.0 - 2024-04-30 = * Fix - Zero sum subscriptions cause CANNOT_BE_ZERO_OR_NEGATIVE when using Vault v3 #2152 From 144c8da3f6fc7d3a73dc4ac0044eb133d287dd82 Mon Sep 17 00:00:00 2001 From: Daniel Dudzic Date: Wed, 5 Jun 2024 20:50:39 +0200 Subject: [PATCH 088/104] Add Litespeed Cache and W3 Total Cache compatibility --- modules/ppcp-compat/services.php | 12 +++++ modules/ppcp-compat/src/CompatModule.php | 60 ++++++++++++++++-------- 2 files changed, 53 insertions(+), 19 deletions(-) diff --git a/modules/ppcp-compat/services.php b/modules/ppcp-compat/services.php index 364686a84..70ae6dac2 100644 --- a/modules/ppcp-compat/services.php +++ b/modules/ppcp-compat/services.php @@ -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+ }, diff --git a/modules/ppcp-compat/src/CompatModule.php b/modules/ppcp-compat/src/CompatModule.php index 44cfd1988..14ee3affc 100644 --- a/modules/ppcp-compat/src/CompatModule.php +++ b/modules/ppcp-compat/src/CompatModule.php @@ -41,7 +41,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 +53,7 @@ class CompatModule implements ModuleInterface { $this->migrate_smart_button_settings( $c ); $this->fix_page_builders(); + $this->exclude_cache_plugins_js_minification( $c ); } /** @@ -88,24 +88,6 @@ class CompatModule implements ModuleInterface { } } ); - - } - - /** - * Fixes the compatibility issue for SiteGround Optimizer plugin. - * - * @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 +311,44 @@ class CompatModule implements ModuleInterface { $parent = $theme->parent(); return ( $parent && $parent->get( 'Name' ) === 'Divi' ); } + + /** + * 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( $scripts ) use ( $ppcp_script_names ) { + return array_merge( $scripts, $ppcp_script_names ); + } + ); + + // LiteSpeed Cache. + add_filter( + 'litespeed_optimize_js_excludes', + function( $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( $do_tag_minification, $script_tag, $file ) { + if ( $file && strpos( $file, 'ppcp' ) !== false ) { + return false; + } + return $do_tag_minification; + }, + 10, + 3 + ); + } } From 1ff8ac6ade76d6c2d9b8f040b945db4db65f5374 Mon Sep 17 00:00:00 2001 From: Daniel Dudzic Date: Wed, 5 Jun 2024 22:15:04 +0200 Subject: [PATCH 089/104] Fix Psalm errors --- modules/ppcp-compat/src/CompatModule.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/ppcp-compat/src/CompatModule.php b/modules/ppcp-compat/src/CompatModule.php index 14ee3affc..6e1553587 100644 --- a/modules/ppcp-compat/src/CompatModule.php +++ b/modules/ppcp-compat/src/CompatModule.php @@ -325,7 +325,7 @@ class CompatModule implements ModuleInterface { // Siteground SG Optimize. add_filter( 'sgo_js_minify_exclude', - function( $scripts ) use ( $ppcp_script_names ) { + function( array $scripts ) use ( $ppcp_script_names ) { return array_merge( $scripts, $ppcp_script_names ); } ); @@ -333,7 +333,7 @@ class CompatModule implements ModuleInterface { // LiteSpeed Cache. add_filter( 'litespeed_optimize_js_excludes', - function( $excluded_js ) use ( $ppcp_script_file_names ) { + function( array $excluded_js ) use ( $ppcp_script_file_names ) { return array_merge( $excluded_js, $ppcp_script_file_names ); } ); @@ -341,7 +341,7 @@ class CompatModule implements ModuleInterface { // W3 Total Cache. add_filter( 'w3tc_minify_js_do_tag_minification', - function( $do_tag_minification, $script_tag, $file ) { + function( bool $do_tag_minification, string $script_tag, string $file ) { if ( $file && strpos( $file, 'ppcp' ) !== false ) { return false; } From 38fc9906b7e27272c97041d35eac5b1d9e160b51 Mon Sep 17 00:00:00 2001 From: Daniel Dudzic Date: Thu, 6 Jun 2024 01:16:52 +0200 Subject: [PATCH 090/104] First attempt: Add a continue button next to the email input --- modules/ppcp-axo/resources/css/styles.scss | 28 ++++++++ modules/ppcp-axo/resources/js/AxoManager.js | 67 ++++++++++++++++++- .../js/Components/DomElementCollection.js | 4 ++ modules/ppcp-axo/src/Assets/AxoManager.php | 1 + modules/ppcp-axo/src/Gateway/AxoGateway.php | 2 +- 5 files changed, 99 insertions(+), 3 deletions(-) diff --git a/modules/ppcp-axo/resources/css/styles.scss b/modules/ppcp-axo/resources/css/styles.scss index 3513d0642..c4949f4b6 100644 --- a/modules/ppcp-axo/resources/css/styles.scss +++ b/modules/ppcp-axo/resources/css/styles.scss @@ -89,3 +89,31 @@ max-height: 25px; } } + +#billing_email_field .woocommerce-input-wrapper { + display: flex; + align-items: center; +} + +#billing_email_field .woocommerce-input-wrapper input { + flex: 1; + margin-right: 10px; +} + +.email-submit-button { + padding: 5px 10px; + background-color: #007cba; + color: white; + border: none; + border-radius: 3px; + cursor: pointer; + flex-shrink: 0; + opacity: 0; + transition: opacity 0.3s ease; /* Add transition for opacity */ +} + +#billing_email_field .woocommerce-input-wrapper.show-button .email-submit-button { + opacity: 1; /* Show the button */ +} + + diff --git a/modules/ppcp-axo/resources/js/AxoManager.js b/modules/ppcp-axo/resources/js/AxoManager.js index 960da7f26..1172b778e 100644 --- a/modules/ppcp-axo/resources/js/AxoManager.js +++ b/modules/ppcp-axo/resources/js/AxoManager.js @@ -245,6 +245,7 @@ class AxoManager { this.showAxoEmailField(); this.el.watermarkContainer.show(); + // Move watermark to after email. this.$(this.el.fieldBillingEmail.selector).append( this.$(this.el.watermarkContainer.selector) @@ -280,6 +281,7 @@ class AxoManager { if (scenario.axoPaymentContainer) { this.el.paymentContainer.show(); + this.el.gatewayDescription.hide(); } else { this.el.paymentContainer.hide(); } @@ -472,7 +474,9 @@ class AxoManager { this.initialized = true; await this.connect(); - this.renderWatermark(); + await this.renderWatermark(true, () => { + this.renderEmailSubmit(); + }); this.watchEmail(); } @@ -493,14 +497,73 @@ class AxoManager { this.el.gatewayRadioButton.trigger('change'); } - async renderWatermark(includeAdditionalInfo = true) { + async renderWatermark(includeAdditionalInfo = true, callback) { (await this.fastlane.FastlaneWatermarkComponent({ includeAdditionalInfo })).render(this.el.watermarkContainer.selector); this.toggleWatermarkLoading(this.el.watermarkContainer, 'ppcp-axo-watermark-loading', 'loader'); + + // Call the callback if provided + if (callback) { + callback(); + } } + async renderEmailSubmit() { + // Create the submit button element + const submitButton = document.createElement('button'); + submitButton.type = 'button'; + submitButton.innerText = this.axoConfig.billing_email_button_text; + submitButton.className = 'email-submit-button'; // Add a class for styling if needed + + // Add an event listener to handle the button click + submitButton.addEventListener('click', async () => { + const emailInput = document.querySelector(this.el.fieldBillingEmail.selector + ' input'); + if (emailInput && emailInput.checkValidity()) { + if (this.lastEmailCheckedIdentity !== emailInput.value) { + log(`Submit button clicked - emailInput: ${emailInput.value}`); + await this.onChangeEmail(); + } + } else { + emailInput.reportValidity(); // Trigger the HTML5 validation message + log('Invalid or empty email input.'); + } + }); + + // Append the button inside the wrapper of the billing email input field + const emailFieldContainer = document.querySelector(this.el.fieldBillingEmail.selector); + if (emailFieldContainer) { + const inputWrapper = emailFieldContainer.querySelector('.woocommerce-input-wrapper'); + if (inputWrapper) { + // Ensure the email input has the required attribute + const emailInput = inputWrapper.querySelector('input'); + emailInput.setAttribute('required', 'required'); + emailInput.style.flex = '1'; // Make the input take the remaining space + emailInput.style.marginRight = '10px'; // Ensure the spacing is consistent + + // Remove any existing loader if present + const existingLoader = inputWrapper.querySelector('.loader'); + if (existingLoader) { + existingLoader.remove(); + } + + // Append the submit button to the input wrapper + inputWrapper.appendChild(submitButton); + + // Force a reflow to apply the transition + submitButton.offsetHeight; + + // Add the class to trigger the animation + inputWrapper.classList.add('show-button'); + } + } + } + + + + + watchEmail() { if (this.useEmailWidget()) { diff --git a/modules/ppcp-axo/resources/js/Components/DomElementCollection.js b/modules/ppcp-axo/resources/js/Components/DomElementCollection.js index 35f19ec6d..f78e29147 100644 --- a/modules/ppcp-axo/resources/js/Components/DomElementCollection.js +++ b/modules/ppcp-axo/resources/js/Components/DomElementCollection.js @@ -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', }); diff --git a/modules/ppcp-axo/src/Assets/AxoManager.php b/modules/ppcp-axo/src/Assets/AxoManager.php index 4e75f56fa..d349fe3ff 100644 --- a/modules/ppcp-axo/src/Assets/AxoManager.php +++ b/modules/ppcp-axo/src/Assets/AxoManager.php @@ -216,6 +216,7 @@ class AxoManager { 'nonce' => wp_create_nonce( FrontendLoggerEndpoint::nonce() ), ), ), + 'billing_email_button_text' => __( 'Continue', 'woocommerce-paypal-payments' ), ); } diff --git a/modules/ppcp-axo/src/Gateway/AxoGateway.php b/modules/ppcp-axo/src/Gateway/AxoGateway.php index 13e81b74c..f104c6d63 100644 --- a/modules/ppcp-axo/src/Gateway/AxoGateway.php +++ b/modules/ppcp-axo/src/Gateway/AxoGateway.php @@ -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 above to proceed.', 'woocommerce-paypal-payments' ); $this->init_form_fields(); $this->init_settings(); From 0ba33e9bb897ca9adecf33bf8cbca6155c06051f Mon Sep 17 00:00:00 2001 From: Daniel Dudzic Date: Thu, 6 Jun 2024 16:08:30 +0200 Subject: [PATCH 091/104] Adding the spinner and overlay on top of the submit button --- modules/ppcp-axo/resources/css/styles.scss | 15 +- modules/ppcp-axo/resources/js/AxoManager.js | 222 +++++++++++++----- .../js/Components/DomElementCollection.js | 9 + 3 files changed, 188 insertions(+), 58 deletions(-) diff --git a/modules/ppcp-axo/resources/css/styles.scss b/modules/ppcp-axo/resources/css/styles.scss index c4949f4b6..472299250 100644 --- a/modules/ppcp-axo/resources/css/styles.scss +++ b/modules/ppcp-axo/resources/css/styles.scss @@ -12,6 +12,19 @@ } } +.email-submit-button { + position: relative; + .loader:before { + display: inline; + height: 12px; + width: 12px; + margin-left: -6px; + margin-top: -6px; + left: auto; + right: auto; + } +} + .ppcp-axo-payment-container { padding: 1rem 0; background-color: #ffffff; @@ -101,7 +114,7 @@ } .email-submit-button { - padding: 5px 10px; + padding: 5px 20px; background-color: #007cba; color: white; border: none; diff --git a/modules/ppcp-axo/resources/js/AxoManager.js b/modules/ppcp-axo/resources/js/AxoManager.js index 1172b778e..7c490a215 100644 --- a/modules/ppcp-axo/resources/js/AxoManager.js +++ b/modules/ppcp-axo/resources/js/AxoManager.js @@ -1,15 +1,14 @@ import Fastlane from "./Connection/Fastlane"; -import {log} from "./Helper/Debug"; +import { log } from "./Helper/Debug"; import DomElementCollection from "./Components/DomElementCollection"; import ShippingView from "./Views/ShippingView"; import BillingView from "./Views/BillingView"; import CardView from "./Views/CardView"; import PayPalInsights from "./Insights/PayPalInsights"; -import {disable,enable} from "../../../ppcp-button/resources/js/modules/Helper/ButtonDisabler"; -import {getCurrentPaymentMethod} from "../../../ppcp-button/resources/js/modules/Helper/CheckoutMethodState"; +import { disable, enable } from "../../../ppcp-button/resources/js/modules/Helper/ButtonDisabler"; +import { getCurrentPaymentMethod } from "../../../ppcp-button/resources/js/modules/Helper/CheckoutMethodState"; class AxoManager { - constructor(axoConfig, ppcpConfig) { this.axoConfig = axoConfig; this.ppcpConfig = ppcpConfig; @@ -51,7 +50,7 @@ class AxoManager { this.registerEventHandlers(); - this.shippingView = new ShippingView(this.el.shippingAddressContainer.selector, this.el, this.states ); + this.shippingView = new ShippingView(this.el.shippingAddressContainer.selector, this.el, this.states); this.billingView = new BillingView(this.el.billingAddressContainer.selector, this.el); this.cardView = new CardView(this.el.paymentContainer.selector + '-details', this.el, this); @@ -64,9 +63,9 @@ class AxoManager { } if ( - this.axoConfig?.insights?.enabled - && this.axoConfig?.insights?.client_id - && this.axoConfig?.insights?.session_id + this.axoConfig?.insights?.enabled && + this.axoConfig?.insights?.client_id && + this.axoConfig?.insights?.session_id ) { PayPalInsights.config(this.axoConfig?.insights?.client_id, { debug: true }); PayPalInsights.setSessionId(this.axoConfig?.insights?.session_id); @@ -88,7 +87,6 @@ class AxoManager { } registerEventHandlers() { - this.$(document).on('change', 'input[name=payment_method]', (ev) => { const map = { 'ppcp-axo-gateway': 'card', @@ -101,7 +99,6 @@ class AxoManager { }); }); - // Listen to Gateway Radio button changes. this.el.gatewayRadioButton.on('change', (ev) => { if (ev.target.checked) { @@ -163,7 +160,7 @@ class AxoManager { // Prevents sending checkout form when pressing Enter key on input field // and triggers customer lookup this.$('form.woocommerce-checkout input').on('keydown', async (ev) => { - if(ev.key === 'Enter' && getCurrentPaymentMethod() === 'ppcp-axo-gateway' ) { + if (ev.key === 'Enter' && getCurrentPaymentMethod() === 'ppcp-axo-gateway') { ev.preventDefault(); log(`Enter key attempt - emailInput: ${this.emailInput.value}`); log(`this.lastEmailCheckedIdentity: ${this.lastEmailCheckedIdentity}`); @@ -182,7 +179,7 @@ class AxoManager { // Listening to status update event document.addEventListener('axo_status_updated', (ev) => { const termsField = document.querySelector("[name='terms-field']"); - if(termsField) { + if (termsField) { const status = ev.detail; const shouldHide = status.active && status.validEmail === false && status.hasProfile === false; @@ -245,11 +242,13 @@ class AxoManager { this.showAxoEmailField(); this.el.watermarkContainer.show(); - // Move watermark to after email. this.$(this.el.fieldBillingEmail.selector).append( this.$(this.el.watermarkContainer.selector) ); + + // Add the submit button to the email field container. + this.renderEmailSubmitNew(); } else { this.el.emailWidgetContainer.hide(); if (!scenario.defaultEmailField) { @@ -258,7 +257,6 @@ class AxoManager { } if (scenario.axoProfileViews) { - this.shippingView.activate(); this.cardView.activate(); @@ -272,7 +270,6 @@ class AxoManager { ); this.el.watermarkContainer.show(); - } else { this.shippingView.deactivate(); this.billingView.deactivate(); @@ -378,7 +375,7 @@ class AxoManager { log(`Status updated: ${JSON.stringify(this.status)}`); - document.dispatchEvent(new CustomEvent("axo_status_updated", {detail: this.status})); + document.dispatchEvent(new CustomEvent("axo_status_updated", { detail: this.status })); this.rerender(); } @@ -448,7 +445,6 @@ class AxoManager { } if (this.useEmailWidget()) { - // Display email widget. const ec = this.el.emailWidgetContainer; if (!document.querySelector(ec.selector)) { @@ -460,7 +456,6 @@ class AxoManager { } } else { - // Move email to the AXO container. let emailRow = document.querySelector(this.el.fieldBillingEmail.selector); wrapperElement.prepend(emailRow); @@ -510,6 +505,19 @@ class AxoManager { } } + async renderEmailSubmitNew() { + const billingEmailSubmitButton = this.el.billingEmailSubmitButton; + if (!document.querySelector(billingEmailSubmitButton.selector)) { + + } + + const submitButton = this.el.billingEmailSubmitButton.selector; + // submitButton.innerText = this.axoConfig.billing_email_button_text; + + const submitButtonSpinner = this.el.billingEmailSubmitButtonSpinner.selector; + console.log(submitButton); + } + async renderEmailSubmit() { // Create the submit button element const submitButton = document.createElement('button'); @@ -517,13 +525,33 @@ class AxoManager { submitButton.innerText = this.axoConfig.billing_email_button_text; submitButton.className = 'email-submit-button'; // Add a class for styling if needed + // Create the spinner element + const spinner = document.createElement('span'); + spinner.className = 'loader ppcp-axo-overlay'; // Use the native loader class + spinner.style.display = 'none'; // Initially hidden + + // Append the spinner to the button + submitButton.appendChild(spinner); + // Add an event listener to handle the button click submitButton.addEventListener('click', async () => { const emailInput = document.querySelector(this.el.fieldBillingEmail.selector + ' input'); if (emailInput && emailInput.checkValidity()) { if (this.lastEmailCheckedIdentity !== emailInput.value) { log(`Submit button clicked - emailInput: ${emailInput.value}`); - await this.onChangeEmail(); + + // Show the spinner, add the class to adjust padding and disable the button + spinner.style.display = 'inline-block'; + submitButton.disabled = true; + + + try { + await this.onChangeEmail(); + } finally { + // Hide the spinner and re-enable the button after the lookup is complete + spinner.style.display = 'none'; + submitButton.disabled = false; + } } } else { emailInput.reportValidity(); // Trigger the HTML5 validation message @@ -560,34 +588,7 @@ class AxoManager { } } - - - - - watchEmail() { - - if (this.useEmailWidget()) { - - // TODO - - } else { - this.emailInput.addEventListener('change', async ()=> { - log(`Change event attempt - emailInput: ${this.emailInput.value}`); - log(`this.lastEmailCheckedIdentity: ${this.lastEmailCheckedIdentity}`); - if (this.emailInput && this.lastEmailCheckedIdentity !== this.emailInput.value) { - this.onChangeEmail(); - } - }); - - log(`Last, this.emailInput.value attempt - emailInput: ${this.emailInput.value}`); - log(`this.lastEmailCheckedIdentity: ${this.lastEmailCheckedIdentity}`); - if (this.emailInput.value) { - this.onChangeEmail(); - } - } - } - - async onChangeEmail () { + async onChangeEmail() { this.clearData(); if (!this.status.active) { @@ -630,8 +631,116 @@ class AxoManager { }); this.disableGatewaySelection(); - await this.lookupCustomerByEmail(); - this.enableGatewaySelection(); + + const submitButton = document.querySelector('.email-submit-button'); + const spinner = submitButton.querySelector('.loader'); + if (submitButton && spinner) { + // Show the spinner and disable the button + spinner.style.display = 'inline-block'; + submitButton.classList.add('show-spinner'); + submitButton.disabled = true; + } + + setTimeout(async () => { + console.log("Delayed for 1 milisecond."); + await this.lookupCustomerByEmail(); + + // Hide the spinner and re-enable the button after the lookup is complete + if (submitButton && spinner) { + spinner.style.display = 'none'; + submitButton.classList.remove('show-spinner'); + submitButton.disabled = false; + } + + this.enableGatewaySelection(); + }, 1); + } + + + watchEmail() { + if (this.useEmailWidget()) { + // TODO + } else { + this.emailInput.addEventListener('change', async () => { + log(`Change event attempt - emailInput: ${this.emailInput.value}`); + log(`this.lastEmailCheckedIdentity: ${this.lastEmailCheckedIdentity}`); + if (this.emailInput && this.lastEmailCheckedIdentity !== this.emailInput.value) { + this.onChangeEmail(); + } + }); + + log(`Last, this.emailInput.value attempt - emailInput: ${this.emailInput.value}`); + log(`this.lastEmailCheckedIdentity: ${this.lastEmailCheckedIdentity}`); + if (this.emailInput.value) { + this.onChangeEmail(); + } + } + } + + async onChangeEmail() { + this.clearData(); + + if (!this.status.active) { + log('Email checking skipped, AXO not active.'); + return; + } + + if (!this.emailInput) { + log('Email field not initialized.'); + return; + } + + log(`Email changed: ${this.emailInput ? this.emailInput.value : ''}`); + + this.$(this.el.paymentContainer.selector + '-detail').html(''); + this.$(this.el.paymentContainer.selector + '-form').html(''); + + this.setStatus('validEmail', false); + this.setStatus('hasProfile', false); + + this.hideGatewaySelection = false; + + this.lastEmailCheckedIdentity = this.emailInput.value; + + if (!this.emailInput.value || !this.emailInput.checkValidity()) { + log('The email address is not valid.'); + return; + } + + this.data.email = this.emailInput.value; + this.billingView.setData(this.data); + + if (!this.fastlane.identity) { + log('Not initialized.'); + return; + } + + PayPalInsights.trackSubmitCheckoutEmail({ + page_type: 'checkout' + }); + + this.disableGatewaySelection(); + + const submitButton = document.querySelector('.email-submit-button'); + const spinner = submitButton.querySelector('.loader'); + if (submitButton && spinner) { + // Show the spinner and disable the button + spinner.style.display = 'inline-block'; + submitButton.disabled = true; + } + + setTimeout(async () => { + console.log("Delayed for 1 milisecond."); + await this.lookupCustomerByEmail(); + + // Hide the spinner and re-enable the button after the lookup is complete + if (submitButton && spinner) { + spinner.style.display = 'none'; + submitButton.disabled = false; + } + + this.enableGatewaySelection(); + }, 1); } async lookupCustomerByEmail() { @@ -670,7 +779,7 @@ class AxoManager { address: cardBillingAddress, }; const phoneNumber = authResponse.profileData?.shippingAddress?.phoneNumber?.nationalNumber ?? ''; - if(phoneNumber) { + if (phoneNumber) { billingData.phoneNumber = phoneNumber } @@ -766,7 +875,7 @@ class AxoManager { this.ensureBillingPhoneNumber(data); - log(`Ryan flow - submitted nonce: ${this.data.card.id}` ) + log(`Ryan flow - submitted nonce: ${this.data.card.id}`) this.submit(this.data.card.id, data); @@ -777,7 +886,7 @@ class AxoManager { this.cardComponent.getPaymentToken( this.tokenizeData() ).then((response) => { - log(`Gary flow - submitted nonce: ${response.id}` ) + log(`Gary flow - submitted nonce: ${response.id}`) this.submit(response.id); }); } catch (e) { @@ -833,7 +942,6 @@ class AxoManager { }); if (data) { - // Ryan flow. const form = document.querySelector('form.woocommerce-checkout'); const formData = new FormData(form); @@ -902,13 +1010,13 @@ class AxoManager { } deleteKeysWithEmptyString = (obj) => { - for(let key of Object.keys(obj)){ - if (obj[key] === ''){ + for (let key of Object.keys(obj)) { + if (obj[key] === '') { delete obj[key]; } - else if (typeof obj[key] === 'object'){ + else if (typeof obj[key] === 'object') { obj[key] = this.deleteKeysWithEmptyString(obj[key]); - if (Object.keys(obj[key]).length === 0 ) delete obj[key]; + if (Object.keys(obj[key]).length === 0) delete obj[key]; } } diff --git a/modules/ppcp-axo/resources/js/Components/DomElementCollection.js b/modules/ppcp-axo/resources/js/Components/DomElementCollection.js index f78e29147..a9fdfd1b4 100644 --- a/modules/ppcp-axo/resources/js/Components/DomElementCollection.js +++ b/modules/ppcp-axo/resources/js/Components/DomElementCollection.js @@ -60,6 +60,15 @@ class DomElementCollection { selector: '#billing_email_field' }); + this.billingEmailSubmitButton = new DomElement({ + selector: '#ppcp-axo-billing-email-submit-button', + }); + + this.billingEmailSubmitButtonSpinner = new DomElement({ + selector: '#ppcp-axo-billing-email-submit-spinner', + className: 'loader ppcp-axo-overlay' + }); + this.submitButtonContainer = new DomElement({ selector: '#ppcp-axo-submit-button-container', }); From 0a06a913e7c00149631a6244acea9582ed5a917c Mon Sep 17 00:00:00 2001 From: Daniel Dudzic Date: Fri, 7 Jun 2024 00:35:55 +0200 Subject: [PATCH 092/104] Refactor the code --- modules/ppcp-axo/resources/css/styles.scss | 50 ++-- modules/ppcp-axo/resources/js/AxoManager.js | 265 +++++------------- .../js/Components/DomElementCollection.js | 5 +- modules/ppcp-axo/src/Assets/AxoManager.php | 18 +- 4 files changed, 108 insertions(+), 230 deletions(-) diff --git a/modules/ppcp-axo/resources/css/styles.scss b/modules/ppcp-axo/resources/css/styles.scss index 472299250..178c73126 100644 --- a/modules/ppcp-axo/resources/css/styles.scss +++ b/modules/ppcp-axo/resources/css/styles.scss @@ -12,8 +12,11 @@ } } -.email-submit-button { +#ppcp-axo-billing-email-submit-button { + margin-top: 0; position: relative; + transition: opacity 0.3s ease; + .loader:before { display: inline; height: 12px; @@ -103,30 +106,35 @@ } } +.ppcp-axo-billing-email-submit-button-hidden { + opacity: 0; +} + +.ppcp-axo-billing-email-submit-button-loaded:not([disabled]) { + opacity: 1; +} + #billing_email_field .woocommerce-input-wrapper { display: flex; align-items: center; + + input { + flex: 1; + margin-right: 10px; + } } -#billing_email_field .woocommerce-input-wrapper input { - flex: 1; - margin-right: 10px; +@media screen and (max-width: 719px) { + #ppcp-axo-billing-email-submit-button { + width: 100%; + margin-top: 0.5rem; + } + #billing_email_field .woocommerce-input-wrapper { + display: block; + + input { + margin-right: 0; + } + } } -.email-submit-button { - padding: 5px 20px; - background-color: #007cba; - color: white; - border: none; - border-radius: 3px; - cursor: pointer; - flex-shrink: 0; - opacity: 0; - transition: opacity 0.3s ease; /* Add transition for opacity */ -} - -#billing_email_field .woocommerce-input-wrapper.show-button .email-submit-button { - opacity: 1; /* Show the button */ -} - - diff --git a/modules/ppcp-axo/resources/js/AxoManager.js b/modules/ppcp-axo/resources/js/AxoManager.js index 7c490a215..74736e3e7 100644 --- a/modules/ppcp-axo/resources/js/AxoManager.js +++ b/modules/ppcp-axo/resources/js/AxoManager.js @@ -1,14 +1,15 @@ import Fastlane from "./Connection/Fastlane"; -import { log } from "./Helper/Debug"; +import {log} from "./Helper/Debug"; import DomElementCollection from "./Components/DomElementCollection"; import ShippingView from "./Views/ShippingView"; import BillingView from "./Views/BillingView"; import CardView from "./Views/CardView"; import PayPalInsights from "./Insights/PayPalInsights"; -import { disable, enable } from "../../../ppcp-button/resources/js/modules/Helper/ButtonDisabler"; -import { getCurrentPaymentMethod } from "../../../ppcp-button/resources/js/modules/Helper/CheckoutMethodState"; +import {disable,enable} from "../../../ppcp-button/resources/js/modules/Helper/ButtonDisabler"; +import {getCurrentPaymentMethod} from "../../../ppcp-button/resources/js/modules/Helper/CheckoutMethodState"; class AxoManager { + constructor(axoConfig, ppcpConfig) { this.axoConfig = axoConfig; this.ppcpConfig = ppcpConfig; @@ -50,7 +51,7 @@ class AxoManager { this.registerEventHandlers(); - this.shippingView = new ShippingView(this.el.shippingAddressContainer.selector, this.el, this.states); + this.shippingView = new ShippingView(this.el.shippingAddressContainer.selector, this.el, this.states ); this.billingView = new BillingView(this.el.billingAddressContainer.selector, this.el); this.cardView = new CardView(this.el.paymentContainer.selector + '-details', this.el, this); @@ -63,9 +64,9 @@ class AxoManager { } if ( - this.axoConfig?.insights?.enabled && - this.axoConfig?.insights?.client_id && - this.axoConfig?.insights?.session_id + this.axoConfig?.insights?.enabled + && this.axoConfig?.insights?.client_id + && this.axoConfig?.insights?.session_id ) { PayPalInsights.config(this.axoConfig?.insights?.client_id, { debug: true }); PayPalInsights.setSessionId(this.axoConfig?.insights?.session_id); @@ -87,6 +88,7 @@ class AxoManager { } registerEventHandlers() { + this.$(document).on('change', 'input[name=payment_method]', (ev) => { const map = { 'ppcp-axo-gateway': 'card', @@ -99,6 +101,7 @@ class AxoManager { }); }); + // Listen to Gateway Radio button changes. this.el.gatewayRadioButton.on('change', (ev) => { if (ev.target.checked) { @@ -160,7 +163,7 @@ class AxoManager { // Prevents sending checkout form when pressing Enter key on input field // and triggers customer lookup this.$('form.woocommerce-checkout input').on('keydown', async (ev) => { - if (ev.key === 'Enter' && getCurrentPaymentMethod() === 'ppcp-axo-gateway') { + if(ev.key === 'Enter' && getCurrentPaymentMethod() === 'ppcp-axo-gateway' ) { ev.preventDefault(); log(`Enter key attempt - emailInput: ${this.emailInput.value}`); log(`this.lastEmailCheckedIdentity: ${this.lastEmailCheckedIdentity}`); @@ -170,6 +173,12 @@ class AxoManager { } }); + this.$('form.woocommerce-checkout input').on('click', async (ev) => { + if (document.querySelector(this.el.billingEmailSubmitButton.selector).hasAttribute('disabled')) { + document.querySelector(this.el.billingEmailSubmitButton.selector).removeAttribute('disabled'); + } + }); + // 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}`); @@ -179,7 +188,7 @@ class AxoManager { // Listening to status update event document.addEventListener('axo_status_updated', (ev) => { const termsField = document.querySelector("[name='terms-field']"); - if (termsField) { + if(termsField) { const status = ev.detail; const shouldHide = status.active && status.validEmail === false && status.hasProfile === false; @@ -246,9 +255,6 @@ class AxoManager { this.$(this.el.fieldBillingEmail.selector).append( this.$(this.el.watermarkContainer.selector) ); - - // Add the submit button to the email field container. - this.renderEmailSubmitNew(); } else { this.el.emailWidgetContainer.hide(); if (!scenario.defaultEmailField) { @@ -257,6 +263,7 @@ class AxoManager { } if (scenario.axoProfileViews) { + this.shippingView.activate(); this.cardView.activate(); @@ -270,6 +277,7 @@ class AxoManager { ); this.el.watermarkContainer.show(); + } else { this.shippingView.deactivate(); this.billingView.deactivate(); @@ -279,6 +287,7 @@ 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(); } @@ -375,7 +384,7 @@ class AxoManager { log(`Status updated: ${JSON.stringify(this.status)}`); - document.dispatchEvent(new CustomEvent("axo_status_updated", { detail: this.status })); + document.dispatchEvent(new CustomEvent("axo_status_updated", {detail: this.status})); this.rerender(); } @@ -445,6 +454,7 @@ class AxoManager { } if (this.useEmailWidget()) { + // Display email widget. const ec = this.el.emailWidgetContainer; if (!document.querySelector(ec.selector)) { @@ -456,6 +466,7 @@ class AxoManager { } } else { + // Move email to the AXO container. let emailRow = document.querySelector(this.el.fieldBillingEmail.selector); wrapperElement.prepend(emailRow); @@ -469,9 +480,8 @@ class AxoManager { this.initialized = true; await this.connect(); - await this.renderWatermark(true, () => { - this.renderEmailSubmit(); - }); + await this.renderWatermark(); + this.renderEmailSubmitButton(); this.watchEmail(); } @@ -492,176 +502,40 @@ class AxoManager { this.el.gatewayRadioButton.trigger('change'); } - async renderWatermark(includeAdditionalInfo = true, callback) { + async renderWatermark(includeAdditionalInfo = true) { (await this.fastlane.FastlaneWatermarkComponent({ includeAdditionalInfo })).render(this.el.watermarkContainer.selector); this.toggleWatermarkLoading(this.el.watermarkContainer, 'ppcp-axo-watermark-loading', 'loader'); - - // Call the callback if provided - if (callback) { - callback(); - } } - async renderEmailSubmitNew() { + renderEmailSubmitButton() { const billingEmailSubmitButton = this.el.billingEmailSubmitButton; + const billingEmailSubmitButtonSpinner = this.el.billingEmailSubmitButtonSpinner; + if (!document.querySelector(billingEmailSubmitButton.selector)) { + this.emailInput.insertAdjacentHTML('afterend', ` + + `); - } - - const submitButton = this.el.billingEmailSubmitButton.selector; - // submitButton.innerText = this.axoConfig.billing_email_button_text; - - const submitButtonSpinner = this.el.billingEmailSubmitButtonSpinner.selector; - console.log(submitButton); + 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).offsetHeight; + document.querySelector(this.el.billingEmailSubmitButton.selector).classList.add('ppcp-axo-billing-email-submit-button-loaded'); } } - async renderEmailSubmit() { - // Create the submit button element - const submitButton = document.createElement('button'); - submitButton.type = 'button'; - submitButton.innerText = this.axoConfig.billing_email_button_text; - submitButton.className = 'email-submit-button'; // Add a class for styling if needed - - // Create the spinner element - const spinner = document.createElement('span'); - spinner.className = 'loader ppcp-axo-overlay'; // Use the native loader class - spinner.style.display = 'none'; // Initially hidden - - // Append the spinner to the button - submitButton.appendChild(spinner); - - // Add an event listener to handle the button click - submitButton.addEventListener('click', async () => { - const emailInput = document.querySelector(this.el.fieldBillingEmail.selector + ' input'); - if (emailInput && emailInput.checkValidity()) { - if (this.lastEmailCheckedIdentity !== emailInput.value) { - log(`Submit button clicked - emailInput: ${emailInput.value}`); - - // Show the spinner, add the class to adjust padding and disable the button - spinner.style.display = 'inline-block'; - submitButton.disabled = true; - - - try { - await this.onChangeEmail(); - } finally { - // Hide the spinner and re-enable the button after the lookup is complete - spinner.style.display = 'none'; - submitButton.disabled = false; - } - } - } else { - emailInput.reportValidity(); // Trigger the HTML5 validation message - log('Invalid or empty email input.'); - } - }); - - // Append the button inside the wrapper of the billing email input field - const emailFieldContainer = document.querySelector(this.el.fieldBillingEmail.selector); - if (emailFieldContainer) { - const inputWrapper = emailFieldContainer.querySelector('.woocommerce-input-wrapper'); - if (inputWrapper) { - // Ensure the email input has the required attribute - const emailInput = inputWrapper.querySelector('input'); - emailInput.setAttribute('required', 'required'); - emailInput.style.flex = '1'; // Make the input take the remaining space - emailInput.style.marginRight = '10px'; // Ensure the spacing is consistent - - // Remove any existing loader if present - const existingLoader = inputWrapper.querySelector('.loader'); - if (existingLoader) { - existingLoader.remove(); - } - - // Append the submit button to the input wrapper - inputWrapper.appendChild(submitButton); - - // Force a reflow to apply the transition - submitButton.offsetHeight; - - // Add the class to trigger the animation - inputWrapper.classList.add('show-button'); - } - } - } - - async onChangeEmail() { - this.clearData(); - - if (!this.status.active) { - log('Email checking skipped, AXO not active.'); - return; - } - - if (!this.emailInput) { - log('Email field not initialized.'); - return; - } - - log(`Email changed: ${this.emailInput ? this.emailInput.value : ''}`); - - this.$(this.el.paymentContainer.selector + '-detail').html(''); - this.$(this.el.paymentContainer.selector + '-form').html(''); - - this.setStatus('validEmail', false); - this.setStatus('hasProfile', false); - - this.hideGatewaySelection = false; - - this.lastEmailCheckedIdentity = this.emailInput.value; - - if (!this.emailInput.value || !this.emailInput.checkValidity()) { - log('The email address is not valid.'); - return; - } - - this.data.email = this.emailInput.value; - this.billingView.setData(this.data); - - if (!this.fastlane.identity) { - log('Not initialized.'); - return; - } - - PayPalInsights.trackSubmitCheckoutEmail({ - page_type: 'checkout' - }); - - this.disableGatewaySelection(); - - const submitButton = document.querySelector('.email-submit-button'); - const spinner = submitButton.querySelector('.loader'); - if (submitButton && spinner) { - // Show the spinner and disable the button - spinner.style.display = 'inline-block'; - submitButton.classList.add('show-spinner'); - submitButton.disabled = true; - } - - setTimeout(async () => { - console.log("Delayed for 1 milisecond."); - await this.lookupCustomerByEmail(); - - // Hide the spinner and re-enable the button after the lookup is complete - if (submitButton && spinner) { - spinner.style.display = 'none'; - submitButton.classList.remove('show-spinner'); - submitButton.disabled = false; - } - - this.enableGatewaySelection(); - }, 1); - } - - watchEmail() { + if (this.useEmailWidget()) { + // TODO + } else { - this.emailInput.addEventListener('change', async () => { + this.emailInput.addEventListener('change', async ()=> { log(`Change event attempt - emailInput: ${this.emailInput.value}`); log(`this.lastEmailCheckedIdentity: ${this.lastEmailCheckedIdentity}`); if (this.emailInput && this.lastEmailCheckedIdentity !== this.emailInput.value) { @@ -677,7 +551,7 @@ class AxoManager { } } - async onChangeEmail() { + async onChangeEmail () { this.clearData(); if (!this.status.active) { @@ -719,28 +593,12 @@ class AxoManager { page_type: 'checkout' }); + this.disableGatewaySelection(); - - const submitButton = document.querySelector('.email-submit-button'); - const spinner = submitButton.querySelector('.loader'); - if (submitButton && spinner) { - // Show the spinner and disable the button - spinner.style.display = 'inline-block'; - submitButton.disabled = true; - } - - setTimeout(async () => { - console.log("Delayed for 1 milisecond."); - await this.lookupCustomerByEmail(); - - // Hide the spinner and re-enable the button after the lookup is complete - if (submitButton && spinner) { - spinner.style.display = 'none'; - submitButton.disabled = false; - } - - this.enableGatewaySelection(); - }, 1); + 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() { @@ -779,7 +637,7 @@ class AxoManager { address: cardBillingAddress, }; const phoneNumber = authResponse.profileData?.shippingAddress?.phoneNumber?.nationalNumber ?? ''; - if (phoneNumber) { + if(phoneNumber) { billingData.phoneNumber = phoneNumber } @@ -875,7 +733,7 @@ class AxoManager { this.ensureBillingPhoneNumber(data); - log(`Ryan flow - submitted nonce: ${this.data.card.id}`) + log(`Ryan flow - submitted nonce: ${this.data.card.id}` ) this.submit(this.data.card.id, data); @@ -886,7 +744,7 @@ class AxoManager { this.cardComponent.getPaymentToken( this.tokenizeData() ).then((response) => { - log(`Gary flow - submitted nonce: ${response.id}`) + log(`Gary flow - submitted nonce: ${response.id}` ) this.submit(response.id); }); } catch (e) { @@ -942,6 +800,7 @@ class AxoManager { }); if (data) { + // Ryan flow. const form = document.querySelector('form.woocommerce-checkout'); const formData = new FormData(form); @@ -1010,13 +869,13 @@ class AxoManager { } deleteKeysWithEmptyString = (obj) => { - for (let key of Object.keys(obj)) { - if (obj[key] === '') { + for(let key of Object.keys(obj)){ + if (obj[key] === ''){ delete obj[key]; } - else if (typeof obj[key] === 'object') { + else if (typeof obj[key] === 'object'){ obj[key] = this.deleteKeysWithEmptyString(obj[key]); - if (Object.keys(obj[key]).length === 0) delete obj[key]; + if (Object.keys(obj[key]).length === 0 ) delete obj[key]; } } @@ -1049,6 +908,14 @@ class AxoManager { } } + 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}`); diff --git a/modules/ppcp-axo/resources/js/Components/DomElementCollection.js b/modules/ppcp-axo/resources/js/Components/DomElementCollection.js index a9fdfd1b4..52e48ac5d 100644 --- a/modules/ppcp-axo/resources/js/Components/DomElementCollection.js +++ b/modules/ppcp-axo/resources/js/Components/DomElementCollection.js @@ -61,11 +61,14 @@ class DomElementCollection { }); 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({ - selector: '#ppcp-axo-billing-email-submit-spinner', + id: 'ppcp-axo-billing-email-submit-button-spinner', + selector: '#ppcp-axo-billing-email-submit-button-spinner', className: 'loader ppcp-axo-overlay' }); diff --git a/modules/ppcp-axo/src/Assets/AxoManager.php b/modules/ppcp-axo/src/Assets/AxoManager.php index d349fe3ff..3196e74f0 100644 --- a/modules/ppcp-axo/src/Assets/AxoManager.php +++ b/modules/ppcp-axo/src/Assets/AxoManager.php @@ -162,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' => @@ -182,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' ) : '', @@ -201,16 +201,16 @@ 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' ), ), ), - 'icons_directory' => esc_url( $this->wcgateway_module_url ) . 'assets/images/axo/', - 'module_url' => untrailingslashit( $this->module_url ), - 'ajax' => array( + '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() ), From 8f869450597dd79899bc1bba9dac887f833c19b2 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Fri, 7 Jun 2024 10:46:03 +0200 Subject: [PATCH 093/104] Get purchase units from order when in pay for order context --- .../src/Endpoint/CaptureCardPayment.php | 18 ++++++++++++++---- .../src/Gateway/CreditCardGateway.php | 2 +- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/modules/ppcp-wc-gateway/src/Endpoint/CaptureCardPayment.php b/modules/ppcp-wc-gateway/src/Endpoint/CaptureCardPayment.php index bfb8b4bc4..a2289acee 100644 --- a/modules/ppcp-wc-gateway/src/Endpoint/CaptureCardPayment.php +++ b/modules/ppcp-wc-gateway/src/Endpoint/CaptureCardPayment.php @@ -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( diff --git a/modules/ppcp-wc-gateway/src/Gateway/CreditCardGateway.php b/modules/ppcp-wc-gateway/src/Gateway/CreditCardGateway.php index 6e21e70df..cea67537f 100644 --- a/modules/ppcp-wc-gateway/src/Gateway/CreditCardGateway.php +++ b/modules/ppcp-wc-gateway/src/Gateway/CreditCardGateway.php @@ -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() ); From 457ac5a5cda6c0d204488ee0bb629b92778580f5 Mon Sep 17 00:00:00 2001 From: Daniel Dudzic Date: Fri, 7 Jun 2024 11:41:30 +0200 Subject: [PATCH 094/104] CSS clean up --- modules/ppcp-axo/resources/css/styles.scss | 23 ++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/modules/ppcp-axo/resources/css/styles.scss b/modules/ppcp-axo/resources/css/styles.scss index 178c73126..64ebfcf30 100644 --- a/modules/ppcp-axo/resources/css/styles.scss +++ b/modules/ppcp-axo/resources/css/styles.scss @@ -28,6 +28,16 @@ } } +.ppcp-axo-billing-email-submit-button { + &-hidden { + opacity: 0; + } + + &-loaded:not([disabled]) { + opacity: 1; + } +} + .ppcp-axo-payment-container { padding: 1rem 0; background-color: #ffffff; @@ -106,15 +116,7 @@ } } -.ppcp-axo-billing-email-submit-button-hidden { - opacity: 0; -} - -.ppcp-axo-billing-email-submit-button-loaded:not([disabled]) { - opacity: 1; -} - -#billing_email_field .woocommerce-input-wrapper { +.ppcp-axo-customer-details #billing_email_field .woocommerce-input-wrapper { display: flex; align-items: center; @@ -129,7 +131,7 @@ width: 100%; margin-top: 0.5rem; } - #billing_email_field .woocommerce-input-wrapper { + .ppcp-axo-customer-details #billing_email_field .woocommerce-input-wrapper { display: block; input { @@ -138,3 +140,4 @@ } } + From ebccacf964665380ed46b56b0977c93393ea72fd Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Fri, 7 Jun 2024 14:50:09 +0200 Subject: [PATCH 095/104] Ensure save payment method when subscription in cart --- .../ppcp-blocks/resources/js/Components/card-fields.js | 8 ++++++++ modules/ppcp-blocks/resources/js/card-fields-config.js | 1 - 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/modules/ppcp-blocks/resources/js/Components/card-fields.js b/modules/ppcp-blocks/resources/js/Components/card-fields.js index f7ff6c7e0..542d40bd3 100644 --- a/modules/ppcp-blocks/resources/js/Components/card-fields.js +++ b/modules/ppcp-blocks/resources/js/Components/card-fields.js @@ -25,6 +25,14 @@ export function CardFields({config, eventRegistration, emitResponse, components} } const hasSubscriptionProducts = cartHasSubscriptionProducts(config.scriptData); + useEffect(() => { + localStorage.removeItem('ppcp-save-card-payment'); + + if(hasSubscriptionProducts) { + localStorage.setItem('ppcp-save-card-payment', 'true'); + } + + }, [hasSubscriptionProducts]) useEffect( () => diff --git a/modules/ppcp-blocks/resources/js/card-fields-config.js b/modules/ppcp-blocks/resources/js/card-fields-config.js index 37d939a5f..6c0101c3b 100644 --- a/modules/ppcp-blocks/resources/js/card-fields-config.js +++ b/modules/ppcp-blocks/resources/js/card-fields-config.js @@ -35,7 +35,6 @@ export async function onApprove(data) { }) .then((response) => response.json()) .then((data) => { - console.log(data) localStorage.removeItem('ppcp-save-card-payment'); }) .catch((err) => { From 1c515d7f1a849a1a5ac8a864c71b16f9e8e9fab6 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Fri, 7 Jun 2024 16:24:24 +0200 Subject: [PATCH 096/104] Update changelog --- changelog.txt | 1 + readme.txt | 1 + 2 files changed, 2 insertions(+) diff --git a/changelog.txt b/changelog.txt index 17c03cb83..e8de50404 100644 --- a/changelog.txt +++ b/changelog.txt @@ -8,6 +8,7 @@ * 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 diff --git a/readme.txt b/readme.txt index ac1315805..1542090d5 100644 --- a/readme.txt +++ b/readme.txt @@ -187,6 +187,7 @@ If you encounter issues with the PayPal buttons not appearing after an update, p * 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 From 7537967266fa488fb649b46642607f45e699dbff Mon Sep 17 00:00:00 2001 From: Daniel Dudzic Date: Fri, 7 Jun 2024 22:07:21 +0200 Subject: [PATCH 097/104] Final polish --- modules/ppcp-axo/resources/css/styles.scss | 30 +++++++++---------- modules/ppcp-axo/resources/js/AxoManager.js | 20 +++++++++---- .../js/Components/DomElementCollection.js | 5 ++++ modules/ppcp-axo/src/Gateway/AxoGateway.php | 2 +- 4 files changed, 36 insertions(+), 21 deletions(-) diff --git a/modules/ppcp-axo/resources/css/styles.scss b/modules/ppcp-axo/resources/css/styles.scss index 64ebfcf30..3c879dcb6 100644 --- a/modules/ppcp-axo/resources/css/styles.scss +++ b/modules/ppcp-axo/resources/css/styles.scss @@ -12,10 +12,17 @@ } } +#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; @@ -117,27 +124,20 @@ } .ppcp-axo-customer-details #billing_email_field .woocommerce-input-wrapper { - display: flex; - align-items: center; - - input { - flex: 1; - margin-right: 10px; - } + flex: 1 1 auto; } @media screen and (max-width: 719px) { - #ppcp-axo-billing-email-submit-button { - width: 100%; - margin-top: 0.5rem; - } - .ppcp-axo-customer-details #billing_email_field .woocommerce-input-wrapper { - display: block; + #ppcp-axo-billing-email { + &-field-wrapper { + flex-direction: column; + } - input { - margin-right: 0; + &-submit-button { + align-self: auto; } } } + diff --git a/modules/ppcp-axo/resources/js/AxoManager.js b/modules/ppcp-axo/resources/js/AxoManager.js index 74736e3e7..0267093a7 100644 --- a/modules/ppcp-axo/resources/js/AxoManager.js +++ b/modules/ppcp-axo/resources/js/AxoManager.js @@ -225,8 +225,10 @@ class AxoManager { if (scenario.defaultSubmitButton) { this.el.defaultSubmitButton.show(); + this.el.billingEmailSubmitButton.hide(); } else { this.el.defaultSubmitButton.hide(); + this.el.billingEmailSubmitButton.show(); } if (scenario.defaultEmailField) { @@ -252,8 +254,8 @@ 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(); @@ -433,10 +435,18 @@ class AxoManager { `); } + // billingEmailFieldWrapper + const befw = this.el.billingEmailFieldWrapper; + if (!document.querySelector(befw.selector)) { + document.querySelector('#billing_email_field .woocommerce-input-wrapper').insertAdjacentHTML('afterend', ` +
+ `); + } + // Watermark container const wc = this.el.watermarkContainer; if (!document.querySelector(wc.selector)) { - this.emailInput.insertAdjacentHTML('afterend', ` + document.querySelector(befw.selector).insertAdjacentHTML('beforeend', `
`); } @@ -466,10 +476,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')) } } @@ -515,7 +525,7 @@ class AxoManager { const billingEmailSubmitButtonSpinner = this.el.billingEmailSubmitButtonSpinner; if (!document.querySelector(billingEmailSubmitButton.selector)) { - this.emailInput.insertAdjacentHTML('afterend', ` + document.querySelector(this.el.billingEmailFieldWrapper.selector).insertAdjacentHTML('beforeend', `