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

Refactor payment status #8110

Merged
merged 19 commits into from
Feb 14, 2023
Merged

Refactor payment status #8110

merged 19 commits into from
Feb 14, 2023

Conversation

alexflorisca
Copy link
Member

@alexflorisca alexflorisca commented Jan 5, 2023

I've refactored the payment status to make it less confusing and more inline with current Checkout flow.

Payment status was being set to SUCCESS or FAIL before the payment actually gets made.

We fire an event PAYMENT_PROCESSING, which enables third party plugins to subscribe to and run some code, returning either a FAIL, SUCCESS or ERROR response. We then set the payment status in the data store accordingly.

Historically (from speaking to @nerrad briefly), this was done to provide an API to allow third party plugins to make payments client side. I think this isn't the case anymore and all payments are actually made server side. In which case, the client side API we provide via the PAYMENT_PROCESSING event, is mainly for creating payment intents and running any code BEFORE the actual payment gets made. This is how stripe works. So while it makes sense to return an ERROR, it doesn't make sense to return FAIL or SUCCESS, which implies the payment has been attempted.

The FAIL response from the onPaymentProcessing observer was also throwing the Checkout into a weird state, an overlay and loading indicator on the Place Order button, so this wasn't being handled well anyway. To keep backwards compatibility with any plugins which may return a FAIL response, we now set the payment status in the data store to ERROR when we receive either an ERROR or FAIL response from the observers.

  • Deprecate the SUCCESS and FAIL statuses and associated actions, action types, selectors and reducers.
  • Set the payment status to READY instead of SUCCESS, which better indicates that we are in a state ready to make the payment.

isPaymentFinished selector was relying on the SUCCESS and FAIL payment statuses which were being set before the payment had even started

  • Deprecate the isPaymentFinished selector

We reset to PRISTINE multiple times

PRISTINE implies a clean slate, before anything runs, yet we set the status to PRISTINE multiple times throughout the payment lifecycle.

  • Replace PRISTINE status with IDLE

The STARTED status only applies to express payment methods

  • Rename the STARTED status to EXPRESS_STARTED and the associated selectors and actions

Checkout status PRISTINE is not needed

There is nowhere in the codebase that we check wether the checkout status is PRISTINE or not. There is also some unnecessary logic in the reducer to set the status to IDLE after the first action is performed on the checkout store. If we ever need to check that state of the checkout, it can be done with the data that is in the data store quite simply.

  • Remove checkout status PRISTINE

Here is what the happy path will look like after this PR and #8381

Untitled-2023-02-02-2008

Fixes #7667

Testing

User Facing Testing

  1. Go through the checkout process (place an order) and pay using Stripe Credit Card.
    • Trigger an error using a test card which returns errors such as 4000 0000 0000 0002
    • Use a valid test card to place the order successfully.
  2. Go through the checkout process (place an order) using an express payment method. Ensure there are no regressions.

Internal Testing

  1. Run the Stripe plugin locally on your machine with npm start.
  2. Open a page with the Checkout Block
  3. Checkout with 4000 0000 0000 0002
  4. You should see an error within the payment area
  5. Open up Redux dev tools and select the wc/store/payment store
  6. You should see the something similar to the following:

Screenshot 2023-01-05 at 16 54 06

  1. In your local version of the stripe plugin, open up use-payment-processing.js and before the return statement on line 123, add
return {
    type: 'failure',
    message: 'Fail',
};
  1. Refresh the Checkout page, fill in the details and check out with the same card from step 3
  2. You should see an error notice within the payment section saying Fail.
  3. In redux dev tools, you should see the payment status get set to has_error
  4. Repeat step 7 with the following code:
return {
    type: 'error',
    message: 'Error',
};
  1. Refresh the Checkout page, fill in the details and check out with the same card from step 3
  2. You should see an error notice within the payment section saying Error.
  3. In redux dev tools, you should see the payment status get set to has_error
  4. Reset all code changes
  5. Go through the checkout process with a valid card and make sure it redirects to the order summary page ok
  6. Go through the checkout process with an express payment method and make sure all is ok.
  • Do not include in the Testing Notes

WooCommerce Visibility

  • WooCommerce Core
  • Feature plugin
  • Experimental

Performance Impact

Changelog

Refactor the payment status in the payment data store to better reflect the payment lifecycle during the checkout process

@github-actions
Copy link
Contributor

github-actions bot commented Jan 5, 2023

The release ZIP for this PR is accessible via:

https://wcblocks.wpcomstaging.com/wp-content/uploads/woocommerce-gutenberg-products-block-8110.zip

Script Dependencies Report

There is no changed script dependency between this branch and trunk.

