Problem/Motivation
`PaymentGatewayForm::dispatchFailedPaymentEvent()` assumes the current route always contains the `commerce_order` parameter. This isn’t always true, given that the form is inline, causing failed payment events to miss the order context and not to be dispatched.
Affected code:
- `modules/contrib/commerce/modules/payment/src/Plugin/Commerce/InlineForm/PaymentGatewayForm.php` reads `$this->routeMatch->getParameter('commerce_order')` as the sole source.
Steps to reproduce
1. Invoke the inline form inside a flow without `commerce_order` in the route.
2. Trigger a gateway failure.
3. Observe missing order context in the dispatched `FailedPaymentEvent`.
Proposed resolution
- Resolve the order via robust fallbacks:
1. Check `FormStateInterface::getBuildInfo()['callback_object']` and call `getOrder()` if available.
Issue fork commerce-3566256
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 #3
ahmad khader commentedComment #4
jsacksick commentedI don't think the change is acceptable as is...Checking if a method_exists over an interface isn't really optimal... Isn't there a getEntity() method in your form? Isn't your form implementing the EntityFormInterface?
Comment #5
ahmad khader commentedHi jsacksick,
Given that it's an inline form, I feel like we cannot assume that `getEntity()` will always return an order. The `getEntity()` method in my case returns `orderItem()`. One potential solution is to create an interface to verify if the callback_object form is an instance of this interface, and it's safe to use getOrder(), BTW Drupal\commerce_checkout\Plugin\Commerce\CheckoutFlow\MultistepDefault (what fresh install use) already uses the getOrder function Alternatively, we could pass the order as a configuration parameter and access it using `$configuration->['order']`.
Comment #6
ahmad khader commentedThis image shows a fresh install of the Kickstart, where the plugin form doesn't have getEntity() and doesn't implement the EntityFormInterface.

We could introduce a new interface, something like 'SupportsOrderEntity' with getOrder(), so that any form using an inline form payment gateway could implement it.
Comment #7
jsacksick commentedOk so maybe what we can do is the following instead?
The CheckoutFlowInterface has a getOrder() method.
Comment #8
ahmad khader commentedHmmm, what if we removed them from CheckoutFlowInterface and let it extend the new interface?
Because not every form will have getPreviousStepId and getNextStepId (be checkoutflow form).
and we say if ($form_state->getFormObject() instanceof SupportsOrderEntity)?
Comment #9
ahmad khader commentedI updated the logic. Let me know if that works for you.
Comment #10
jsacksick commentedhm... In theory yes, but feels weird for this new interface to have a setter...
Maybe we should go with your first commit... I feel more comfortable shipping with this workaround for now rather than trying to figure out where this interface should live, figuring the naming, the setter etc...
So let's just stick to the first version.
Comment #12
ahmad khader commentedAgreed!
Comment #13
ahmad khader commentedComment #14
jsacksick commentedTests are failing, several calls to dispatchFailedPaymentEvent() weren't updated. So this needs additional work.
Comment #15
ahmad khader commentedComment #17
jsacksick commentedMerged, thank you!