From ce280e9cbaee475282bc7280be852826e5bbc1f4 Mon Sep 17 00:00:00 2001 From: Daniel Dudzic Date: Thu, 9 May 2024 13:06:20 +0200 Subject: [PATCH 1/4] Add new helper class that provides methods for Cart and Checkout type detection --- .../src/Helper/CartCheckoutDetector.php | 124 ++++++++++++++++++ 1 file changed, 124 insertions(+) create mode 100644 modules/ppcp-wc-gateway/src/Helper/CartCheckoutDetector.php diff --git a/modules/ppcp-wc-gateway/src/Helper/CartCheckoutDetector.php b/modules/ppcp-wc-gateway/src/Helper/CartCheckoutDetector.php new file mode 100644 index 000000000..c9826bfe3 --- /dev/null +++ b/modules/ppcp-wc-gateway/src/Helper/CartCheckoutDetector.php @@ -0,0 +1,124 @@ +get_elementor_widgets( wc_get_page_id( 'checkout' ) ); + + if ( $elementor_widgets ) { + return in_array( 'woocommerce-checkout-page', $elementor_widgets); + } + + return false; + } + + /** + * Check if the Cart page is using Elementor. + * + * @return bool + */ + public function has_elementor_cart(): bool { + $elementor_widgets = $this->get_elementor_widgets( wc_get_page_id( 'cart' ) ); + + if ( $elementor_widgets ) { + return in_array( 'woocommerce-cart-page', $elementor_widgets); + } + + return false; + } + + /** + * Check if the Checkout page is using the block checkout. + * + * @return bool + */ + public function has_block_checkout(): bool { + $checkout_page_id = wc_get_page_id( 'checkout' ); + return $checkout_page_id && has_block( 'woocommerce/checkout', $checkout_page_id ); + } + + /** + * Check if the Cart page is using the block cart. + * + * @return bool + */ + public function has_block_cart(): bool { + $cart_page_id = wc_get_page_id( 'cart' ); + return $cart_page_id && has_block( 'woocommerce/cart', $cart_page_id ); + } + + /** + * Check if the Checkout page is using the classic checkout. + * + * @return bool + */ + public 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 ); + } + + /** + * Check if the Cart page is using the classic cart. + * + * @return bool + */ + public 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 ); + } +} From 1f3bc015266a96cc0de19d7b7304ecc124f2db47 Mon Sep 17 00:00:00 2001 From: Daniel Dudzic Date: Fri, 10 May 2024 02:07:51 +0200 Subject: [PATCH 2/4] Add the checkout detection notice to the AXO gateway settings --- modules/ppcp-axo/extensions.php | 14 +++++++ modules/ppcp-axo/services.php | 28 +++++++++++++ .../ppcp-wc-gateway/resources/css/common.scss | 30 +++++++++++++ .../src/Helper/CartCheckoutDetector.php | 42 ++++++++++--------- .../src/Settings/SettingsRenderer.php | 25 +++++++++++ .../ppcp-wc-gateway/src/WCGatewayModule.php | 1 + 6 files changed, 120 insertions(+), 20 deletions(-) diff --git a/modules/ppcp-axo/extensions.php b/modules/ppcp-axo/extensions.php index e81176244..09b590fd7 100644 --- a/modules/ppcp-axo/extensions.php +++ b/modules/ppcp-axo/extensions.php @@ -9,6 +9,7 @@ declare(strict_types=1); namespace WooCommerce\PayPalCommerce\Axo; +use WooCommerce\PayPalCommerce\Axo\Helper\NoticeRenderer; use WooCommerce\PayPalCommerce\Axo\Helper\PropertiesDictionary; use WooCommerce\PayPalCommerce\Onboarding\State; use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface; @@ -82,6 +83,7 @@ return array( ->rule() ->condition_element( 'axo_enabled', '1' ) ->action_visible( 'axo_gateway_title' ) + ->action_visible( 'axo_checkout_config_notice' ) ->action_visible( 'axo_privacy' ) ->action_visible( 'axo_name_on_card' ) ->action_visible( 'axo_style_heading' ) @@ -112,6 +114,18 @@ return array( ), 'classes' => array( 'ppcp-valign-label-middle', 'ppcp-align-label-center' ), ), + 'axo_checkout_config_notice' => array( + 'heading' => '', + 'html' => $container->get( 'axo.checkout-config-notice' ), + 'type' => 'ppcp-html', + 'classes' => array( 'ppcp-field-indent' ), + 'class' => array(), + 'screens' => array( + State::STATE_ONBOARDED, + ), + 'requirements' => array( 'dcc', 'axo' ), + 'gateway' => array( 'dcc', 'axo' ), + ), 'axo_gateway_title' => array( 'title' => __( 'Gateway Title', 'woocommerce-paypal-payments' ), 'type' => 'text', diff --git a/modules/ppcp-axo/services.php b/modules/ppcp-axo/services.php index 780f59a8a..980f5bbbd 100644 --- a/modules/ppcp-axo/services.php +++ b/modules/ppcp-axo/services.php @@ -13,6 +13,7 @@ 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\Helper\CartCheckoutDetector; return array( @@ -145,4 +146,31 @@ return array( ); }, + 'axo.checkout-config-notice' => static function ( ContainerInterface $container ) : string { + $checkout_page_link = get_edit_post_link( wc_get_page_id( 'checkout' ) ); + + if ( CartCheckoutDetector::has_elementor_checkout() ) { + $notice_content = sprintf( + /* translators: %1$s: URL to the Checkout edit page. */ + __( + 'Important: Your store has a Checkout page with the Elementor Checkout widget configured. Fastlane requires your current Checkout page to include a Classic Checkout or [woocommerce_checkout] shortcode to accelerate the payment process.', + 'woocommerce-paypal-payments' + ), + esc_url( $checkout_page_link ) + ); + } elseif ( CartCheckoutDetector::has_block_checkout() ) { + $notice_content = sprintf( + /* translators: %1$s: URL to the Checkout edit page. */ + __( + 'Important: Your store has a Checkout page with the WooCommerce Checkout block configured. Fastlane requires your current Checkout page to include a Classic Checkout or [woocommerce_checkout] shortcode to accelerate the payment process.', + 'woocommerce-paypal-payments' + ), + esc_url( $checkout_page_link ) + ); + } else { + return ''; + } + + return '

' . $notice_content . '

'; + }, ); diff --git a/modules/ppcp-wc-gateway/resources/css/common.scss b/modules/ppcp-wc-gateway/resources/css/common.scss index 1d074ee34..1dc7ecbd4 100644 --- a/modules/ppcp-wc-gateway/resources/css/common.scss +++ b/modules/ppcp-wc-gateway/resources/css/common.scss @@ -56,6 +56,24 @@ $background-ident-color: #fbfbfb; } } +.ppcp-notice { + background: #fff; + border: 1px solid #c3c4c7; + border-left-width: 4px; + box-shadow: 0 1px 1px rgba(0, 0, 0, 0.04); + margin: 5px 15px 2px; + padding: 1px 12px; +} + +.ppcp-notice-warning { + border-left-color: #dba617; + + .highlight { + background: transparent; + color: #dba617; + } +} + // Box indented fields. @media screen and (min-width: 800px) { .ppcp-settings-field { @@ -77,6 +95,18 @@ $background-ident-color: #fbfbfb; th, &.ppcp-settings-field-heading td { padding-left: 40px; + padding-right: 40px; + } + + .ppcp-notice { + margin-left: 40px; + margin-right: 10px; + padding: 1px 12px; + + p { + margin: .5em 0; + padding: 2px; + } } th, td { diff --git a/modules/ppcp-wc-gateway/src/Helper/CartCheckoutDetector.php b/modules/ppcp-wc-gateway/src/Helper/CartCheckoutDetector.php index c9826bfe3..9f3bc8313 100644 --- a/modules/ppcp-wc-gateway/src/Helper/CartCheckoutDetector.php +++ b/modules/ppcp-wc-gateway/src/Helper/CartCheckoutDetector.php @@ -17,28 +17,30 @@ use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings; class CartCheckoutDetector { /** - * Returns Elementor widgets if they exist (for a specific page). - * @param $page_id - * @return array + * Returns a list of Elementor widgets if they exist for a specific page. + * + * @param int $page_id The ID of the page. + * + * @return array List of widget types if any exist, otherwise an empty array. */ - private function get_elementor_widgets( $page_id ): array { + private static function get_elementor_widgets( $page_id ): array { $elementor_data = get_post_meta( $page_id, '_elementor_data' ); - if ( isset($elementor_data[0] ) ) { - // parse elementor json and found all widgets in json for specific page - $reg_exp = '/"widgetType":"([^"]*)/i'; + if ( isset( $elementor_data[0] ) ) { + // Parse the Elementor json and find all widgets for a specific page. + $reg_exp = '/"widgetType":"([^"]*)/i'; $output_array = array(); if ( is_array( $elementor_data[0] ) ) { - $elementor_data[0] = json_encode( $elementor_data[0] ); + $elementor_data[0] = wp_json_encode( $elementor_data[0] ); } preg_match_all( $reg_exp, $elementor_data[0], $output_array, PREG_SET_ORDER ); $widgets_list = array(); - foreach( $output_array as $found ) { - if ( !isset( $found[1] ) ) { + foreach ( $output_array as $found ) { + if ( ! isset( $found[1] ) ) { continue; } @@ -57,11 +59,11 @@ class CartCheckoutDetector { * * @return bool */ - public function has_elementor_checkout(): bool { - $elementor_widgets = $this->get_elementor_widgets( wc_get_page_id( 'checkout' ) ); + public static function has_elementor_checkout(): bool { + $elementor_widgets = self::get_elementor_widgets( wc_get_page_id( 'checkout' ) ); if ( $elementor_widgets ) { - return in_array( 'woocommerce-checkout-page', $elementor_widgets); + return in_array( 'woocommerce-checkout-page', $elementor_widgets, true ); } return false; @@ -72,11 +74,11 @@ class CartCheckoutDetector { * * @return bool */ - public function has_elementor_cart(): bool { - $elementor_widgets = $this->get_elementor_widgets( wc_get_page_id( 'cart' ) ); + public static function has_elementor_cart(): bool { + $elementor_widgets = self::get_elementor_widgets( wc_get_page_id( 'cart' ) ); if ( $elementor_widgets ) { - return in_array( 'woocommerce-cart-page', $elementor_widgets); + return in_array( 'woocommerce-cart-page', $elementor_widgets, true ); } return false; @@ -87,7 +89,7 @@ class CartCheckoutDetector { * * @return bool */ - public function has_block_checkout(): bool { + public static function has_block_checkout(): bool { $checkout_page_id = wc_get_page_id( 'checkout' ); return $checkout_page_id && has_block( 'woocommerce/checkout', $checkout_page_id ); } @@ -97,7 +99,7 @@ class CartCheckoutDetector { * * @return bool */ - public function has_block_cart(): bool { + public static function has_block_cart(): bool { $cart_page_id = wc_get_page_id( 'cart' ); return $cart_page_id && has_block( 'woocommerce/cart', $cart_page_id ); } @@ -107,7 +109,7 @@ class CartCheckoutDetector { * * @return bool */ - public function has_classic_checkout(): bool { + 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 ); } @@ -117,7 +119,7 @@ class CartCheckoutDetector { * * @return bool */ - public function has_classic_cart(): bool { + 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 ); } diff --git a/modules/ppcp-wc-gateway/src/Settings/SettingsRenderer.php b/modules/ppcp-wc-gateway/src/Settings/SettingsRenderer.php index 03ef59ca6..46216b548 100644 --- a/modules/ppcp-wc-gateway/src/Settings/SettingsRenderer.php +++ b/modules/ppcp-wc-gateway/src/Settings/SettingsRenderer.php @@ -268,6 +268,31 @@ class SettingsRenderer { return $html; } + /** + * Renders the html field. + * + * @param string $field The current field HTML. + * @param string $key The current key. + * @param array $config The configuration array. + * @param string $value The current value. + * + * @return string + */ + public function render_html( $field, $key, $config, $value ): string { + + if ( 'ppcp-html' !== $config['type'] ) { + return $field; + } + + $html = sprintf( + '
%s
', + esc_attr( implode( ' ', $config['classes'] ) ), + $config['html'] + ); + + return $html; + } + /** * Renders the table row. * diff --git a/modules/ppcp-wc-gateway/src/WCGatewayModule.php b/modules/ppcp-wc-gateway/src/WCGatewayModule.php index 8e765a8e9..19f67ef53 100644 --- a/modules/ppcp-wc-gateway/src/WCGatewayModule.php +++ b/modules/ppcp-wc-gateway/src/WCGatewayModule.php @@ -627,6 +627,7 @@ class WCGatewayModule implements ModuleInterface { $field = $renderer->render_password( $field, $key, $args, $value ); $field = $renderer->render_heading( $field, $key, $args, $value ); $field = $renderer->render_table( $field, $key, $args, $value ); + $field = $renderer->render_html( $field, $key, $args, $value ); return $field; }, 10, From 2adc7d12895e661f65b2131be6efacae56fcd4ed Mon Sep 17 00:00:00 2001 From: Daniel Dudzic Date: Mon, 13 May 2024 17:22:33 +0200 Subject: [PATCH 3/4] Update the AXO warning messages --- modules/ppcp-axo/services.php | 30 ++++++++++++++----- .../ppcp-wc-gateway/resources/css/common.scss | 1 + .../src/Helper/CartCheckoutDetector.php | 9 ++++-- 3 files changed, 30 insertions(+), 10 deletions(-) diff --git a/modules/ppcp-axo/services.php b/modules/ppcp-axo/services.php index 980f5bbbd..fd288ef7f 100644 --- a/modules/ppcp-axo/services.php +++ b/modules/ppcp-axo/services.php @@ -147,25 +147,41 @@ return array( }, 'axo.checkout-config-notice' => static function ( ContainerInterface $container ) : string { - $checkout_page_link = get_edit_post_link( wc_get_page_id( 'checkout' ) ); + $checkout_page_link = esc_url( get_edit_post_link( wc_get_page_id( 'checkout' ) ) ?? '' ); + $block_checkout_docs_link = __( + 'https://woocommerce.com/document/cart-checkout-blocks-status/#reverting-to-the-cart-and-checkout-shortcodes', + 'woocommerce-paypal-payments' + ); if ( CartCheckoutDetector::has_elementor_checkout() ) { $notice_content = sprintf( - /* translators: %1$s: URL to the Checkout edit page. */ + /* translators: %1$s: URL to the Checkout edit page. %2$s: URL to the block checkout docs. */ __( - 'Important: Your store has a Checkout page with the Elementor Checkout widget configured. Fastlane requires your current Checkout page to include a Classic Checkout or [woocommerce_checkout] shortcode to accelerate the payment process.', + 'Warning: The Checkout page of your store currently uses the Elementor Checkout widget. To enable Fastlane and accelerate payments, the page must include either the Classic Checkout or the [woocommerce_checkout] shortcode. See this page for instructions on how to switch to the classic layout.', 'woocommerce-paypal-payments' ), - esc_url( $checkout_page_link ) + esc_url( $checkout_page_link ), + esc_url( $block_checkout_docs_link ) ); } elseif ( CartCheckoutDetector::has_block_checkout() ) { $notice_content = sprintf( - /* translators: %1$s: URL to the Checkout edit page. */ + /* translators: %1$s: URL to the Checkout edit page. %2$s: URL to the block checkout docs. */ __( - 'Important: Your store has a Checkout page with the WooCommerce Checkout block configured. Fastlane requires your current Checkout page to include a Classic Checkout or [woocommerce_checkout] shortcode to accelerate the payment process.', + 'Warning: The Checkout page of your store currently uses the WooCommerce Checkout block. To enable Fastlane and accelerate payments, the page must include either the Classic Checkout or the [woocommerce_checkout] shortcode. See this page for instructions on how to switch to the classic layout.', 'woocommerce-paypal-payments' ), - esc_url( $checkout_page_link ) + esc_url( $checkout_page_link ), + esc_url( $block_checkout_docs_link ) + ); + } elseif ( ! CartCheckoutDetector::has_classic_checkout() ) { + $notice_content = sprintf( + /* translators: %1$s: URL to the Checkout edit page. %2$s: URL to the block checkout docs. */ + __( + 'Warning: The Checkout page of your store does not seem to be properly configured or uses an incompatible third-party Checkout solution. To enable Fastlane and accelerate payments, the page must include either the Classic Checkout or the [woocommerce_checkout] shortcode. See this page for instructions on how to switch to the classic layout.', + 'woocommerce-paypal-payments' + ), + esc_url( $checkout_page_link ), + esc_url( $block_checkout_docs_link ) ); } else { return ''; diff --git a/modules/ppcp-wc-gateway/resources/css/common.scss b/modules/ppcp-wc-gateway/resources/css/common.scss index 1dc7ecbd4..1345cb810 100644 --- a/modules/ppcp-wc-gateway/resources/css/common.scss +++ b/modules/ppcp-wc-gateway/resources/css/common.scss @@ -71,6 +71,7 @@ $background-ident-color: #fbfbfb; .highlight { background: transparent; color: #dba617; + font-weight: 600; } } diff --git a/modules/ppcp-wc-gateway/src/Helper/CartCheckoutDetector.php b/modules/ppcp-wc-gateway/src/Helper/CartCheckoutDetector.php index 9f3bc8313..176bc05b2 100644 --- a/modules/ppcp-wc-gateway/src/Helper/CartCheckoutDetector.php +++ b/modules/ppcp-wc-gateway/src/Helper/CartCheckoutDetector.php @@ -60,10 +60,13 @@ class CartCheckoutDetector { * @return bool */ public static function has_elementor_checkout(): bool { - $elementor_widgets = self::get_elementor_widgets( wc_get_page_id( 'checkout' ) ); + // Check if Elementor is installed and activated + if ( did_action( 'elementor/loaded' ) ) { + $elementor_widgets = self::get_elementor_widgets( wc_get_page_id( 'checkout' ) ); - if ( $elementor_widgets ) { - return in_array( 'woocommerce-checkout-page', $elementor_widgets, true ); + if ( $elementor_widgets ) { + return in_array( 'woocommerce-checkout-page', $elementor_widgets, true ); + } } return false; From b389ed861b97d476dbebebc04a2eaf0b1ce5925b Mon Sep 17 00:00:00 2001 From: Daniel Dudzic Date: Mon, 13 May 2024 17:31:58 +0200 Subject: [PATCH 4/4] Fix unrelated PHPCS errors --- .../src/Exception/PayPalApiException.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/modules/ppcp-api-client/src/Exception/PayPalApiException.php b/modules/ppcp-api-client/src/Exception/PayPalApiException.php index 9db633729..11405b22b 100644 --- a/modules/ppcp-api-client/src/Exception/PayPalApiException.php +++ b/modules/ppcp-api-client/src/Exception/PayPalApiException.php @@ -155,11 +155,11 @@ class PayPalApiException extends RuntimeException { return $json->message; } $improved_keys_messages = array( - 'PAYMENT_DENIED' => __( 'PayPal rejected the payment. Please reach out to the PayPal support for more information.', 'woocommerce-paypal-payments' ), - 'TRANSACTION_REFUSED' => __( 'The transaction has been refused by the payment processor. Please reach out to the PayPal support for more information.', 'woocommerce-paypal-payments' ), - 'DUPLICATE_INVOICE_ID' => __( 'The transaction has been refused because the Invoice ID already exists. Please create a new order or reach out to the store owner.', 'woocommerce-paypal-payments' ), - 'PAYER_CANNOT_PAY' => __( 'There was a problem processing this transaction. Please reach out to the store owner.', 'woocommerce-paypal-payments' ), - 'PAYEE_ACCOUNT_RESTRICTED' => __( 'There was a problem processing this transaction. Please reach out to the store owner.', 'woocommerce-paypal-payments' ), + 'PAYMENT_DENIED' => __( 'PayPal rejected the payment. Please reach out to the PayPal support for more information.', 'woocommerce-paypal-payments' ), + 'TRANSACTION_REFUSED' => __( 'The transaction has been refused by the payment processor. Please reach out to the PayPal support for more information.', 'woocommerce-paypal-payments' ), + 'DUPLICATE_INVOICE_ID' => __( 'The transaction has been refused because the Invoice ID already exists. Please create a new order or reach out to the store owner.', 'woocommerce-paypal-payments' ), + 'PAYER_CANNOT_PAY' => __( 'There was a problem processing this transaction. Please reach out to the store owner.', 'woocommerce-paypal-payments' ), + 'PAYEE_ACCOUNT_RESTRICTED' => __( 'There was a problem processing this transaction. Please reach out to the store owner.', 'woocommerce-paypal-payments' ), 'AGREEMENT_ALREADY_CANCELLED' => __( 'The requested agreement is already canceled. Please reach out to the PayPal support for more information.', 'woocommerce-paypal-payments' ), ); $improved_errors = array_filter(