Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix required fields breaking Google Pay and Apple pay #2886

Merged
merged 8 commits into from
Feb 21, 2024

Conversation

Mayisha
Copy link
Contributor

@Mayisha Mayisha commented Feb 9, 2024

Fixes #2343 #2169

If there is any required field on the checkout page, for example Company field, user can not purchase with Google/Apple pay. The reason of this issue is, when we create the order during payment, the validation fails due to the required field. Because we map the order data from the Google Pay response which does not include the required field (company).

Changes proposed in this Pull Request:

  • From the checkout page when paying with Google/Apple Pay, we are now checking if the outstanding required field has value on the checkout page form field. If yes, then we include that value in the order data.
  • From shortcode cart we are hiding the payment request button if there are required fields (except for the default ones) present in the checkout page. We can get the form fields via WC()->checkout()->get_checkout_fields(). Similarly when the shop is using shortcode checkout (and has required field) we hide the button from product page as well.
  • On the Block cart the button is not hidden for required checkout field as we don't have a get_checkout_fields function to utilize for the blocks. However, the js error mentioned in Required fields breaking Apple Pay #2169 does not happen here and properly shows the error message on top of the page.
  • The js error mentioned in Required fields breaking Apple Pay #2169 was already fixed in PR Fix PRB's error message not showing up in classic checkout #2690

Testing instructions

Shortcode

  • If you already don't have, then create a shortcode checkout and shortcode cart page.
  • Go to Appearance -> Customize -> WooCommerce -> Checkout and make the Company name field required.
  • Go to WooCommerce -> Settings -> Payments -> Stripe. Customize Google/Apple Pay button locations and select Checkout, Cart, and Product.
  • If you already don't have, then create a shortcode checkout and shortcode cart page.
  • As a shopper, go to any product page. You should not see the Google/Apple Pay button.
  • Add the product to the cart, then go to the cart page. You should not see the Google/Apple Pay button.
  • Now proceed to the checkout page. Here the button should be present.
  • Try to pay with Google/Apple Pay without filling up the company field. You should see an error message on the page.
  • Add any value to the company field. Try again to pay with Google/Apple Pay. This time the purchase should be successful.
  • Check the order data in your wp-admin and make sure the company name is there.
  • Test with both Ship to a different address checked/unchecked and verify the order data.

Block

  • If you already don't have, then create a Block checkout and Block cart page.
  • Go to the edit Block checkout page, select the shipping address block and make the Company name field required from the right side panel. Similarly, make the Company field required for the billing address.
  • Go to WooCommerce -> Settings -> Payments -> Stripe. Customize Google/Apple Pay button locations and select Checkout, Cart, and Product.
  • As a shopper add a product to the cart, then go to the cart page. You should see the Google/Apple Pay button for the cart block but trying to purchase with Google//Apple pay should not create any fatal error. A proper error message should appear on the page.
  • Now proceed to the checkout page. Try to pay with Google/Apple Pay without filling up the company field. You should see an error message on the page.
  • Add any value to the company field. Try again to pay with Google/Apple Pay. This time the purchase should be successful.
  • Check the order data in your wp-admin and make sure the company name is there.
  • Test with both Use same address for billing checked/unchecked and verify the order data.

@Mayisha Mayisha marked this pull request as draft February 9, 2024 05:28
@Mayisha Mayisha requested review from a team and wjrosa and removed request for a team February 19, 2024 15:43
@Mayisha Mayisha marked this pull request as ready for review February 19, 2024 15:43
Copy link
Contributor

@wjrosa wjrosa left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! Thanks for all your guidance through testing this!

I had only one issue that does not seem related to your change. When checking out using the Cart Block directly, Google form keeps loading for like 2 minutes before stopping and disabling the G Pay button. I will do more tests here.

@Mayisha Mayisha merged commit 07eb158 into develop Feb 21, 2024
32 checks passed
@Mayisha Mayisha deleted the fix/2169-required-field-breaking-prb branch February 21, 2024 07:34
@Mayisha Mayisha mentioned this pull request Feb 28, 2024
5 tasks
@therealgilles
Copy link

