Commit c6dd7b44a6 for woocommerce
commit c6dd7b44a6f0d8476a543a88b5de449c11aebb95
Author: Taha Paksu <3295+tpaksu@users.noreply.github.com>
Date: Tue Feb 24 19:32:49 2026 +0300
Update store task list shipping plugin recommendations per country (#63379)
diff --git a/plugins/woocommerce/src/Admin/API/ShippingPartnerSuggestions.php b/plugins/woocommerce/src/Admin/API/ShippingPartnerSuggestions.php
index 0c5c8c2a95..ec56e77bcd 100644
--- a/plugins/woocommerce/src/Admin/API/ShippingPartnerSuggestions.php
+++ b/plugins/woocommerce/src/Admin/API/ShippingPartnerSuggestions.php
@@ -54,7 +54,6 @@ class ShippingPartnerSuggestions extends \WC_REST_Data_Controller {
'schema' => array( $this, 'get_suggestions_schema' ),
)
);
-
}
/**
@@ -154,40 +153,40 @@ class ShippingPartnerSuggestions extends \WC_REST_Data_Controller {
),
),
'properties' => array(
- 'name' => array(
+ 'name' => array(
'description' => __( 'Plugin name.', 'woocommerce' ),
'type' => 'string',
'required' => true,
'context' => array( 'view', 'edit' ),
'readonly' => true,
),
- 'slug' => array(
+ 'slug' => array(
'description' => __( 'Plugin slug used in https://wordpress.org/plugins/{slug}.', 'woocommerce' ),
'type' => 'string',
'context' => array( 'view', 'edit' ),
'readonly' => true,
),
- 'layout_row' => $layout_def,
- 'layout_column' => $layout_def,
- 'description' => array(
+ 'layout_row' => $layout_def,
+ 'layout_column' => $layout_def,
+ 'description' => array(
'description' => __( 'Description', 'woocommerce' ),
'type' => 'string',
'context' => array( 'view', 'edit' ),
'readonly' => true,
),
- 'learn_more_link' => array(
+ 'learn_more_link' => array(
'description' => __( 'Learn more link .', 'woocommerce' ),
'type' => 'string',
'context' => array( 'view', 'edit' ),
'readonly' => true,
),
- 'is_visible' => array(
+ 'is_visible' => array(
'description' => __( 'Suggestion visibility.', 'woocommerce' ),
'type' => 'boolean',
'context' => array( 'view', 'edit' ),
'readonly' => true,
),
- 'available_layouts' => array(
+ 'available_layouts' => array(
'description' => __( 'Available layouts -- single, dual, or both', 'woocommerce' ),
'type' => 'array',
'items' => array(
@@ -197,6 +196,15 @@ class ShippingPartnerSuggestions extends \WC_REST_Data_Controller {
'context' => array( 'view', 'edit' ),
'readonly' => true,
),
+ 'countries_where_primary' => array(
+ 'description' => __( 'Countries where this partner should appear first.', 'woocommerce' ),
+ 'type' => 'array',
+ 'items' => array(
+ 'type' => 'string',
+ ),
+ 'context' => array( 'view', 'edit' ),
+ 'readonly' => true,
+ ),
),
);
diff --git a/plugins/woocommerce/src/Admin/Features/OnboardingTasks/Tasks/Shipping.php b/plugins/woocommerce/src/Admin/Features/OnboardingTasks/Tasks/Shipping.php
index f06534b87c..6b2a501118 100644
--- a/plugins/woocommerce/src/Admin/Features/OnboardingTasks/Tasks/Shipping.php
+++ b/plugins/woocommerce/src/Admin/Features/OnboardingTasks/Tasks/Shipping.php
@@ -116,7 +116,7 @@ class Shipping extends Task {
return true;
}
- return in_array( $store_country, array( 'CA', 'AU', 'NZ', 'SG', 'HK', 'GB', 'ES', 'IT', 'DE', 'FR', 'CL', 'AR', 'PE', 'BR', 'UY', 'GT', 'NL', 'AT', 'BE' ), true );
+ return in_array( $store_country, array( 'US', 'CA', 'AU', 'NZ', 'SG', 'HK', 'GB', 'ES', 'IT', 'DE', 'FR', 'MX', 'CO', 'CL', 'AR', 'PE', 'BR', 'UY', 'GT', 'NL', 'AT', 'BE' ), true );
}
return self::has_physical_products();
diff --git a/plugins/woocommerce/src/Admin/Features/ShippingPartnerSuggestions/DefaultShippingPartners.php b/plugins/woocommerce/src/Admin/Features/ShippingPartnerSuggestions/DefaultShippingPartners.php
index 39ddc01abd..ec803289ca 100644
--- a/plugins/woocommerce/src/Admin/Features/ShippingPartnerSuggestions/DefaultShippingPartners.php
+++ b/plugins/woocommerce/src/Admin/Features/ShippingPartnerSuggestions/DefaultShippingPartners.php
@@ -45,15 +45,15 @@ class DefaultShippingPartners {
return array(
array(
- 'id' => 'woocommerce-shipstation-integration',
- 'name' => 'ShipStation',
- 'slug' => 'woocommerce-shipstation-integration',
- 'description' => __( 'Powerful yet easy-to-use solution:', 'woocommerce' ),
- 'layout_column' => array(
+ 'id' => 'woocommerce-shipstation-integration',
+ 'name' => 'ShipStation',
+ 'slug' => 'woocommerce-shipstation-integration',
+ 'description' => __( 'Powerful yet easy-to-use solution:', 'woocommerce' ),
+ 'layout_column' => array(
'image' => $asset_base_url . 'shipstation-column.svg',
'features' => $column_layout_features,
),
- 'layout_row' => array(
+ 'layout_row' => array(
'image' => $asset_base_url . 'shipstation-row.svg',
'features' => array(
array(
@@ -84,52 +84,55 @@ class DefaultShippingPartners {
),
),
),
- 'learn_more_link' => 'https://wordpress.org/plugins/woocommerce-shipstation-integration/',
- 'is_visible' => array(
- self::get_rules_for_countries( array( 'AU', 'CA', 'GB' ) ),
+ 'learn_more_link' => 'https://wordpress.org/plugins/woocommerce-shipstation-integration/',
+ 'is_visible' => array(
+ self::get_rules_for_countries( array( 'US', 'AU', 'NZ', 'CA', 'GB' ) ),
),
- 'available_layouts' => array( 'row', 'column' ),
+ 'available_layouts' => array( 'row', 'column' ),
+ 'countries_where_primary' => array( 'NZ', 'CA', 'GB' ),
),
array(
- 'id' => 'skydropx-cotizador-y-envios',
- 'name' => 'Skydropx',
- 'slug' => 'skydropx-cotizador-y-envios',
- 'layout_column' => array(
+ 'id' => 'skydropx-cotizador-y-envios',
+ 'name' => 'Skydropx',
+ 'slug' => 'skydropx-cotizador-y-envios',
+ 'layout_column' => array(
'image' => $asset_base_url . 'skydropx-column.svg',
'features' => $column_layout_features,
),
- 'description' => '',
- 'learn_more_link' => 'https://wordpress.org/plugins/skydropx-cotizador-y-envios/',
- 'is_visible' => array(
+ 'description' => '',
+ 'learn_more_link' => 'https://wordpress.org/plugins/skydropx-cotizador-y-envios/',
+ 'is_visible' => array(
self::get_rules_for_countries( array() ), // No countries eligible for SkydropX promotion at this time.
),
- 'available_layouts' => array( 'column' ),
+ 'available_layouts' => array( 'column' ),
+ 'countries_where_primary' => array(),
),
array(
- 'id' => 'envia',
- 'name' => 'Envia',
- 'slug' => '',
- 'description' => '',
- 'layout_column' => array(
+ 'id' => 'envia',
+ 'name' => 'Envia',
+ 'slug' => '',
+ 'description' => '',
+ 'layout_column' => array(
'image' => $asset_base_url . 'envia-column.svg',
'features' => $column_layout_features,
),
- 'learn_more_link' => 'https://woocommerce.com/products/envia-shipping-and-fulfillment/',
- 'is_visible' => array(
- self::get_rules_for_countries( array( 'CL', 'AR', 'PE', 'BR', 'UY', 'GT' ) ),
+ 'learn_more_link' => 'https://woocommerce.com/products/envia-shipping-and-fulfillment/',
+ 'is_visible' => array(
+ self::get_rules_for_countries( array( 'MX', 'CO', 'CL', 'AR', 'PE', 'BR', 'UY', 'GT' ) ),
),
- 'available_layouts' => array( 'column' ),
+ 'available_layouts' => array( 'column' ),
+ 'countries_where_primary' => array( 'MX', 'CO', 'CL', 'AR', 'PE', 'BR', 'UY', 'GT' ),
),
array(
- 'id' => 'easyship-woocommerce-shipping-rates',
- 'name' => 'Easyship',
- 'slug' => 'easyship-woocommerce-shipping-rates',
- 'description' => __( 'Simplified shipping with: ', 'woocommerce' ),
- 'layout_column' => array(
+ 'id' => 'easyship-woocommerce-shipping-rates',
+ 'name' => 'Easyship',
+ 'slug' => 'easyship-woocommerce-shipping-rates',
+ 'description' => __( 'Simplified shipping with: ', 'woocommerce' ),
+ 'layout_column' => array(
'image' => $asset_base_url . 'easyship-column.svg',
'features' => $column_layout_features,
),
- 'layout_row' => array(
+ 'layout_row' => array(
'image' => $asset_base_url . 'easyship-row.svg',
'features' => array(
array(
@@ -157,22 +160,23 @@ class DefaultShippingPartners {
),
),
),
- 'learn_more_link' => 'https://woocommerce.com/products/easyship-shipping-rates/',
- 'is_visible' => array(
+ 'learn_more_link' => 'https://woocommerce.com/products/easyship-shipping-rates/',
+ 'is_visible' => array(
self::get_rules_for_countries( array( 'SG', 'HK', 'AU', 'NZ' ) ),
),
- 'available_layouts' => array( 'row', 'column' ),
+ 'available_layouts' => array( 'row', 'column' ),
+ 'countries_where_primary' => array( 'SG', 'HK', 'AU' ),
),
array(
- 'id' => 'packlink-pro-shipping',
- 'name' => 'Packlink',
- 'slug' => 'packlink-pro-shipping',
- 'description' => __( 'Optimize your full shipping process:', 'woocommerce' ),
- 'layout_column' => array(
+ 'id' => 'packlink-pro-shipping',
+ 'name' => 'Packlink',
+ 'slug' => 'packlink-pro-shipping',
+ 'description' => __( 'Optimize your full shipping process:', 'woocommerce' ),
+ 'layout_column' => array(
'image' => $asset_base_url . 'packlink-column.svg',
'features' => $column_layout_features,
),
- 'layout_row' => array(
+ 'layout_row' => array(
'image' => $asset_base_url . 'packlink-row.svg',
'features' => array(
array(
@@ -206,18 +210,19 @@ class DefaultShippingPartners {
),
),
),
- 'learn_more_link' => 'https://wordpress.org/plugins/packlink-pro-shipping/',
- 'is_visible' => array(
- self::get_rules_for_countries( array( 'FR', 'DE', 'ES', 'IT' ) ),
+ 'learn_more_link' => 'https://wordpress.org/plugins/packlink-pro-shipping/',
+ 'is_visible' => array(
+ self::get_rules_for_countries( array( 'FR', 'DE', 'ES', 'IT', 'NL', 'AT', 'BE' ) ),
),
- 'available_layouts' => array( 'row', 'column' ),
+ 'available_layouts' => array( 'row', 'column' ),
+ 'countries_where_primary' => array( 'FR', 'DE', 'ES', 'IT', 'NL', 'AT', 'BE' ),
),
array(
- 'id' => 'woocommerce-shipping',
- 'name' => 'WooCommerce Shipping',
- 'slug' => 'woocommerce-shipping',
- 'description' => __( 'Save time and money by printing your shipping labels right from your computer with WooCommerce Shipping. Try WooCommerce Shipping for free.', 'woocommerce' ),
- 'layout_column' => array(
+ 'id' => 'woocommerce-shipping',
+ 'name' => 'WooCommerce Shipping',
+ 'slug' => 'woocommerce-shipping',
+ 'description' => __( 'Save time and money by printing your shipping labels right from your computer with WooCommerce Shipping. Try WooCommerce Shipping for free.', 'woocommerce' ),
+ 'layout_column' => array(
'image' => $asset_base_url . 'wcs-column.svg',
'features' => array(
array(
@@ -237,8 +242,8 @@ class DefaultShippingPartners {
),
),
),
- 'learn_more_link' => 'https://woocommerce.com/products/shipping/',
- 'is_visible' => array(
+ 'learn_more_link' => 'https://woocommerce.com/products/shipping/',
+ 'is_visible' => array(
self::get_rules_for_countries( array( 'US' ) ),
(object) array(
'type' => 'not',
@@ -250,7 +255,8 @@ class DefaultShippingPartners {
),
),
),
- 'available_layouts' => array( 'column' ),
+ 'available_layouts' => array( 'column' ),
+ 'countries_where_primary' => array( 'US' ),
),
);
}
diff --git a/plugins/woocommerce/src/Admin/Features/ShippingPartnerSuggestions/ShippingPartnerSuggestions.php b/plugins/woocommerce/src/Admin/Features/ShippingPartnerSuggestions/ShippingPartnerSuggestions.php
index d681d0fb36..faa32cecd7 100644
--- a/plugins/woocommerce/src/Admin/Features/ShippingPartnerSuggestions/ShippingPartnerSuggestions.php
+++ b/plugins/woocommerce/src/Admin/Features/ShippingPartnerSuggestions/ShippingPartnerSuggestions.php
@@ -37,7 +37,44 @@ class ShippingPartnerSuggestions extends RemoteSpecsEngine {
ShippingPartnerSuggestionsDataSourcePoller::get_instance()->set_specs_transient( array( $locale => $specs_to_save ), 3 * HOUR_IN_SECONDS );
}
- return $specs_to_return;
+ return self::sort_by_primary( $specs_to_return );
+ }
+
+ /**
+ * Sort suggestions so that partners whose countries_where_primary list contains the
+ * current store country appear first.
+ *
+ * @param array $suggestions Suggestions to sort.
+ * @return array Sorted suggestions.
+ */
+ private static function sort_by_primary( array $suggestions ) {
+ $country = wc_get_base_location()['country'] ?? '';
+
+ // Attach original indices to preserve input order for equal-priority items
+ // (usort is not stable on PHP < 8.0).
+ $indexed = array_map(
+ function ( $item, $idx ) {
+ return array( $item, $idx );
+ },
+ $suggestions,
+ array_keys( $suggestions )
+ );
+
+ usort(
+ $indexed,
+ function ( $a, $b ) use ( $country ) {
+ $a_primary = isset( $a[0]->countries_where_primary ) && is_array( $a[0]->countries_where_primary ) && in_array( $country, $a[0]->countries_where_primary, true );
+ $b_primary = isset( $b[0]->countries_where_primary ) && is_array( $b[0]->countries_where_primary ) && in_array( $country, $b[0]->countries_where_primary, true );
+
+ if ( $a_primary === $b_primary ) {
+ return $a[1] - $b[1];
+ }
+
+ return $a_primary ? -1 : 1;
+ }
+ );
+
+ return array_column( $indexed, 0 );
}
/**
diff --git a/plugins/woocommerce/tests/php/src/Admin/Features/ShippingPartnerSuggestions/DefaultShippingPartnersTest.php b/plugins/woocommerce/tests/php/src/Admin/Features/ShippingPartnerSuggestions/DefaultShippingPartnersTest.php
index ceae6b0d21..ffb9aa8b93 100644
--- a/plugins/woocommerce/tests/php/src/Admin/Features/ShippingPartnerSuggestions/DefaultShippingPartnersTest.php
+++ b/plugins/woocommerce/tests/php/src/Admin/Features/ShippingPartnerSuggestions/DefaultShippingPartnersTest.php
@@ -57,8 +57,14 @@ class DefaultShippingPartnersTest extends WC_Unit_Test_Case {
$results = EvaluateSuggestion::evaluate_specs( $specs );
$this->assertCount( 0, $results['errors'] );
- $this->assertCount( 1, $results['suggestions'] );
- $this->assertEquals( 'woocommerce-shipping', $results['suggestions'][0]->id );
+
+ $ids = array_map(
+ function ( $s ) {
+ return $s->id;
+ },
+ $results['suggestions']
+ );
+ $this->assertContains( 'woocommerce-shipping', $ids );
}
/**
@@ -96,7 +102,14 @@ class DefaultShippingPartnersTest extends WC_Unit_Test_Case {
// Assert.
$this->assertCount( 0, $results['errors'] );
- $this->assertCount( 0, $results['suggestions'] );
+
+ $ids = array_map(
+ function ( $s ) {
+ return $s->id;
+ },
+ $results['suggestions']
+ );
+ $this->assertNotContains( 'woocommerce-shipping', $ids );
// Clean up.
self::rmdir( dirname( $shipping_plugin_file_path ) );
diff --git a/plugins/woocommerce/tests/php/src/Internal/Admin/ShippingPartnerSuggestions/ShippingPartnerSuggestionsTest.php b/plugins/woocommerce/tests/php/src/Internal/Admin/ShippingPartnerSuggestions/ShippingPartnerSuggestionsTest.php
index c9a3e01cab..f8e06793ee 100644
--- a/plugins/woocommerce/tests/php/src/Internal/Admin/ShippingPartnerSuggestions/ShippingPartnerSuggestionsTest.php
+++ b/plugins/woocommerce/tests/php/src/Internal/Admin/ShippingPartnerSuggestions/ShippingPartnerSuggestionsTest.php
@@ -175,6 +175,80 @@ class ShippingPartnerSuggestionsTest extends WC_Unit_Test_Case {
remove_filter( 'woocommerce_admin_remote_specs_evaluator_should_log', '__return_true' );
}
+ /**
+ * Test that primary suggestions are sorted before non-primary ones.
+ */
+ public function test_sort_primary_suggestions_first() {
+ update_option( 'woocommerce_default_country', 'US' );
+
+ $specs = array(
+ (object) array(
+ 'id' => 'non-primary',
+ 'is_visible' => true,
+ ),
+ (object) array(
+ 'id' => 'primary-partner',
+ 'is_visible' => true,
+ 'countries_where_primary' => array( 'US' ),
+ ),
+ (object) array(
+ 'id' => 'another-non-primary',
+ 'is_visible' => true,
+ ),
+ );
+
+ $suggestions = ShippingPartnerSuggestions::get_suggestions( $specs );
+ $ids = array_map(
+ function ( $s ) {
+ return $s->id;
+ },
+ $suggestions
+ );
+
+ $this->assertSame( 'primary-partner', $ids[0] );
+ }
+
+ /**
+ * Test that suggestions with equal primary status preserve their original order (stable sort).
+ */
+ public function test_sort_preserves_order_for_equal_priority() {
+ update_option( 'woocommerce_default_country', 'US' );
+
+ $specs = array(
+ (object) array(
+ 'id' => 'primary-a',
+ 'is_visible' => true,
+ 'countries_where_primary' => array( 'US' ),
+ ),
+ (object) array(
+ 'id' => 'primary-b',
+ 'is_visible' => true,
+ 'countries_where_primary' => array( 'US' ),
+ ),
+ (object) array(
+ 'id' => 'non-primary-a',
+ 'is_visible' => true,
+ ),
+ (object) array(
+ 'id' => 'non-primary-b',
+ 'is_visible' => true,
+ ),
+ );
+
+ $suggestions = ShippingPartnerSuggestions::get_suggestions( $specs );
+ $ids = array_map(
+ function ( $s ) {
+ return $s->id;
+ },
+ $suggestions
+ );
+
+ $this->assertSame(
+ array( 'primary-a', 'primary-b', 'non-primary-a', 'non-primary-b' ),
+ $ids
+ );
+ }
+
/**
* Overrides the WC logger.
*