From be9c62426423cfe9fcf1080538d67bde8f964f4d Mon Sep 17 00:00:00 2001 From: Alex P Date: Tue, 28 Jun 2022 12:09:18 +0300 Subject: [PATCH] Add filter allowing to disable the basic client-side validation In case some conflicts occur. --- modules/ppcp-button/resources/js/button.js | 22 ++--- modules/ppcp-button/services.php | 39 +++++---- .../ppcp-button/src/Assets/SmartButton.php | 81 +++++++++++-------- 3 files changed, 82 insertions(+), 60 deletions(-) diff --git a/modules/ppcp-button/resources/js/button.js b/modules/ppcp-button/resources/js/button.js index 8038c936e..c9869dd63 100644 --- a/modules/ppcp-button/resources/js/button.js +++ b/modules/ppcp-button/resources/js/button.js @@ -31,17 +31,19 @@ const bootstrap = () => { const onSmartButtonClick = (data, actions) => { window.ppcpFundingSource = data.fundingSource; - // TODO: quick fix to get the error about empty form before attempting PayPal order - // it should solve #513 for most of the users, but proper solution should be implemented later. - const requiredFields = jQuery('form.woocommerce-checkout .validate-required:visible :input'); - requiredFields.each((i, input) => { - jQuery(input).trigger('validate'); - }); - if (jQuery('form.woocommerce-checkout .woocommerce-invalid:visible').length) { - errorHandler.clear(); - errorHandler.message(PayPalCommerceGateway.labels.error.js_validation); + if (PayPalCommerceGateway.basic_checkout_validation_enabled) { + // TODO: quick fix to get the error about empty form before attempting PayPal order + // it should solve #513 for most of the users, but proper solution should be implemented later. + const requiredFields = jQuery('form.woocommerce-checkout .validate-required:visible :input'); + requiredFields.each((i, input) => { + jQuery(input).trigger('validate'); + }); + if (jQuery('form.woocommerce-checkout .woocommerce-invalid:visible').length) { + errorHandler.clear(); + errorHandler.message(PayPalCommerceGateway.labels.error.js_validation); - return actions.reject(); + return actions.reject(); + } } const form = document.querySelector('form.woocommerce-checkout'); diff --git a/modules/ppcp-button/services.php b/modules/ppcp-button/services.php index f8b6c415e..b5867aa14 100644 --- a/modules/ppcp-button/services.php +++ b/modules/ppcp-button/services.php @@ -27,7 +27,7 @@ use WooCommerce\PayPalCommerce\Onboarding\Environment; use WooCommerce\PayPalCommerce\Onboarding\State; return array( - 'button.client_id' => static function ( ContainerInterface $container ): string { + 'button.client_id' => static function ( ContainerInterface $container ): string { $settings = $container->get( 'wcgateway.settings' ); $client_id = $settings->has( 'client_id' ) ? $settings->get( 'client_id' ) : ''; @@ -45,7 +45,7 @@ return array( return $env->current_environment_is( Environment::SANDBOX ) ? CONNECT_WOO_SANDBOX_CLIENT_ID : CONNECT_WOO_CLIENT_ID; }, - 'button.smart-button' => static function ( ContainerInterface $container ): SmartButtonInterface { + 'button.smart-button' => static function ( ContainerInterface $container ): SmartButtonInterface { $state = $container->get( 'onboarding.state' ); /** @@ -88,19 +88,20 @@ return array( $settings_status, $currency, $container->get( 'wcgateway.all-funding-sources' ), + $container->get( 'button.basic-checkout-validation-enabled' ), $container->get( 'woocommerce.logger.woocommerce' ) ); }, - 'button.url' => static function ( ContainerInterface $container ): string { + 'button.url' => static function ( ContainerInterface $container ): string { return plugins_url( '/modules/ppcp-button/', dirname( realpath( __FILE__ ), 3 ) . '/woocommerce-paypal-payments.php' ); }, - 'button.request-data' => static function ( ContainerInterface $container ): RequestData { + 'button.request-data' => static function ( ContainerInterface $container ): RequestData { return new RequestData(); }, - 'button.endpoint.change-cart' => static function ( ContainerInterface $container ): ChangeCartEndpoint { + 'button.endpoint.change-cart' => static function ( ContainerInterface $container ): ChangeCartEndpoint { if ( ! \WC()->cart ) { throw new RuntimeException( 'cant initialize endpoint at this moment' ); } @@ -112,7 +113,7 @@ return array( $logger = $container->get( 'woocommerce.logger.woocommerce' ); return new ChangeCartEndpoint( $cart, $shipping, $request_data, $repository, $data_store, $logger ); }, - 'button.endpoint.create-order' => static function ( ContainerInterface $container ): CreateOrderEndpoint { + 'button.endpoint.create-order' => static function ( ContainerInterface $container ): CreateOrderEndpoint { $request_data = $container->get( 'button.request-data' ); $cart_repository = $container->get( 'api.repository.cart' ); $purchase_unit_factory = $container->get( 'api.factory.purchase-unit' ); @@ -136,7 +137,7 @@ return array( $logger ); }, - 'button.helper.early-order-handler' => static function ( ContainerInterface $container ) : EarlyOrderHandler { + 'button.helper.early-order-handler' => static function ( ContainerInterface $container ) : EarlyOrderHandler { $state = $container->get( 'onboarding.state' ); $order_processor = $container->get( 'wcgateway.order-processor' ); @@ -144,7 +145,7 @@ return array( $prefix = $container->get( 'api.prefix' ); return new EarlyOrderHandler( $state, $order_processor, $session_handler, $prefix ); }, - 'button.endpoint.approve-order' => static function ( ContainerInterface $container ): ApproveOrderEndpoint { + 'button.endpoint.approve-order' => static function ( ContainerInterface $container ): ApproveOrderEndpoint { $request_data = $container->get( 'button.request-data' ); $order_endpoint = $container->get( 'api.endpoint.order' ); $session_handler = $container->get( 'session.handler' ); @@ -164,7 +165,7 @@ return array( $logger ); }, - 'button.endpoint.data-client-id' => static function( ContainerInterface $container ) : DataClientIdEndpoint { + 'button.endpoint.data-client-id' => static function( ContainerInterface $container ) : DataClientIdEndpoint { $request_data = $container->get( 'button.request-data' ); $identity_token = $container->get( 'api.endpoint.identity-token' ); $logger = $container->get( 'woocommerce.logger.woocommerce' ); @@ -174,31 +175,39 @@ return array( $logger ); }, - 'button.endpoint.vault-paypal' => static function( ContainerInterface $container ) : StartPayPalVaultingEndpoint { + 'button.endpoint.vault-paypal' => static function( ContainerInterface $container ) : StartPayPalVaultingEndpoint { return new StartPayPalVaultingEndpoint( $container->get( 'button.request-data' ), $container->get( 'api.endpoint.payment-token' ), $container->get( 'woocommerce.logger.woocommerce' ) ); }, - 'button.helper.three-d-secure' => static function ( ContainerInterface $container ): ThreeDSecure { + 'button.helper.three-d-secure' => static function ( ContainerInterface $container ): ThreeDSecure { $logger = $container->get( 'woocommerce.logger.woocommerce' ); return new ThreeDSecure( $logger ); }, - 'button.helper.messages-apply' => static function ( ContainerInterface $container ): MessagesApply { + 'button.helper.messages-apply' => static function ( ContainerInterface $container ): MessagesApply { return new MessagesApply( $container->get( 'api.shop.country' ) ); }, - 'button.is-logged-in' => static function ( ContainerInterface $container ): bool { + 'button.is-logged-in' => static function ( ContainerInterface $container ): bool { return is_user_logged_in(); }, - 'button.registration-required' => static function ( ContainerInterface $container ): bool { + 'button.registration-required' => static function ( ContainerInterface $container ): bool { return WC()->checkout()->is_registration_required(); }, - 'button.current-user-must-register' => static function ( ContainerInterface $container ): bool { + 'button.current-user-must-register' => static function ( ContainerInterface $container ): bool { return ! $container->get( 'button.is-logged-in' ) && $container->get( 'button.registration-required' ); }, + + 'button.basic-checkout-validation-enabled' => static function ( ContainerInterface $container ): bool { + /** + * The filter allowing to disable the basic client-side validation of the checkout form + * when the PayPal button is clicked. + */ + return (bool) apply_filters( 'woocommerce_paypal_payments_basic_checkout_validation_enabled', true ); + }, ); diff --git a/modules/ppcp-button/src/Assets/SmartButton.php b/modules/ppcp-button/src/Assets/SmartButton.php index d6b772fbf..a187b8129 100644 --- a/modules/ppcp-button/src/Assets/SmartButton.php +++ b/modules/ppcp-button/src/Assets/SmartButton.php @@ -144,6 +144,13 @@ class SmartButton implements SmartButtonInterface { */ private $all_funding_sources; + /** + * Whether the basic JS validation of the form iss enabled. + * + * @var bool + */ + private $basic_checkout_validation_enabled; + /** * The logger. * @@ -176,6 +183,7 @@ class SmartButton implements SmartButtonInterface { * @param SettingsStatus $settings_status The Settings status helper. * @param string $currency 3-letter currency code of the shop. * @param array $all_funding_sources All existing funding sources. + * @param bool $basic_checkout_validation_enabled Whether the basic JS validation of the form iss enabled. * @param LoggerInterface $logger The logger. */ public function __construct( @@ -194,25 +202,27 @@ class SmartButton implements SmartButtonInterface { SettingsStatus $settings_status, string $currency, array $all_funding_sources, + bool $basic_checkout_validation_enabled, LoggerInterface $logger ) { - $this->module_url = $module_url; - $this->version = $version; - $this->session_handler = $session_handler; - $this->settings = $settings; - $this->payer_factory = $payer_factory; - $this->client_id = $client_id; - $this->request_data = $request_data; - $this->dcc_applies = $dcc_applies; - $this->subscription_helper = $subscription_helper; - $this->messages_apply = $messages_apply; - $this->environment = $environment; - $this->payment_token_repository = $payment_token_repository; - $this->settings_status = $settings_status; - $this->currency = $currency; - $this->all_funding_sources = $all_funding_sources; - $this->logger = $logger; + $this->module_url = $module_url; + $this->version = $version; + $this->session_handler = $session_handler; + $this->settings = $settings; + $this->payer_factory = $payer_factory; + $this->client_id = $client_id; + $this->request_data = $request_data; + $this->dcc_applies = $dcc_applies; + $this->subscription_helper = $subscription_helper; + $this->messages_apply = $messages_apply; + $this->environment = $environment; + $this->payment_token_repository = $payment_token_repository; + $this->settings_status = $settings_status; + $this->currency = $currency; + $this->all_funding_sources = $all_funding_sources; + $this->basic_checkout_validation_enabled = $basic_checkout_validation_enabled; + $this->logger = $logger; } /** @@ -765,17 +775,17 @@ class SmartButton implements SmartButtonInterface { $this->request_data->enqueue_nonce_fix(); $localize = array( - 'script_attributes' => $this->attributes(), - 'data_client_id' => array( + 'script_attributes' => $this->attributes(), + 'data_client_id' => array( 'set_attribute' => ( is_checkout() && $this->dcc_is_enabled() ) || $this->can_save_vault_token(), 'endpoint' => \WC_AJAX::get_endpoint( DataClientIdEndpoint::ENDPOINT ), 'nonce' => wp_create_nonce( DataClientIdEndpoint::nonce() ), 'user' => get_current_user_id(), 'has_subscriptions' => $this->has_subscriptions(), ), - 'redirect' => wc_get_checkout_url(), - 'context' => $this->context(), - 'ajax' => array( + 'redirect' => wc_get_checkout_url(), + 'context' => $this->context(), + 'ajax' => array( 'change_cart' => array( 'endpoint' => \WC_AJAX::get_endpoint( ChangeCartEndpoint::ENDPOINT ), 'nonce' => wp_create_nonce( ChangeCartEndpoint::nonce() ), @@ -793,13 +803,13 @@ class SmartButton implements SmartButtonInterface { 'nonce' => wp_create_nonce( StartPayPalVaultingEndpoint::nonce() ), ), ), - 'enforce_vault' => $this->has_subscriptions(), - 'can_save_vault_token' => $this->can_save_vault_token(), - 'is_free_trial_cart' => $is_free_trial_cart, - 'vaulted_paypal_email' => ( is_checkout() && $is_free_trial_cart ) ? $this->get_vaulted_paypal_email() : '', - 'bn_codes' => $this->bn_codes(), - 'payer' => $this->payerData(), - 'button' => array( + 'enforce_vault' => $this->has_subscriptions(), + 'can_save_vault_token' => $this->can_save_vault_token(), + 'is_free_trial_cart' => $is_free_trial_cart, + 'vaulted_paypal_email' => ( is_checkout() && $is_free_trial_cart ) ? $this->get_vaulted_paypal_email() : '', + 'bn_codes' => $this->bn_codes(), + 'payer' => $this->payerData(), + 'button' => array( 'wrapper' => '#ppc-button', 'mini_cart_wrapper' => '#ppc-button-minicart', 'cancel_wrapper' => '#ppcp-cancel', @@ -820,7 +830,7 @@ class SmartButton implements SmartButtonInterface { 'tagline' => $this->style_for_context( 'tagline', $this->context() ), ), ), - 'hosted_fields' => array( + 'hosted_fields' => array( 'wrapper' => '#ppcp-hosted-fields', 'mini_cart_wrapper' => '#ppcp-hosted-fields-mini-cart', 'labels' => array( @@ -840,10 +850,10 @@ class SmartButton implements SmartButtonInterface { 'valid_cards' => $this->dcc_applies->valid_cards(), 'contingency' => $this->get_3ds_contingency(), ), - 'messages' => $this->message_values(), - 'labels' => array( + 'messages' => $this->message_values(), + 'labels' => array( 'error' => array( - 'generic' => __( + 'generic' => __( 'Something went wrong. Please try again or choose another payment source.', 'woocommerce-paypal-payments' ), @@ -853,9 +863,10 @@ class SmartButton implements SmartButtonInterface { ), ), ), - 'order_id' => 'pay-now' === $this->context() ? absint( $wp->query_vars['order-pay'] ) : 0, - 'single_product_buttons_enabled' => $this->settings->has( 'button_product_enabled' ) && $this->settings->get( 'button_product_enabled' ), - 'mini_cart_buttons_enabled' => $this->settings->has( 'button_mini-cart_enabled' ) && $this->settings->get( 'button_mini-cart_enabled' ), + 'order_id' => 'pay-now' === $this->context() ? absint( $wp->query_vars['order-pay'] ) : 0, + 'single_product_buttons_enabled' => $this->settings->has( 'button_product_enabled' ) && $this->settings->get( 'button_product_enabled' ), + 'mini_cart_buttons_enabled' => $this->settings->has( 'button_mini-cart_enabled' ) && $this->settings->get( 'button_mini-cart_enabled' ), + 'basic_checkout_validation_enabled' => $this->basic_checkout_validation_enabled, ); if ( $this->style_for_context( 'layout', 'mini-cart' ) !== 'horizontal' ) {