Skip to content
This repository has been archived by the owner on Feb 23, 2024. It is now read-only.

Don't throw an error when registering a payment method fails #3134

Merged
merged 19 commits into from Sep 18, 2020

Conversation

Aljullu
Copy link
Contributor

@Aljullu Aljullu commented Sep 9, 2020

Fixes #3125.
Fixes #3157.

This PR has some changes in how failing payment methods are handled: they will silently fail for shoppers, but a notice will be shown for admins.

How to test the changes in this Pull Request:

Excuses in advance for the long testing steps, this PR touches some logic that affects several critical flows so I tried to list the steps as detailed as possible.

Test #3125.

Preparation

Steps from #3125:

  1. Install & activate WooCommerce Stripe.
  2. Enable Stripe CC payment method - don't add an api key (or delete the option).
  3. Enable BACS or other payment methods (e.g. cheque).
  4. Add checkout block to checkout page.
  5. On front end, add something to cart and proceed to checkout.
Tests
  1. Make sure you have an admin user with saved payment methods* and a user without saved payment methods (or a guest).
  2. With those two users, check the Checkout page in the frontend. Also open the Checkout page in the editor.
  3. Verify the statements below are true. 👇
Scenario With Stripe API key Without Stripe API key
Admin with saved payment methods / Editor
  • Saved payment methods are displayed.
  • Credit Card payment method is displayed (under use a new payment method).
  • There is no error notice.
  • Saved payment methods are not displayed.
  • There is an error notice about Stripe not being registered correctly.
  • Other payment methods are displayed as usual.
Guest user
  • Saved payment methods are not displayed.
  • Credit Card payment method is displayed.
  • There is no error notice.
  • Saved payment methods are not displayed.
  • Credit Card payment method is not displayed.
  • There is no error notice.
  • Other payment methods are displayed as usual.

* In order to save a payment method with a user. Enable the WooCommerce Stripe plugin, set the keys and make a purchase with a user selecting the Save payment information to my account for future purchases. option. Next time you visit the Checkout with that user, the saved payment method will show up.

Test #3157

Assuming you already have a user with saved credit cards in Stripe from the steps above.

  1. Go to Stripe settings and uncheck Enable Payment via Saved Cards. Make sure you have added back the API keys that you might have removed in the steps above.
  2. Start a purchase with a user that has saved payment methods and go to the Checkout block.
  3. Verify saved credit cards are not shown in the Payment method options.

Regression testing

It would also be great to do a purchase flow with some of the scenarios described above to ensure the payment flow still works properly.

Changelog

Fixed an issue that was not showing other payment methods if one failed to load when the user was an admin.

@Aljullu Aljullu added status: needs review type: bug The issue/PR concerns a confirmed bug. block: checkout Issues related to the checkout block. labels Sep 9, 2020
@Aljullu Aljullu added this to the 3.4.0 milestone Sep 9, 2020
@Aljullu Aljullu requested a review from haszari September 9, 2020 14:28
@Aljullu Aljullu requested a review from a team as a code owner September 9, 2020 14:28
@Aljullu Aljullu self-assigned this Sep 9, 2020
@github-actions
Copy link
Contributor

github-actions bot commented Sep 9, 2020

Size Change: -301 B (0%)

Total Size: 1.11 MB