This comment was automatically generated by the ./github/compare-assets action.

TypeScript Errors Report

  • Files with errors: 496
  • Total errors: 2342

⚠️ ⚠️ This PR introduces new TS errors on 1 files:

assets/js/data/checkout/default-state.ts

comments-aggregator

@github-actions
Copy link
Contributor

github-actions bot commented Jan 5, 2023

Size Change: +206 B (0%)

Total Size: 1.1 MB

Filename Size Change
build/active-filters.js 7.31 kB +1 B (0%)
build/all-products.js 33.7 kB +7 B (0%)
build/attribute-filter.js 12.3 kB +1 B (0%)
build/cart-blocks/cart-express-payment--checkout-blocks/express-payment-frontend.js 5.17 kB +99 B (+2%)
build/cart-frontend.js 28.7 kB -39 B (0%)
build/cart.js 47.1 kB +98 B (0%)
build/checkout-blocks/payment-frontend.js 8.43 kB +107 B (+1%)
build/checkout-frontend.js 30.3 kB -47 B (0%)
build/checkout.js 43.7 kB +135 B (0%)
build/mini-cart-component-frontend.js 27.9 kB +1 B (0%)
build/mini-cart-contents-block/footer-frontend.js 2.79 kB -31 B (-1%)
build/mini-cart-contents.js 17 kB -23 B (0%)
build/product-button.js 3.99 kB +1 B (0%)
build/product-image.js 4.09 kB +2 B (0%)
build/product-price.js 1.58 kB +1 B (0%)
build/product-sku.js 378 B +1 B (0%)
build/product-stock-indicator.js 646 B +1 B (0%)
build/product-tag-list.js 497 B -1 B (0%)
build/product-title.js 3.46 kB +2 B (0%)
build/rating-filter.js 7.37 kB +1 B (0%)
build/single-product.js 9.98 kB +14 B (0%)
build/wc-blocks-data.js 21.4 kB -119 B (-1%)
build/wc-blocks-registry.js 3.15 kB -6 B (0%)
ℹ️ View Unchanged
Filename Size
build/active-filters-frontend.js 7.98 kB
build/active-filters-wrapper-frontend.js 6 kB
build/all-products-frontend.js 11.7 kB
build/all-reviews.js 7.66 kB
build/attribute-filter-frontend.js 22.9 kB
build/attribute-filter-wrapper-frontend.js 7.67 kB
build/blocks-checkout.js 41.2 kB
build/breadcrumbs.js 2.05 kB
build/cart-blocks/cart-accepted-payment-methods-frontend.js 1.38 kB
build/cart-blocks/cart-cross-sells-frontend.js 253 B
build/cart-blocks/cart-cross-sells-products-frontend.js 9.69 kB
build/cart-blocks/cart-express-payment-frontend.js 720 B
build/cart-blocks/cart-items-frontend.js 299 B
build/cart-blocks/cart-line-items--mini-cart-contents-block/products-table-frontend.js 5.36 kB
build/cart-blocks/cart-line-items-frontend.js 1.07 kB
build/cart-blocks/cart-order-summary-frontend.js 1.24 kB
build/cart-blocks/cart-totals-frontend.js 321 B
build/cart-blocks/empty-cart-frontend.js 345 B
build/cart-blocks/filled-cart-frontend.js 654 B
build/cart-blocks/order-summary-coupon-form-frontend.js 1.62 kB
build/cart-blocks/order-summary-discount-frontend.js 2.12 kB
build/cart-blocks/order-summary-fee-frontend.js 274 B
build/cart-blocks/order-summary-heading-frontend.js 455 B
build/cart-blocks/order-summary-shipping-frontend.js 14.8 kB
build/cart-blocks/order-summary-subtotal-frontend.js 274 B
build/cart-blocks/order-summary-taxes-frontend.js 435 B
build/cart-blocks/proceed-to-checkout-frontend.js 1.24 kB
build/catalog-sorting.js 1.7 kB
build/checkout-blocks/actions-frontend.js 1.85 kB
build/checkout-blocks/billing-address--checkout-blocks/shipping-address-frontend.js 3.92 kB
build/checkout-blocks/billing-address-frontend.js 1.18 kB
build/checkout-blocks/contact-information-frontend.js 2.05 kB
build/checkout-blocks/express-payment-frontend.js 1.13 kB
build/checkout-blocks/fields-frontend.js 344 B
build/checkout-blocks/order-note-frontend.js 1.14 kB
build/checkout-blocks/order-summary-cart-items-frontend.js 3.67 kB
build/checkout-blocks/order-summary-coupon-form-frontend.js 1.78 kB
build/checkout-blocks/order-summary-discount-frontend.js 2.29 kB
build/checkout-blocks/order-summary-fee-frontend.js 277 B
build/checkout-blocks/order-summary-frontend.js 1.24 kB
build/checkout-blocks/order-summary-shipping-frontend.js 14.9 kB
build/checkout-blocks/order-summary-subtotal-frontend.js 275 B
build/checkout-blocks/order-summary-taxes-frontend.js 435 B
build/checkout-blocks/pickup-options-frontend.js 2.8 kB
build/checkout-blocks/shipping-address-frontend.js 1.14 kB
build/checkout-blocks/shipping-method-frontend.js 2.27 kB
build/checkout-blocks/shipping-methods-frontend.js 4.78 kB
build/checkout-blocks/terms-frontend.js 1.56 kB
build/checkout-blocks/totals-frontend.js 324 B
build/customer-account.js 3.12 kB
build/featured-category.js 13.1 kB
build/featured-product.js 13.4 kB
build/filter-wrapper-frontend.js 14 kB
build/filter-wrapper.js 2.39 kB
build/general-style-rtl.css 1.31 kB
build/general-style.css 1.31 kB
build/handpicked-products.js 7.24 kB
build/legacy-template.js 2.85 kB
build/mini-cart-contents-block/empty-cart-frontend.js 366 B
build/mini-cart-contents-block/filled-cart-frontend.js 268 B
build/mini-cart-contents-block/items-frontend.js 237 B
build/mini-cart-contents-block/products-table-frontend.js 591 B
build/mini-cart-contents-block/shopping-button-frontend.js 313 B
build/mini-cart-contents-block/title-frontend.js 367 B
build/mini-cart-frontend.js 2 kB
build/mini-cart.js 4.31 kB
build/price-filter-frontend.js 13.9 kB
build/price-filter-wrapper-frontend.js 6.99 kB
build/price-filter.js 8.36 kB
build/price-format.js 1.19 kB
build/product-add-to-cart--product-button--product-category-list--product-image--product-price--product-r--a0326d00.js 228 B
build/product-add-to-cart--product-button--product-image--product-rating--product-title.js 151 B
build/product-add-to-cart-frontend.js 6.73 kB
build/product-add-to-cart.js 8.63 kB
build/product-best-sellers.js 7.6 kB
build/product-button--product-category-list--product-image--product-price--product-rating--product-sale-b--e17c7c01.js 494 B
build/product-button--product-image--product-price--product-rating--product-sale-badge--product-title.js 258 B
build/product-button-frontend.js 2.19 kB
build/product-categories.js 2.36 kB
build/product-category-list-frontend.js 1.19 kB
build/product-category-list.js 503 B
build/product-category.js 8.58 kB
build/product-image-frontend.js 2.2 kB
build/product-new.js 7.59 kB
build/product-on-sale.js 7.91 kB
build/product-price-frontend.js 2.29 kB
build/product-query.js 6.08 kB
build/product-rating-frontend.js 1.62 kB
build/product-rating.js 919 B
build/product-results-count.js 1.66 kB
build/product-sale-badge-frontend.js 1.43 kB
build/product-sale-badge.js 815 B
build/product-search.js 2.63 kB
build/product-sku-frontend.js 629 B
build/product-stock-indicator-frontend.js 1.32 kB
build/product-summary-frontend.js 1.58 kB
build/product-summary.js 918 B
build/product-tag-list-frontend.js 1.18 kB
build/product-tag.js 8.07 kB
build/product-title-frontend.js 1.62 kB
build/product-top-rated.js 7.84 kB
build/products-by-attribute.js 8.52 kB
build/rating-filter-frontend.js 21.4 kB
build/rating-filter-wrapper-frontend.js 6.19 kB
build/reviews-by-category.js 11.2 kB
build/reviews-by-product.js 12.3 kB
build/reviews-frontend.js 7.14 kB
build/single-product-frontend.js 17.8 kB
build/stock-filter-frontend.js 21.1 kB
build/stock-filter-wrapper-frontend.js 5.85 kB
build/stock-filter.js 8.11 kB
build/store-notices.js 1.65 kB
build/vendors--attribute-filter-wrapper--cart-blocks/cart-cross-sells-products--cart-blocks/order-summary--82e4ed06-frontend.js 6.86 kB
build/vendors--attribute-filter-wrapper--rating-filter-wrapper--stock-filter-wrapper-frontend.js 7.7 kB
build/vendors--cart-blocks/cart-cross-sells-products--cart-blocks/cart-line-items--cart-blocks/cart-order--3c5fe802-frontend.js 5.26 kB
build/vendors--cart-blocks/cart-cross-sells-products--cart-blocks/order-summary-shipping--checkout-blocks--18f9376a-frontend.js 19.4 kB
build/vendors--cart-blocks/cart-cross-sells-products--product-add-to-cart-frontend.js 7.53 kB
build/vendors--cart-blocks/cart-line-items--checkout-blocks/order-summary-cart-items--mini-cart-contents---233ab542-frontend.js 3.14 kB
build/vendors--cart-blocks/order-summary-shipping--checkout-blocks/billing-address--checkout-blocks/order--5b8feb0b-frontend.js 4.83 kB
build/vendors--checkout-blocks/shipping-method-frontend.js 12 kB
build/vendors--checkout-blocks/shipping-methods-frontend.js 9.48 kB
build/wc-blocks-editor-style-rtl.css 5.5 kB
build/wc-blocks-editor-style.css 5.5 kB
build/wc-blocks-google-analytics.js 1.56 kB
build/wc-blocks-middleware.js 933 B
build/wc-blocks-shared-context.js 1.52 kB
build/wc-blocks-shared-hocs.js 1.73 kB
build/wc-blocks-style-rtl.css 26.5 kB
build/wc-blocks-style.css 26.5 kB
build/wc-blocks-vendors-style-rtl.css 1.96 kB
build/wc-blocks-vendors-style.css 1.96 kB
build/wc-blocks-vendors.js 64.3 kB
build/wc-blocks.js 2.65 kB
build/wc-payment-method-bacs.js 816 B
build/wc-payment-method-cheque.js 811 B
build/wc-payment-method-cod.js 909 B
build/wc-payment-method-paypal.js 837 B
build/wc-settings.js 2.6 kB
build/wc-shipping-method-pickup-location.js 29.7 kB
build/wp-directives-runtime.js 2.4 kB
build/wp-directives-vendors.js 7.89 kB

