Skip to content

Commit

Permalink
Fixed WooPay opt-in checks that would fail with a hidden message when…
Browse files Browse the repository at this point in the history
… the customer selected a payment method other than Credit Card (#9210)
  • Loading branch information
leonardola authored Aug 5, 2024
1 parent 7fee76b commit 7d2024c
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 45 deletions.
4 changes: 4 additions & 0 deletions changelog/fix-opt-in-checks
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: fix

opt-in checks to prevent blocking customers using other payment methods
99 changes: 54 additions & 45 deletions client/components/woopay/save-user/checkout-page-save-user.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/* eslint-disable max-len */
/* global jQuery */
/**
* External dependencies
*/
Expand Down Expand Up @@ -131,55 +132,51 @@ const CheckoutPageSaveUser = ( { isBlocksCheckout } ) => {
}, [ isPhoneValid ] );

useEffect( () => {
const formSubmitButton = isBlocksCheckout
? document.querySelector(
'button.wc-block-components-checkout-place-order-button'
)
: document.querySelector(
'form.woocommerce-checkout button[type="submit"]'
);
const checkoutForm = jQuery( 'form.woocommerce-checkout' );

if ( ! formSubmitButton ) {
checkoutForm.on( 'checkout_place_order', function () {
jQuery( '#validate-error-invalid-woopay-phone-number' ).show();
} );
}, [] );

const updatePhoneNumberValidationError = useCallback( () => {
if ( ! isSaveDetailsChecked ) {
clearValidationError( errorId );
if ( isPhoneValid !== null ) {
onPhoneValidationChange( null );
}
return;
}

const updateFormSubmitButton = () => {
if ( isSaveDetailsChecked && isPhoneValid ) {
clearValidationError( errorId );

// Set extension data if checkbox is selected and phone number is valid in blocks checkout.
if ( isBlocksCheckout ) {
sendExtensionData( false );
}
}
if ( isSaveDetailsChecked && isPhoneValid ) {
clearValidationError( errorId );

if ( isSaveDetailsChecked && ! isPhoneValid ) {
setValidationErrors( {
[ errorId ]: {
message: __(
'Please enter a valid mobile phone number.',
'woocommerce-payments'
),
// Hides errors when the number has not been typed yet but shows when trying to place the order.
hidden: isPhoneValid === null,
},
} );
// Set extension data if checkbox is selected and phone number is valid in blocks checkout.
if ( isBlocksCheckout ) {
sendExtensionData( false );
}
};

updateFormSubmitButton();
return;
}

return () => {
clearValidationError( errorId );
};
if ( isSaveDetailsChecked && ! isPhoneValid ) {
setValidationErrors( {
[ errorId ]: {
message: __(
'Please enter a valid mobile phone number.',
'woocommerce-payments'
),
// Hides errors when the number has not been typed yet but shows when trying to place the order.
hidden: isPhoneValid === null,
},
} );
}
}, [
setValidationErrors,
errorId,
clearValidationError,
isBlocksCheckout,
isPhoneValid,
isSaveDetailsChecked,
sendExtensionData,
setValidationErrors,
] );

// In classic checkout the saved tokens are under WCPay, so we need to check if new token is selected or not,
Expand All @@ -198,9 +195,12 @@ const CheckoutPageSaveUser = ( { isBlocksCheckout } ) => {
if ( isBlocksCheckout && userDataSent ) {
sendExtensionData( true );
}
clearValidationError( errorId );
return null;
}

updatePhoneNumberValidationError();

return (
<Container
isBlocksCheckout={ isBlocksCheckout }
Expand Down Expand Up @@ -270,11 +270,7 @@ const CheckoutPageSaveUser = ( { isBlocksCheckout } ) => {
name="woopay_viewport"
value={ `${ viewportWidth }x${ viewportHeight }` }
/>
<div
className={
isPhoneValid === false ? 'has-error' : ''
}
>
<div className={ isPhoneValid ? '' : 'has-error' }>
<PhoneNumberInput
value={ phoneNumber }
onValueChange={ setPhoneNumber }
Expand All @@ -289,10 +285,23 @@ const CheckoutPageSaveUser = ( { isBlocksCheckout } ) => {
isBlocksCheckout={ isBlocksCheckout }
/>
</div>
<ValidationInputError
elementId={ errorId }
propertyName={ errorId }
/>
{ isBlocksCheckout && (
<ValidationInputError
elementId={ errorId }
propertyName={ errorId }
/>
) }
{ ! isBlocksCheckout && ! isPhoneValid && (
<p
id="validate-error-invalid-woopay-phone-number"
hidden={ isPhoneValid !== false }
>
{ __(
'Please enter a valid mobile phone number.',
'woocommerce-payments'
) }
</p>
) }
<AdditionalInformation />
<Agreement />
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
// eslint-disable-next-line import/no-unresolved
import { extensionCartUpdate } from '@woocommerce/blocks-checkout';
import { addAction } from '@wordpress/hooks';

/**
* Internal dependencies
Expand All @@ -16,6 +17,30 @@ import useSelectedPaymentMethod from '../../hooks/use-selected-payment-method';
import { getConfig } from 'utils/checkout';
import { useDispatch } from '@wordpress/data';

const jQueryMock = ( selector ) => {
if ( typeof selector === 'function' ) {
return selector( jQueryMock );
}

return {
on: ( event, callbackOrSelector, callback2 ) =>
addAction(
`payment-request-test.jquery-event.${ selector }${
typeof callbackOrSelector === 'string'
? `.${ callbackOrSelector }`
: ''
}.${ event }`,
'tests',
typeof callbackOrSelector === 'string'
? callback2
: callbackOrSelector
),
val: () => null,
is: () => null,
remove: () => null,
};
};

jest.mock( '../../hooks/use-woopay-user', () => jest.fn() );
jest.mock( '../../hooks/use-selected-payment-method', () => jest.fn() );
jest.mock( 'utils/checkout', () => ( {
Expand Down Expand Up @@ -79,6 +104,8 @@ const BlocksCheckoutEnvironmentMock = ( { children } ) => (

describe( 'CheckoutPageSaveUser', () => {
beforeEach( () => {
global.$ = jQueryMock;
global.jQuery = jQueryMock;
useDispatch.mockImplementation( () => {
return {
setValidationErrors: jest.fn(),
Expand Down
14 changes: 14 additions & 0 deletions includes/class-wc-payments.php
Original file line number Diff line number Diff line change
Expand Up @@ -1799,6 +1799,7 @@ public static function init_woopay() {
// Update email field location.
add_action( 'woocommerce_checkout_billing', [ __CLASS__, 'woopay_fields_before_billing_details' ], -50 );
add_filter( 'woocommerce_form_field_email', [ __CLASS__, 'filter_woocommerce_form_field_woopay_email' ], 20, 4 );
add_action( 'woocommerce_checkout_process', [ __CLASS__, 'maybe_show_woopay_phone_number_error' ] );

include_once __DIR__ . '/woopay-user/class-woopay-save-user.php';

Expand Down Expand Up @@ -2027,4 +2028,17 @@ public static function maybe_disable_wcpay_subscriptions_on_update() {
update_option( WC_Payments_Features::WCPAY_SUBSCRIPTIONS_FLAG_NAME, '0' );
}
}

/**
* Show error when WooPay opt-in is checked but no phone number was typed.
*/
public static function maybe_show_woopay_phone_number_error() {
// phpcs:ignore WordPress.Security.NonceVerification.Missing
if ( isset( $_POST['save_user_in_woopay'] ) && 'true' === $_POST['save_user_in_woopay'] ) {
// phpcs:ignore WordPress.Security.NonceVerification.Missing
if ( ! isset( $_POST['woopay_user_phone_field'] ) || ! isset( $_POST['woopay_user_phone_field']['no-country-code'] ) || empty( $_POST['woopay_user_phone_field']['no-country-code'] ) ) {
wc_add_notice( '<strong>' . __( 'Mobile Number', 'woocommerce-payments' ) . '</strong> ' . __( 'is required to create an WooPay account.', 'woocommerce-payments' ), 'error' );
}
}
}
}

0 comments on commit 7d2024c

Please sign in to comment.