=== Market Feeds for WooCommerce — Rozetka, Prom, Epicentr, Kasta, Google, Meta ===
Contributors: catcode
Tags: woocommerce, rozetka, prom, product feed, google merchant center
Requires at least: 5.7
Tested up to: 7.1
Requires PHP: 7.4
Stable tag: 1.4.1
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

Export WooCommerce products as XML/YML feeds for Ukrainian marketplaces: Rozetka and Prom.ua for free; Epicentr, Kasta, Google, Meta, OpenAI (ChatGPT) and a UCP catalog for AI agents in Pro.

== Description ==

The plugin publishes your catalogue in the format Ukrainian marketplaces accept: a `yml_catalog` document with categories, offers, prices, stock, images and attributes. The settings screen is in English, Ukrainian and Russian (it follows the language of the WordPress admin).

**Free**

* Rozetka feed (`stock_quantity`) and Prom.ua feed (`quantity_in_stock`)
* Streaming generation through XMLWriter — a catalogue of tens of thousands of offers never sits in memory
* Variations exported as separate offers with `group_id`, variation attributes as `<param>`
* Sale price becomes `<price>`, the regular one becomes `<price_old>`
* Prices with or without tax, configurable number of decimals
* Stock quantity, stock threshold, backorders, custom availability labels
* Full / trimmed / no description, optional HTML stripping
* SKU or product ID as `vendorCode`, GTIN as `barcode`, brand from the WooCommerce brand taxonomy or an attribute
* Real dispatch time per product (or shop-wide): Prom.ua gets «Готово до відправки» only for goods that ship within a day
* Scheduled file cache and a token-protected feed URL
* `/llms.txt` for AI assistants: a short Markdown guide to the shop (categories, best sellers with price and stock, policy pages), off until switched on

**Pro**

* Epicentr feed in Epicentr's own XML (`yml_catalog > offers`, no `<shop>`): price and stock for the cabinet's daily auto-update, or the full version with name, description, photos and barcode; out-of-stock products marked `out_of_stock`, backorders as `under_the_order`
* Kasta feed
* Google Merchant Center and Meta (Facebook / Instagram) catalogue feeds: every variation is its own item with a shared `g:item_group_id`, `g:size` and `g:color`, sale price, GTIN, brand, category path and `g:min_handling_time` / `g:max_handling_time`
* OpenAI Product Feed for ChatGPT: gzipped JSON Lines per the OpenAI spec, one row per product or variation (`group_id`, `listing_has_variations`, `variant_dict`), shop prices without the marketplace multiplier, GTIN only with a valid check digit, seller and returns data
* Universal Commerce Protocol catalog: the business profile at `/.well-known/ucp` and the REST catalog search and lookup (protocol release 2026-08-25) — AI agents search the shop and read products, variants, prices and stock. Read-only: no agent checkout
* Filters: selected categories, selected brands, excluded product IDs
* Price multiplier (for example +5% to cover the marketplace fee)
* Product name prefix and suffix
* UTM tags on product links
* Hourly cache refresh

Pro unlocks with a licence key, or with a real 7-day trial key you request in one click from the settings screen (e-mail only, no card). The trial never starts by itself: a fresh install is simply the free tier.

When Pro lapses the shop is not left without feeds — Rozetka and Prom.ua keep being served, on the whole catalogue, and the XML files already generated are not deleted. Only what was paid for stops: Epicentr, Kasta, Google, Meta, OpenAI and the UCP catalog, filters, the price multiplier, name prefix/suffix, UTM tags and hourly refresh.

The key is checked against catcode.com.ua and re-checked once a day; if our server is unreachable Pro keeps working for another 14 days. Licences are annual (1–5 years) and include updates and support for that term.

== Installation ==

1. Install and activate the plugin.
2. Go to WooCommerce → Marketplace feeds.
3. Enable the marketplaces you need, adjust price and stock rules, save.
4. Copy the feed URL and paste it into your marketplace account.