compressed-size-action

@alexflorisca alexflorisca marked this pull request as ready for review January 5, 2023 17:05
@woocommercebot woocommercebot requested review from a team and tarhi-saad and removed request for a team January 5, 2023 17:05
@alexflorisca alexflorisca requested review from mikejolley, senadir and opr and removed request for tarhi-saad January 5, 2023 17:06
@opr
Copy link
Contributor

opr commented Jan 6, 2023

Historically (from speaking to @nerrad briefly), this was done to provide an API to allow third party plugins to make payments client side.

As mentioned when pairing on this, I don't think "client-side only" payments are something we would ever have wanted to do, Darren please correct me if I'm wrong, but it seems insecure. Given a payment gateway often has a "secret" key that allows an application to interact with it, allowing this so take place on the client-side seems unreasonable.

Perhaps it was made to allow async processing of payments, before the submit button was actually pressed?


We can either deprecate these, or @opr and I were talking about if we should set the status to SUCCESS or FAIL after a payment attempt via the /checkout StoreApi fetch, rather than setting it to idle. I feel like this would provide third party developers with something to hook into post payment attempt, should they wish to show users a notice or something like that.

We also expose status through various flags in the use-payment-method-interface.tsx, So it might be beneficial to expose the FINISHED, FAILED and SUCCESS states here. But only if we don't reset the status back to IDLE, otherwise it would be short lived.

