Commit 0eab1bff0a6 for woocommerce

commit 0eab1bff0a60d4cb5e188772498043530421ff93
Author: Néstor Soriano <konamiman@konamiman.com>
Date:   Tue Jun 16 17:24:42 2026 +0200

    Add the woocommerce_variable_product_taxes_influence_price filter (#65394)

    Add woocommerce_variable_product_taxes_influence_price filter

    Lets extensions override whether displayed variable-product prices need
    separate cache entries for tax-inclusive and tax-exclusive lookups in
    WC_Product_Variable_Data_Store_CPT::read_price_data().

    By default the method returns true only when the product is taxable
    and tax rates are configured for the customer's location; otherwise
    it returns false and the opposite-hash optimization stores the same
    computed prices under both direction hashes in one pass. That's the
    right default for stock WooCommerce, but it breaks setups where
    another extension produces direction-dependent prices on top - for
    example, dynamic page caching keyed by country cookies for locations
    without configured tax rates. The previous workaround was to register
    a 0% rate for those locations just to keep the cache from collapsing.

diff --git a/plugins/woocommerce/changelog/add-filter-for-variable-product-taxes-influence-price b/plugins/woocommerce/changelog/add-filter-for-variable-product-taxes-influence-price
new file mode 100644
index 00000000000..49a7aade1bd
--- /dev/null
+++ b/plugins/woocommerce/changelog/add-filter-for-variable-product-taxes-influence-price
@@ -0,0 +1,4 @@
+Significance: minor
+Type: add
+
+Add the `woocommerce_variable_product_taxes_influence_price` filter.
\ No newline at end of file
diff --git a/plugins/woocommerce/includes/data-stores/class-wc-product-variable-data-store-cpt.php b/plugins/woocommerce/includes/data-stores/class-wc-product-variable-data-store-cpt.php
index bd271a223b4..780790a085d 100644
--- a/plugins/woocommerce/includes/data-stores/class-wc-product-variable-data-store-cpt.php
+++ b/plugins/woocommerce/includes/data-stores/class-wc-product-variable-data-store-cpt.php
@@ -569,21 +569,33 @@ class WC_Product_Variable_Data_Store_CPT extends WC_Product_Data_Store_CPT imple
 	 */
 	protected function taxes_influence_price( $product ): bool {
 		if ( ! $product->is_taxable() ) {
-			return false;
-		}
-
-		if ( empty( WC_Tax::get_rates( $product->get_tax_class() ) ) ) {
-			return false;
+			$taxes_influence_price = false;
+		} elseif ( empty( WC_Tax::get_rates( $product->get_tax_class() ) ) ) {
+			$taxes_influence_price = false;
+		} else {
+			// Taxes influence the price regardless of VAT exempt status. Even when a
+			// customer is VAT exempt, the displayed prices differ from non-exempt
+			// prices, so they need separate cache entries and the opposite_price_hash
+			// optimization should not apply. Returning false here was causing cached
+			// non-exempt prices to be served to VAT exempt customers.
+			$taxes_influence_price = true;
 		}

-		// Taxes influence the price regardless of VAT exempt status. Even when a
-		// customer is VAT exempt, the displayed prices differ from non-exempt
-		// prices, so they need separate cache entries and the opposite_price_hash
-		// optimization should not apply. Returning false here was causing cached
-		// non-exempt prices to be served to VAT exempt customers.
-		// See: https://github.com/woocommerce/woocommerce/issues/63716
-
-		return true;
+		/**
+		 * Filters whether taxes influence the displayed price of a variable product.
+		 *
+		 * Return `true` from this filter to force separate cache entries for the two
+		 * variants. This is needed when an extension produces displayed prices that
+		 * differ from raw prices independently of the standard tax calculation, for
+		 * example when computing per-country prices for locations that have no
+		 * configured tax rates.
+		 *
+		 * @param bool       $taxes_influence_price Default decision based on product taxability and configured tax rates.
+		 * @param WC_Product $product               The variable product being evaluated.
+		 *
+		 * @since 10.9.0
+		 */
+		return (bool) apply_filters( 'woocommerce_variable_product_taxes_influence_price', $taxes_influence_price, $product );
 	}

 	/**
diff --git a/plugins/woocommerce/tests/php/includes/data-stores/class-wc-product-variable-data-store-cpt-test.php b/plugins/woocommerce/tests/php/includes/data-stores/class-wc-product-variable-data-store-cpt-test.php
index 12eff53c3f4..ca578e846ac 100644
--- a/plugins/woocommerce/tests/php/includes/data-stores/class-wc-product-variable-data-store-cpt-test.php
+++ b/plugins/woocommerce/tests/php/includes/data-stores/class-wc-product-variable-data-store-cpt-test.php
@@ -540,6 +540,49 @@ class WC_Product_Variable_Data_Store_CPT_Test extends WC_Unit_Test_Case {
 		remove_filter( 'woocommerce_matched_rates', array( $this, '__return_rates' ) );
 	}

+	/**
+	 * @testdox The woocommerce_variable_product_taxes_influence_price filter can override the default decision.
+	 *
+	 * @testWith [true,  true,  true,  true,  true]
+	 *           [true,  true,  true,  false, false]
+	 *           [false, true,  false, true,  true]
+	 *           [true,  false, false, true,  true]
+	 *           [false, false, false, true,  true]
+	 *           [true,  false, false, false, false]
+	 *
+	 * @param bool $taxable_product  Product is taxable or not.
+	 * @param bool $tax_has_rates    Product tax has defined rates or not.
+	 * @param bool $expected_default Value the filter callback should receive as the default decision.
+	 * @param bool $filter_returns   Value the filter callback will return.
+	 * @param bool $expected         Expected return value from taxes_influence_price.
+	 */
+	public function test_taxes_influence_price_filter_overrides_default( bool $taxable_product, bool $tax_has_rates, bool $expected_default, bool $filter_returns, bool $expected ) {
+		add_filter( 'wc_tax_enabled', '__return_true' );
+		add_filter( 'woocommerce_product_is_taxable', $taxable_product ? '__return_true' : '__return_false' );
+		add_filter( 'woocommerce_matched_rates', $tax_has_rates ? array( $this, '__return_rates' ) : '__return_empty_array' );
+
+		$received_default = null;
+		$received_product = null;
+		$filter_callback  = function ( $default_value, $product ) use ( $filter_returns, &$received_default, &$received_product ) {
+			$received_default = $default_value;
+			$received_product = $product;
+			return $filter_returns;
+		};
+		add_filter( 'woocommerce_variable_product_taxes_influence_price', $filter_callback, 10, 2 );
+
+		$product             = WC_Helper_Product::create_variation_product();
+		$extended_data_store = $this->get_data_store_with_public_taxes_influence_price();
+
+		$this->assertSame( $expected, $extended_data_store->taxes_influence_price( $product ) );
+		$this->assertSame( $expected_default, $received_default, 'Filter callback should receive the default decision.' );
+		$this->assertSame( $product->get_id(), $received_product->get_id(), 'Filter callback should receive the product being evaluated.' );
+
+		remove_filter( 'woocommerce_variable_product_taxes_influence_price', $filter_callback, 10 );
+		remove_filter( 'wc_tax_enabled', '__return_true' );
+		remove_filter( 'woocommerce_product_is_taxable', $taxable_product ? '__return_true' : '__return_false' );
+		remove_filter( 'woocommerce_matched_rates', $tax_has_rates ? array( $this, '__return_rates' ) : '__return_empty_array' );
+	}
+
 	/**
 	 * @testdox read_prices does separate caching for prices for display and not for display when they are different.
 	 *