Problem/Motivation

When using the paypal payment method, and on Stripe's paypal checkout test page pressing "Fail", I get returned to my site with a WSOD.

InvalidArgumentException: Invalid step ID "" passed to redirectToStep(). in Drupal\commerce_checkout\Plugin\Commerce\CheckoutFlow\CheckoutFlowBase->redirectToStep() (line 227 of modules/contrib/commerce/modules/checkout/src/Plugin/Commerce/CheckoutFlow/CheckoutFlowBase.php).
Drupal\commerce_payment\Controller\PaymentCheckoutController->returnPage(Object, Object)

The underlying problem is that the PaymentCheckoutController is being passed 'review' as the step in the route, but there is no visible pane prior to 'review' so when the controller catches the PaymentGatewayException triggered by the failed offsite payment, it has no valid step to redirect to.

Proposed resolution

We cannot give the return url 'payment' not 'review' as the step, because 'review' is saved as the step on the order and PaymentCheckoutController::validateStep() enforces that the two match.

Instead, we will have to replicate some of the try/catch logic from PaymentCheckoutController::returnPage() in StripePaymentElement::onReturn() and do our own error handling that redirects to 'review' explicitly.

Remaining tasks

User interface changes

API changes

Data model changes

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

jonathanshaw created an issue. See original summary.

anybody’s picture

Confirming this! We just had the same.

jonathanshaw’s picture

Title: WSOD when remote payment method fails » WSOD when offsite payment method fails
Issue summary: View changes
jonathanshaw’s picture

Issue summary: View changes

tomtech’s picture

Assigned: Unassigned » tomtech

Want to make sure I'm understanding this scenario.

there is no visible pane prior to 'review'

I'm not encountering this in an OOTB site. Before the payment step, there is the order information step, which typically collects at least an email address via the contact information pane, along with a payment information pane, allowing for selection of a saved payment method (if the user has one and they are authenticated.)

When I choose the "Fail Test Payment" option, I am returned to the Order Information...no WSOD.

Can I presume you are either using a custom checkout flow or at least the OOTB flow, but with nothing on the order information pane?

If so, curious if/how you are collecting customer information.

jonathanshaw’s picture

Fair question. You're right that my checkout flow is unusual in having no visible step prior to review.

I'm taking a donation, so fraud is a non-issue, so I don't ask for the customer address. If the user is logged in, they go straight to review. They enter the checkout from a webform where they choose the amount they wish to donate.

tomtech’s picture

@jonathanshaw, thanks for that info.

@anybody, is your checkout flow similar? not having a visible step before review? Or is your situation different?

PaymentCheckoutController does presume there is a previous step. This might be something that is better addressed in core.

Alternatively, we could model after the payment process pane. (We don't/can't use that step for a variety of reasons, but it specifically has a getErrorStepId() method that returns the step containing the "payment_information" pane...though this is also another presumptive approach.)

anybody’s picture

@tomtech I can confirm it was similar, we changed our checkout flow in the meantime so it didn't happen to us any more :) Thanks!

jonathanshaw’s picture

My checkout flow doesn't have payment_information either. It's just review, payment [skiped by commerce stripe], gift aid, complete. I don't need payment information because Stripe's payment element does a better job of handling saved payment methods, etc, with a little coaxing.

So what I'm realising now is that the issue is actually quite idiosyncratic to the way my checkout flow is setup, making 'review' the right step for me to return to as it's where I give payment choices, but other sites give payment choices potentially split over both payment_information and review. So if review fails, should they go back to review or payment information? I can see both could be optimal in different circumstances, and I can see an argument either way for what the best default is.

I think the best solution is
(1) to do what I do in the MR, which is move the error handling into onReturn so we can control it
(2) provide a getErrorStepId() method in the gateway to help customising which step to redirect to
(3) make the default either
(a) 'review' (because that kind of makes sense with payment element, and they can always use 'previous' to go back to payment_information), or
(b) 'payment_information' if it's visible/available on the checkout (same approach as getErrorStepId()), otherwise 'review'

jonathanshaw’s picture