Skip to content

Google Merchant Center Feed

Flow ID: IN-06 | Module(s): feeds | Complexity: Medium Last Updated: 2026-04-04

Business Overview

The Google Merchant Center integration generates an RSS 2.0 product feed with the g: (Google Base) namespace for submission to Google Shopping. The feed controller (AdvGoogleCatalog) extends the shared AdvXml base class and produces XML output via OutputGoogleMerchantXml. It supports sale price differentiation, multi-image output, custom price modifiers, currency conversion, multi-language category breadcrumbs, and token-based access protection.

This feed is one of the most reused output formats in the platform -- the OutputGoogleMerchantXml class is also inherited by the Facebook Catalog, Criteo Catalog, Manago, and API Search feeds.

API Reference

Feed Endpoint

MethodURLAuthResponse
GET/xml/google-catalogToken query param (optional)RSS 2.0 XML

Query Parameters:

ParamTypeDescription
tokenstringRequired when IS_PROTECTED_GOOGLE is enabled
brandint/arrayFilter by brand ID(s)
categoryint/arrayFilter by category ID(s)
topintLimit to top N sellers
stockintLimit to top N by stock
currstringCurrency code (e.g., EUR, USD)

XML Schema

xml
<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:g="http://base.google.com/ns/1.0" version="2.0">
  <channel>
    <title>{Brand} Products</title>
    <link><![CDATA[{feed_url}]]></link>
    <description>{Brand} Products Catalog List</description>
    <item>
      <g:id><![CDATA[{prefix}{product_code_id}]]></g:id>
      <g:title><![CDATA[{name}]]></g:title>
      <g:description><![CDATA[{description_truncated_5000}]]></g:description>
      <g:google_product_category><![CDATA[{general_category}]]></g:google_product_category>
      <g:link><![CDATA[{product_url}]]></g:link>
      <g:gtin><![CDATA[{barcode}]]></g:gtin>
      <g:image_link><![CDATA[{main_image_url}]]></g:image_link>
      <g:condition><![CDATA[new]]></g:condition>
      <g:price><![CDATA[{price} {currency}]]></g:price>
      <g:sale_price><![CDATA[{sale_price} {currency}]]></g:sale_price>
      <g:brand><![CDATA[{vendor_name}]]></g:brand>
      <g:availability><![CDATA[in stock|out of stock]]></g:availability>
      <g:additional_image_link><![CDATA[{image_url}]]></g:additional_image_link>
      <g:custom_label_0><![CDATA[{line_name}]]></g:custom_label_0>
      <g:product_type><![CDATA[{category_breadcrumb}]]></g:product_type>
    </item>
  </channel>
</rss>

Code Flow

Controller Inheritance

CI_Controller
  -> AppController (application/core/)
    -> Adv_base_controller (ecommercen/core/)
      -> Base_c (application/core/)
        -> AdvXml (ecommercen/feeds/core/)
          -> AdvGoogleCatalog (ecommercen/feeds/controllers/)
            -> Google_catalog (application/controllers/) [thin wrapper]

Request Processing

  1. Route: application/config/routes.php maps /xml/google-catalog and /{lang}/xml/google-catalog to Google_catalog/index
  2. Security (secureXml()): Checks XML_FEEDS.IS_ENABLED_GOOGLE registry flag. If IS_PROTECTED_GOOGLE is enabled, validates token query param against XML_FEEDS.GOOGLE_TOKEN
  3. Data Fetch (getDbData()): Calls $this->product_model->getFeedProductsForGoogle() with optional brand/category/top/stock filters
  4. Enrichment (getProductData() in AdvXml): Merges live pricing, product codes/SKUs, and per-code images via getLiveProductsParsed(), getProductsCodesParsed(), getProductsCodeImagesParsed()
  5. Parsing (parseForXml()): Loads category hierarchy via product_category_model->get_childs_combo() with PSCache TTL
  6. Item Mapping (parseItem()): Transforms each product into the g: namespace array (see Field Mapping below)
  7. Output (OutputGoogleMerchantXml->output()): Renders RSS 2.0 XML with CDATA wrapping and sends Content-Type: text/xml header

Price Logic

customPrice = registry('XML_FEEDS', 'CUSTOM_PRICE_GOOGLE')

if (customPrice):
    finalPrice = product.final_price + round(final_price * priceModifier / 100, 2)
else:
    finalPrice = product.final_price

g:price = applyCurrencyRate(finalPrice, currency.rate) + " " + currency.code

if (final_price < original_price):
    g:price = applyCurrencyRate(original_price, rate) + " " + code    # original
    g:sale_price = applyCurrencyRate(final_price, rate) + " " + code  # discounted

Product ID Generation

Product IDs are prefixed via a registry-configurable prefix for Google tracking alignment:

php
// ecommercen/helpers/tracking_helper.php
function productIdToGoogleId($productId) {
    return registry->value('TRACKING', 'PRODUCT_ID_PREFIX_GOOGLE') . $productId;
}

