Skip to content

Missing field "hasMerchantReturnPolicy" and "shippingDetails" in WooCommerce: what they mean and three fixes

Both are recommended properties of a merchant listing, not required ones, so the product stays valid. WooCommerce prints neither. Settle them with shipping and return settings in Search Console or Merchant Center, with markup added through a WooCommerce filter, or with an extension.

By
WP Ministry
Published
Tested on
WordPress 7.1.3, WooCommerce 11.2.0, PHP 8.3.35

In short

  • Google lists hasMerchantReturnPolicy and shippingDetails as recommended for a merchant listing. What it requires is a name, an image and an offer with a price and a currency.
  • Search Console calls an issue of this kind non-critical. The item stays valid and can still appear as a rich result.
  • WooCommerce's product markup contains neither property, so every product with a price gets both notices at once.
  • Shipping and return settings in Search Console need no code, and Google uses them ahead of any markup on the page.
  • The filter woocommerce_structured_data_product_offer adds both properties to each product's offer from one small file.
  • State only the policy the store really has. The values in every example on this page are stand-ins to replace.

A notice in Search Console that the field hasMerchantReturnPolicy or shippingDetails is missing does not mean a product is broken. Google's documentation for merchant listings lists both properties as recommended, not required, so the product is still a valid item and can still be shown. The notice says that Google has not been told, in the page's markup, what the store's return policy and shipping terms are.

WooCommerce's own product markup contains neither property, which is why a store sees both notices on every product at once. There are three ways to settle them: enter shipping and return settings in Search Console or Merchant Center, add the two properties to the markup, or use an extension that does one of those for you.

What the two notices say

Search Console's help lists the common messages of its rich result reports. One has the form Missing field "property name", and the advice beside it is to add the property. This page is about that message with two names in it, in the Merchant listings report, which Search Console keeps under Shopping.

PropertyWhat it describesWhere Google's documentation puts it
hasMerchantReturnPolicyWhether a product can be returned, within how many days, how, and at what costIn the product's offer, or once for the whole business under Organization
shippingDetailsWhat shipping costs, where the store ships to, and how long delivery takesIn the product's offer

Google's page on merchant listing structured data separates the two. These are its lists for a product and for the offer inside it.

RequiredRecommended
Productname, image, offersaggregateRating, brand.name, description, a GTIN, sku and others
Offerprice and priceCurrency, given directly or inside priceSpecificationavailability, hasMerchantReturnPolicy, itemCondition, priceValidUntil, shippingDetails, url, validFrom, validThrough

Both properties are in the recommended column. Of shipping, the same page says outright that OfferShippingDetails is not required.

Google's page gives the difference in one line each. Required properties are what make a page eligible to be shown as a rich result. Recommended properties add information, which the page says could give people a better experience. Its general guidelines add that an item missing a required property is not eligible for rich results, and that the more recommended properties are provided, the higher the quality of the result is to users.

For these two properties the page is specific about what is at stake. The return properties are what make a listing eligible to show return policy information, such as the return window and the fees. The shipping properties are what make it eligible for what Google calls the shipping details enhancement: shipping cost and an estimated delivery time. So the cost of leaving them out is that part of the listing, and only when Google has no other source for it. The settings described below are another source.

How Search Console labels the two kinds of issue

Search Console's help uses two terms.

  • A critical issue makes an item invalid. An invalid item cannot appear as a rich result. The report lists these in a table named "Why items are invalid".
  • A non-critical issue leaves the item valid. The report lists these in a table named "Improve item appearance".

The help page for the Rich Results Test speaks of errors and warnings. Google's 2023 announcement of shipping and return information in search results says that a product without it gets "warnings in the Merchant Listings report", and a notice by email.

So look at which table your notice is in. A recommended property belongs to the second. If a product is listed in "Why items are invalid", something else is at fault, such as a required property, and that is a different job. What WooCommerce outputs for a product and how to fill the gaps covers those.

What WooCommerce prints for a product's offer

WooCommerce writes a product's structured data as JSON-LD near the end of the product page. This is the offer it printed for a simple product priced at 120.00 on WooCommerce 11.2.0 with no other plugin active. The store's name and address are swapped for examples, and line breaks are added.

