Appearance
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
| Method | URL | Auth | Response |
|---|---|---|---|
| GET | /xml/google-catalog | Token query param (optional) | RSS 2.0 XML |
Query Parameters:
| Param | Type | Description |
|---|---|---|
token | string | Required when IS_PROTECTED_GOOGLE is enabled |
brand | int/array | Filter by brand ID(s) |
category | int/array | Filter by category ID(s) |
top | int | Limit to top N sellers |
stock | int | Limit to top N by stock |
curr | string | Currency 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
- Route:
application/config/routes.phpmaps/xml/google-catalogand/{lang}/xml/google-catalogtoGoogle_catalog/index - Security (
secureXml()): ChecksXML_FEEDS.IS_ENABLED_GOOGLEregistry flag. IfIS_PROTECTED_GOOGLEis enabled, validatestokenquery param againstXML_FEEDS.GOOGLE_TOKEN - Data Fetch (
getDbData()): Calls$this->product_model->getFeedProductsForGoogle()with optional brand/category/top/stock filters - Enrichment (
getProductData()inAdvXml): Merges live pricing, product codes/SKUs, and per-code images viagetLiveProductsParsed(),getProductsCodesParsed(),getProductsCodeImagesParsed() - Parsing (
parseForXml()): Loads category hierarchy viaproduct_category_model->get_childs_combo()with PSCache TTL - Item Mapping (
parseItem()): Transforms each product into theg:namespace array (see Field Mapping below) - Output (
OutputGoogleMerchantXml->output()): Renders RSS 2.0 XML with CDATA wrapping and sendsContent-Type: text/xmlheader
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 # discountedProduct 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
| File | Purpose |
|---|---|
application/controllers/Google_catalog.php | Thin CI router wrapper |
ecommercen/feeds/controllers/AdvGoogleCatalog.php | Feed controller -- security, data fetch, item parsing |
ecommercen/feeds/core/AdvXml.php | Base feed class -- enrichment pipeline, currency, attribute caches |
src/Feeds/Output/OutputGoogleMerchantXml.php | RSS 2.0 XML renderer with g: namespace |
src/Feeds/Output/OutputGenericXml.php | Base output class -- XML generation, CDATA wrapping |
src/Feeds/Output/OutputXmlConfig.php | XML config VO (root element, collection, item names) |
src/Feeds/Interfaces/OutputInterface.php | Output contract |
src/Feeds/Feed.php | Feed orchestrator (data + output) |
ecommercen/helpers/tracking_helper.php | productIdToGoogleId() helper |
Output Class Hierarchy
OutputInterface
-> OutputGenericXml
-> OutputGoogleMerchantXml # RSS 2.0 with g: namespace, array support for additional_image_linkOutputGoogleMerchantXml overrides two methods:
addItem(): Handles array values (e.g., multipleg:additional_image_linkelements) by iterating and wrapping each in CDATAcreateXml(): 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 Field | Source | Notes |
|---|---|---|
g:id | productIdToGoogleId(product.{dbIdKey}) | Registry-prefixed ID |
g:title | product.name | HTML-encoded, XML-sanitized |
g:description | product.description | Truncated to 5000 chars, sanitized |
g:google_product_category | Translation key hct_xml_general_product_category | Static category from language files |
g:link | resolveProductUrl(product) | Vendor slug or category slug path, with currency param |
g:gtin | product.barcode | EAN/GTIN from product data |
g:image_link | getMainProductImage(product.images) | Primary product image |
g:additional_image_link | Remaining unique images | Array -- produces multiple XML elements |
g:condition | Hardcoded new | All products treated as new |
g:price | finalPrice with currency rate | Format: {amount} {code} |
g:sale_price | final_price when discounted | Only present when final_price < original_price |
g:brand | product.vendor_name | Manufacturer/brand name |
g:availability | Stock check | in stock if stock > 0 or negative_stock allowed |
g:custom_label_0 | product.line_name | Product line, only if present |
g:product_type | Category breadcrumbs | Comma-separated, > delimited hierarchy |
Key Database Tables
| Table | Role |
|---|---|
shop_product | Product master data |
shop_product_mui | Multi-language product names/descriptions |
product_codes | SKU-level data (stock, barcodes, images) |
shop_vendor / shop_vendor_mui | Brand/manufacturer data |
shop_product_category / shop_product_category_mui | Category hierarchy |
Configuration
Registry Keys (XML_FEEDS Group)
| Key | Type | Purpose |
|---|---|---|
IS_ENABLED_GOOGLE | bool | Master feed toggle |
IS_PROTECTED_GOOGLE | bool | Enable token authentication |
GOOGLE_TOKEN | string | Expected token value for protected access |
CUSTOM_PRICE_GOOGLE | bool | Enable custom price modifier (applies priceModifier percentage) |
Registry Keys (TRACKING Group)
| Key | Type | Purpose |
|---|---|---|
PRODUCT_ID_PREFIX_GOOGLE | string | Prefix prepended to product IDs in feed |
Config Files
| File | Key | Purpose |
|---|---|---|
application/config/routes.php | xml/google-catalog | URL routing |
| CI config | xmlDbIdKey.googleCatalog | Which product identifier column to use |
Client Extension Points
- Override controller: Create
application/controllers/Google_catalog.php(already exists as thin wrapper -- override methods likeparseItem()for custom field mapping) - Custom price logic: Enable
XML_FEEDS.CUSTOM_PRICE_GOOGLEand configure per-productpriceModifiervalues - Product filtering: Use query params
brand,category,top,stockto segment feeds - Currency: Pass
currparam to generate feeds in different currencies - Product ID format: Configure
TRACKING.PRODUCT_ID_PREFIX_GOOGLEto match Google Ads account requirements - Category override: Customize the
hct_xml_general_product_categorytranslation key per language
Business Rules
- Feed disabled: Returns 403 when
IS_ENABLED_GOOGLEis falsy - Token mismatch: Returns 403 when protection is enabled and token does not match
- Sale price logic:
g:sale_priceonly appears whenfinal_price < original_price;g:pricethen shows original price - Custom price modifier: When enabled, adds a percentage-based markup to
final_pricebefore applying sale logic - Stock availability: Products with
stock > 0ornegative_stockflag set report asin stock - Description truncation: Descriptions capped at 5000 characters (Google Merchant Center limit)
- Image deduplication: Additional images exclude the main image and are deduplicated
- Currency rate: All prices converted using active currency rate via
applyCurrencyRate()
Related Flows
- IN-01 Feed Generation -- Base feed framework shared by all XML feeds
- IN-07 Facebook Catalog -- Extends this feed (inherits 95% of logic)
- AD-02 Product Management -- Product data that populates feed fields