I wonder if it would be beneficial to set the status to SUCCESS or FAILED even if it is very short lived. It would allow the extensions to know whether it succeeded or failed, before reverting back to IDLE. After thinking about this, I don' believe going straight to IDLE is the best flow now.

Copy link
Contributor

@opr opr left a comment

Choose a reason for hiding this comment

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

I won't add an approval or rejection here since I worked on it with you, and I commented above with my thoughts on success/failed.

I just added some inline comments about following the WP Coding guidelines re comments (They should begin with a capital letter and end in a full stop).

assets/js/data/payment/thunks.ts Outdated Show resolved Hide resolved
assets/js/data/payment/thunks.ts Outdated Show resolved Hide resolved
assets/js/data/payment/thunks.ts Outdated Show resolved Hide resolved
assets/js/data/payment/selectors.ts Outdated Show resolved Hide resolved
assets/js/data/payment/constants.ts Outdated Show resolved Hide resolved
@nerrad
Copy link
Contributor

nerrad commented Jan 6, 2023

As mentioned when pairing on this, I don't think "client-side only" payments are something we would ever have wanted to do, Darren please correct me if I'm wrong, but it seems insecure.

I wasn't very clear when giving some historical context. What I meant is that there are some payment methods that have preliminary processing to do client side (that have nothing necessarily to do with completing the payment) usually involving contacting the payment processor (for example getting a token). These statuses provided a way for payment methods to signal to the checkout flow that they were busy doing this so checkout won't submit to the server yet. My memory is blurry but I think this is especially important for express payment methods.