json
{
  "@type": "Offer",
  "priceSpecification": [
    {
      "@type": "UnitPriceSpecification",
      "price": "120.00",
      "priceCurrency": "USD",
      "validThrough": "2027-12-31"
    }
  ],
  "priceValidUntil": "2027-12-31",
  "availability": "https://schema.org/InStock",
  "url": "https://example.com/product/example-product/",
  "seller": {
    "@type": "Organization",
    "name": "Example Store",
    "url": "https://example.com"
  },
  "price": "120.00",
  "priceCurrency": "USD"
}

That is the whole offer: a price, a currency, a date the price is valid until, the stock status, the product's address and the store as seller. The date is WooCommerce's own assumption, the last day of next year, unless the product is on sale with an end date. It has no hasMerchantReturnPolicy and no shippingDetails, and nothing in WooCommerce's settings adds them:

  • A shipping zone does not reach the markup. With a flat rate set for a zone under WooCommerce > Settings > Shipping, the offer was the same.
  • The refund and returns page does not either. WooCommerce creates that page as a draft. Published, it carried no structured data at all, and the product's offer was unchanged.

The file that builds this markup in WooCommerce 11.2.0 does not contain either property's name. The development version of the same file, which is not in a release yet, adds OnlineStore data to the published refund and returns page with a hasMerchantReturnPolicy that holds a link to that page and nothing else. Its code comment marks it for 11.3.0, and it adds nothing to a product's offer.

To see what your own store prints, ask for a product page and look for the two names. Put a real product's address in place of the example.

bash
curl -s https://example.com/product/example-product/ | grep -o -E '"(hasMerchantReturnPolicy|shippingDetails)":' | sort -u

On a store that prints neither, the command prints nothing.

A variable product whose variations differ in price gets an AggregateOffer with a lowest and a highest price in place of an Offer. Google's page says a merchant listing requires an Offer, so that product has a different matter to settle first. The page on how WooCommerce variations look to a search engine covers it.

Three ways to settle them

WayWhere the policy is keptWhat it takes
Settings in Search Console or Merchant CenterIn your Google accountNo code. Access to the Search Console property as an owner or a full user
MarkupIn the store's pagesOne PHP file, shown below
An extensionDepends on the extensionReading its documentation to learn which of the first two it does

The ways can be combined, and Google says which it believes when they are. Its merchant listing page gives this order for shipping and return policies defined in more than one place, from strongest to weakest:

  1. Product-level feeds submitted in Merchant Center
  2. Settings in the Content API for Shopping
  3. Settings in Merchant Center or Search Console
  4. Product-level merchant listing markup
  5. Organization-level markup

Google's page on return policy markup gives the plain case: if a site has return policy markup and return policy settings in Search Console, Google uses only what is in Search Console. So whichever ways you use, they have to say the same thing, and a setting made in a Google account outranks anything written into the page.

The way that needs no code: settings in Search Console or Merchant Center

Search Console

Google's help page for the setting says it is under Settings > Shopping > Shipping and returns. It takes a simplified version of the store's policy: delivery time, shipping cost, return window and return cost.

  • Who sees it. Google shows the setting to sites it has identified as online merchants, and only to an owner or a full user of the property. The help page says a Merchant Center account is not needed. If the store already has one, it has to be associated with the Search Console property first.
  • A shipping policy. On the Shipping tab, "Add shipping policy" asks for the country, a minimum and a maximum number of days for delivery, and the cost: a flat rate, free over a certain amount, free, or other. Repeat it for each country the store ships to.
  • A return policy. On the Returns tab, "Add return policy" asks for the country, the address of the page where customers can read the policy, the return window in days or no returns, and the cost of a return: a flat rate, free, or a percentage of the product's price.
  • How long it takes. The help page says shipping details are approved automatically, and that a return policy is verified by hand, which can take about 10 to 13 days. Until then it is marked as pending.

The return policy needs a page on the store that states the policy. WooCommerce's refund and returns page is a draft until someone writes it and publishes it.

Merchant Center

A store with a Merchant Center account can make the same settings there, with more options. Google's help says to go to "Shipping and returns" under "Products & store" in the left menu, and to use the "Return policies" tab or the "Shipping policies" tab. It says Google verifies a return policy before using it, and shows the result in a status column. A policy made in Search Console is added to Merchant Center automatically. A policy made or edited in Merchant Center can only be managed there.

What Google does not say

