Problem/Motivation
The PaymentNotificationController triggers the PaymentOrderUpdater to avoid a race condition. However, the PaymentOrderUpdater saves the order while it is still in draft status. This results in the order pre-save marking the order as needing a refresh on save. The OrderStorage dutifully refreshes the order. Refreshing in the order in a webhoook causes any promotions using coditions that require a session or user context to be removed. This can potentially result in the order paid event not being triggered and the order not being placed.
Steps to reproduce
Add a user based promotion (commerce_promotion) to an order (e.g. requiring a user to have a specific role).
Complete the payment with Datatrans.
Order refreshes in webhook.
Promotion removed from the cart.
Order paid subscriber not being triggered or other deleterious effect, such as giveway promotion product removed from the cart.
Proposed resolution
Don't refresh in the webhook. Instruct the order to skip the refresh and then pass the order directly to the updater (rather than calling PaymentOrderUpdater::updateOrders()), which reloads the order.
// Immediately trigger the payment updater, as doing that during
// the kernel destruct event might result in race conditions with
// the user returning from Datatrans.
$order->setRefreshState(OrderInterface::REFRESH_SKIP);
$this->paymentOrderUpdater->updateOrder($order);
I am really not sure if this is the best place to fix the issue. Potentially it would be better to not refresh locked orders. However, I am not sure if there is a valid use case for refreshing locked orders. I am happy to open an issue in commerce core based on your feedback.
Remaining tasks
MR to follow.
User interface changes
None.
API changes
None.
Data model changes
None.
Issue fork commerce_datatrans-3323429
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
tcrawford commentedComment #4
tcrawford commentedNote:
This can be solved in some use cases by using a customer based commerce condition (where the user is got from the customer on the order, rather than from the session).
However, we have use cases where the commerce condition must be user (session) based, which causes the issue described. As we have issues with multiple payment gateways I believe the best solution is to address this in commerce_core. I will open an issue there (and link here once done).
Comment #5
tcrawford commentedComment #6
tcrawford commentedClosing as a duplicate due to the creation of an issue in the commerce_core module, where this is better fixed.
Comment #7
tcrawford commented