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(modal-checkout): optimize iframe load and refactor anonymous cart generation #1896

Merged
merged 22 commits into from
Oct 11, 2024

Conversation

miguelpeixe
Copy link
Member

@miguelpeixe miguelpeixe commented Oct 8, 2024

All Submissions:

Changes proposed in this Pull Request:

This PR addresses a few different issues:

Introduces a new strategy to detect iframe load so it can be ready sooner. The load event dispatch waits for render-blocking assets to load before firing. The new strategy watches for the iframe content document to trigger when the DOM is ready.

The dequeuing strategy is now reversed so that only whitelisted assets can be enqueued. This prevents unnecessary assets from loading.

Cart generation via ajax

To handle an edge case in which carts are getting wiped between an account registration and the modal checkout render, this PR changes the strategy for anonymous readers.

Carts should now be generated in parallel to the auth/account creation flow. This allows for the cart session to be created before the authentication, which, once authenticated, Woo should handle the transition without issues.

Because of this new strategy, readers signing in via link should also be able to restore the checkout flow with the existing cart. The pending checkout logic has been simplified here and in Automattic/newspack-plugin#3471.

How to test the changes in this Pull Request:

  1. Checkout this branch and fix(auth): simplify pending checkout newspack-plugin#3471
  2. In a fresh session, start the checkout button flow with a subscription product
  3. Register a new reader and confirm you are able to purchase without issues
  4. In another fresh session, start the checkout button flow with the same product
  5. This time, enter an existing email and click to sign in via link
  6. Paste the link in your session and confirm you are authenticated and able to proceed with the checkout
  7. Repeat steps 2-6 but through the Donate block

Other information:

  • Have you added an explanation of what your changes do and why you'd like us to include them?
  • Have you written new tests for your changes, as applicable?
  • Have you successfully ran tests with your changes locally?

Comment on lines +640 to +641
'wc-',
'wc_',
Copy link
Contributor

Choose a reason for hiding this comment

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

Its not uncommon for woo extensions to utilize these prefixes for scripts, so we probably wouldn't be fully covered. That said this is definitely a more thorough approach and better than what we have now.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think we talked about making this filterable so that publishers can have other payment gateways if they'd like.

Copy link
Member Author

Choose a reason for hiding this comment

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

Right! Thank you for reminding me, updated in 893a9c4

Copy link
Contributor

@chickenn00dle chickenn00dle left a comment

Choose a reason for hiding this comment

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

Nice! Very cool way to decouple the iframe logic from the load event. I like this approach, although it doesn't seem to take into account the transition between completing checkout and loading the thank you view.

Screenshot 2024-10-08 at 12 25 21

I think we will need to unflag the ready state during this transition so the handler doesn't return early. Or maybe implement a new flag that triggers the complete state separately?

@miguelpeixe
Copy link
Member Author

doesn't seem to take into account the transition between completing checkout and loading the thank you view.

Oops! Thank you for catching that. That was actually because the newsletter subscription script wasn't whitelisted to be enqueued. Fix in f0972b7

Also, the iframe resize observer failed to resize the modal after the checkout because of the new iframe._ready checks. I tweaked it a bit in f55c4bb and should work now.

@miguelpeixe
Copy link
Member Author

miguelpeixe commented Oct 8, 2024

25f6118 removes the init_checkout listener and runs the init() directly. The Woo trigger is called on the main thread, whereas our listener is attached asynchronously (both async script tag and after domReady). This causes a race condition and the modal to eventually never leave the loading state.

It's safe to run the logic outside the trigger. To keep the diff reasonable, I'm maintaining the code inside an init() function, but we might want to remove that.

You can reproduce the race condition by throttling your network to 3G and disabling the cache before starting the modal while on the epic branch.

Copy link
Contributor

@chickenn00dle chickenn00dle left a comment

Choose a reason for hiding this comment

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

Thanks for the changes @miguelpeixe!

I am running into a new issue where stripe throws the following exception when removing the init logic from the init_checkout event, which causes modal checkout to be stuck in a blocked state on the payment gateway step:

{
  "message": "We could not retrieve data from the specified Element.\n              Please make sure the Element you are attempting to use is still mounted."
}

This only happens when I've throttled my network speed to 3G and when going through the auth + checkout flow as a new reader, so maybe another race condition between Woo payment gateway logic and our init method? Not sure.

@miguelpeixe
Copy link
Member Author

I found another race condition issue with the domReady callback. It's an immediately invoked function that will not run as a domReady callback. Updated in 7dcfc12