Try to pay with Google/Apple Pay without filling up the company field. You should see an error message on the page.

Can someone clarify how that error is generated? I am using the WooCommerce Checkout Fields extension to add additional required fields and they seemed ignored.

@Mayisha
Copy link
Contributor Author

Mayisha commented May 3, 2024

@therealgilles The validation for the default checkout fields is done in the validate_posted_data function and the error notice is generated here.

The validation is done during process_checkout. You can add your custom validation by making use of any available hooks, e.g woocommerce_checkout_process action etc.

@therealgilles
Copy link

therealgilles commented May 4, 2024

@Mayisha Thank you, that's very helpful.

One thing I've noticed is that I have a required checkbox and using Apple Pay or Google Pay automatically considers the box checked. It does not show an error if left unchecked and does not tell the user about it. I don't think this has to do with my setup and it seems either an oversight or an odd on-purpose behavior. If on-purpose, it should be documented.

PS: Your 2nd link is the same as the first one, probably a copy/paste error?

UPDATE: I checked the $this->get_checkout_fields() value and I got this for the checkbox (that I did not check), yet Google Pay did not complain:

            [my_checkbox] => Array
                (
                    [type] => checkbox
                    [label] => I agree to this
                    [options] => Array
                        (
                        )

                    [placeholder] => 
                    [priority] => 50
                    [enabled] => 1
                    [validate] => Array
                        (
                            [0] => required
                        )

                    [required] => 1
                    [custom] => 1
                    [display_options] => Array
                        (
                            [0] => emails
                            [1] => view_order
                        )

                    [class] => Array
                        (
                            [0] => form-row-wide
                        )

                )

UPDATE1: Looking at the $_POST data, the checkbox is marked as checked even though I did not. And I see the terms were also automatically accepted. I'd like to know if required checkboxes are automatically checked. Which is not what I would expect:

    ...
    [payment_method] => stripe
    [terms] => 1
    [payment_request_type] => google_pay
    [shipping_email] => ...
    [my_checkbox] => 1
    [g-recaptcha-response] => null

@therealgilles
Copy link

therealgilles commented May 4, 2024

The JS code below makes the classic mistake of using .val() but that does not work for checkboxes. Instead, as shown right below, you are supposed to use .is(':checked'). Unless of course your intention is to consider the checkbox checked by default, even though it's not.

See: https://stackoverflow.com/questions/5621324/jquery-val-and-checkboxes

                getRequiredFieldDataFromCheckoutForm: function( data ) {
                        const requiredfields = $( 'form.checkout' ).find( '.validate-required' );

                        if ( requiredfields.length ) {
                                requiredfields.each( function() {
                                        const field = $( this ).find( ':input' );
                                        const value = field.val(); //  <-- HERE
                                        const name = field.attr( 'name' );
                                        if ( value && name ) {
                                                if ( ! data[ name ] ) {
                                                        data[ name ] = value;
                                                }

                                                // if shipping same as billing is selected, copy the billing field to shipping field.
                                                const shipToDiffAddress = $( '#ship-to-different-address' ).find( 'input' ).is( ':checked' );

Here is the code from WooCommerce in comparison:

                                if ( validate_required ) {
                                        if ( 'checkbox' === $this.attr( 'type' ) && ! $this.is( ':checked' ) ) {
                                                $parent.removeClass( 'woocommerce-validated' ).addClass( 'woocommerce-invalid woocommerce-invalid-required-field' );
                                                validated = false;
                                        } else if ( $this.val() === '' ) {
                                                $parent.removeClass( 'woocommerce-validated' ).addClass( 'woocommerce-invalid woocommerce-invalid-required-field' );
                                                validated = false;
                                        }
                                }

Please fix :)

@therealgilles
Copy link

therealgilles commented May 4, 2024

It also appears that the checkout_error event is not triggered from displayErrorMessage, when I believe it should be.

I'm going to add one more thing. The UX would be way better if the (required) fields were validated before the Apple/Google Pay dialog opens up. This could be fairly easily done by capturing the button click.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Clarify usage of Apple/Google Pay and custom required fields on checkout page
3 participants