Repository: woocommerce/woocommerce (PHP)

Issue:
The endpoint `wp-json/wc/v3/products?orderby=price` returns incorrect ordering when grouped products are included.
## **Describe the bug**

Using the attached [sample_products.csv](https://github.com/user-attachments/files/21013364/sample_products.csv), fetching `wp-json/wc/v3/products?orderby=price` returns this JSON response:

[https://gist.github.com/gigitux/78e671dfb86aa3f532eb1159ed2c6e6f](https://gist.github.com/gigitux/78e671dfb86aa3f532eb1159ed2c6e6f)

Extracting prices with JSONPath `$[*].price` query, the results are:

```json
[
  "90",
  "55",
  "18",
  "45",
  "45",
  "42",
  "35",
  "25",
  "20",
  "15",
  "18",
  "18",
  "18",
  "18",
  "16",
  "15",
  "11.05",
  "2"
]
```

Notice the third element is "18", which corresponds to the grouped product Logo Collection. This indicates incorrect sorting in descending order.

When ordering ascending with `/wp-json/wc/v3/products?orderby=price&order=asc&per_page=100`, the order appears correct. The extracted prices are:

```json
[
  "2",
  "11.05",
  "15",
  "15",
  "16",
  "18",
  "18",
  "18",
  "18",
  "18",
  "20",
  "25",
  "35",
  "42",
  "45",
  "45",
  "55",
  "90"
]
```

## **To Reproduce**

Steps to reproduce the behavior:

1. Import the  [sample_products.csv](https://github.com/user-attachments/files/21013364/sample_products.csv).
2. [Configure the WooCommerce Rest API ](https://woocommerce.com/document/woocommerce-rest-api)
3. Fetch the `wp-json/wc/v3/products?orderby=price`
4. Notice the third element is "18", which corresponds to the grouped product Logo Collection.

## **Expected behavior**

I’m not certain what the expected behavior should be, but if the intention is to sort by the product’s minimum price, then the current placement of the grouped product is incorrect.



Fix (patch):
diff --git a/plugins/woocommerce/changelog/59586-wooplug-4867-the-endpoint-wp-jsonwcv3productsorderbyprice-returns b/plugins/woocommerce/changelog/59586-wooplug-4867-the-endpoint-wp-jsonwcv3productsorderbyprice-returns
new file mode 100644
index 0000000000000..61d13d00d777d
--- /dev/null
+++ b/plugins/woocommerce/changelog/59586-wooplug-4867-the-endpoint-wp-jsonwcv3productsorderbyprice-returns
@@ -0,0 +1,4 @@
+Significance: minor
+Type: add
+
+Add experimental REST API support for grouped product min/max prices
\ No newline at end of file
diff --git a/plugins/woocommerce/client/admin/config/core.json b/plugins/woocommerce/client/admin/config/core.json
index 6ac4628df9159..f938334629475 100644
--- a/plugins/woocommerce/client/admin/config/core.json
+++ b/plugins/woocommerce/client/admin/config/core.json
@@ -41,6 +41,7 @@
 		"launch-your-store": true,
 		"product-editor-template-system": false,
 		"use-wp-horizon": false,
-		"add-to-cart-with-options-stepper-layout": true
+		"add-to-cart-with-options-stepper-layout": true,
+		"experimental-wc-rest-api": false
 	}
 }
diff --git a/plugins/woocommerce/client/admin/config/development.json b/plugins/woocommerce/client/admin/config/development.json
index 9f312978e3528..11296f7f03284 100644
--- a/plugins/woocommerce/client/admin/config/development.json
+++ b/plugins/woocommerce/client/admin/config/development.json
@@ -41,6 +41,7 @@
 		"launch-your-store": true,
 		"product-editor-template-system": false,
 		"use-wp-horizon": false,
-		"add-to-cart-with-options-stepper-layout": true
+		"add-to-cart-with-options-stepper-layout": true,
+		"experimental-wc-rest-api": true
 	}
 }
