diff --git a/changelog/fix-8141-fraud-prevention-token-site-editor-problem b/changelog/fix-8141-fraud-prevention-token-site-editor-problem new file mode 100644 index 00000000000..de8375ef6b7 --- /dev/null +++ b/changelog/fix-8141-fraud-prevention-token-site-editor-problem @@ -0,0 +1,4 @@ +Significance: patch +Type: fix + +Fix fraud prevention token not showing up on site editor checkout page diff --git a/client/checkout/blocks/generate-payment-method.js b/client/checkout/blocks/generate-payment-method.js index 61e9e74c42c..5908e743784 100644 --- a/client/checkout/blocks/generate-payment-method.js +++ b/client/checkout/blocks/generate-payment-method.js @@ -39,9 +39,7 @@ const generatePaymentMethod = async ( paymentMethod: { id }, } = await request.send(); - const fraudPreventionToken = document - .querySelector( '#wcpay-fraud-prevention-token' ) - ?.getAttribute( 'value' ); + const fraudPreventionToken = window.wcpayFraudPreventionToken; return { type: 'success', diff --git a/client/checkout/blocks/payment-processor.js b/client/checkout/blocks/payment-processor.js index a88c74fa7cd..2dec9a22ae8 100644 --- a/client/checkout/blocks/payment-processor.js +++ b/client/checkout/blocks/payment-processor.js @@ -46,9 +46,7 @@ const getBillingDetails = ( billingData ) => { }; const getFraudPreventionToken = () => { - return document - .querySelector( '#wcpay-fraud-prevention-token' ) - ?.getAttribute( 'value' ); + return window.wcpayFraudPreventionToken ?? ''; }; const PaymentProcessor = ( { diff --git a/client/checkout/blocks/saved-token-handler.js b/client/checkout/blocks/saved-token-handler.js index 86becd43f88..3d12595d8e6 100644 --- a/client/checkout/blocks/saved-token-handler.js +++ b/client/checkout/blocks/saved-token-handler.js @@ -20,9 +20,7 @@ export const SavedTokenHandler = ( { useEffect( () => { return onPaymentSetup( () => { - const fraudPreventionToken = document - .querySelector( '#wcpay-fraud-prevention-token' ) - ?.getAttribute( 'value' ); + const fraudPreventionToken = window.wcpayFraudPreventionToken; return { type: 'success', diff --git a/client/checkout/classic/payment-processing.js b/client/checkout/classic/payment-processing.js index e551d49f80e..c59cb7eb895 100644 --- a/client/checkout/classic/payment-processing.js +++ b/client/checkout/classic/payment-processing.js @@ -9,6 +9,7 @@ import { getFingerprint, } from 'wcpay/checkout/utils/fingerprint'; import { + appendFraudPreventionTokenInputToForm, appendPaymentMethodIdToForm, getPaymentMethodTypes, getSelectedUPEGatewayPaymentMethod, @@ -473,6 +474,7 @@ export const processPayment = ( $form, paymentMethodObject.paymentMethod.id ); + appendFraudPreventionTokenInputToForm( $form ); await additionalActionsHandler( paymentMethodObject.paymentMethod, $form, diff --git a/client/checkout/utils/upe.js b/client/checkout/utils/upe.js index 87ba0308330..b84df66a638 100644 --- a/client/checkout/utils/upe.js +++ b/client/checkout/utils/upe.js @@ -172,6 +172,13 @@ export const appendPaymentMethodIdToForm = ( $form, paymentMethodId ) => { ); }; +export const appendFraudPreventionTokenInputToForm = ( $form ) => { + const fraudPreventionToken = window.wcpayFraudPreventionToken ?? ''; + $form.append( + `` + ); +}; + /** * Checks if the customer is using a saved payment method. * diff --git a/client/payment-request/utils/normalize.js b/client/payment-request/utils/normalize.js index 90a9cdacd6c..e55b78675c0 100644 --- a/client/payment-request/utils/normalize.js +++ b/client/payment-request/utils/normalize.js @@ -35,9 +35,7 @@ export const normalizeOrderData = ( paymentData ) => { const phone = paymentData?.paymentMethod?.billing_details?.phone ?? ''; const billing = paymentData?.paymentMethod?.billing_details?.address ?? {}; const shipping = paymentData?.shippingAddress ?? {}; - const fraudPreventionTokenInput = document.querySelector( - 'input[name="wcpay-fraud-prevention-token"]' - ); + const fraudPreventionTokenValue = window.wcpayFraudPreventionToken ?? ''; let paymentRequestType = 'payment_request_api'; if ( paymentData?.walletName === 'applePay' ) { @@ -78,7 +76,7 @@ export const normalizeOrderData = ( paymentData ) => { terms: 1, 'wcpay-payment-method': paymentData?.paymentMethod?.id, payment_request_type: paymentRequestType, - 'wcpay-fraud-prevention-token': fraudPreventionTokenInput?.value ?? '', + 'wcpay-fraud-prevention-token': fraudPreventionTokenValue, }; }; diff --git a/includes/class-wc-payments-blocks-payment-method.php b/includes/class-wc-payments-blocks-payment-method.php index 8172cab1438..993637fc6e2 100644 --- a/includes/class-wc-payments-blocks-payment-method.php +++ b/includes/class-wc-payments-blocks-payment-method.php @@ -35,8 +35,6 @@ public function initialize() { $this->name = WC_Payment_Gateway_WCPay::GATEWAY_ID; $this->gateway = WC_Payments::get_gateway(); $this->wc_payments_checkout = WC_Payments::get_wc_payments_checkout(); - - add_filter( 'the_content', [ $this, 'maybe_add_card_testing_token' ] ); } /** @@ -72,6 +70,8 @@ public function get_payment_method_script_handles() { WC_Payments::register_script_with_dependencies( 'WCPAY_BLOCKS_CHECKOUT', 'dist/blocks-checkout', [ 'stripe' ] ); wp_set_script_translations( 'WCPAY_BLOCKS_CHECKOUT', 'woocommerce-payments' ); + Fraud_Prevention_Service::maybe_append_fraud_prevention_token(); + return [ 'WCPAY_BLOCKS_CHECKOUT' ]; } @@ -101,24 +101,4 @@ public function get_payment_method_data() { $this->wc_payments_checkout->get_payment_fields_js_config() ); } - - /** - * Adds the hidden input containing the card testing prevention token to the blocks checkout page. - * - * @param string $content The content that's going to be flushed to the browser. - * - * @return string - */ - public function maybe_add_card_testing_token( $content ) { - if ( ! wp_script_is( 'WCPAY_BLOCKS_CHECKOUT' ) || ! WC()->session ) { - return $content; - } - - $fraud_prevention_service = Fraud_Prevention_Service::get_instance(); - // phpcs:ignore WordPress.Security.NonceVerification.Missing,WordPress.Security.ValidatedSanitizedInput.MissingUnslash,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized - if ( $fraud_prevention_service->is_enabled() ) { - $content .= ''; - } - return $content; - } } diff --git a/includes/class-wc-payments-checkout.php b/includes/class-wc-payments-checkout.php index 5301573bd04..511d0acf692 100644 --- a/includes/class-wc-payments-checkout.php +++ b/includes/class-wc-payments-checkout.php @@ -129,6 +129,8 @@ public function register_scripts() { $script_dependencies[] = 'woocommerce-tokenization-form'; } + Fraud_Prevention_Service::maybe_append_fraud_prevention_token(); + $script = 'dist/checkout'; WC_Payments::register_script_with_dependencies( 'wcpay-upe-checkout', $script, $script_dependencies ); @@ -417,10 +419,6 @@ function() use ( $payment_fields ) { - session && Fraud_Prevention_Service::get_instance()->is_enabled() ) : ?> - - - gateway->id ); diff --git a/includes/class-wc-payments-payment-request-button-handler.php b/includes/class-wc-payments-payment-request-button-handler.php index 536973e46a3..346cb7529a7 100644 --- a/includes/class-wc-payments-payment-request-button-handler.php +++ b/includes/class-wc-payments-payment-request-button-handler.php @@ -736,6 +736,8 @@ public function scripts() { wp_enqueue_script( 'WCPAY_PAYMENT_REQUEST' ); + Fraud_Prevention_Service::maybe_append_fraud_prevention_token(); + $gateways = WC()->payment_gateways->get_available_payment_gateways(); if ( isset( $gateways['woocommerce_payments'] ) ) { WC_Payments::get_wc_payments_checkout()->register_scripts(); @@ -749,9 +751,7 @@ public function display_payment_request_button_html() { if ( ! $this->should_show_payment_request_button() ) { return; } - if ( WC()->session && Fraud_Prevention_Service::get_instance()->is_enabled() ) : ?> - - + ?>
diff --git a/includes/fraud-prevention/class-fraud-prevention-service.php b/includes/fraud-prevention/class-fraud-prevention-service.php index fdb2f1a9d0c..ea351d1a326 100644 --- a/includes/fraud-prevention/class-fraud-prevention-service.php +++ b/includes/fraud-prevention/class-fraud-prevention-service.php @@ -64,6 +64,40 @@ public static function get_instance( $session = null, $gateway = null ): self { return self::$instance; } + /** + * Appends the fraud prevention token to the JS context if the protection is enabled, and a session exists. + * + * @return void + */ + public static function maybe_append_fraud_prevention_token() { + // Check session first before trying to append the token. + if ( ! WC()->session ) { + return; + } + + $instance = self::get_instance(); + + // Don't add the token if the prevention is not enabled. + if ( ! $instance->is_enabled() ) { + return; + } + + // Don't add the token if the user isn't on the cart or checkout page. + // Checking the cart page too because the user can pay quickly via the payment buttons on that page. + if ( ! is_checkout() && ! is_cart() ) { + return; + } + + wp_register_script( self::TOKEN_NAME, '', [], time(), true ); + wp_enqueue_script( self::TOKEN_NAME ); + // Add the fraud prevention token to the checkout configuration. + wp_add_inline_script( + self::TOKEN_NAME, + "window.wcpayFraudPreventionToken = '" . esc_js( $instance->get_token() ) . "';", + 'after' + ); + } + /** * Sets a instance to be used in request cycle. * Introduced primarily for supporting unit tests. diff --git a/tests/unit/test-class-wc-payments-checkout.php b/tests/unit/test-class-wc-payments-checkout.php index 72a7688187d..669080afec3 100644 --- a/tests/unit/test-class-wc-payments-checkout.php +++ b/tests/unit/test-class-wc-payments-checkout.php @@ -139,71 +139,6 @@ public function tear_down() { WC_Payments::set_gateway( $this->default_gateway ); } - public function test_fraud_prevention_token_added_when_prevention_service_enabled() { - $token_value = 'test-token'; - $fraud_prevention_service_mock = $this->getMockBuilder( Fraud_Prevention_Service::class ) - ->disableOriginalConstructor() - ->getMock(); - - $fraud_prevention_service_mock - ->expects( $this->once() ) - ->method( 'is_enabled' ) - ->willReturn( true ); - - $fraud_prevention_service_mock - ->expects( $this->once() ) - ->method( 'get_token' ) - ->willReturn( $token_value ); - - Fraud_Prevention_Service::set_instance( $fraud_prevention_service_mock ); - - $this->mock_wcpay_gateway - ->expects( $this->any() ) - ->method( 'get_payment_method_ids_enabled_at_checkout' ) - ->willReturn( [] ); - - // Use a callback to get and test the output (also suppresses the output buffering being printed to the CLI). - $this->setOutputCallback( - function ( $output ) use ( $token_value ) { - $result = preg_match_all( '/]*type="hidden"[^>]*name="wcpay-fraud-prevention-token"[^>]*value="' . preg_quote( $token_value, '/' ) . '"[^>]*>/', $output ); - - $this->assertSame( 1, $result ); - } - ); - - $this->system_under_test->payment_fields(); - } - - public function test_fraud_prevention_token_not_added_when_prevention_service_disabled() { - $token_value = 'test-token'; - $fraud_prevention_service_mock = $this->getMockBuilder( Fraud_Prevention_Service::class ) - ->disableOriginalConstructor() - ->getMock(); - - $fraud_prevention_service_mock - ->expects( $this->once() ) - ->method( 'is_enabled' ) - ->willReturn( true ); - - Fraud_Prevention_Service::set_instance( $fraud_prevention_service_mock ); - - $this->mock_wcpay_gateway - ->expects( $this->any() ) - ->method( 'get_payment_method_ids_enabled_at_checkout' ) - ->willReturn( [] ); - - // Use a callback to get and test the output (also suppresses the output buffering being printed to the CLI). - $this->setOutputCallback( - function ( $output ) use ( $token_value ) { - $result = preg_match_all( '/]*type="hidden"[^>]*name="wcpay-fraud-prevention-token"[^>]*value="' . preg_quote( $token_value, '/' ) . '"[^>]*>/', $output ); - - $this->assertSame( 0, $result ); - } - ); - - $this->system_under_test->payment_fields(); - } - public function test_save_payment_method_checkbox_not_called_when_saved_cards_disabled() { // given: prepare the dependencies. wp_set_current_user( 1 );