== Frequently Asked Questions ==

= Does it work without pretty permalinks? =

Yes. The `?ccmf_feed=rozetka&token=…` form is always available; with permalinks enabled you also get `/ccmf-feed/rozetka.xml?token=…`.

= Can anyone download my catalogue? =

The feed is protected by a token in the URL. You can regenerate the token in one click — the old link stops working immediately.

= Will ChatGPT pick up the OpenAI feed by itself? =

No. OpenAI onboards product feeds for approved merchants only (apply at chatgpt.com/merchants) and takes them by SFTP upload or its API, not from a URL. The plugin produces the file in OpenAI's format and keeps it current; uploading it is done with the credentials OpenAI issues. OpenAI's standard upload currently targets the US market.

= What does the UCP catalog need from the server? =

HTTPS, WordPress at the root of the domain (not in a sub-folder), and a web server that passes `/.well-known/ucp` and `/wp-json/` to WordPress. The settings screen has a "check addresses" button that fetches both the way an agent would.

= Why does Epicentr say «Невідомий тег shop у рядку 3»? =

Epicentr does not read the Rozetka/Prom YML: its import and its stock auto-update take `yml_catalog > offers > offer` without a `<shop>` block. Since 1.4.1 the Epicentr feed is that XML. A shop that already served the Epicentr feed before 1.4.1 keeps the old YML after the update (the link may be in use) — switch "Epicentr feed format" in the settings; the link stays the same.

= Can the Epicentr feed create new products in the Epicentr cabinet? =

No. A new product there needs Epicentr's category, attribute-set and characteristic codes, which WooCommerce does not hold. The feed updates prices and stock (and, in the full version, names, descriptions and photos) of products already in the cabinet, matched by the offer ID = the WooCommerce product or variation ID in the cabinet's «Артикул». New products with characteristics are what the CatCode module «Epicentr Price and Stock Sync» does.

= How large a catalogue does it handle? =

Generation is streamed and paginated, so the limit is PHP execution time rather than memory. For large catalogues keep the cache on: the marketplace then downloads a ready file.

== Changelog ==

