- 🇫🇷France nicolas bouteille
Hello,
I totally agree with this. We are about to use Stripe Checkout on our site which means customers will leave our site right after cart validation and Stripe will handle billing information and payment steps.
We need to lock the order as soon as the customer leaves our site to make sure he cannot update the order once we've communicated the price to be paid to Stripe.
But for the reasons explained above, it can happen that the customer comes back on our website on the cart step.
As mentioned above, the default behavior is to hide locked carts so getting back to /cart will show an empty cart. From this point, adding something to his cart will create a new order while the first order still exists and can be paid.
We too did not like this behavior so here is what we did:We patched CartProvider.php and commented out:
//if ($cart->isLocked()) { //continue; //}
Now when the cart is locked, it still shows up in /cart
This means customers can now interact on a locked cart and change the order, so we had to reinforce the code to make sure that never happens.In CartManager.php, functions emptyCart(), addOrderItem(), removeOrderItem() and updateOrderItem() need to be updated to do nothing (return;) if the $cart->isLocked
For the record, for those who would like to do it on their site while this is not in core, this time, to make it easier to maintain, we did not create a patch, we created a custom class MyModuleCartManager.php that extends CartManager and that overrides only the functions mentioned above. For example:public function emptyCart(OrderInterface $cart, $save_cart = TRUE) { if ($cart->isLocked()) { return; } parent::emptyCart($cart, $save_cart); }
For this custom class to replace CartManager, we created a custom ServiceProvider that extends ServiceProviderBase as explained here:
https://www.drupal.org/docs/drupal-apis/services-and-dependency-injectio... →
Be aware to name it exactly in PascalCase MyModuleNameServiceProvider and place it in my_module/src/.
This is what the code looks like:class MyModuleNameServiceProvider extends ServiceProviderBase { public function alter(ContainerBuilder $container) { $cart_manager = $container->getDefinition('commerce_cart.cart_manager'); $cart_manager->setClass(MyModuleCartManager::class); } }
We also made sure to visually remove the buttons on the cart form that would allow to remove cart items or update quantities...
We also had to do the same for Coupons.
In CouponRedemption.php buildInlineForm() needs to hide the coupon form if the order is locked and validateInlineForm() must stop and show an error in case a coupon is submitted but the order has been locked since.
For this, we also created a custom class MyModuleCouponRedemption.php but this time we used hook_commerce_inline_form_info_alter to replace CouponRedemption with our class:function my_module_commerce_inline_form_info_alter(&$definitions, $b = null, $c = null, $d = null) { if (isset($definitions['coupon_redemption'])) { $definitions['coupon_redemption']['class'] = MyModuleCouponRedemption::class; } }
So now the customer can see his locked cart, but cannot interact with it anymore. We needed to explain to him what was happening.
When the customer is at /cart and his cart is locked, we now display a warning message that says the order is locked and cannot be updated anymore because its price needs to remain what's been communicated to the payment partner.
We also give the opportunity to the customer to definitely cancel this order. So we added a custom button that will cancel the Stripe PaymentIntent and the order. Then the customer sees an empty cart.
The custom code here is added in views-view--commerce_cart_form.html.twig based on new variables passed through hook_preprocess_views_view__commerce_cart_formI understand that this Cancel order button is probably where and why Commerce Core is going to have some trouble implementing all this. Because in OffsitePaymentGatewayInterface there is no function cancelRemotePayment yet. So Commerce Core probably does not have a way to cancel the offsite payment of an order. So if you cannot make sure to properly cancel the order with the remote payment intent as well, you'd better hide the locked cart and allow the customer to start a new cart.
Maybe this cancel button should be visible only under some conditions like the PaymentGateway is not Offsite or the function cancelRemotePayment is implemented… - 🇺🇸United States Erik_MC
Commenting to mention that we are experiencing the same issue on a client site of mine and interested in exploring a solution so that the end user doesn't think their cart is gone.