=== 2khat Catalog Sync === Contributors: mohammad61 Requires at least: 5.0 Tested up to: 7.0 Requires PHP: 7.4 Stable tag: 1.1.0 License: GPLv2 or later License URI: https://www.gnu.org/licenses/gpl-2.0.html Official WooCommerce catalog sync plugin for the 2khat platform. == Description == 2khat connects your WooCommerce store to the 2khat platform by exposing a secure, paginated REST API for product catalog synchronization. After registering your store with the 2khat team, install and activate this plugin to allow 2khat to fetch your published products and variations. == Installation == 1. Upload the `2khat` folder to `/wp-content/plugins/`. 2. Activate the plugin through the WordPress Plugins screen. 3. Ensure WooCommerce is installed and active. 4. Confirm `public_key.pem` is present in the plugin directory. == Security == `public_key.pem` is the 2khat RSA public key used to verify API request signatures. It is not a private credential and must remain in the plugin directory. == Authentication == All 2khat API requests require RSA domain signature verification. **Signing (2khat server side):** 1. Normalize the store domain: use the host from `site_url()`, without a `www.` prefix. 2. Sign the domain string with the 2khat private key using SHA256. 3. Base64-encode the binary signature. 4. Send it in the request header: `Authorization: Twokcs ` Legacy clients may also use `Authorization: Basic ` during migration. **Example (OpenSSL CLI):** ``` echo -n "example.com" | openssl dgst -sha256 -sign 2khat_private.pem | openssl base64 -A ``` == REST API == **GET** `/wp-json/twokcs/v1/version` Returns the plugin version. **GET** `/wp-json/twokcs/v1/products?page=1&per_page=100` Returns paginated published products with nested variations. **GET** `/wp-json/twokcs/v1/categories?page=1&per_page=100` Returns paginated product categories. **GET** `/wp-json/twokcs/v1/attributes?page=1&per_page=100` Returns paginated product attributes. **GET** `/wp-json/twokcs/v1/attribute-terms?page=1&per_page=100` Returns paginated attribute terms across all attributes. Optionally filter by attribute: **GET** `/wp-json/twokcs/v1/attribute-terms?attribute_id=1&page=1&per_page=100` **GET** `/wp-json/twokcs/v1/variants?product_ids=10,20` Returns compact published variants grouped by parent product ID. Each item includes parent product fields (`product_id`, `type`, `manage_stock`, `price`, `regular_price`, `sale_price`, `stock_quantity`, `stock_status`) and a `variants` array with: `id`, `on_sale`, `status`, `price`, `regular_price`, `sale_price`, `stock_quantity`, `stock_status`. Simple products return an empty `variants` array and their own price/stock on the parent object. **GET** `/wp-json/twokcs/v1/variants?ids=305,306,307` Returns compact published variants for the given variation IDs in request order. Response shape: `{ count, results: [...] }` with the same variant fields as above. **GET** `/wp-json/twokcs/v1/variant?id=305` Returns a single compact published variant object with the same fields as above. Returns 404 when the variation is missing or not published. **POST** `/wp-json/twokcs/v1/orders` Creates a WooCommerce order for the 2khat platform. Request body (JSON): * `external_order_id` (required) — platform order id, stored as `_twokcs_external_order_id` * `line_items` (required) — array of `{ product_id, variation_id?, quantity, external_item_id? }`; `external_item_id` is the platform line item id, stored as `_twokcs_external_item_id` and usable in `/order/update` * `billing` / `shipping` — address objects * `status` — WooCommerce status without `wc-` prefix (default: `processing`) * `customer_note` — optional note * `meta_data` — optional array of `{ key, value }` (public keys and `_twokcs_*` only) Returns **201** with a compact order object. Duplicate `external_order_id` returns **409** with `existing_order_id`. **GET** `/wp-json/twokcs/v1/order` Lists orders as `{ count, max_pages, results }`. All filters are optional and combinable: `id` (single or comma-separated, e.g. `id=12,13`), `external_order_id`, `status`, plus `page` / `per_page`. Bare `GET /order` returns a paginated list. No matches return an empty `results` array (200). If both `id` and `external_order_id` are provided and refer to different orders, the request returns 400. Examples: * `/wp-json/twokcs/v1/order` * `/wp-json/twokcs/v1/order?status=processing` * `/wp-json/twokcs/v1/order?id=123` * `/wp-json/twokcs/v1/order?id=123,124,125` * `/wp-json/twokcs/v1/order?external_order_id=2khat-12345` * `/wp-json/twokcs/v1/order?id=123&status=processing` Each result includes: `id`, `status`, `external_order_id`, `tracking_code`, `tracking_provider`, `shipping_method`, `date_created`, `date_modified`, `total`, `currency`, `line_items` (each with `id`, `external_item_id`, `product_id`, `variation_id`, `name`, `quantity`, `total`). **POST** `/wp-json/twokcs/v1/order/update` Changes line items of a 2khat order in `pending`, `processing`, or `on-hold` status (including paid orders). Request body (JSON): * `id` and/or `external_order_id` (one required) — the order to update * `line_items` (required) — array where each entry is either: * `{ id, quantity }` — change an existing line item (`id` from the order's `line_items[].id`); `quantity: 0` removes it * `{ external_item_id, product_id, variation_id?, quantity }` — change the line item created with that `external_item_id`. For orders created before 1.1.0, falls back to the single untagged line item with the same `product_id` / `variation_id`. An unknown `external_item_id` with quantity > 0 adds a new item; with quantity 0 it is ignored * `{ product_id, variation_id?, quantity }` — add a new item * `reason` — optional text added to the order note Items not listed stay unchanged. All entries are validated before any change is applied. Existing items keep their original unit price; new items use the current product price. Stock is adjusted when the order has already reduced stock. Totals are recalculated and an order note records the change; no refund is issued. Returns **200** with the compact order object plus `previous_total`. Removing every item returns **400** (`twokcs_order_empty`) — use cancel instead. Unknown order or line item returns **404**; non-editable status returns **409**. **POST** `/wp-json/twokcs/v1/order/cancel` Cancels a whole 2khat order in `pending`, `processing`, or `on-hold` status. WooCommerce restores reduced stock. No refund is issued. Request body (JSON): `id` and/or `external_order_id` (one required), optional `reason`. Returns **200** with the compact order object (also when the order is already cancelled). Unknown order returns **404**; other statuses return **409**. Only orders created through `POST /orders` (carrying `_twokcs_external_order_id`) can be updated or cancelled. Query parameters (all catalog endpoints): * `page` — page number (default: 1, minimum: 1) * `per_page` — items per page (default: 100, maximum: 200) * `attribute_id` — (attribute-terms only) filter terms to a single attribute * `product_ids` — (variants only) comma-separated parent product IDs; returns variants grouped by product * `ids` — (variants only) comma-separated variation IDs; returns a flat list in request order * `id` — (variant / order) single variation ID, or WooCommerce order ID(s), comma-separated for orders * `external_order_id` — (order only) filter by 2khat external order id meta * `status` — (order only) optional WooCommerce status filter * `debug` — include SQL query stats only when `WP_DEBUG` and `TWOKCS_DEBUG` are enabled, or when the `twokcs_allow_debug` filter returns true. Full query details require `SAVEQUERIES` enabled in `wp-config.php`. == Changelog == = 1.1.0 = * Add `POST /order/update` to change, remove, or add line items on pending/processing/on-hold orders * Add `POST /order/cancel` to cancel an order and restore stock * Store `external_item_id` on line items created via `POST /orders` and return it in order responses * `GET /order` accepts comma-separated `id` values = 1.0.9 = * Include parent product type, stock, and prices on GET /variants?product_ids= = 1.0.8 = * `GET /order` returns a paginated list; `id`, `external_order_id`, and `status` are optional filters = 1.0.7 = * Add create order (`POST /orders`) and track order (`GET /order`) endpoints = 1.0.6 = * Align text domain with WordPress.org slug `2khat-catalog-sync` = 1.0.5 = * Rename all plugin globals from `dokhat` to `twokcs` prefix for WordPress.org compliance * Update REST namespace to `twokcs/v1` and auth scheme to `Twokcs` = 1.0.4 = * Align plugin folder and main file name with WordPress.org slug `2khat` * Fix auth header documentation to match the `Dokhat` scheme accepted by the plugin = 1.0.3 = * Rename display name to 2khat Catalog Sync (WordPress.org trademark requirement) = 1.0.2 = * Rename plugin for WordPress.org compliance (2khat WooCommerce Catalog Sync) * Remove runtime SAVEQUERIES global side effects from debug mode * Document public_key.pem as intentional RSA public key = 1.0.1 = * Align plugin metadata for WordPress.org submission = 1.0.0 = * Initial 2khat release * Modular plugin architecture * RSA domain authentication with 2khat key pair * Optimized bulk product export endpoint * Compatibility with Discount Rules for WooCommerce and Woodmart theme