Google's pages say these settings are used, and that they come ahead of markup. None of them says that the Merchant listings report then stops listing the property as missing from the page's markup. The report describes the structured data Google found on the page, and settings do not change the page. If the notice stays after a policy is approved, check the policy's status on the settings screen before deciding that anything failed.

The way with markup

Google's documentation describes two places for a return policy, and recommends the first.

Once for the whole business

A standard return policy that applies to most or all products goes under Organization markup, or its more specific type OnlineStore, on one page. Google recommends the page that describes the return policy and says it does not need to be on every page.

json
{
  "@context": "https://schema.org",
  "@type": "OnlineStore",
  "name": "Example Store",
  "url": "https://example.com",
  "hasMerchantReturnPolicy": {
    "@type": "MerchantReturnPolicy",
    "applicableCountry": "US",
    "returnPolicyCategory": "https://schema.org/MerchantReturnFiniteReturnWindow",
    "merchantReturnDays": 30,
    "returnMethod": "https://schema.org/ReturnByMail",
    "returnFees": "https://schema.org/FreeReturn"
  }
}

A business-wide shipping policy has a property of its own, hasShippingService, which Google documents on its page for merchant shipping policy markup. It is not the shippingDetails the notice names.

On each product's offer

Google's page says to put a policy on the offer when a product's terms differ from the standard ones, or when the business provides no standard policy. This is also the place the notice names, so it is the one the WooCommerce filter below writes to.

json
{
  "@type": "Offer",
  "price": "120.00",
  "priceCurrency": "USD",
  "hasMerchantReturnPolicy": {
    "@type": "MerchantReturnPolicy",
    "applicableCountry": "US",
    "returnPolicyCategory": "https://schema.org/MerchantReturnFiniteReturnWindow",
    "merchantReturnDays": 30,
    "returnMethod": "https://schema.org/ReturnByMail",
    "returnFees": "https://schema.org/FreeReturn"
  },
  "shippingDetails": {
    "@type": "OfferShippingDetails",
    "shippingRate": {
      "@type": "MonetaryAmount",
      "value": "5.00",
      "currency": "USD"
    },
    "shippingDestination": {
      "@type": "DefinedRegion",
      "addressCountry": "US"
    },
    "deliveryTime": {
      "@type": "ShippingDeliveryTime",
      "handlingTime": {
        "@type": "QuantitativeValue",
        "minValue": 0,
        "maxValue": 1,
        "unitCode": "DAY"
      },
      "transitTime": {
        "@type": "QuantitativeValue",
        "minValue": 2,
        "maxValue": 5,
        "unitCode": "DAY"
      }
    }
  }
}

Where both exist, Google's page says it defaults to the product-level return policy.

What each type requires

Once a property is present, Google checks what is inside it. These are the properties its pages list as required for each type.

TypeRequired by GoogleNotes from the same pages
MerchantReturnPolicy on an offerapplicableCountry, returnPolicyCategorymerchantReturnDays is required when the category is MerchantReturnFiniteReturnWindow. The country is a two-letter ISO 3166-1 code, and up to 50 can be given
MerchantReturnPolicy under OrganizationEither applicableCountry and returnPolicyCategory, or merchantReturnLink alonemerchantReturnLink is the address of the page that describes the policy. It is not among the properties listed for an offer
OfferShippingDetailsdeliveryTime, shippingDestination, shippingRateThe rate needs currency and either value or maxValue. The currency must be the offer's own. A value of 0 means free shipping
DefinedRegion, inside shippingDestinationaddressCountryA two-letter ISO 3166-1 code
QuantitativeValue, inside handlingTime and transitTimeminValue, maxValue, unitCodeWhole days, with a unit of DAY or d

returnFees and returnMethod are recommended. A store that takes no returns states that too: returnPolicyCategory has the value https://schema.org/MerchantReturnNotPermitted for it, and then no number of days is needed.

Add both properties in WooCommerce

WooCommerce passes each product's offer through a filter, woocommerce_structured_data_product_offer, before printing it. The filter hands over the offer as an array and the product it belongs to. A function attached to it can add the two properties to every product in one place.

