Commit 27917d17631 for woocommerce

commit 27917d17631bff4771e9a2aee6943b8600d88ea8
Author: Ann <annchichi@users.noreply.github.com>
Date:   Wed Jul 15 22:43:11 2026 +0800

    Fix duplicate shipping marketplace link (#66110)

    * Fix duplicate shipping marketplace link

    * Add changelog entry for duplicate shipping link fix

    * Move shipping Marketplace fallback into recommendations

    * Use dismiss hook for shipping recommendations

    * Keep ShippingTour visibility logic unchanged

    * Restore explicit ShippingTour fallback condition

    * Render shipping fallback inside dismissed list

    * Fix hidden shipping recommendations fallback

    * Add spacing to shipping fallback link

    * Align shipping fallback background

    * Align shipping fallback row background

    * Fix shipping fallback selector lint

    * Keep shipping fallback on settings background

    * Keep shipping fallback spacing on grey background

    * Tighten shipping fallback review coverage

    * Fix shipping recommendations fallback display

    * Fix shipping recommendations refresh flicker

diff --git a/plugins/woocommerce/changelog/fix-duplicate-shipping-marketplace-link b/plugins/woocommerce/changelog/fix-duplicate-shipping-marketplace-link
new file mode 100644
index 00000000000..334c0bddf4b
--- /dev/null
+++ b/plugins/woocommerce/changelog/fix-duplicate-shipping-marketplace-link
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Remove duplicate Shipping settings Marketplace link when shipping recommendations are shown.
diff --git a/plugins/woocommerce/client/admin/client/hooks/test/use-endpoint-dismiss.test.ts b/plugins/woocommerce/client/admin/client/hooks/test/use-endpoint-dismiss.test.ts
index f112e298fa2..f1976b6b1d7 100644
--- a/plugins/woocommerce/client/admin/client/hooks/test/use-endpoint-dismiss.test.ts
+++ b/plugins/woocommerce/client/admin/client/hooks/test/use-endpoint-dismiss.test.ts
@@ -28,6 +28,7 @@ describe( 'useEndpointDismiss', () => {
 		const { result } = renderHook( () => useEndpointDismiss( PATH, true ) );

 		expect( result.current.isDismissed ).toBe( true );
+		expect( result.current.hasResolved ).toBe( true );
 	} );

 	it( 'optimistically dismisses and POSTs to the endpoint', async () => {
diff --git a/plugins/woocommerce/client/admin/client/hooks/test/use-option-dismiss.test.ts b/plugins/woocommerce/client/admin/client/hooks/test/use-option-dismiss.test.ts
index 59338abe850..cb55bd14c03 100644
--- a/plugins/woocommerce/client/admin/client/hooks/test/use-option-dismiss.test.ts
+++ b/plugins/woocommerce/client/admin/client/hooks/test/use-option-dismiss.test.ts
@@ -46,6 +46,7 @@ describe( 'useOptionDismiss', () => {
 		const { result } = renderHook( () => useOptionDismiss( OPTION_NAME ) );

 		expect( result.current.isDismissed ).toBe( true );
+		expect( result.current.hasResolved ).toBe( false );
 	} );

 	it( 'is dismissed when the resolved option is "yes"', () => {
@@ -54,6 +55,7 @@ describe( 'useOptionDismiss', () => {
 		const { result } = renderHook( () => useOptionDismiss( OPTION_NAME ) );

 		expect( result.current.isDismissed ).toBe( true );
+		expect( result.current.hasResolved ).toBe( true );
 	} );

 	it( 'is not dismissed when the resolved option is not "yes"', () => {
@@ -62,6 +64,7 @@ describe( 'useOptionDismiss', () => {
 		const { result } = renderHook( () => useOptionDismiss( OPTION_NAME ) );

 		expect( result.current.isDismissed ).toBe( false );
+		expect( result.current.hasResolved ).toBe( true );
 	} );

 	it( 'persists the dismissal through updateOptions', () => {
diff --git a/plugins/woocommerce/client/admin/client/hooks/use-endpoint-dismiss.ts b/plugins/woocommerce/client/admin/client/hooks/use-endpoint-dismiss.ts
index 54d6b5c6cd4..55dd73489d0 100644
--- a/plugins/woocommerce/client/admin/client/hooks/use-endpoint-dismiss.ts
+++ b/plugins/woocommerce/client/admin/client/hooks/use-endpoint-dismiss.ts
@@ -43,5 +43,5 @@ export const useEndpointDismiss = (
 		} );
 	};

-	return { isDismissed, onDismiss };
+	return { isDismissed, hasResolved: true, onDismiss };
 };
diff --git a/plugins/woocommerce/client/admin/client/hooks/use-option-dismiss.ts b/plugins/woocommerce/client/admin/client/hooks/use-option-dismiss.ts
index 8880370d859..b8e56592de2 100644
--- a/plugins/woocommerce/client/admin/client/hooks/use-option-dismiss.ts
+++ b/plugins/woocommerce/client/admin/client/hooks/use-option-dismiss.ts
@@ -6,6 +6,7 @@ import { optionsStore } from '@woocommerce/data';

 export type DismissState = {
 	isDismissed: boolean;
+	hasResolved: boolean;
 	onDismiss: () => void;
 };

@@ -20,7 +21,7 @@ export type DismissState = {
  * @return The current dismissal state and a callback to dismiss.
  */
 export const useOptionDismiss = ( optionName: string ): DismissState => {
-	const isDismissed = useSelect(
+	const { isDismissed, hasResolved } = useSelect(
 		( select ) => {
 			const { getOption, hasFinishedResolution } = select( optionsStore );

@@ -31,13 +32,16 @@ export const useOptionDismiss = ( optionName: string ): DismissState => {
 			// the card would stay hidden forever.
 			const value = getOption( optionName );

-			const hasResolved = hasFinishedResolution( 'getOption', [
+			const optionHasResolved = hasFinishedResolution( 'getOption', [
 				optionName,
 			] );

 			// Treat "not yet resolved" as dismissed so the card does not flash
 			// before the option value is known.
-			return ! hasResolved || value === 'yes';
+			return {
+				isDismissed: ! optionHasResolved || value === 'yes',
+				hasResolved: optionHasResolved,
+			};
 		},
 		[ optionName ]
 	);
@@ -48,5 +52,5 @@ export const useOptionDismiss = ( optionName: string ): DismissState => {
 		updateOptions( { [ optionName ]: 'yes' } );
 	};

-	return { isDismissed, onDismiss };
+	return { isDismissed, hasResolved, onDismiss };
 };
diff --git a/plugins/woocommerce/client/admin/client/shipping/shipping-recommendations-utils.tsx b/plugins/woocommerce/client/admin/client/shipping/shipping-recommendations-utils.tsx
index c1a7ab75b05..84633f96b50 100644
--- a/plugins/woocommerce/client/admin/client/shipping/shipping-recommendations-utils.tsx
+++ b/plugins/woocommerce/client/admin/client/shipping/shipping-recommendations-utils.tsx
@@ -18,7 +18,17 @@ import {
 	DismissableListHeading,
 } from '../settings-recommendations/dismissable-list';
 import { TrackedLink } from '~/components/tracked-link/tracked-link';
-import { useOptionDismiss } from '~/hooks/use-option-dismiss';
+import { type DismissState } from '~/hooks/use-option-dismiss';
+
+export const SHIPPING_RECOMMENDATIONS_DISMISS_OPTION =
+	'woocommerce_settings_shipping_recommendations_hidden';
+
+type ShippingRecommendationsMarketplaceLinkProps = {
+	textProps?: {
+		as?: string;
+		className?: string;
+	};
+};

 export const useInstallPlugin = () => {
 	const [ pluginsBeingSetup, setPluginsBeingSetup ] = useState<
@@ -68,14 +78,32 @@ export const useInstallPlugin = () => {
 	return [ pluginsBeingSetup, handleInstall, handleActivate ] as const;
 };

+export const ShippingRecommendationsMarketplaceLink = ( {
+	textProps,
+}: ShippingRecommendationsMarketplaceLinkProps ) => (
+	<TrackedLink
+		textProps={ textProps }
+		message={ __(
+			// translators: {{Link}} is a placeholder for a html element.
+			'Visit {{Link}}the WooCommerce Marketplace{{/Link}} to find more shipping, delivery, and fulfillment solutions.',
+			'woocommerce'
+		) }
+		targetUrl={ getAdminLink(
+			'admin.php?page=wc-admin&tab=extensions&path=/extensions&category=shipping-delivery-and-fulfillment'
+		) }
+		linkType="wc-admin"
+		eventName="settings_shipping_recommendation_visit_marketplace_click"
+	/>
+);
+
 export const ShippingRecommendationsList = ( {
 	children,
+	dismissState,
 }: {
 	children: React.ReactNode;
+	dismissState: DismissState;
 } ) => {
-	const { isDismissed, onDismiss } = useOptionDismiss(
-		'woocommerce_settings_shipping_recommendations_hidden'
-	);
+	const { isDismissed, onDismiss } = dismissState;

 	return (
 		<DismissableList
@@ -105,18 +133,7 @@ export const ShippingRecommendationsList = ( {
 				) ) }
 			</ul>
 			<CardFooter>
-				<TrackedLink
-					message={ __(
-						// translators: {{Link}} is a placeholder for a html element.
-						'Visit {{Link}}the WooCommerce Marketplace{{/Link}} to find more shipping, delivery, and fulfillment solutions.',
-						'woocommerce'
-					) }
-					targetUrl={ getAdminLink(
-						'admin.php?page=wc-admin&tab=extensions&path=/extensions&category=shipping-delivery-and-fulfillment'
-					) }
-					linkType="wc-admin"
-					eventName="settings_shipping_recommendation_visit_marketplace_click"
-				/>
+				<ShippingRecommendationsMarketplaceLink />
 			</CardFooter>
 		</DismissableList>
 	);
diff --git a/plugins/woocommerce/client/admin/client/shipping/shipping-recommendations.scss b/plugins/woocommerce/client/admin/client/shipping/shipping-recommendations.scss
index 7fd64251b55..d85085d8cdd 100644
--- a/plugins/woocommerce/client/admin/client/shipping/shipping-recommendations.scss
+++ b/plugins/woocommerce/client/admin/client/shipping/shipping-recommendations.scss
@@ -41,3 +41,9 @@
 		max-width: 749px;
 	}
 }
+
+.woocommerce-recommended-shipping__fallback-link {
+	margin: 0;
+	padding: 16px 0 60px;
+	color: $gray-700;
+}
diff --git a/plugins/woocommerce/client/admin/client/shipping/shipping-recommendations.tsx b/plugins/woocommerce/client/admin/client/shipping/shipping-recommendations.tsx
index abc40fbedc3..770fffdf5a9 100644
--- a/plugins/woocommerce/client/admin/client/shipping/shipping-recommendations.tsx
+++ b/plugins/woocommerce/client/admin/client/shipping/shipping-recommendations.tsx
@@ -14,11 +14,14 @@ import { recordEvent } from '@woocommerce/tracks';
  * Internal dependencies
  */
 import { getCountryCode } from '~/dashboard/utils';
+import { useOptionDismiss } from '~/hooks/use-option-dismiss';
 import WooCommerceShippingItem from './woocommerce-shipping-item';
 import ShipStationItem from './shipstation-item';
 import PacklinkItem from './packlink-item';
 import {
+	SHIPPING_RECOMMENDATIONS_DISMISS_OPTION,
 	ShippingRecommendationsList,
+	ShippingRecommendationsMarketplaceLink,
 	useInstallPlugin,
 } from './shipping-recommendations-utils';
 import './shipping-recommendations.scss';
@@ -52,20 +55,35 @@ const EXTENSION_PLUGIN_SLUGS: Record< ExtensionId, string > = {
 const ShippingRecommendations = () => {
 	const [ pluginsBeingSetup, handleInstall, handleActivate ] =
 		useInstallPlugin();
+	const recommendationsDismissState = useOptionDismiss(
+		SHIPPING_RECOMMENDATIONS_DISMISS_OPTION
+	);
+	const {
+		isDismissed: isRecommendationsHidden,
+		hasResolved: hasRecommendationsDismissResolved,
+	} = recommendationsDismissState;

 	const {
 		installedPlugins,
 		activePlugins,
 		countryCode,
 		isSellingDigitalProductsOnly,
+		hasRecommendationEligibilityResolved,
 	} = useSelect( ( select ) => {
-		const settings = select( settingsStore ).getSettings( 'general' );
+		const {
+			getSettings,
+			hasFinishedResolution: hasSettingsFinishedResolution,
+		} = select( settingsStore );
+		const {
+			getProfileItems,
+			hasFinishedResolution: hasOnboardingFinishedResolution,
+		} = select( onboardingStore );
+		const settings = getSettings( 'general' );

 		const { getInstalledPlugins, getActivePlugins } =
 			select( pluginsStore );

-		const profileItems =
-			select( onboardingStore ).getProfileItems()?.product_types;
+		const profileItems = getProfileItems()?.product_types;

 		return {
 			installedPlugins: getInstalledPlugins(),
@@ -75,6 +93,9 @@ const ShippingRecommendations = () => {
 			),
 			isSellingDigitalProductsOnly:
 				profileItems?.length === 1 && profileItems[ 0 ] === 'downloads',
+			hasRecommendationEligibilityResolved:
+				hasSettingsFinishedResolution( 'getSettings', [ 'general' ] ) &&
+				hasOnboardingFinishedResolution( 'getProfileItems', [] ),
 		};
 	}, [] );

@@ -92,13 +113,40 @@ const ShippingRecommendations = () => {
 		? []
 		: extensionsForCountry;

+	const hasVisibleExtensions = visibleExtensions.length > 0;
+	// Country and product type both determine which final state should render.
+	// Wait for them to settle so the fallback and recommendations do not swap.
+	const shouldWaitForRecommendationData =
+		! hasRecommendationEligibilityResolved ||
+		( ! hasRecommendationsDismissResolved && hasVisibleExtensions );
+	const shouldShowRecommendationsFallback =
+		( hasRecommendationsDismissResolved && isRecommendationsHidden ) ||
+		! hasVisibleExtensions ||
+		isSellingDigitalProductsOnly;
+	const shouldTrackRecommendationsImpression =
+		hasRecommendationEligibilityResolved &&
+		hasRecommendationsDismissResolved &&
+		! isRecommendationsHidden &&
+		hasVisibleExtensions;
+
 	const visiblePluginSlugs = visibleExtensions
 		.map( ( ext ) => EXTENSION_PLUGIN_SLUGS[ ext ] )
 		.join( ',' );
+	const marketplaceFallbackLink = (
+		<ShippingRecommendationsMarketplaceLink
+			textProps={ {
+				as: 'p',
+				className: 'woocommerce-recommended-shipping__fallback-link',
+			} }
+		/>
+	);

 	const impressionFired = useRef( false );
 	useEffect( () => {
-		if ( visibleExtensions.length > 0 && ! impressionFired.current ) {
+		if (
+			shouldTrackRecommendationsImpression &&
+			! impressionFired.current
+		) {
 			recordEvent( 'shipping_partner_impression', {
 				context: 'settings',
 				country: normalizedCountry,
@@ -106,75 +154,94 @@ const ShippingRecommendations = () => {
 			} );
 			impressionFired.current = true;
 		}
-	}, [ visibleExtensions.length, normalizedCountry, visiblePluginSlugs ] );
-
-	if ( isSellingDigitalProductsOnly ) {
-		return <ShippingTour showShippingRecommendationsStep={ false } />;
+	}, [
+		shouldTrackRecommendationsImpression,
+		normalizedCountry,
+		visiblePluginSlugs,
+	] );
+
+	if ( shouldWaitForRecommendationData ) {
+		return (
+			<>
+				<ShippingTour showShippingRecommendationsStep={ false } />
+			</>
+		);
 	}

-	if ( visibleExtensions.length === 0 ) {
-		return <ShippingTour showShippingRecommendationsStep={ false } />;
+	if ( shouldShowRecommendationsFallback ) {
+		return (
+			<>
+				<ShippingTour showShippingRecommendationsStep={ false } />
+				{ marketplaceFallbackLink }
+			</>
+		);
 	}

 	return (
-		<div style={ { paddingBottom: 60 } }>
-			<ShippingTour showShippingRecommendationsStep={ true } />
-			<ShippingRecommendationsList>
-				{ visibleExtensions.map( ( ext ) => {
-					const isPluginInstalled = installedPlugins.includes(
-						EXTENSION_PLUGIN_SLUGS[ ext ]
-					);
-					const isPluginActive = activePlugins.includes(
-						EXTENSION_PLUGIN_SLUGS[ ext ]
-					);
-					const trackingProps = {
-						context: 'settings' as const,
-						country: normalizedCountry,
-						plugins: visiblePluginSlugs,
-					};
-					switch ( ext ) {
-						case 'woocommerce-shipping':
-							return (
-								<WooCommerceShippingItem
-									key={ ext }
-									isPluginInstalled={ isPluginInstalled }
-									isPluginActive={ isPluginActive }
-									pluginsBeingSetup={ pluginsBeingSetup }
-									onInstallClick={ handleInstall }
-									onActivateClick={ handleActivate }
-									tracking={ trackingProps }
-								/>
-							);
-						case 'shipstation':
-							return (
-								<ShipStationItem
-									key={ ext }
-									isPluginInstalled={ isPluginInstalled }
-									isPluginActive={ isPluginActive }
-									pluginsBeingSetup={ pluginsBeingSetup }
-									onInstallClick={ handleInstall }
-									onActivateClick={ handleActivate }
-									tracking={ trackingProps }
-								/>
-							);
-						case 'packlink':
-							return (
-								<PacklinkItem
-									key={ ext }
-									isPluginInstalled={ isPluginInstalled }
-									isPluginActive={ isPluginActive }
-									pluginsBeingSetup={ pluginsBeingSetup }
-									onInstallClick={ handleInstall }
-									onActivateClick={ handleActivate }
-									tracking={ trackingProps }
-								/>
-							);
-						default:
-							return null;
-					}
-				} ) }
-			</ShippingRecommendationsList>
-		</div>
+		<>
+			<ShippingTour
+				showShippingRecommendationsStep={ ! isRecommendationsHidden }
+			/>
+			<div style={ { paddingBottom: 60 } }>
+				<ShippingRecommendationsList
+					dismissState={ recommendationsDismissState }
+				>
+					{ visibleExtensions.map( ( ext ) => {
+						const isPluginInstalled = installedPlugins.includes(
+							EXTENSION_PLUGIN_SLUGS[ ext ]
+						);
+						const isPluginActive = activePlugins.includes(
+							EXTENSION_PLUGIN_SLUGS[ ext ]
+						);
+						const trackingProps = {
+							context: 'settings' as const,
+							country: normalizedCountry,
+							plugins: visiblePluginSlugs,
+						};
+						switch ( ext ) {
+							case 'woocommerce-shipping':
+								return (
+									<WooCommerceShippingItem
+										key={ ext }
+										isPluginInstalled={ isPluginInstalled }
+										isPluginActive={ isPluginActive }
+										pluginsBeingSetup={ pluginsBeingSetup }
+										onInstallClick={ handleInstall }
+										onActivateClick={ handleActivate }
+										tracking={ trackingProps }
+									/>
+								);
+							case 'shipstation':
+								return (
+									<ShipStationItem
+										key={ ext }
+										isPluginInstalled={ isPluginInstalled }
+										isPluginActive={ isPluginActive }
+										pluginsBeingSetup={ pluginsBeingSetup }
+										onInstallClick={ handleInstall }
+										onActivateClick={ handleActivate }
+										tracking={ trackingProps }
+									/>
+								);
+							case 'packlink':
+								return (
+									<PacklinkItem
+										key={ ext }
+										isPluginInstalled={ isPluginInstalled }
+										isPluginActive={ isPluginActive }
+										pluginsBeingSetup={ pluginsBeingSetup }
+										onInstallClick={ handleInstall }
+										onActivateClick={ handleActivate }
+										tracking={ trackingProps }
+									/>
+								);
+							default:
+								return null;
+						}
+					} ) }
+				</ShippingRecommendationsList>
+			</div>
+		</>
 	);
 };

diff --git a/plugins/woocommerce/client/admin/client/shipping/test/shipping-recommendations.test.tsx b/plugins/woocommerce/client/admin/client/shipping/test/shipping-recommendations.test.tsx
new file mode 100644
index 00000000000..6c06500bf95
--- /dev/null
+++ b/plugins/woocommerce/client/admin/client/shipping/test/shipping-recommendations.test.tsx
@@ -0,0 +1,294 @@
+/**
+ * External dependencies
+ */
+import { render, screen } from '@testing-library/react';
+import { useSelect, useDispatch } from '@wordpress/data';
+import { recordEvent } from '@woocommerce/tracks';
+
+/**
+ * Internal dependencies
+ */
+import ShippingRecommendations from '../shipping-recommendations';
+import { SHIPPING_RECOMMENDATIONS_DISMISS_OPTION } from '../shipping-recommendations-utils';
+
+jest.mock( '@wordpress/data', () => ( {
+	...jest.requireActual( '@wordpress/data' ),
+	useSelect: jest.fn(),
+	useDispatch: jest.fn(),
+} ) );
+jest.mock( '~/components/tracked-link/tracked-link', () => ( {
+	TrackedLink: ( { textProps }: { textProps?: { className?: string } } ) => (
+		<span className={ textProps?.className }>
+			the WooCommerce Marketplace
+		</span>
+	),
+} ) );
+jest.mock( '../../settings-recommendations/dismissable-list', () => ( {
+	DismissableList: ( {
+		children,
+		isDismissed,
+	}: {
+		children: React.ReactNode;
+		isDismissed?: boolean;
+	} ) => (
+		<div
+			data-dismissed={ String( Boolean( isDismissed ) ) }
+			data-testid="dismissable-list"
+		>
+			{ ! isDismissed && children }
+		</div>
+	),
+	DismissableListHeading: ( { children }: { children: React.ReactNode } ) =>
+		children,
+} ) );
+jest.mock( '~/guided-tours/shipping-tour', () => ( {
+	ShippingTour: ( {
+		showShippingRecommendationsStep,
+	}: {
+		showShippingRecommendationsStep: boolean;
+	} ) => (
+		<div
+			data-show-recommendations-step={ String(
+				showShippingRecommendationsStep
+			) }
+			data-testid="shipping-tour"
+		/>
+	),
+} ) );
+jest.mock( '../woocommerce-shipping-item', () => () => (
+	<div>WooCommerce Shipping</div>
+) );
+jest.mock( '../shipstation-item', () => () => <div>ShipStation</div> );
+jest.mock( '../packlink-item', () => () => <div>Packlink PRO</div> );
+jest.mock( '../../lib/notices', () => ( {
+	createNoticesFromResponse: () => null,
+} ) );
+jest.mock( '@woocommerce/tracks', () => ( {
+	recordEvent: jest.fn(),
+} ) );
+
+const defaultSelectReturn = {
+	getActivePlugins: () => [],
+	getInstalledPlugins: () => [],
+	getSettings: () => ( {
+		general: {
+			woocommerce_default_country: 'US',
+		},
+	} ),
+	getProfileItems: () => ( {} ),
+	getOption: jest.fn().mockReturnValue( 'no' ),
+	hasFinishedResolution: jest.fn().mockReturnValue( true ),
+};
+
+const mockSelect = ( overrides: Record< string, unknown > = {} ) => {
+	( useSelect as jest.Mock ).mockImplementation( ( fn ) =>
+		fn( () => ( {
+			...defaultSelectReturn,
+			...overrides,
+		} ) )
+	);
+};
+
+describe( 'ShippingRecommendations', () => {
+	beforeEach( () => {
+		mockSelect();
+		( useDispatch as jest.Mock ).mockReturnValue( {
+			installPlugins: () => Promise.resolve(),
+			activatePlugins: () => Promise.resolve(),
+		} );
+		( recordEvent as jest.Mock ).mockClear();
+	} );
+
+	it( 'renders recommendations and the shipping tour recommendations step', () => {
+		render( <ShippingRecommendations /> );
+
+		expect(
+			screen.queryByText( 'WooCommerce Shipping' )
+		).toBeInTheDocument();
+		expect( screen.queryByText( 'ShipStation' ) ).toBeInTheDocument();
+		expect( screen.getByTestId( 'shipping-tour' ) ).toHaveAttribute(
+			'data-show-recommendations-step',
+			'true'
+		);
+		expect( recordEvent ).toHaveBeenCalledWith(
+			'shipping_partner_impression',
+			{
+				context: 'settings',
+				country: 'US',
+				plugins:
+					'woocommerce-shipping,woocommerce-shipstation-integration',
+			}
+		);
+	} );
+
+	it( 'waits for the dismissal option without remounting the shipping tour', () => {
+		let hasDismissResolved = false;
+		mockSelect( {
+			hasFinishedResolution: ( selector: string ) =>
+				selector === 'getOption' ? hasDismissResolved : true,
+		} );
+
+		const { rerender } = render( <ShippingRecommendations /> );
+		const initialShippingTour = screen.getByTestId( 'shipping-tour' );
+
+		expect(
+			screen.queryByText( 'the WooCommerce Marketplace' )
+		).not.toBeInTheDocument();
+		expect(
+			screen.queryByTestId( 'dismissable-list' )
+		).not.toBeInTheDocument();
+		expect( screen.getByTestId( 'shipping-tour' ) ).toHaveAttribute(
+			'data-show-recommendations-step',
+			'false'
+		);
+		expect( recordEvent ).not.toHaveBeenCalledWith(
+			'shipping_partner_impression',
+			expect.anything()
+		);
+
+		hasDismissResolved = true;
+		rerender( <ShippingRecommendations /> );
+
+		expect( screen.getByTestId( 'shipping-tour' ) ).toBe(
+			initialShippingTour
+		);
+		expect( screen.getByTestId( 'dismissable-list' ) ).toBeInTheDocument();
+		expect( recordEvent ).toHaveBeenCalledTimes( 1 );
+		expect( recordEvent ).toHaveBeenCalledWith(
+			'shipping_partner_impression',
+			{
+				context: 'settings',
+				country: 'US',
+				plugins:
+					'woocommerce-shipping,woocommerce-shipstation-integration',
+			}
+		);
+	} );
+
+	it( 'does not render recommendations before the country settings resolve', () => {
+		mockSelect( {
+			getSettings: () => ( { general: {} } ),
+			hasFinishedResolution: ( selector: string ) =>
+				selector !== 'getSettings',
+		} );
+
+		render( <ShippingRecommendations /> );
+
+		expect(
+			screen.queryByText( 'the WooCommerce Marketplace' )
+		).not.toBeInTheDocument();
+		expect(
+			screen.queryByTestId( 'dismissable-list' )
+		).not.toBeInTheDocument();
+		expect( screen.getByTestId( 'shipping-tour' ) ).toHaveAttribute(
+			'data-show-recommendations-step',
+			'false'
+		);
+		expect( recordEvent ).not.toHaveBeenCalledWith(
+			'shipping_partner_impression',
+			expect.anything()
+		);
+	} );
+
+	it( 'does not render recommendations before the product profile resolves', () => {
+		mockSelect( {
+			hasFinishedResolution: ( selector: string ) =>
+				selector !== 'getProfileItems',
+		} );
+
+		render( <ShippingRecommendations /> );
+
+		expect(
+			screen.queryByText( 'the WooCommerce Marketplace' )
+		).not.toBeInTheDocument();
+		expect(
+			screen.queryByTestId( 'dismissable-list' )
+		).not.toBeInTheDocument();
+		expect( screen.getByTestId( 'shipping-tour' ) ).toHaveAttribute(
+			'data-show-recommendations-step',
+			'false'
+		);
+		expect( recordEvent ).not.toHaveBeenCalledWith(
+			'shipping_partner_impression',
+			expect.anything()
+		);
+	} );
+
+	it( 'renders the marketplace fallback when recommendations are dismissed', () => {
+		mockSelect( {
+			getOption: ( option: string ) =>
+				option === SHIPPING_RECOMMENDATIONS_DISMISS_OPTION
+					? 'yes'
+					: undefined,
+		} );
+
+		render( <ShippingRecommendations /> );
+
+		expect(
+			screen.queryByText( 'WooCommerce Shipping' )
+		).not.toBeInTheDocument();
+		expect( screen.queryByText( 'ShipStation' ) ).not.toBeInTheDocument();
+		expect(
+			screen.queryByText( 'the WooCommerce Marketplace' )
+		).toBeInTheDocument();
+		expect(
+			screen.queryByTestId( 'dismissable-list' )
+		).not.toBeInTheDocument();
+		expect( screen.getByTestId( 'shipping-tour' ) ).toHaveAttribute(
+			'data-show-recommendations-step',
+			'false'
+		);
+		expect( recordEvent ).not.toHaveBeenCalledWith(
+			'shipping_partner_impression',
+			expect.anything()
+		);
+	} );
+
+	it( 'renders the marketplace fallback when there are no country recommendations', () => {
+		mockSelect( {
+			getSettings: () => ( {
+				general: {
+					woocommerce_default_country: 'JP',
+				},
+			} ),
+		} );
+
+		render( <ShippingRecommendations /> );
+
+		expect(
+			screen.queryByText( 'WooCommerce Shipping' )
+		).not.toBeInTheDocument();
+		expect(
+			screen.queryByText( 'the WooCommerce Marketplace' )
+		).toBeInTheDocument();
+		expect( screen.getByTestId( 'shipping-tour' ) ).toHaveAttribute(
+			'data-show-recommendations-step',
+			'false'
+		);
+	} );
+
+	it( 'renders the marketplace fallback for stores selling digital products only', () => {
+		mockSelect( {
+			getProfileItems: () => ( {
+				product_types: [ 'downloads' ],
+			} ),
+		} );
+
+		render( <ShippingRecommendations /> );
+
+		expect(
+			screen.queryByText( 'WooCommerce Shipping' )
+		).not.toBeInTheDocument();
+		expect(
+			screen.queryByText( 'the WooCommerce Marketplace' )
+		).toBeInTheDocument();
+		expect( screen.getByTestId( 'shipping-tour' ) ).toHaveAttribute(
+			'data-show-recommendations-step',
+			'false'
+		);
+		expect( recordEvent ).not.toHaveBeenCalledWith(
+			'shipping_partner_impression',
+			expect.anything()
+		);
+	} );
+} );
diff --git a/plugins/woocommerce/includes/admin/views/html-admin-settings.php b/plugins/woocommerce/includes/admin/views/html-admin-settings.php
index 378fa9c4484..84e9fb1c7c4 100644
--- a/plugins/woocommerce/includes/admin/views/html-admin-settings.php
+++ b/plugins/woocommerce/includes/admin/views/html-admin-settings.php
@@ -87,12 +87,6 @@ $marketplace_links = array(
 		/* translators: %1$s: opening link tag, %2$s: closing link tag */
 		'message'     => __( '%1$sExplore solutions%2$s that help with tax calculations, compliance, and regional requirements.', 'woocommerce' ),
 	),
-	'shipping' => array(
-		'url'         => $marketplace_base_url . 'shipping-delivery-and-fulfillment/',
-		'is_external' => true,
-		/* translators: %1$s: opening link tag, %2$s: closing link tag */
-		'message'     => __( '%1$sExplore solutions%2$s that enhance shipping, delivery, and fulfillment workflows.', 'woocommerce' ),
-	),
 	'account'  => array(
 		'url'         => $marketplace_base_url . 'store-content-and-customizations/cart-and-checkout-features/',
 		'is_external' => true,
diff --git a/plugins/woocommerce/tests/php/includes/admin/views/class-wc-admin-settings-view-test.php b/plugins/woocommerce/tests/php/includes/admin/views/class-wc-admin-settings-view-test.php
new file mode 100644
index 00000000000..30bf6b48394
--- /dev/null
+++ b/plugins/woocommerce/tests/php/includes/admin/views/class-wc-admin-settings-view-test.php
@@ -0,0 +1,127 @@
+<?php
+declare( strict_types = 1 );
+/**
+ * Class WC_Admin_Settings_View_Test file.
+ *
+ * @package WooCommerce\Tests\Admin\Views
+ */
+
+/**
+ * Unit tests for admin settings views.
+ */
+class WC_Admin_Settings_View_Test extends WC_Unit_Test_Case {
+
+	/**
+	 * Hook priority for admin feature mocks.
+	 *
+	 * @var int
+	 */
+	private const HOOK_PRIORITY = 9999;
+
+	/**
+	 * Mocked WooCommerce Admin feature flags.
+	 *
+	 * @var string[]
+	 */
+	private $enabled_admin_features_mock = array();
+
+	/**
+	 * Admin user ID.
+	 *
+	 * @var int
+	 */
+	private $admin_user_id = 0;
+
+	/**
+	 * Set up test fixtures.
+	 */
+	public function setUp(): void {
+		parent::setUp();
+
+		add_filter( 'woocommerce_admin_features', array( $this, 'get_mocked_admin_features' ), self::HOOK_PRIORITY );
+
+		$this->admin_user_id = self::factory()->user->create( array( 'role' => 'administrator' ) );
+		wp_set_current_user( $this->admin_user_id );
+		update_option( 'woocommerce_show_marketplace_suggestions', 'yes' );
+		update_option( 'woocommerce_default_country', 'US:CA' );
+	}
+
+	/**
+	 * Tear down test fixtures.
+	 */
+	public function tearDown(): void {
+		remove_filter( 'woocommerce_admin_features', array( $this, 'get_mocked_admin_features' ), self::HOOK_PRIORITY );
+		unset( $_GET['zone_id'] );
+		delete_option( 'woocommerce_show_marketplace_suggestions' );
+		delete_option( 'woocommerce_default_country' );
+		delete_option( 'woocommerce_onboarding_profile' );
+		wp_set_current_user( 0 );
+
+		parent::tearDown();
+	}
+
+	/**
+	 * @testdox Should not render the generic shipping marketplace link on Shipping settings.
+	 */
+	public function test_shipping_marketplace_link_is_not_rendered_on_shipping_settings(): void {
+		$output = $this->render_settings_view( 'shipping' );
+
+		$this->assertStringNotContainsString(
+			'wc-settings-marketplace-link',
+			$output,
+			'Shipping settings marketplace fallback links are rendered by the shipping recommendations component.'
+		);
+		$this->assertStringNotContainsString(
+			'shipping-delivery-and-fulfillment',
+			$output,
+			'The generic Shipping settings marketplace URL should not be rendered by the PHP settings template.'
+		);
+	}
+
+	/**
+	 * @testdox Should keep non-shipping marketplace links when shipping smart defaults are enabled.
+	 */
+	public function test_non_shipping_marketplace_links_are_rendered_when_shipping_smart_defaults_are_enabled(): void {
+		$this->enabled_admin_features_mock = array( 'shipping-smart-defaults' );
+
+		$output = $this->render_settings_view( 'products' );
+
+		$this->assertStringContainsString(
+			'data-settings-tab="products"',
+			$output,
+			'Marketplace links for unrelated settings tabs should not be affected.'
+		);
+		$this->assertStringContainsString(
+			'merchandising',
+			$output,
+			'Marketplace URLs for unrelated settings tabs should not be affected.'
+		);
+	}
+
+	/**
+	 * Render the admin settings view.
+	 *
+	 * @param string $current_tab The current settings tab.
+	 * @param string $current_section The current settings section.
+	 * @return string
+	 */
+	private function render_settings_view( string $current_tab, string $current_section = '' ): string {
+		$tabs = array(
+			'products' => 'Products',
+			'shipping' => 'Shipping',
+		);
+
+		ob_start();
+		include WC_ABSPATH . 'includes/admin/views/html-admin-settings.php';
+		return (string) ob_get_clean();
+	}
+
+	/**
+	 * Returns a mocked list of enabled WC Admin features.
+	 *
+	 * @return string[]
+	 */
+	public function get_mocked_admin_features(): array {
+		return $this->enabled_admin_features_mock;
+	}
+}