cornerstone icon indicating copy to clipboard operation
cornerstone copied to clipboard

Rich results returning errors for products with price range

Open sacr3dc0w opened this issue 4 years ago • 6 comments

Expected behavior

Structured data to be parsed by Google without error.

Actual behavior

If conditional of {{#if product.price.price_range}} is met, an error gets thrown, as there's no price for the Offer.

Steps to reproduce behavior

Always displaying price does the trick for me.

Link to rich results (I'll leave up for 24 hours, but will be pushing the fix below definitely before Wednesday): https://search.google.com/test/rich-results/result?id=f09f3CeP4GFgAlRExDpa9w

templates/components/products/schema.html

BEFORE:

        "offers": {
            "@type": "Offer",
            "priceCurrency": "{{currency_selector.active_currency_code}}",
            {{#if product.price.price_range}}
            "minPrice": "{{#if product.price.price_range.min.with_tax}}{{product.price.price_range.min.with_tax.value}}{{else}}{{product.price.price_range.min.without_tax.value}}{{/if}}",
            "maxPrice": "{{#if product.price.price_range.max.with_tax}}{{product.price.price_range.max.with_tax.value}}{{else}}{{product.price.price_range.max.without_tax.value}}{{/if}}",
            {{else}}
            "price": "{{#if product.price.with_tax}}{{product.price.with_tax.value}}{{else}}{{product.price.without_tax.value}}{{/if}}",
            {{/if}}
            "itemCondition" : "https://schema.org/{{#if product.condition}}{{product.condition}}{{else}}New{{/if}}Condition",
            "availability" : "https://schema.org/{{#if product.pre_order}}PreOrder{{else if product.out_of_stock}}OutOfStock{{else if product.can_purchase '===' false}}OutOfStock{{else}}InStock{{/if}}",
            "url" : "{{product.url}}",
            "priceValidUntil": "{{moment (moment add='31536000000') 'YYYY-MM-DD' }}"
        }

AFTER:

        "offers": {
            "@type": "Offer",
            "priceCurrency": "{{currency_selector.active_currency_code}}",
            {{#if product.price.price_range}}
            "minPrice": "{{#if product.price.price_range.min.with_tax}}{{product.price.price_range.min.with_tax.value}}{{else}}{{product.price.price_range.min.without_tax.value}}{{/if}}",
            "maxPrice": "{{#if product.price.price_range.max.with_tax}}{{product.price.price_range.max.with_tax.value}}{{else}}{{product.price.price_range.max.without_tax.value}}{{/if}}",
            {{/if}}
            "price": "{{#if product.price.with_tax}}{{product.price.with_tax.value}}{{else}}{{product.price.without_tax.value}}{{/if}}",
            "itemCondition" : "https://schema.org/{{#if product.condition}}{{product.condition}}{{else}}New{{/if}}Condition",
            "availability" : "https://schema.org/{{#if product.pre_order}}PreOrder{{else if product.out_of_stock}}OutOfStock{{else if product.can_purchase '===' false}}OutOfStock{{else}}InStock{{/if}}",
            "url" : "{{product.url}}",
            "priceValidUntil": "{{moment (moment add='31536000000') 'YYYY-MM-DD' }}"
        }

sacr3dc0w avatar Dec 20 '21 23:12 sacr3dc0w

That's how we do it.

Tiggerito avatar Dec 21 '21 02:12 Tiggerito

An update here:

minPrice and maxPrice are not part of an Offer, but they can be added to a PriceSpecification.

Google does support AggregateOffer, which has a lowPrice and highPrice, but it is not appropriate for BigCommerce stores. It is for product aggregators. Its use may exclude a product from merchant listings. So I would not use AggregateOffer.

https://developers.google.com/search/docs/appearance/structured-data/product#aggregate-offer-properties

Tiggerito avatar Oct 16 '22 20:10 Tiggerito

Hello,

We ran into the same problem and have implemented the same fix including adding the PriceSpecification element as follows:

"offers": {
            "@type": "Offer",
            "priceCurrency": "{{currency_selector.active_currency_code}}",
            {{#if product.price.price_range}}
            "priceSpecification": {
                "minPrice": "{{#if product.price.price_range.min.with_tax}}{{product.price.price_range.min.with_tax.value}}{{else}}{{product.price.price_range.min.without_tax.value}}{{/if}}",
                "maxPrice": "{{#if product.price.price_range.max.with_tax}}{{product.price.price_range.max.with_tax.value}}{{else}}{{product.price.price_range.max.without_tax.value}}{{/if}}"
            },
            {{/if}}
            "price": "{{#if product.price.with_tax}}{{product.price.with_tax.value}}{{else}}{{product.price.without_tax.value}}{{/if}}",
            "itemCondition" : "https://schema.org/{{#if product.condition}}{{product.condition}}{{else}}New{{/if}}Condition",
            "availability" : "https://schema.org/{{#if product.pre_order}}PreOrder{{else if product.out_of_stock}}OutOfStock{{else if product.can_purchase '===' false}}OutOfStock{{else}}InStock{{/if}}",
            "url" : "{{product.url}}",
            "priceValidUntil": "{{moment (moment add='31536000000') 'YYYY-MM-DD' }}"
        }

After this the code validates correcly both in the schema.org validator and google rich results test.

https://developers.google.com/search/docs/appearance/structured-data

RobDNZ avatar May 24 '23 10:05 RobDNZ

I'd also test that it causes the desired rich snippets in Google, as that is what helps your clients.

Tiggerito avatar May 24 '23 12:05 Tiggerito

I don't think we can actually test this before it is pushed to the live store but we will definitely test this first and monitor this after deployment to production.

RobDNZ avatar May 24 '23 12:05 RobDNZ

The changes have been deployed to our live store and our marketing department has confirmed that they are seeing the expected improvements. In our case the improvements are even bigger because this store did not have the JSON-LD structure yet (they still had the old meta tag stuff).

RobDNZ avatar Aug 11 '23 10:08 RobDNZ