The smallest place for that function is a must-use plugin: a single PHP file in wp-content/mu-plugins/ that WordPress loads on every request, with nothing to activate.

  1. Step 1: Create the mu-plugins folder if it is not there

    Look in wp-content. If there is no folder named mu-plugins, create one. A fresh WordPress does not have it.

  2. Step 2: Add the file

    Save this as offer-policies.php in that folder. A mistake in a PHP file can stop the site from loading, so copy it exactly and keep SFTP or the host's file manager open. If the site shows a critical error afterwards, delete the file and the site comes back.

    wp-content/mu-plugins/offer-policies.php
    <?php
    /**
     * Plugin Name: Product offer policies
     * Description: Adds this store's return policy and shipping details to the structured data of each product's offer.
     */
    
    add_filter(
    	'woocommerce_structured_data_product_offer',
    	function ( $offer, $product ) {
    		// A price range (AggregateOffer) is left as WooCommerce wrote it.
    		if ( ! is_array( $offer ) || 'Offer' !== ( $offer['@type'] ?? '' ) ) {
    			return $offer;
    		}
    
    		$offer['hasMerchantReturnPolicy'] = array(
    			'@type'                => 'MerchantReturnPolicy',
    			'applicableCountry'    => 'US',
    			'returnPolicyCategory' => 'https://schema.org/MerchantReturnFiniteReturnWindow',
    			'merchantReturnDays'   => 30,
    			'returnMethod'         => 'https://schema.org/ReturnByMail',
    			'returnFees'           => 'https://schema.org/FreeReturn',
    		);
    
    		// Shipping is stated only for a product that is shipped.
    		if ( $product instanceof WC_Product && $product->needs_shipping() ) {
    			$offer['shippingDetails'] = array(
    				'@type'               => 'OfferShippingDetails',
    				'shippingRate'        => array(
    					'@type'    => 'MonetaryAmount',
    					'value'    => '5.00',
    					'currency' => get_woocommerce_currency(),
    				),
    				'shippingDestination' => array(
    					'@type'          => 'DefinedRegion',
    					'addressCountry' => 'US',
    				),
    				'deliveryTime'        => array(
    					'@type'        => 'ShippingDeliveryTime',
    					'handlingTime' => array(
    						'@type'    => 'QuantitativeValue',
    						'minValue' => 0,
    						'maxValue' => 1,
    						'unitCode' => 'DAY',
    					),
    					'transitTime'  => array(
    						'@type'    => 'QuantitativeValue',
    						'minValue' => 2,
    						'maxValue' => 5,
    						'unitCode' => 'DAY',
    					),
    				),
    			);
    		}
    
    		return $offer;
    	},
    	10,
    	2
    );
  3. Step 3: Change the values to your store's

    LineWhat it tells GooglePut in its place
    applicableCountry, addressCountryThe country the policy applies to and the country the store ships toYour country's two-letter code
    returnPolicyCategory, merchantReturnDaysReturns are accepted for 30 days after deliveryYour window in days, or MerchantReturnNotPermitted with the days line removed
    returnMethodReturns go back by mailReturnInStore or ReturnAtKiosk if that is how yours work
    returnFeesReturning a product costs the customer nothingReturnFeesCustomerResponsibility if the customer arranges and pays for the return
    shippingRateShipping costs 5.00 in the store's currencyThe rate checkout charges, or 0 for free shipping
    handlingTime, transitTimeUp to 1 day to dispatch, then 2 to 5 days in transitYour own shortest and longest, in whole days

    Each of those values is one Google's pages list for the property. A fee the store charges for a return has a third returnFees value with an amount beside it, and Google's page shows how to write it.

  4. Step 4: Read the product page again

    Clear any page cache, then run the command from earlier on this page against a product's address. It now prints both names.

    text
    "hasMerchantReturnPolicy":
    "shippingDetails":

What the file does to each kind of product on WooCommerce 11.2.0:

  • A simple product that is shipped gets both properties, with the values exactly as written in the file.
  • A virtual product gets the return policy and no shippingDetails. The file asks WooCommerce whether the product needs shipping, and WooCommerce answers no for a virtual product.
  • A variable product with a price range gets neither, because its offer is an AggregateOffer and the file leaves that alone.

The file reads the currency from the store's settings, so the shipping rate is always in the currency of the price, which Google requires. The file shows on the Plugins screen under "Must-Use" and cannot be switched off there. With WooCommerce deactivated it does nothing and the site loads as before. To undo the change, delete the file.

One file states one policy for the whole store. If a few products ship or return on other terms, the function has the product in hand and can decide by it, and that is work for a developer. WP Ministry's WordPress SEO audit covers WooCommerce stores, and the fixes on the WordPress side are part of the job, up to the hours it includes.