= 1.4.1 =
* Fixed (Pro): the Epicentr feed is now Epicentr's own XML — `yml_catalog date > offers > offer id available` with `price`, `price_old`, `availability` (in_stock / under_the_order / out_of_stock) and no `<shop>`, currencies or category list. The Rozetka-style YML it served before was rejected by the Epicentr import with «Невідомий тег shop у рядку 3».
* New (Pro): "Epicentr feed format" — "price and stock" (the short file of the cabinet's stock auto-update, the default), "full" (plus name, description with the tags Epicentr keeps, photos in one `<picture>` separated by «, », barcode) and the old YML.
* A shop that served the Epicentr feed before the update keeps the old YML byte for byte until the owner switches the format; the settings screen explains why. Fresh installs and shops that never switched the Epicentr feed on get the new XML.
* The Epicentr file always carries out-of-stock products (`available="false"`, `out_of_stock`): Epicentr does not touch a product that simply disappears from the file, so it would stay "in stock" there.
* Rozetka, Prom.ua, Kasta, Google, Meta and OpenAI feeds are byte-for-byte unchanged.

= 1.4.0 =
* New (Pro): OpenAI Product Feed for ChatGPT shopping — `openai.jsonl.gz`, JSON Lines in gzip as the OpenAI file-upload spec asks. Every product or variation is a row with the nine required fields; variations carry `group_id`, `listing_has_variations` and `variant_dict`. Prices are the shop's own (no price multiplier, no name prefix/suffix): ChatGPT sends the buyer to the product page and a different price there drops the offer. Rows without a brand, an image or a price are left out and counted on the settings screen instead of being padded with placeholders.
* New (Pro): Universal Commerce Protocol catalog. `/.well-known/ucp` publishes the business profile (release 2026-08-25, HTTPS only, cacheable, no redirect), and `/wp-json/ccmf-ucp/v1/catalog/search`, `/catalog/lookup` and `/catalog/product` answer agents with products, variants, option availability, prices in minor units and stock. Each request is negotiated as the spec requires: the platform profile from the `UCP-Agent` header is fetched over HTTPS without redirects, private addresses are refused, versions and capabilities are checked. Read-only — no carts, checkouts or orders.
* New (free): `/llms.txt`, a short Markdown guide to the shop for AI assistants (llmstxt.org format): categories, best sellers with price and stock, policy pages. Its headings follow the site language. A physical llms.txt left by an SEO plugin is detected and reported.
* New: "check addresses" on the settings screen fetches `/llms.txt` and `/.well-known/ucp` the way an agent would, so a server rule that never lets them reach WordPress shows up at once.
* Updates of the Pro build now come from catcode.com.ua (Dashboard → Updates).
* All new features are off after the update; the existing Rozetka, Prom.ua, Epicentr, Kasta, Google and Meta feeds are byte-for-byte unchanged.
* The interface is now written in English and ships with Ukrainian and Russian translations. A shop whose admin is in Ukrainian sees exactly the same text as before.
* Uninstall now also removes the run statistics of the Google, Meta and OpenAI feeds and the cached /llms.txt.
* A second copy of the plugin (for example a renamed folder) no longer breaks the site: it stays inactive and says so.

= 1.3.0 =
* Google Merchant Center product data specification of 14 April 2026: the feed now carries the cutoff time `g:handling_cutoff_time` (with `g:country` and the IANA `g:cutoff_timezone`), the fulfilment days `g:shipping_handling_business_days` and the minimum order value `g:minimum_order_value`. Without a cutoff time Merchant Center counts handling days from midnight, so an order placed at 18:00 was promised the same delivery date as a morning one, and the shop collected "delivery date mismatch" warnings.
* Fulfilment days are typed the way a person writes them — `Пн-Сб`, `Mon-Fri`, `M;W;F`, `MWRFS` — and are stored in Google's own notation. Empty means Google keeps assuming Mon-Fri.
* Minimum order value goes out only for products that cost no more than it: Google rejects the attribute on a product already above the minimum.
* The cutoff timezone defaults to the site's timezone, but only when that is a real IANA name: a site set to a bare `UTC+3` offset sends no timezone instead of a value Google would reject.
* All new fields are empty after the update, so a feed already accepted by Merchant Center does not change until they are filled in.

= 1.2.0 =
* New (Pro): Google Merchant Center and Meta catalogue feeds — RSS 2.0 with `g:` attributes. Variations go out as separate items grouped by `g:item_group_id` (the parent ID) with `g:size` and `g:color`, so the ad platforms show one product card instead of rotating ads on duplicates. Size and colour attributes are picked in the settings or recognised by name.
* New: real dispatch time. A "Строк відправки" field on the product's Shipping tab and a shop-wide default ("2-6" working days). Google gets `g:min_handling_time` / `g:max_handling_time`; the Prom.ua feed marks `in_stock="true"` («Готово до відправки») only for products that ship within one day.
* Default brand and Google product category for the Google/Meta feeds.

= 1.1.0 =
* Licensing moved to the CatCode licence server: key activation, daily re-check, 14-day offline grace, one activation slot per shop.
* Trial on request instead of automatically: a click and an e-mail issue a real 7-day key. A fresh install no longer pretends to be Pro.
* Pro fields stay in place, greyed out, with a `Pro` badge and a line saying what each one gives.
* One notice instead of a recurring trial banner: it appears only after a feed has actually been generated or fetched, and closing it closes it for good.
* Saving the settings form no longer wipes the licence key, status or trial flag.
* Fixed: switching the licence on or off now re-plans the cache cron in the same request, so hourly refresh starts (and stops) immediately.

= 1.0.0 =
* First release: Rozetka, Prom.ua, Epicentr, Kasta, scheduled cache, token-protected URLs.
