diff --git a/includes/class-wc-cart-session.php b/includes/class-wc-cart-session.php index de5e99198d1..4d3a69b36d2 100644 --- a/includes/class-wc-cart-session.php +++ b/includes/class-wc-cart-session.php @@ -61,6 +61,7 @@ final class WC_Cart_Session { * @since 3.2.0 */ public function get_cart_from_session() { + // Flag to indicate the stored cart should be updated. $update_cart_session = false; $totals = WC()->session->get( 'cart_totals', null ); $cart = WC()->session->get( 'cart', null ); @@ -99,15 +100,15 @@ final class WC_Cart_Session { if ( ! empty( $product ) && $product->exists() && $values['quantity'] > 0 ) { if ( ! $product->is_purchasable() ) { - $update_cart_session = true; // Flag to indicate the stored cart should be updated. + $update_cart_session = true; /* translators: %s: product name */ wc_add_notice( sprintf( __( '%s has been removed from your cart because it can no longer be purchased. Please contact us if you need assistance.', 'woocommerce' ), $product->get_name() ), 'error' ); do_action( 'woocommerce_remove_cart_item_from_session', $key, $values ); - } elseif ( isset( $values['date_modified'] ) && $values['date_modified'] instanceof WC_DateTime && $values['date_modified']->getTimestamp() !== $product->get_date_modified()->getTimestamp() ) { - $update_cart_session = true; // Flag to indicate the stored cart should be updated. + } elseif ( ! empty( $values['data_hash'] ) && ! hash_equals( $values['data_hash'], wc_get_cart_item_data_hash( $product ) ) ) { + $update_cart_session = true; /* translators: %1$s: product name. %2$s product permalink */ - wc_add_notice( sprintf( __( 'Product %1$s has been removed from your cart because its contents have changed. Please add it to your cart again by clicking here.', 'woocommerce' ), $product->get_name(), $product->get_permalink() ), 'error' ); + wc_add_notice( sprintf( __( '%1$s has been removed from your cart because it has since been modified. You can add it back to your cart here.', 'woocommerce' ), $product->get_name(), $product->get_permalink() ), 'notice' ); do_action( 'woocommerce_remove_cart_item_from_session', $key, $values ); } else { diff --git a/includes/class-wc-cart.php b/includes/class-wc-cart.php index dba7580ea00..a9f4cc2b76a 100644 --- a/includes/class-wc-cart.php +++ b/includes/class-wc-cart.php @@ -1112,6 +1112,7 @@ class WC_Cart extends WC_Legacy_Cart { 'variation' => $variation, 'quantity' => $quantity, 'data' => $product_data, + 'data_hash' => wc_get_cart_item_data_hash( $product_data ), ) ), $cart_item_key ); } diff --git a/includes/wc-cart-functions.php b/includes/wc-cart-functions.php index ff0d8fe84a9..3d3644aaaa9 100644 --- a/includes/wc-cart-functions.php +++ b/includes/wc-cart-functions.php @@ -481,3 +481,18 @@ function wc_shipping_methods_have_changed( $key, $package ) { WC()->session->set( 'previous_shipping_methods', $previous_shipping_methods ); return $new_rates !== $prev_rates; } + +/** + * Gets a hash of important product data that when changed should cause cart items to be invalidated. + * + * The woocommerce_cart_item_data_to_validate filter can be used to add custom properties. + * + * @param WC_Product $product Product object. + * @return string + */ +function wc_get_cart_item_data_hash( $product ) { + return md5( wp_json_encode( apply_filters( 'woocommerce_cart_item_data_to_validate', array( + 'price' => $product->get_type(), + 'attributes' => 'variation' === $product->get_type() ? $product->get_variation_attributes() : '', + ) ) ) ); +}