The way with an extension

An extension settles the notices in one of the two ways above, and its documentation says which. Read it for two things before installing anything: whether it sends shipping and return settings to a Google account or writes properties into the page, and which properties those are.

Google for WooCommerce, the extension that connects a store to Merchant Center, is of the first kind for shipping. Its documentation says it can sync shipping rates from WooCommerce's Flat Rate method to Merchant Center, and offers a choice under Marketing > Google for WooCommerce > Settings > Shipping rates between syncing from WooCommerce and setting shipping up in Merchant Center. The pages of its documentation read for this article do not mention a return policy, so plan to enter that in Merchant Center or Search Console yourself.

An extension that writes structured data is of the second kind. After setting it up, run the command above on a product page. If a property's name is not printed, the extension is not putting it in the page, whatever its settings screen shows.

Check the result

Read the page. The command above shows what a crawler receives. Run it on two or three products of different kinds, not one.

Test one page with Google's tool. Open the Rich Results Test and give it the full address of a product page. Google's help says the test lists the rich result types it found, with any errors or warnings under each item, and that the page must be reachable by an anonymous visitor. It reports a status such as "N valid item(s) detected" or "N valid item(s) with warnings detected". Open the merchant listing item and look at whether the two names are still among its warnings.

Ask Search Console to validate. Open the issue in the Merchant listings report. Google's help says "Validate fix" on the issue's details page starts a check of a sample of pages first, then the rest, and that validation can take two weeks or more depending on how often the site is crawled. It also says Google may notice a fix during ordinary crawling without validation being started. Nobody outside Google sets that date.

Check a setting where it was made. A policy entered in Search Console or Merchant Center shows its own status on the screen it was entered on.

What not to do

  • Do not state a policy the store does not offer. Google's general structured data guidelines say markup "must be a true representation of the page content", and tell sites not to use structured data to deceive or mislead users. A 30-day free return written into the markup to clear a notice is a statement to every shopper who sees it.
  • Do not mark up what a visitor cannot read. The same guidelines say not to mark up content that is not visible to readers of the page. Publish the return policy and the shipping terms on the store, where a customer can find them without logging in.
  • Do not let the markup and checkout disagree. Google's help for the Search Console setting says to check that the shipping cost given to Google matches the one on the website exactly, and that a listing might be rejected if Google finds a lower cost on the listing than on the site.
  • Do not paste the examples unchanged. They are stand-ins.
  • Do not treat the notice as an emergency. The item is valid while it is listed. A wrong policy published in a hurry does more harm than a recommended property left empty for a week.

Missing brand and a missing GTIN are notices of the same kind in the same report. They have a page of their own: the brand and GTIN notices on a WooCommerce store.

Common questions

Do these notices stop my products from appearing on Google?

No. Both properties are recommended, and Search Console's help says an item with only non-critical issues is valid and can appear as a rich result. What the properties add is eligibility for return and shipping information in the listing. Whether any rich result is shown remains Google's decision.

Should I use the settings or the markup?

It depends on the store. One policy for every product is the least to maintain as a setting, and Google uses a setting ahead of markup. A policy that differs by product needs markup on the offer, or product data in Merchant Center. If you use more than one way, make them say the same thing.

Will the notice go away once I add a policy in Search Console?

Google's documentation does not say. It says settings are used and take precedence over markup. The report lists what is in the page's markup, and a setting does not change the page. What changes the report's reading is the markup, so add the property there as well, with the same terms, if the notice itself matters to you.

My store does not accept returns. What do I put?

State that. returnPolicyCategory has a value for it, https://schema.org/MerchantReturnNotPermitted, and merchantReturnDays is then left out. In Search Console the return window offers "no returns" as a choice.

Do digital products need shippingDetails?

There is nothing to ship, so there is nothing true to put in it. The file on this page leaves the property off a virtual product. It is a recommended property, so the product stays valid without it.

Why does every product have the notice?

Because the markup comes from one template. WooCommerce builds every product's offer with the same code, and that code adds neither property. Google's help makes the same point about fixing: if an issue comes from a template, fixing the template resolves it on every page that uses it.

More on this subject

SEO audit, done for you

SEO audit is $249. A written audit, each finding with its source, and up to 2 hours of fixes done and tested again. It starts with a free diagnosis.