Filename Size Change
build/active-filters-frontend.js 8.8 kB +7 B (0%)
build/active-filters.js 8.85 kB +1 B
build/all-products-frontend.js 31.2 kB -22 B (0%)
build/all-products.js 35.9 kB +9 B (0%)
build/all-reviews.js 9.78 kB +2 B (0%)
build/atomic-block-components/add-to-cart-frontend.js 8.89 kB +2 B (0%)
build/atomic-block-components/add-to-cart~atomic-block-components/button.js 3.17 kB +10 B (0%)
build/atomic-block-components/button-frontend.js 2.02 kB -1 B
build/atomic-block-components/button.js 838 B -1 B
build/atomic-block-components/category-list-frontend.js 469 B -1 B
build/atomic-block-components/category-list.js 478 B +1 B
build/atomic-block-components/image-frontend.js 1.7 kB -3 B (0%)
build/atomic-block-components/image.js 1.15 kB -2 B (0%)
build/atomic-block-components/price-frontend.js 2.29 kB +4 B (0%)
build/atomic-block-components/price.js 2.32 kB -1 B
build/atomic-block-components/sale-badge-frontend.js 862 B +1 B
build/atomic-block-components/stock-indicator-frontend.js 568 B -1 B
build/atomic-block-components/summary-frontend.js 916 B +1 B
build/atomic-block-components/tag-list-frontend.js 466 B -1 B
build/atomic-block-components/tag-list.js 473 B -1 B
build/atomic-block-components/title-frontend.js 1.23 kB +1 B
build/atomic-block-components/title.js 1.06 kB -1 B
build/attribute-filter-frontend.js 18.1 kB -14 B (0%)
build/attribute-filter.js 12.4 kB +5 B (0%)
build/blocks.js 3.54 kB +1 B
build/cart-frontend.js 69.5 kB -125 B (0%)
build/cart.js 38.2 kB +287 B (0%)
build/checkout-frontend.js 84.5 kB -531 B (0%)
build/checkout.js 41.3 kB -181 B (0%)
build/featured-category.js 7.72 kB -1 B
build/featured-product.js 9.97 kB -4 B (0%)
build/price-filter-frontend.js 14.8 kB +310 B (2%)
build/price-filter.js 10.3 kB +3 B (0%)
build/product-best-sellers.js 7.43 kB +1 B
build/product-categories.js 3.23 kB +3 B (0%)
build/product-category.js 8.37 kB +3 B (0%)
build/product-new.js 7.6 kB +1 B
build/product-search.js 3.56 kB +1 B
build/product-top-rated.js 7.58 kB +3 B (0%)
build/reviews-by-category.js 11.8 kB +2 B (0%)
build/reviews-by-product.js 13.4 kB +9 B (0%)
build/reviews-frontend.js 9.36 kB -3 B (0%)
build/single-product-frontend.js 33.8 kB -257 B (0%)
build/single-product.js 10.1 kB -6 B (0%)
build/vendors.js 415 kB +171 B (0%)
build/wc-payment-method-stripe.js 12 kB +17 B (0%)
ℹ️ View Unchanged
Filename Size Change
build/atomic-block-components/add-to-cart.js 7.46 kB 0 B
build/atomic-block-components/add-to-cart~atomic-block-components/image~atomic-block-components/title.js 335 B 0 B
build/atomic-block-components/rating-frontend.js 524 B 0 B
build/atomic-block-components/rating.js 526 B 0 B
build/atomic-block-components/sale-badge.js 867 B 0 B
build/atomic-block-components/sku-frontend.js 388 B 0 B
build/atomic-block-components/sku.js 394 B 0 B
build/atomic-block-components/stock-indicator.js 572 B 0 B
build/atomic-block-components/summary.js 926 B 0 B
build/editor-rtl.css 13.9 kB 0 B
build/editor.css 13.9 kB 0 B
build/handpicked-products.js 7.37 kB 0 B
build/product-on-sale.js 7.99 kB 0 B
build/product-tag.js 6.52 kB 0 B
build/products-by-attribute.js 8.32 kB 0 B
build/style-rtl.css 18 kB 0 B
build/style.css 18 kB 0 B
build/vendors-style-rtl.css 1.03 kB 0 B
build/vendors-style.css 1.03 kB 0 B
build/vendors~atomic-block-components/price-frontend.js 5.65 kB 0 B
build/wc-blocks-data.js 7.09 kB 0 B
build/wc-blocks-middleware.js 931 B 0 B
build/wc-blocks-registry.js 2.28 kB 0 B
build/wc-payment-method-bacs.js 790 B 0 B
build/wc-payment-method-cheque.js 787 B 0 B
build/wc-payment-method-cod.js 879 B 0 B
build/wc-payment-method-paypal.js 831 B 0 B
build/wc-settings.js 2.33 kB 0 B
build/wc-shared-context.js 1.53 kB 0 B
build/wc-shared-hocs.js 1.66 kB 0 B

compressed-size-action

Copy link
Member

@haszari haszari left a comment

Choose a reason for hiding this comment

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

Switching to console.error fixes the issue, but I'm thinking we should revisit our intentions here, and either make this a real feature, or remove this special support for admins.

There are two options as I see it:

  1. Silently hide the misconfigured payment method – i.e. same behaviour for admin and guest/customer. There are other ways for the merchant to figure out why their payment method is not working - this isn't a core responsibility of the block front-end code.
  2. Show a notice (to admins) informing them that there's a problem with one of their payment methods.

Option 2 seems preferable, based on the comments in the code, seems like it's what we intended/designed.

Option 1 seems like a fine fallback if we hit issues/complexity with other options. While we could help some merchants figure out what's wrong, it doesn't feel like an essential feature to me. Having said that, I'm not clear on our approach/policy for showing "editor"/merchant notices or guidance in the front end.