@miguelpeixe miguelpeixe changed the title fix(modal-checkout): optimize iframe load fix(modal-checkout): optimize iframe load and refactor anonymous cart generation Oct 11, 2024
return;
}

$is_newspack_checkout = filter_input( INPUT_GET, 'newspack_checkout', FILTER_SANITIZE_NUMBER_INT );
$is_newspack_donate = filter_input( INPUT_GET, 'newspack_donate', FILTER_SANITIZE_NUMBER_INT );
Copy link
Member Author

Choose a reason for hiding this comment

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

I removed this because donations are not handled here. Please let me know if I'm missing something.

Copy link
Contributor

Choose a reason for hiding this comment

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

IIRC, this method will set the registration checkout flag for donations, which is why this was added. This was to set some order meta so we know whether to send a regular thank you email, or a welcome + thank you email. Maybe there is a better way to handle this now with the async cart.

Copy link
Member Author

Choose a reason for hiding this comment

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

Is it working? Because donation checkout requests are processed in a different method. We can replicate the flag logic there.

Copy link
Member Author

Choose a reason for hiding this comment

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

Ah I see it works because it's before the is_newspack_product check. WDYT we replicate it to the donate request method?

Copy link
Member Author

Choose a reason for hiding this comment

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

Hmm... Given the entire logic for the checkout registration flag lives in the blocks plugin, I see why it's better to have it there. I'll restore it.

Copy link
Member Author

Choose a reason for hiding this comment

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

Updated in 35b6d59. I've also changed the is_newspack_checkout to be a guard clause for consistency.

Copy link
Member Author

@miguelpeixe miguelpeixe Oct 11, 2024

Choose a reason for hiding this comment

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

I just now realized that because of the new strategy, the registration flag has to be handled differently. The anonymous cart generation doesn't know if it's a registration or not. Moved in 50f0e68 so this can set the WC session var.

Copy link
Contributor

@chickenn00dle chickenn00dle left a comment

Choose a reason for hiding this comment

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

Nice work @miguelpeixe!

This is working very nicely for the most part. It looks like I am no longer able to receive the welcome + thankyou emails for new registration checkout though. These should look like this:

Screenshot 2024-10-11 at 15 51 49

We use that checkout registration flag to set session data, and when the order is created set some order meta that our email logic can use to determine which thank you email to send:

public static function maybe_add_checkout_registration_order_meta( $order ) {

iframe.addEventListener( 'load', handleIframeReady );

/**
* Generate cart.
Copy link
Contributor

Choose a reason for hiding this comment

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

Non-blocking suggestion: Just so it's documented somewhere in the code, maybe its worth adding some further details about the need to trigger this asynchronously to avoid potential session buginess?

Copy link
Member Author

Choose a reason for hiding this comment

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

Good call!

Copy link
Member Author

Choose a reason for hiding this comment

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

Updated in bffe929, let me know if it's clear enough

@miguelpeixe
Copy link
Member Author

Thank you for reviewing, @chickenn00dle!

I am no longer able to receive the welcome + thankyou emails for new registration checkout though. These should look like this

Yes! I noticed that too, I've recently pushed some changes that should address that. I commented further here

Copy link
Contributor

@chickenn00dle chickenn00dle left a comment

Choose a reason for hiding this comment

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

Thanks for all the changes @miguelpeixe! This is looking good now! I left one non-blocking comment but feel free to ignore that.

triggerCheckout( form );
}
}

/**
* Handle modal checkout url param triggers.
*/
const handleModalCheckoutUrlParams = () => {
Copy link
Contributor

Choose a reason for hiding this comment

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

Non-blocking nit: Even though we are no longer using url params for the magic link, I think it might be worth keeping this checkoutURLParam logic so publishers can do things like craft urls in newsletters so readers can be taken directly to checkout for xyz product.

Copy link
Member Author

Choose a reason for hiding this comment

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

Good call! Restored in 21f542c

Copy link
Contributor

@chickenn00dle chickenn00dle left a comment

Choose a reason for hiding this comment

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

Great. Thanks for the quick turnaround @miguelpeixe! 🙌

@miguelpeixe
Copy link
Member Author

Thank you for the reviews!

@miguelpeixe miguelpeixe merged commit 3968033 into epic/ras-acc Oct 11, 2024
9 checks passed
@miguelpeixe miguelpeixe deleted the fix/optimize-modal-checkout-load branch October 11, 2024 22:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants