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() : '',
+ ) ) ) );
+}