So think of this less as "payment status" and more as "payment method readiness/processing status". I also can't remember if these statuses were also used during payment method initialization.

@opr
Copy link
Contributor

opr commented Jan 6, 2023

Thanks @nerrad that's super helpful!

@alexflorisca
Copy link
Member Author

alexflorisca commented Jan 9, 2023

These statuses provided a way for payment methods to signal to the checkout flow that they were busy doing this so checkout won't submit to the server yet. My memory is blurry but I think this is especially important for express payment methods.

Thanks Darren that makes sense. So in this case, I think having the payment status change to READY and ERROR is appropriate, indicating an error from the payment provider (via the observers) or a readiness to proceed with the payment.

I wonder if it would be beneficial to set the status to SUCCESS or FAILED even if it is very short lived.

Yeah I was thinking the same. I'm not even sure we need IDLE but I could be wrong.

@github-actions
Copy link
Contributor

This PR has been marked as stale because it has not seen any activity within the past 7 days. Our team uses this tool to help surface pull requests that have slipped through review.

If deemed still relevant, the pr can be kept active by ensuring it's up to date with the main branch and removing the stale label.

@github-actions github-actions bot added the status: stale Stale issues and PRs have had no updates for 60 days. label Jan 18, 2023
@opr opr removed the status: stale Stale issues and PRs have had no updates for 60 days. label Jan 18, 2023
@github-actions
Copy link
Contributor

This PR has been marked as stale because it has not seen any activity within the past 7 days. Our team uses this tool to help surface pull requests that have slipped through review.

If deemed still relevant, the pr can be kept active by ensuring it's up to date with the main branch and removing the stale label.

@github-actions github-actions bot added the status: stale Stale issues and PRs have had no updates for 60 days. label Jan 28, 2023
@alexflorisca alexflorisca added type: refactor The issue/PR is related to refactoring. block: checkout Issues related to the checkout block. type: technical debt This issue/PR represents/solves the technical debt of the project. and removed status: stale Stale issues and PRs have had no updates for 60 days. labels Feb 3, 2023
@alexflorisca alexflorisca self-assigned this Feb 3, 2023
// allows for other observers that return true for continuing through
// to the next observer (or bailing if there's a problem).
if ( isPaymentProcessing ) {
__internalEmitPaymentProcessingEvent(
Copy link
Member Author

Choose a reason for hiding this comment

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

This was moved in a useEffect above, the same one where we set the payment status to processing

@@ -26,7 +26,6 @@ describe.only( 'Checkout Store Reducer', () => {
const expectedState = {
...defaultState,
redirectUrl: 'https://example.com',
status: STATUS.IDLE,
Copy link
Member Author

Choose a reason for hiding this comment

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

The reason for removing all the STATUS.IDLEs here is because we got rid of the reducer code that sets the status to IDLE for the first action performed on the store. That was a sideeffect essentially so we don't need to mock that in the test anymore

@@ -97,12 +96,15 @@ describe.only( 'Checkout Store Reducer', () => {
} );

it( 'should handle SET_HAS_ERROR when status is anything else', () => {
Copy link
Member Author

Choose a reason for hiding this comment

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

anything else here means not PROCESSING or BEFORE_PROCESSING

Copy link
Member

@mikejolley mikejolley left a comment

Choose a reason for hiding this comment

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

Some questions from my side. I will do in depth testing afterwards.

@alexflorisca alexflorisca mentioned this pull request Feb 3, 2023
8 tasks
@alexflorisca
Copy link
Member Author

Just a note on deprecating. I've assumed that this PR will be merged before 9.6.0 is released. If it isn't, the version number inside the deprecated function will need to change to reflect when this PR will be released

@senadir senadir removed their request for review February 6, 2023 12:06
@mikejolley mikejolley self-assigned this Feb 14, 2023
Copy link
Member

@mikejolley mikejolley left a comment

Choose a reason for hiding this comment

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

I've tested and rebased this. I did tests using Stripe (CC and Express) and all worked as expected. I updated the test instructions to smoke test this aspect too.

Happy to merge 🚢

@mikejolley mikejolley merged commit cffafeb into trunk Feb 14, 2023
@mikejolley mikejolley deleted the fix/payment-status branch February 14, 2023 12:08
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: refactor The issue/PR is related to refactoring. type: technical debt This issue/PR represents/solves the technical debt of the project.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

The ERROR and FAIL payment status values are not accurate
4 participants