Note – if for some reason it's best to go with shipping this as a console.error, I don't see any issue with that (i.e. all the above is non blocking). But in that case please tweak the comments to make the intention clear 🔮

throw new Error(
// This is only shown to admin users, so it's ok to print an error to the browser console.
// eslint-disable-next-line no-console
console.error(
Copy link
Member

Choose a reason for hiding this comment

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

Seems like we have a few lines of code just to throw up a console.error – is it worth it?

Later in the file, there's a comment about this info being presented as a notice. That makes sense to me – if we're going to add explicit support for admins of misconfigured sites, we might as well use notice to show in the UI.

@Aljullu
Copy link
Contributor Author

Aljullu commented Sep 10, 2020

Thanks for the review @haszari!

Switching to console.error fixes the issue, but I'm thinking we should revisit our intentions here, and either make this a real feature, or remove this special support for admins.

I thought it was the expected behavior to show a console error instead of a frontend notice when a payment method fails to load in the frontend, but tagging @nerrad for confirmation.

In 8985a56 I updated this PR to use notices instead of a console error and I also updated the PR testing steps. We can easily revert these changes depending on the final decision taken.

@nerrad
Copy link
Contributor

nerrad commented Sep 10, 2020

The intention with the thrown error was that when admins are logged in, the error boundary would catch the error. At one point I think it did, but somewhere along the way it stopped. So ideally I'd like to some investigation around why the error boundary isn't working to catch this error (we might have to implement the useThrowError custom hook somewhere here (I haven't looked closely at the code).

If not present (or removed somewhere along the way), we should ideally have an error boundary around the payment method section of the ui.

@Aljullu
Copy link
Contributor Author

Aljullu commented Sep 10, 2020

Ah yes, the error boundary is used in the editor to display the error. I will move this PR back to in progress so I can investigate it further.

@Aljullu Aljullu force-pushed the fix/3125-dont-throw-on-payment-registration-error branch 2 times, most recently from d2515c5 to 4faf8f5 Compare September 11, 2020 14:19
@Aljullu
Copy link
Contributor Author

Aljullu commented Sep 11, 2020

Hmmm, I have been investigating this and I'm not sure if it ever worked as described. If it did, I couldn't find when/how. The issue seems to be that we are throwing inside a for loop, so the execution stops and other payment methods are not loaded. To fix that we would need to directly throw on payment method initialization inside the component that renders it. This is what I tried to do in the last commits in this PR.

What they do is to enable all payment methods for admin users, so if one is failing, the error will be caught by the error boundary and shown as a notice. That works mostly fine, but I needed to add a filter to hide saved payment options for methods that had thrown an error, because by default they are unfiltered.

I'm not a big fan of this approach since it requires some special logic for a specific use case (admin user), but I can't think of any better approach (ideas welcome).

I split the commits in self-contained changes, so it might be easier to review commit-by-commit than all at once. I also updated the testing instructions in the PR description.

@haszari
Copy link
Member

haszari commented Sep 14, 2020

Hmmm, I have been investigating this and I'm not sure if it ever worked as described. If it did, I couldn't find when/how. The issue seems to be that we are throwing inside a for loop, so the execution stops and other payment methods are not loaded. To fix that we would need to directly throw on payment method initialization inside the component that renders it. This is what I tried to do in the last commits in this PR.
What they do is to enable all payment methods for admin users, so if one is failing, the error will be caught by the error boundary and shown as a notice. That works mostly fine, but I needed to add a filter to hide saved payment options for methods that had thrown an error, because by default they are unfiltered.
I'm not a big fan of this approach since it requires some special logic for a specific use case (admin user), but I can't think of any better approach (ideas welcome).

Valiant effort @Aljullu !! 🙌😅

If this feature hasn't ever worked as intended, maybe it's better to keep this simple, and remove the feature (i.e. admins get the exact same behaviour as other users). This is not an introduced issue, and the impact is small – this is a small convenience to give admins a clue in the front end why Stripe is not visible (or another misconfigured payment gateway). We can always implement it later if there's a clear need for it.

I haven't reviewed the recent code changes – I think we should clarify that we actually want/need this feature before investing any further time.

@nerrad do you have thoughts on this – do we need to display an admin-only notice if a payment method fails init or is misconfigured?

What they do is to enable all payment methods for admin users, so if one is failing, the error will be caught by the error boundary and shown as a notice.

My naive assumption when I heard about this feature was that there'd be a try/catch within the loop body, to protect against rogue payment methods. Something like this:

// pseudocode!!!
// initialising payment methods and deciding whether they should be shown as tabs
foreach ( current in all_payment_methods ) {
  try {
    current.init();
    if ( current.canMakePayment() ) { 
       addAvailablePaymentMethod( current );
    }
  }
  catch ( error ) {
     if ( userIsAdmin ) {
        addNotice( { message: `Payment method ${ current.name } failed initialisation with error ${ error }` } );
     } 
  }
}

However, personally I don't think this is worth following up now, and there might be extra complexity getting the error handling right across components/hooks in a react context.

@Aljullu
Copy link
Contributor Author

Aljullu commented Sep 14, 2020

Thanks for the feedback @haszari! Yeah, let's wait for @nerrad's thoughts in order to decide how to proceed.

My naive assumption when I heard about this feature was that there'd be a try/catch within the loop body, to protect against rogue payment methods.

There isn't but in some way React error boundaries work similarly (but in a React-ish way). So if an error happens inside a component, the error boundary catches it and handles it as required; in this case, showing a notice.

Here, the error is thrown in the payment method components and caught by PaymentMethodErrorBoundary.

@nerrad nerrad removed this from the 3.4.0 milestone Sep 14, 2020
@nerrad nerrad force-pushed the fix/3125-dont-throw-on-payment-registration-error branch from 4faf8f5 to 73a19d1 Compare September 14, 2020 15:57
@nerrad
Copy link
Contributor

nerrad commented Sep 14, 2020

@nerrad do you have thoughts on this – do we need to display an admin-only notice if a payment method fails init or is misconfigured?

Short answer yes.

Long answer: Anything we can do to make it easier for merchant's to more easily see what's wrong (especially when there are potential configuration issues) is of benefit. As for why admin only, I don't think we want to expose faulty payment method options to shoppers or potentially expose sensitive info to shoppers given we have no control over the contents of error notices. So restricting display of these notices to only Admin's limits the potential problematic surface area.

As for this feature never working, that may be the case, but the intention was still always to display useful error messages to admins of a store for troubleshooting configuration issues.

@Aljullu, assuming this tests well (I haven't tested) I'm fine with rolling with this iteration for now. However, I've pulled from the 3.4.0 milestone to give it a bit more time on the main branch for our development cycle (which hopefully will surface any potential issues between releases).

Copy link
Member

@haszari haszari left a comment

Choose a reason for hiding this comment

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

I found a bug with this - haven't yet done a full test/review yet sorry. Lots of code impact here so will need to review carefully!

The issue I found is that if COD is dependent on shipping method, it's always shown. It's supposed to hide (dynamically) if a non-COD-compatible shipping method is selected. I switched branch to main to confirm the correct behaviour – so confirming that the issue is introduced with these changes. It's a delicate balance getting these payment tabs working well.

Here's the relevant option in COD config:

Screen Shot 2020-09-15 at 10 03 26 AM

@Aljullu
Copy link
Contributor Author

Aljullu commented Sep 15, 2020

The issue I found is that if COD is dependent on shipping method, it's always shown. It's supposed to hide (dynamically) if a non-COD-compatible shipping method is selected. I switched branch to main to confirm the correct behaviour – so confirming that the issue is introduced with these changes. It's a delicate balance getting these payment tabs working well.

Aaah, good catch! That was a consequence of loading all payment methods for admins, without checking what was returned by canMakePayment. In 5856bb8 I changed this behavior so payment methods that don't allow payments aren't shown.

Copy link
Member

@haszari haszari left a comment

Choose a reason for hiding this comment

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

Epic work on this @Aljullu - this is a really tricky issue and you're really getting to the heart of it. 🎖️ 🦖

I found another issue (sorry). And also note I've paused reviewing/testing because of the issue I hit - so this is not a full review.

Here's how to reproduce the issue:

  • Enable COD and/or Cheque payment method.
  • Add a throw statement in canMakePayment for COD or Cheque payment method.
  • View checkout block in front end.

Expected: see all valid payment methods as tabs, any saved cards as radio buttons, and a tab for the broken payment method (COD or cheque) with a notice.

Actual: no payment methods! Note this applies to admin and anon shopper.

Now .. why was I testing this? The most important part of this issue is that we want to ensure payment which throw errors are not able to break the checkout (i.e. other payment options should be unaffected).

I also saw another specific issue with Stripe (kind of a separate issue but related). If there's no key, it returns an api object with an error property, but our check for successful init is if the api object is truthy. We probably need to check for stripe.error and if so, throw the error for the notice.

	return canPayStripePromise.then( ( stripe ) => {
                // Need to check `stripe.error` here and if so throw the error for the notice.
		if ( stripe === null ) {
			isStripeInitialized = true;
			return canPay;
		}
		// Do a test payment to confirm if payment method is available.
                // This line is an error if API keys are missing, because stripe.paymentRequest is not a function.
		const paymentRequest = stripe.paymentRequest( {
			total: {
				label: 'Test total',
				amount: 1000,
			},
			country: getSetting( 'baseLocation', {} )?.country,
			currency: currencyCode,
		} );
		return paymentRequest.canMakePayment().then( ( result ) => {
			canPay = !! result;
			isStripeInitialized = true;
			return canPay;
		} );
	} );

const updateOptions = useCallback( async () => {
// Admin users have all payment methods enabled, so we need to filter
// the ones that do not accept payments.
const getFilteredPaymentMethods = async ( paymentMethods ) => {
Copy link
Member

Choose a reason for hiding this comment

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

This is a pretty big, complex change - I'm having trouble reviewing it to be honest!

It looks like this component now needs to manually replicate a lot of the core logic for calling canMakePayment, specifically for the admin use case. I think this is so the payment method tab is rendered (showing the error) but any saved cards for that payment method are not rendered. (Let me know if I've got that wrong!)

So to put it another way, we're mixing concerns/use cases here and that's led to this complexity. The two use cases are:

  • Admin using the front end of the shop to buy stuff, as a shopper. If Stripe settings are broken, their (stripe) saved card should not be shown, because it wouldn't work.
  • Admin using the front end to confirm that payment methods are set up correctly. If Stripe settings are broken, we should display a notice so admin can fix it.

First I think we need to clarify some of this in the PR description - it's taken me a while to get my head around it and it's an important aspect of the fix.

Second, I'm hoping that we can avoid having to "reimplement" the canMakePayment call in both places. This feels to me like it should be managed by the payment context (i.e. store/reducer state). I suspect we should be able to represent the state of the payment methods sufficiently in the store, so the component doesn't need to manually filter.

Right now the payment methods are either present in the store (as customerPaymentMethods) or not, if not available (e.g. due to canMakePayment() returning false. I think we need to represent the fact that admins see more payment methods in the store/context. So for example, store all payment methods in context, and have a property for whether they are functioning/available, and another property for if they have an error. Sorry I don't have a more specific suggestion here, I think this would take a bit of work but might be a more appropriate fix which is easier to maintain.

The key for me is that payment method has an error is real state that should be in the store, not computed inside component (UI). But I could be confused :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

All good points, I agree calling canMakePayment in two different places was not ideal. In fact that was what was making me not to be really comfortable with that approach either.

I took another take at this issue. Instead of filtering saved payment methods with canMakePayment, now I filter them with the supports.savePaymentInfo property, which we now set to false if the payment failed to register.

I also considered adding another attribute like error or hasError to 'mark' the payment methods that failed to register but are still added because the user is an admin. But I feel setting savePaymentInfo to false is a more natural approach.

In addition to that, I needed to replace the content so the error is thrown in the component (and then caught by the error boundary). Let me know if this new approach looks good to you.

@@ -167,7 +208,7 @@ const SavedPaymentMethodOptions = ( { onSelect } ) => {

// In the editor, show `Use a new payment method` option as selected.
const selectedOption = isEditor ? '0' : selectedToken + '';
return currentOptions.current.length > 0 ? (
return currentOptions.current.length > 1 ? (
Copy link
Member

Choose a reason for hiding this comment

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

I'm curious why this change - is it needed, does it make a difference, or is it just "more correct"?

I'm assuming that it works either way because we always have the Use a new method radio button, so it's 0 or 2+.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That was part of the refactor in 73a19d1 (see related lines of code) in order to make it easier to understand. Thinking about it twice, I should have broken it in two different PRs in order to make it easier to review. Let me know if you want me to split it.

The idea behind the refactor was to remove the if clause from the effect and the push to the array stored in the ref. I was having a hard time understanding how they worked and felt I was simplifying it. But I might have achieved the opposite. 😅

I did another try to simplify this (e3ca65d), basically instead of appending the default option inside the effect, we append it in the render.

return typeMethods.map( ( paymentMethod ) => {
const method =
standardMethods[ paymentMethod.method.gateway ];
if ( ! method?.supports?.savePaymentInfo ) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This block of code might be easier to review with Hide whitespace changes. The only change inside this useEffect which is not a refactor is this new check: if savePaymentInfo is not truthy, the saved payment methods are not appended (ie: if Stripe fails to load, saved credit cards will not be appended).

Before, that would be achieved because the payment method would not have been registered at all. But with the changes in this PR, all payment methods are registered for admins, so we need to add this extra check.

Copy link
Member

Choose a reason for hiding this comment

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

Gotcha, thanks for the tip about Hide whitespace changes - much clearer now :)

@Aljullu
Copy link
Contributor Author

Aljullu commented Sep 16, 2020

I found another issue

Good catch! Thanks for testing and finding all those issue @haszari! The issue should be fixed now, that was because I removed the try / catch block around canMakePayment. I added it again in aa8ddda.

@Aljullu
Copy link
Contributor Author

Aljullu commented Sep 16, 2020

While working on this PR I noticed an issue in main (#3157). Fortunately, it's fixed in this PR with the new approach to check savePaymentInfo before saved payment methods are listed.

Copy link
Member

@haszari haszari left a comment

Choose a reason for hiding this comment

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

Excellent job @Aljullu !! 🙌

The new approach seems really robust to me – should work well as more payment methods add support for blocks. I'm curious too whether the new error-catching support in this PR will help us fix various edge-cases for payment errors (e.g. #2743).

I tested a variety of scenarios with admin, editor, and shopper roles, saved cards, various payment methods broken in different ways, and saved card option enabled/disabled, and didn't see any issues.

Ship it!!! 🎉 🚢 :shipit:

@@ -32,6 +32,9 @@ function paymentRequestAvailable( currencyCode ) {
isStripeInitialized = true;
return canPay;
}
if ( stripe.error && stripe.error instanceof Error ) {
Copy link
Member

Choose a reason for hiding this comment

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

This check can doesn't need to be too specific, e.g. could just check stripe.error (type doesn't matter). Or check the API we need is available, e.g. stripe.error || ! stripe.paymentRequest instanceof Function )

Copy link
Member

Choose a reason for hiding this comment

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

(Not a blocker, just could be more robust, assume less about error behaviour.)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm not sure about this one... Considering in the next line we call throw stripe.error, we need to ensure it's an instance of Error.

We could do something like:

if ( stripe.error ) {
	if ( stripe.error instanceof Error ) {
		throw stripe.error;
	} else {
		throw new Error( stripe.error );
	}
}

But I would like to ensure there is a case where that's needed before adding the extra logic.

@@ -57,7 +42,8 @@ const handleRegistrationError = ( error ) => {
const usePaymentMethodRegistration = (
dispatcher,
registeredPaymentMethods,
paymentMethodsSortOrder
paymentMethodsSortOrder,
noticeContext
Copy link
Member

Choose a reason for hiding this comment

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

Don't forget to document the new param in JSdoc!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added in 4d10aac.

@@ -118,10 +99,25 @@ const usePaymentMethodRegistration = (
throw new Error( canPay.error.message );
}
addAvailablePaymentMethod( paymentMethod );
removeNotice(
Copy link
Member

Choose a reason for hiding this comment

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

Does this happen – i.e. we have a payment method fail, show a notice, then on a later render succeed? Just curious about when this removeNotice is needed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good point... The notice is only added on registration, so probably no need for the removeNotice. Removed this line in 93d37bc.

// when a payment method fails to register.
if ( isEditor || CURRENT_USER_IS_ADMIN ) {
return (
<StoreNoticesProvider context="wc/express-payment-area"></StoreNoticesProvider>
Copy link
Member

Choose a reason for hiding this comment

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

I see we have constants for these notice context ids now (useEmitResponse().noticeContexts) - would be good to consistently use the constant rather than hard-code. (Not a blocker)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Absolutely! Changed in 3d5309e.

@Aljullu
Copy link
Contributor Author

Aljullu commented Sep 18, 2020

Thanks for all these reviews and helping me shape this up, @haszari. Really appreciated!

I'm curious too whether the new error-catching support in this PR will help us fix various edge-cases for payment errors (e.g. #2743).

I didn't take a deep look at that issue. I suppose if the error is in canMakePayment or in the components, it might be easier to debug once this PR is merged, but if the error is thrown somewhere else, this PR might not make any difference.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
block: checkout Issues related to the checkout block. type: bug The issue/PR concerns a confirmed bug.
Projects
None yet
4 participants