Commit 4ba08834d3f for woocommerce
commit 4ba08834d3f3098d31d161341a759a7b2f7a8362
Author: Tom Cafferkey <tjcafferkey@gmail.com>
Date: Fri Jul 17 09:30:41 2026 +0100
Fix cart clear filter override (#66414)
* Fix cart clear filter override
* Add tests around order/cart hashing when deciding when to clear the cart
* Unit test for hook being the final value
diff --git a/plugins/woocommerce/changelog/fix-cart-clear-filter-override b/plugins/woocommerce/changelog/fix-cart-clear-filter-override
new file mode 100644
index 00000000000..843b0a089b6
--- /dev/null
+++ b/plugins/woocommerce/changelog/fix-cart-clear-filter-override
@@ -0,0 +1,5 @@
+Significance: patch
+Type: fix
+Comment: Allow woocommerce_should_clear_cart_after_payment to override cart hash mismatch checks.
+
+
diff --git a/plugins/woocommerce/includes/wc-cart-functions.php b/plugins/woocommerce/includes/wc-cart-functions.php
index 7d4ff014f58..41e5ee9968a 100644
--- a/plugins/woocommerce/includes/wc-cart-functions.php
+++ b/plugins/woocommerce/includes/wc-cart-functions.php
@@ -211,6 +211,11 @@ function wc_clear_cart_after_payment() {
return;
}
+ // If the order is different from the cart, don't clear the cart. This can happen if the user has multiple tabs open and completes a different order than the one in the cart.
+ if ( $should_clear_cart_after_payment && $order instanceof WC_Order && ! WC()->cart->is_empty() ) {
+ $should_clear_cart_after_payment = $order->has_cart_hash( WC()->cart->get_cart_hash() );
+ }
+
/**
* Determine whether the cart should be cleared after payment.
*
@@ -219,11 +224,6 @@ function wc_clear_cart_after_payment() {
*/
$should_clear_cart_after_payment = apply_filters( 'woocommerce_should_clear_cart_after_payment', $should_clear_cart_after_payment );
- // If the order is different from the cart, don't clear the cart. This can happen if the user has multiple tabs open and completes a different order than the one in the cart.
- if ( $should_clear_cart_after_payment && $order instanceof WC_Order && ! WC()->cart->is_empty() ) {
- $should_clear_cart_after_payment = $order->has_cart_hash( WC()->cart->get_cart_hash() );
- }
-
if ( $should_clear_cart_after_payment ) {
WC()->cart->empty_cart();
}
diff --git a/plugins/woocommerce/tests/php/includes/class-wc-cart-test.php b/plugins/woocommerce/tests/php/includes/class-wc-cart-test.php
index 783ce78c632..fef08aa2d27 100644
--- a/plugins/woocommerce/tests/php/includes/class-wc-cart-test.php
+++ b/plugins/woocommerce/tests/php/includes/class-wc-cart-test.php
@@ -905,6 +905,130 @@ class WC_Cart_Test extends \WC_Unit_Test_Case {
$order->delete( true );
}
+ /**
+ * @testdox Should clear the cart after payment when the order cart hash matches.
+ */
+ public function test_clear_cart_after_payment_clears_cart_when_order_cart_hash_matches(): void {
+ global $wp;
+
+ $previous_query_vars = $wp->query_vars;
+ $previous_order_key = isset( $_GET['key'] ) ? wc_clean( wp_unslash( $_GET['key'] ) ) : null; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Preserving request state for test cleanup.
+ $order = null;
+
+ try {
+ $product = WC_Helper_Product::create_simple_product();
+ WC()->cart->add_to_cart( $product->get_id(), 1 );
+
+ $order = WC_Helper_Order::create_order( 1, $product, array( 'status' => OrderStatus::COMPLETED ) );
+ $order->set_cart_hash( WC()->cart->get_cart_hash() );
+ $order->save();
+
+ $wp->query_vars['order-received'] = $order->get_id();
+ $_GET['key'] = $order->get_order_key();
+
+ wc_clear_cart_after_payment();
+
+ $this->assertTrue( WC()->cart->is_empty(), 'Cart should be emptied when the paid order matches the current cart hash.' );
+ } finally {
+ $wp->query_vars = $previous_query_vars;
+
+ if ( null === $previous_order_key ) {
+ unset( $_GET['key'] );
+ } else {
+ $_GET['key'] = $previous_order_key;
+ }
+
+ if ( $order instanceof WC_Order ) {
+ WC_Helper_Order::delete_order( $order->get_id() );
+ }
+ }
+ }
+
+ /**
+ * @testdox Should not clear the cart after payment when the order cart hash differs.
+ */
+ public function test_clear_cart_after_payment_keeps_cart_when_order_cart_hash_differs(): void {
+ global $wp;
+
+ $previous_query_vars = $wp->query_vars;
+ $previous_order_key = isset( $_GET['key'] ) ? wc_clean( wp_unslash( $_GET['key'] ) ) : null; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Preserving request state for test cleanup.
+ $order = null;
+
+ try {
+ $product = WC_Helper_Product::create_simple_product();
+ WC()->cart->add_to_cart( $product->get_id(), 1 );
+
+ $order = WC_Helper_Order::create_order( 1, $product, array( 'status' => OrderStatus::COMPLETED ) );
+ $order->set_cart_hash( 'different-cart-hash' );
+ $order->save();
+
+ $wp->query_vars['order-received'] = $order->get_id();
+ $_GET['key'] = $order->get_order_key();
+
+ wc_clear_cart_after_payment();
+
+ $this->assertFalse( WC()->cart->is_empty(), 'Cart should not be emptied when the paid order does not match the current cart hash.' );
+ } finally {
+ $wp->query_vars = $previous_query_vars;
+
+ if ( null === $previous_order_key ) {
+ unset( $_GET['key'] );
+ } else {
+ $_GET['key'] = $previous_order_key;
+ }
+
+ if ( $order instanceof WC_Order ) {
+ WC_Helper_Order::delete_order( $order->get_id() );
+ }
+ }
+ }
+
+ /**
+ * @testdox Should allow woocommerce_should_clear_cart_after_payment to override the final clear cart value.
+ */
+ public function test_clear_cart_after_payment_filter_can_override_final_value(): void {
+ global $wp;
+
+ $previous_query_vars = $wp->query_vars;
+ $previous_order_key = isset( $_GET['key'] ) ? wc_clean( wp_unslash( $_GET['key'] ) ) : null; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Preserving request state for test cleanup.
+ $order = null;
+ $filter = function ( $should_clear_cart_after_payment ) {
+ $this->assertFalse( $should_clear_cart_after_payment, 'The filter should receive the final value after the cart hash check.' );
+ return true;
+ };
+
+ try {
+ $product = WC_Helper_Product::create_simple_product();
+ WC()->cart->add_to_cart( $product->get_id(), 1 );
+
+ $order = WC_Helper_Order::create_order( 1, $product, array( 'status' => OrderStatus::COMPLETED ) );
+ $order->set_cart_hash( 'different-cart-hash' );
+ $order->save();
+
+ $wp->query_vars['order-received'] = $order->get_id();
+ $_GET['key'] = $order->get_order_key();
+
+ add_filter( 'woocommerce_should_clear_cart_after_payment', $filter );
+
+ wc_clear_cart_after_payment();
+
+ $this->assertTrue( WC()->cart->is_empty(), 'Cart should be emptied when the filter overrides the final value.' );
+ } finally {
+ remove_filter( 'woocommerce_should_clear_cart_after_payment', $filter );
+ $wp->query_vars = $previous_query_vars;
+
+ if ( null === $previous_order_key ) {
+ unset( $_GET['key'] );
+ } else {
+ $_GET['key'] = $previous_order_key;
+ }
+
+ if ( $order instanceof WC_Order ) {
+ WC_Helper_Order::delete_order( $order->get_id() );
+ }
+ }
+ }
+
/**
* @testdox should clear shipping data from session when the cart is empty
*/