# WooCommerce demo & local setup Descrybe v2 pulls **products** (push), **orders**, and **reviews** from WooCommerce REST API, then uses synced orders for campaign audiences (`purchased` / `not_purchased`). ## Point at a Woo store 1. In WordPress/WooCommerce: **WooCommerce → Settings → Advanced → REST API** → Add key (Read/Write). 2. Copy **Consumer key** (`ck_…`) and **Consumer secret** (`cs_…`). 3. In Descrybe: open **`/woocommerce`** → enable sync → paste **Store URL** (https; `http://localhost` allowed for local) + credentials → **Save** → **Test Connection**. 4. Queue **product sync**, then **Orders** / **Reviews**. Run the worker (`make worker` / `go run ./cmd/worker`) so pending flags are claimed. ### Env vars (optional live path) Set in process env or untracked `.env` (never commit secrets): | Variable | Purpose | |---|---| | `WOO_STORE_URL` | Store base URL, e.g. `https://shop.example.com` or `http://localhost/myshop` | | `WOO_CONSUMER_KEY` | REST consumer key | | `WOO_CONSUMER_SECRET` | REST consumer secret | Aliases `WOOCOMMERCE_STORE_URL` / `WOOCOMMERCE_CONSUMER_KEY` / `WOOCOMMERCE_CONSUMER_SECRET` also work. Credentials saved via the UI are encrypted at rest (`CREDENTIALS_ENCRYPTION_KEY` — see [ops-runtime.md](ops-runtime.md)). ### Local Laragon / XAMPP Woo If you already run WordPress+Woo under Laragon, use that URL (often `http://localhost/...`). Descrybe allows **http only for localhost/loopback**; private LAN IPs are blocked by SSRF guards — tunnel or use public/https hosts for remote shops. ### No live Woo? Seed demo data Full WordPress+Woo docker is intentionally **not** bundled (heavy). Use the DB seed instead: ```powershell cd apps/api $env:DATABASE_URL = "postgres://descrybe:descrybe@localhost:5433/descrybe?sslmode=disable" # Optional: load CREDENTIALS_ENCRYPTION_KEY / TOKEN_SIGNING_SECRET from root .env so config encrypt matches the API go run ./cmd/seed-woo-demo -postgres $env:DATABASE_URL -company "Local Demo Co" ``` Or from repo root: `make seed-woo`. What it inserts (idempotent upserts): - Category **Demo Electronics** - 3 demo `processed_products` (`DEMO-WOO-*` SKUs) - 4 `woo_orders` + line items (3 buyers of Demo Electronics, 1 Accessories-only) - 3 `product_reviews` - `woocommerce_configs` row (placeholder URL unless `WOO_*` set) - Draft campaign **Woo demo — purchased electronics** with `audience_filter.type=purchased` If `WOO_*` is set, the seed encrypts those credentials, enables sync, and tests REST (`-live` fails closed on connection errors). ## APIs to exercise | Method | Path | Notes | |---|---|---| | GET | `/api/woocommerce` | Config + sync badges | | PUT | `/api/woocommerce` | Save store URL + credentials | | POST | `/api/woocommerce/test` | Live REST system status | | POST | `/api/woocommerce/sync` | Queue product push | | POST | `/api/woocommerce/sync-orders` | Queue orders pull | | POST | `/api/woocommerce/sync-reviews` | Queue reviews pull | | GET | `/api/woocommerce/orders` | List synced orders | | GET | `/api/woocommerce/reviews` | List synced reviews | | POST | `/api/woocommerce/audience` | `{ "bought_category": "Demo Electronics" }` | Campaign send resolves `audience_filter` with `type` + `category_ids` (UI) or `bought_category` (API). ## Sync → audience → campaigns 1. `go run ./cmd/seed-woo-demo` (or live sync orders). 2. UI: `/woocommerce` → Orders / Reviews populated. 3. `POST /api/woocommerce/audience` with `bought_category: "Demo Electronics"` → Anna / Ben / Cara. 4. `/campaigns` → open draft **Woo demo — purchased electronics** (purchased audience on Demo Electronics). 5. Wizard also exposes **Purchased in category** when any orders exist. ## Quick verify SQL ```sql SELECT count(*) FROM woo_orders WHERE company_id = (SELECT id FROM companies WHERE name = 'Local Demo Co'); SELECT count(*) FROM product_reviews WHERE company_id = (SELECT id FROM companies WHERE name = 'Local Demo Co'); SELECT name, audience_filter FROM email_campaigns WHERE name ILIKE 'Woo demo%'; ```