@spindlecommerce/plugin-google-shopping
v1.0.0
Published
Google Shopping for SpindleCommerce: a product feed for Google Merchant Center, with an item for each variant, priced and worded as the channel's storefront shows it.
Maintainers
Readme
Google Shopping, a SpindleCommerce plugin
A product feed for Google Merchant Center, so your products can
appear in Shopping ads and free listings on Google. The feed is RSS 2.0 in Google's format, served by
the plugin at /plugins/google-shopping/feed.xml. Merchant Center fetches it on a schedule.
- One item per variant that has a price, with its own SKU, price, availability, images, color
and size. The variants of a product are grouped with
item_group_id. - As the storefront shows it. Names, descriptions and prices come from the catalog as a guest sees it in the channel's storefront, in the feed's locale and currency. Products kept out of listings and search are left out.
- Streamed. The feed is written a page of 100 products at a time, so a large catalog is never held in memory.
Setup
Add the plugin to your SpindleCommerce installation and deploy. It needs plugin SDK 1.1 or later, which has the catalog service.
pnpm add @spindlecommerce/plugin-google-shopping// ecom.config.ts import { googleShopping } from "@spindlecommerce/plugin-google-shopping"; export default defineConfig({ plugins: [email(), stripe(), googleShopping()] });Check the links. Products link to the channel's storefront, from Settings › Storefronts.
- If the storefront puts the language in the path (
/de/products/…), fill in Locale prefixes. - If its product pages aren't at
/products/<slug>, change Product URL.
- If the storefront puts the language in the path (
Settings › Plugins › Google Shopping. The plugin works without settings. To keep the feed private, set a Feed token: the feed then needs
?token=in its URL. Without one, anyone with the URL can read the feed, which shows what the storefront shows.Open the feed in a browser to check it, for example
https://shop-api.example.com/plugins/google-shopping/feed.xml?channel=eu&locale=en-GB¤cy=GBP.Add it to Merchant Center by scheduled fetch.
- Open Products › Data sources. Google's help reaches it through Settings › Data sources.
- Select Add product source, then Add products from a file, then Enter a link to your file.
- Paste the feed URL, with its token if it has one.
- Choose when Google fetches it: daily, by default.
- Choose the language and countries of the feed's locale and currency. For example, a feed with
locale=en-GB¤cy=GBPis for the United Kingdom, in English.
Add a source for each channel, locale and currency you advertise in.
Set up shipping in Merchant Center. The feed has no shipping costs (see Limitations).
Settings
Product URL (default
{storefront}/products/{slug}): how the feed links to products. It must make absolutehttporhttpslinks. Placeholders:{storefront}: the channel's storefront URL, followed by the locale's prefix;{slug}: the product's slug, in the feed's locale;{locale}: the locale, such asde-DE;{language}: its language, such asde.
Examples are
{storefront}/{language}/p/{slug}, orhttps://shop.example.com/products/{slug}for a storefront that isn't under Settings › Storefronts.Locale prefixes (for example
de-DE=/de, fr-FR=/fr): paths the storefront puts after its URL for some locales. The prefix goes after{storefront}for that locale.- A prefix for a language, such as
fr=/fr, serves each of its locales. - Locales not listed have no prefix.
- A prefix for a language, such as
Brand attribute (
brand), Color attribute (color) and Size attribute (size): the codes of the attributes that hold them.- The brand is a product attribute.
- Color and size are variant attributes. A product attribute with the same code serves products whose variants don't differ by it.
Feed token (secret, optional): when set, the feed answers only requests with
?token=and this value, and others with 401.
Feed URL
The feed is at /plugins/google-shopping/feed.xml on your SpindleCommerce API: the address of the
admin, without /admin.
| Parameter | Value |
|---|---|
| channel | A channel's code. Default: the default channel. |
| locale | A locale, such as en-GB. Default: the channel's default locale. A locale the channel lacks falls back to another of the same language, then to the default. |
| currency | A currency code, such as GBP. Default: the channel's default currency, also used for a currency the channel doesn't sell in. |
| token | The feed token, when one is set. |
Examples:
# The default channel, in its default locale and currency
https://shop-api.example.com/plugins/google-shopping/feed.xml
# The eu channel in German, priced in euros
https://shop-api.example.com/plugins/google-shopping/feed.xml?channel=eu&locale=de-DE¤cy=EUR
# The eu channel in English, priced in pounds, with a feed token
https://shop-api.example.com/plugins/google-shopping/feed.xml?channel=eu&locale=en-GB¤cy=GBP&token=…The answer is the feed, as application/xml; charset=utf-8, unless:
- the token is missing or wrong: 401;
- the channel doesn't exist or isn't active: 404;
- the plugin is disabled, or products can't be linked to: 503. Links fail when the Product URL uses
{storefront}and the channel has no storefront URL, or when it doesn't makehttporhttpslinks. The JSON answer says which.
If reading the catalog fails part-way, the response is broken off rather than ended early. A feed that looked complete would make Google remove the products it lacks.
Field mapping
| Google attribute | Value |
|---|---|
| g:id | The variant's SKU. Google allows 50 characters. |
| g:item_group_id | The product's ID, when it has more than one variant. |
| title | The variant's name, such as "Oxford Shirt – Blue / M", or the product's name when it has one variant. At most 150 characters. |
| description | The product's description as plain text: tags dropped, entities decoded, paragraphs and list items on lines of their own. Else its SEO description, else the title. At most 5,000 characters. |
| link | The product's page, from Product URL. |
| g:image_link | The variant's own image, else the product's first image. |
| g:additional_image_link | Up to 10 of the product's other images, leaving out the images of other variants. Videos aren't sent. |
| g:availability | in_stock when the variant can be bought, else out_of_stock. |
| g:price | The compare-at price when there is one above the price, else the price, such as 59.00 GBP. |
| g:sale_price | The price, when there is a compare-at price above it. |
| g:brand | The display value of the brand attribute. At most 70 characters. |
| g:gtin | The barcode, when it is a valid GTIN (below). |
| g:identifier_exists | no when there is neither a brand nor a GTIN. |
| g:condition | new. |
| g:color, g:size | The variant's option for the color or size attribute, by its label, such as Blue or M. |
| g:product_type | The path of the product's first category, such as Men > Shirts. |
| g:shipping_weight | The variant's weight, such as 250 g. |
- Prices are what a guest pays on the storefront, including tax when the channel's prices do.
They're written with the currency's decimals:
59.00 GBP,1200 JPY,5.250 KWD. - GTINs. A barcode is sent when, once spaces and dashes are dropped, it has 8, 12, 13 or 14 digits with a correct check digit. GS1's ranges for restricted circulation (02, 04 and 2) and for coupons (05, 98 and 99) aren't sent, as Google refuses them.
Limitations
- Vercel's 4.5 MB response cap. Vercel limits a function's response to 4.5 MB, which is about 5,000 items. An item takes 0.7 to 1 KB, more with a long description. The feed is streamed, and Vercel's guide to the cap says streamed responses are exempt, but its limits page makes no such exception. Check the first fetches of a large feed in Merchant Center.
- One feed per channel, locale and currency. Each feed URL has one channel's products in one language and one currency, so Merchant Center needs a data source for each.
- No shipping or tax attributes.
- Set shipping costs, and tax where it applies, in Merchant Center's settings. Google requires shipping costs in many countries, including the US, the UK, Germany and France.
- Google wants prices without tax in the US and Canada, and with VAT or GST everywhere else. The feed's prices include tax when the channel's do, so check the channel's setting.
- Fewer identifiers than Google can use. The feed sends no MPN, and doesn't know Google's
product categories. Outside clothing, Google asks for
identifier_existsnowhen a product has no GTIN and no MPN. A product with a brand but no GTIN gets Google's default instead,yes. - Other attributes aren't sent, such as
gender,age_group,materialandgoogle_product_category. Google requires gender and age group for clothing and accessories in several countries, including the US, the UK, Germany and France. - One link per product. Every variant links to its product's page, with no variant chosen.
- Updates wait for the fetch. Changes to prices and stock reach Google when it next fetches the feed, daily by default.
Develop
pnpm install
pnpm test
pnpm buildThe tests run the feed route on a Hono app, against a fake catalog. The plugin API it builds on is @spindlecommerce/plugin-sdk.
Release
Each version is staged on npm and goes live when a maintainer approves it.
- Raise
versioninpackage.jsonand insrc/index.ts. A test checks they match. - Commit, then tag and push: for example
git tag v1.0.1 && git push origin main v1.0.1. - The Release workflow tests, builds and stages the version, and creates the GitHub release.
- Approve it on npmjs.com, under Staged Packages, with 2FA.
The workflow publishes with npm's trusted publishing, so it needs no token. Before its first release, a maintainer links the package to the workflow:
npm trust github @spindlecommerce/plugin-google-shopping --repo spindlecommerce/plugin-google-shopping --file release.yml --allow-stage-publishRun it just before tagging: npm drops a link that isn't used within 48 hours.
License
MIT
