diff --git a/assets/js/base/context/cart-checkout/checkout-state/index.js b/assets/js/base/context/cart-checkout/checkout-state/index.js index e519ccd2a2f..40a192a35d9 100644 --- a/assets/js/base/context/cart-checkout/checkout-state/index.js +++ b/assets/js/base/context/cart-checkout/checkout-state/index.js @@ -101,6 +101,7 @@ export const CheckoutStateProvider = ( { isSuccessResponse, isErrorResponse, isFailResponse, + shouldRetry, } = useEmitResponse(); // set observers on ref so it's always current. @@ -237,10 +238,7 @@ export const CheckoutStateProvider = ( { addErrorNotice( response.message, errorOptions ); } // irrecoverable error so set complete - if ( - typeof response.retry !== 'undefined' && - response.retry !== true - ) { + if ( ! shouldRetry( response ) ) { dispatch( actions.setComplete( response ) ); } else { dispatch( actions.setIdle() ); @@ -278,7 +276,7 @@ export const CheckoutStateProvider = ( { : undefined; addErrorNotice( response.message, errorOptions ); } - if ( ! response.retry ) { + if ( ! shouldRetry( response ) ) { dispatch( actions.setComplete( response ) ); } else { // this will set an error which will end up diff --git a/assets/js/base/hooks/checkout/use-emit-response.js b/assets/js/base/hooks/checkout/use-emit-response.js index f1220a2a3ef..38b1c4262f2 100644 --- a/assets/js/base/hooks/checkout/use-emit-response.js +++ b/assets/js/base/hooks/checkout/use-emit-response.js @@ -37,6 +37,10 @@ const isFailResponse = ( response ) => { return isResponseOf( response, responseTypes.FAIL ); }; +const shouldRetry = ( response ) => { + return typeof response.retry === 'undefined' || response.retry === true; +}; + /** * A custom hook exposing response utilities for emitters. * @@ -47,6 +51,7 @@ export const useEmitResponse = () => { return { responseTypes, noticeContexts, + shouldRetry, isSuccessResponse, isErrorResponse, isFailResponse, diff --git a/assets/js/type-defs/hooks.js b/assets/js/type-defs/hooks.js index e6578a81fbc..a616b4c68f0 100644 --- a/assets/js/type-defs/hooks.js +++ b/assets/js/type-defs/hooks.js @@ -148,6 +148,8 @@ * be used in returned response objects. * @property {NoticeContexts} noticeContexts An object of various notice contexts that can * be used for targeting where a notice appears. + * @property {function(Object):boolean} shouldRetry Returns whether the user is allowed to retry + * the payment after a failed one. * @property {function(Object):boolean} isSuccessResponse Returns whether the given response is of a * success response type. * @property {function(Object):boolean} isErrorResponse Returns whether the given response is of an diff --git a/docs/extensibility/checkout-flow-and-events.md b/docs/extensibility/checkout-flow-and-events.md index 25b6e85fa9f..3b88ee615db 100644 --- a/docs/extensibility/checkout-flow-and-events.md +++ b/docs/extensibility/checkout-flow-and-events.md @@ -174,6 +174,7 @@ const Component = () => { isFailResponse, noticeContexts, responseTypes, + shouldRetry, } = useEmitResponse; return null; }; @@ -186,6 +187,7 @@ The properties of the object returned by this hook are: - `PAYMENTS`: This is a reference to the notice area in the payment methods step. - `EXPRESS_PAYMENTS`: This is a reference to the notice area in the express payment methods step. - `responseTypes`: This is an object containing properties referencing the various response types that can be returned by observers for some event emitters. It makes it easier for autocompleting the types and avoiding typos due to human error. The types are `SUCCESS`, `FAIL`, `ERROR`. The values for these types also correspond to the [payment status types](https://github.com/woocommerce/woocommerce-gutenberg-products-block/blob/34e17c3622637dbe8b02fac47b5c9b9ebf9e3596/src/Payments/PaymentResult.php#L21) from the [checkout endpoint response from the server](https://github.com/woocommerce/woocommerce-gutenberg-products-block/blob/34e17c3622637dbe8b02fac47b5c9b9ebf9e3596/src/RestApi/StoreApi/Schemas/CheckoutSchema.php#L103-L113). +- `shouldRetry`: This is a function containing the logic whether the checkout flow should allow the user to retry the payment after a previous payment failed. It receives the `response` object and by default checks whether the `retry` property is true/undefined or false. Refer to the [`onCheckoutAfterProcessingWithSuccess`](#oncheckoutafterprocessingwithsuccess) documentation for more details. Note: `noticeContexts` and `responseTypes` are exposed to payment methods via the `emitResponse` prop given to their component: @@ -368,7 +370,7 @@ In all cases, if there are the following properties in the response, additional - `message`: This string will be added as an error notice. - `messageContext`: If present, the notice will be configured to show in the designated notice area (otherwise it will just be a general notice for the checkout block). -- `retry`: If this is `true`, then the checkout status will be set to `IDLE`. This basically means that the error is recoverable (for example try a different payment method) and so checkout will be reset to `IDLE` for another attempt by the shopper. If this is `false`, then the checkout status is set to `COMPLETE` and the checkout will redirect to whatever is currently set as the `redirectUrl`. +- `retry`: If this is `true` or not defined, then the checkout status will be set to `IDLE`. This basically means that the error is recoverable (for example try a different payment method) and so checkout will be reset to `IDLE` for another attempt by the shopper. If this is `false`, then the checkout status is set to `COMPLETE` and the checkout will redirect to whatever is currently set as the `redirectUrl`. - `redirectUrl`: If this is present, then the checkout will redirect to this url when the status is `COMPLETE`. If all observers return `true`, then the checkout status will just be set to `COMPLETE`.