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
Issue fork commerce_stripe-3566460
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
Comment #2
anybodyConfirming this! We just had the same.
Comment #3
jonathanshawComment #4
jonathanshawComment #6
tomtech commentedWant to make sure I'm understanding this scenario.
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.
Comment #7
jonathanshawFair 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.
Comment #8
tomtech commented@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.)
Comment #9
anybody@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!
Comment #10
jonathanshawMy 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'
Comment #11
jonathanshawThis overlaps a lot with #3562298: Unexpected payment intent status requires_payment_method (express and normal checkout)