diff --git a/plugins/woocommerce/includes/class-wc-product-grouped.php b/plugins/woocommerce/includes/class-wc-product-grouped.php
index c242ffade046c..3d08e6c3c919d 100644
--- a/plugins/woocommerce/includes/class-wc-product-grouped.php
+++ b/plugins/woocommerce/includes/class-wc-product-grouped.php
@@ -159,6 +159,40 @@ public function get_visible_children() {
 		return array_filter( $grouped_products, 'wc_products_array_filter_visible_grouped' );
 	}
 
+	/**
+	 * Get the minimum price from visible child products.
+	 *
+	 * @since 10.1.0
+	 * @return string Minimum price or empty string if no children
+	 */
+	public function get_min_price() {
+		$children = array_filter( array_map( 'wc_get_product', $this->get_children() ), 'wc_products_array_filter_visible_grouped' );
+		$prices   = array_map( 'wc_get_price_to_display', $children );
+
+		if ( empty( $prices ) ) {
+			return '';
+		}
+
+		return wc_format_decimal( min( $prices ) );
+	}
+
+	/**
+	 * Get the maximum price from visible child products.
+	 *
+	 * @since 10.1.0
+	 * @return string Maximum price or empty string if no children
+	 */
+	public function get_max_price() {
+		$children = array_filter( array_map( 'wc_get_product', $this->get_children() ), 'wc_products_array_filter_visible_grouped' );
+		$prices   = array_map( 'wc_get_price_to_display', $children );
+
+		if ( empty( $prices ) ) {
+			return '';
+		}
+
+		return wc_format_decimal( max( $prices ) );
+	}
+
 	/*
 	|--------------------------------------------------------------------------
 	| Setters
diff --git a/plugins/woocommerce/includes/rest-api/Controllers/Version3/class-wc-rest-products-controller.php b/plugins/woocommerce/includes/rest-api/Controllers/Version3/class-wc-rest-products-controller.php
index 7ec52106a0045..0b68ba5e95bf5 100644
--- a/plugins/woocommerce/includes/rest-api/Controllers/Version3/class-wc-rest-products-controller.php
+++ b/plugins/woocommerce/includes/rest-api/Controllers/Version3/class-wc-rest-products-controller.php
@@ -8,6 +8,7 @@
  * @since   2.6.0
  */
 
+use Automattic\WooCommerce\Admin\Features\Features;
 use Automattic\WooCommerce\Enums\ProductStatus;
 use Automattic\WooCommerce\Enums\ProductStockStatus;
 use Automattic\WooCommerce\Enums\ProductTaxStatus;
@@ -1764,6 +1765,20 @@ public function get_item_schema() {
 			$schema = $this->add_cogs_related_product_schema( $schema, false );
 		}
 
+		if ( Features::is_enabled( 'experimental-wc-rest-api' ) ) {
+			$schema['properties']['__experimental_min_price'] = array(
+				'description' => __( 'Product minimum price.', 'woocommerce' ),
+				'type'        => 'string',
+				'context'     => array( 'view', 'edit' ),
+			);
+
+			$schema['properties']['__experimental_max_price'] = array(
+				'description' => __( 'Product maximum price.', 'woocommerce' ),
+				'type'        => 'string',
+				'context'     => array( 'view', 'edit' ),
+			);
+		}
+
 		return $this->add_additional_fields_schema( $schema );
 	}
 
@@ -1958,6 +1973,14 @@ protected function get_product_data( $product, $context = 'view' ) {
 				$data['global_unique_id'] = $product->get_global_unique_id( $context );
 			}
 
+			if ( in_array( '__experimental_min_price', $fields, true ) ) {
+				$data['__experimental_min_price'] = method_exists( $product, 'get_min_price' ) ? $product->get_min_price() : '';
+			}
+
+			if ( in_array( '__experimental_max_price', $fields, true ) ) {
+				$data['__experimental_max_price'] = method_exists( $product, 'get_max_price' ) ? $product->get_max_price() : '';
+			}
+
 			$post_type_obj = get_post_type_object( $this->post_type );
 			if ( is_post_type_viewable( $post_type_obj ) && $post_type_obj->public ) {
 				$permalink_template_requested = in_array( 'permalink_template', $fields, true );