The dbIdKey is loaded from config('xmlDbIdKey')['googleCatalog'], determining which product identifier column is used (product code ID vs product ID).

Architecture

File Map

FilePurpose
application/controllers/Google_catalog.phpThin CI router wrapper
ecommercen/feeds/controllers/AdvGoogleCatalog.phpFeed controller -- security, data fetch, item parsing
ecommercen/feeds/core/AdvXml.phpBase feed class -- enrichment pipeline, currency, attribute caches
src/Feeds/Output/OutputGoogleMerchantXml.phpRSS 2.0 XML renderer with g: namespace
src/Feeds/Output/OutputGenericXml.phpBase output class -- XML generation, CDATA wrapping
src/Feeds/Output/OutputXmlConfig.phpXML config VO (root element, collection, item names)
src/Feeds/Interfaces/OutputInterface.phpOutput contract
src/Feeds/Feed.phpFeed orchestrator (data + output)
ecommercen/helpers/tracking_helper.phpproductIdToGoogleId() helper

Output Class Hierarchy

OutputInterface
  -> OutputGenericXml
    -> OutputGoogleMerchantXml   # RSS 2.0 with g: namespace, array support for additional_image_link

OutputGoogleMerchantXml overrides two methods:

  • addItem(): Handles array values (e.g., multiple g:additional_image_link elements) by iterating and wrapping each in CDATA
  • createXml(): Wraps output in <rss xmlns:g="http://base.google.com/ns/1.0" version="2.0"><channel> instead of the generic XML structure

Data Model

Field Mapping

Google FieldSourceNotes
g:idproductIdToGoogleId(product.{dbIdKey})Registry-prefixed ID
g:titleproduct.nameHTML-encoded, XML-sanitized
g:descriptionproduct.descriptionTruncated to 5000 chars, sanitized
g:google_product_categoryTranslation key hct_xml_general_product_categoryStatic category from language files
g:linkresolveProductUrl(product)Vendor slug or category slug path, with currency param
g:gtinproduct.barcodeEAN/GTIN from product data
g:image_linkgetMainProductImage(product.images)Primary product image
g:additional_image_linkRemaining unique imagesArray -- produces multiple XML elements
g:conditionHardcoded newAll products treated as new
g:pricefinalPrice with currency rateFormat: {amount} {code}
g:sale_pricefinal_price when discountedOnly present when final_price < original_price
g:brandproduct.vendor_nameManufacturer/brand name
g:availabilityStock checkin stock if stock > 0 or negative_stock allowed
g:custom_label_0product.line_nameProduct line, only if present
g:product_typeCategory breadcrumbsComma-separated, > delimited hierarchy

Key Database Tables

TableRole
shop_productProduct master data
shop_product_muiMulti-language product names/descriptions
product_codesSKU-level data (stock, barcodes, images)
shop_vendor / shop_vendor_muiBrand/manufacturer data
shop_product_category / shop_product_category_muiCategory hierarchy

Configuration

Registry Keys (XML_FEEDS Group)

KeyTypePurpose
IS_ENABLED_GOOGLEboolMaster feed toggle
IS_PROTECTED_GOOGLEboolEnable token authentication
GOOGLE_TOKENstringExpected token value for protected access
CUSTOM_PRICE_GOOGLEboolEnable custom price modifier (applies priceModifier percentage)

Registry Keys (TRACKING Group)

KeyTypePurpose
PRODUCT_ID_PREFIX_GOOGLEstringPrefix prepended to product IDs in feed

Config Files

FileKeyPurpose
application/config/routes.phpxml/google-catalogURL routing
CI configxmlDbIdKey.googleCatalogWhich product identifier column to use

Client Extension Points

  1. Override controller: Create application/controllers/Google_catalog.php (already exists as thin wrapper -- override methods like parseItem() for custom field mapping)
  2. Custom price logic: Enable XML_FEEDS.CUSTOM_PRICE_GOOGLE and configure per-product priceModifier values
  3. Product filtering: Use query params brand, category, top, stock to segment feeds
  4. Currency: Pass curr param to generate feeds in different currencies
  5. Product ID format: Configure TRACKING.PRODUCT_ID_PREFIX_GOOGLE to match Google Ads account requirements
  6. Category override: Customize the hct_xml_general_product_category translation key per language

Business Rules

  1. Feed disabled: Returns 403 when IS_ENABLED_GOOGLE is falsy
  2. Token mismatch: Returns 403 when protection is enabled and token does not match
  3. Sale price logic: g:sale_price only appears when final_price < original_price; g:price then shows original price
  4. Custom price modifier: When enabled, adds a percentage-based markup to final_price before applying sale logic
  5. Stock availability: Products with stock > 0 or negative_stock flag set report as in stock
  6. Description truncation: Descriptions capped at 5000 characters (Google Merchant Center limit)
  7. Image deduplication: Additional images exclude the main image and are deduplicated
  8. Currency rate: All prices converted using active currency rate via applyCurrencyRate()