# Full app QA report (Descrybe v2) **Date:** 2026-08-04 **Repo:** `f:/laragon/www/_MY/descrybe-v2` **Wave:** 20 parallel agents (credits, analytics, Stripe, tutorial, SEO copy, Woo, E2E, Free tier, settings, products, catalog, admin/API, security, dashboard, processing, exports, nav) + final reconcile **Build gates (reconcile):** `go build ./...` → **PASS** · `npm run check` (apps/web) → **0 errors / 0 warnings** --- ## Executive summary The 20-agent QA wave is complete. Descrybe v2 is green to demo locally: credits and analytics now read the **live wallet** (no more stale cycle inflation), Stripe Checkout works in **mock** (`STRIPE_MOCK=true`) and is ready for real keys, the in-app tutorial is forced-on-actions but skippable, Woo has a seed path without a live shop, and both catalog and marketing E2E suites passed. **Local Demo Co is back on Enterprise** after mock Stripe checkout temporarily assigned a paid self-serve plan — restored to ~1M AI credits / Unlimited packaging for continued demo use. | You want… | Status | |-----------|--------| | Demo the product end-to-end | **Ready** — login below | | Buy a plan in prod | Set Stripe env (see § Stripe) | | Connect a real Woo store | UI `/woocommerce` or `WOO_*` env (see § Woo) | | Try Woo audiences without WP | `make seed-woo` / `seed-woo-demo` | **Still not production-cutover:** FTP feed sync, daily credit ledger, live Stripe/Woo untested in this wave, and cutover gates in [cutover.md](cutover.md). --- ## Demo login (use this) | | | |---|---| | Email | `demo@descrybe.local` (alias `demo@descrybe.test` also works) | | Password | `DemoPass123!` | | Company | **Local Demo Co** (`ee246275-dec0-4446-9e83-58d0c16c258a`) | | Plan | **Enterprise** · ~1,000,000 AI credits · `can_use_ai=true` (restored after mock Stripe) | | Platform admin | yes | | Web | http://127.0.0.1:5174/login | | API | http://127.0.0.1:8080 | | Docs | http://127.0.0.1:5174/docs | | Demo API key | `dk_demo_local_descrybe_test_key_v1` | Do **not** use this account or key in production. Details: [demo-user.md](demo-user.md). --- ## Executive matrix | Area | Verdict | Evidence doc | |------|---------|--------------| | Credits / billing | **PASS** (bugs fixed) | [billing-credits-audit.md](billing-credits-audit.md) | | Analytics / usage | **PASS** (bugs fixed) | [analytics-audit.md](analytics-audit.md) | | Stripe Checkout / portal / webhooks | **PASS** (mock + real path; demo Enterprise restored after mock) | [stripe-setup.md](stripe-setup.md) | | Free tier gates | **PASS** | [free-tier-verify.md](free-tier-verify.md) · [free-tier.md](free-tier.md) | | Tutorial (forced + skippable) | **PASS** | [tutorial.md](tutorial.md) | | SEO / marketing copy | **PASS** | sibling SEO pass + `/pricing` polish | | WooCommerce | **PASS** (seed path; live optional) | [woocommerce-demo.md](woocommerce-demo.md) | | E2E feeds → process → export | **PASS** | [e2e-feeds-process-export.md](e2e-feeds-process-export.md) | | E2E marketing (brand/SEO/campaign/email) | **PASS** | [e2e-marketing.md](e2e-marketing.md) | | Processing UI | **PASS** | [e2e-processing.md](e2e-processing.md) | | Admin / health / public API | **PASS** 20/20 | [api-surface-smoke.md](api-surface-smoke.md) | | Security (Stripe webhook / SSRF) | **PASS** (hardened) | [security-notes.md](security-notes.md) | | Settings / API keys / invites | **PASS** | sibling settings QA | | Products / categories / attributes | **PASS** (UX fixes) | siblings | | Export public URLs | **PASS** | sibling export QA | | AI (Green Chat LAN) | **PASS** when `:8767` open | [ai-full-smoke.md](ai-full-smoke.md) · [green-chat-smoke.md](green-chat-smoke.md) | --- ## 1. Credits & billing **Verdict:** Demo Enterprise wallet and Billing UI agree: **1,000,000 remaining / 0 used**. Free grants **0** AI credits. ### What was wrong 1. `/api/billing/usage` preferred stale `billing_cycles` rows → showed e.g. **22 credits used** while wallet was 1M/0. 2. Campaign AI swallowed `ConsumeCredits` errors (silent free AI). 3. SEO AI only checked `CanUseAI` (true on paid with empty wallet). 4. Billing page fake date-range + false “Out of credits” on Free. ### Fixes - `UsageSummary` always uses live `credit_balances` for credits; range filters products/tokens only. - `AssignPlan` closes open cycles and opens a fresh cycle; `ConsumeCredits` updates only open cycles. - Campaign/SEO require `RemainingCredits ≥ 1`; debit after AI; map insufficient → **402**. - Billing UI: real `?range=`, Free info banner, used/total + Enterprise “Unlimited” label. ### Smoke ```text CREDITS plan=Enterprise total=1000000 used=0 rem=1000000 USAGE matches /auth/me AFTER_FREE → rem=0 can_use_ai=false AFTER_ENT → rem=1000000 ``` Full write-up: [billing-credits-audit.md](billing-credits-audit.md). --- ## 2. Analytics & usage **Verdict:** Tenant + admin meters match live Postgres for Local Demo Co. | Meter | API | DB | |-------|-----|-----| | Wallet used / total | 0 / 1_000_000 | match | | Products (all) | ~4326 | match | | Input / export feeds | 12 / 5 | match | | Plan cycle | Aug → Sep 2026 | `company_plans` | Admin analytics now includes feed counts and real `ai_provider_mode` rollups (after API rebuild). Details: [analytics-audit.md](analytics-audit.md). --- ## 3. Stripe (purchase path) **Verdict:** Checkout + Customer Portal + signed webhooks implemented. Local works in **mock** without a Stripe account. ### Configure for real 1. Set in **root** `.env` (never commit secrets; placeholders in `.env.example`): | Variable | Purpose | |----------|---------| | `STRIPE_SECRET_KEY` | `sk_test_…` / `sk_live_…` | | `STRIPE_WEBHOOK_SECRET` | `whsec_…` | | `STRIPE_MOCK` | `false` for live | | `STRIPE_PRICE_STARTER_MONTHLY` / `_YEARLY` | Price IDs | | `STRIPE_PRICE_GROWTH_MONTHLY` / `_YEARLY` | Price IDs | | `STRIPE_PRICE_BUSINESS_MONTHLY` / `_YEARLY` | Price IDs | | `WEB_ORIGIN` | e.g. `https://app.example.com` (Checkout return URLs) | 2. Stripe Dashboard: Products + Prices for Starter / Growth / Business; enable Customer Portal. 3. Webhook → `https:///api/webhooks/stripe` Events: `checkout.session.completed`, `customer.subscription.created|updated|deleted`. 4. Local: `stripe listen --forward-to localhost:8080/api/webhooks/stripe` → paste `whsec_…`. 5. Apply migration `016_stripe_billing.sql` (`pwsh -File scripts/migrate.ps1`), restart API. ### Mock local QA `STRIPE_MOCK=true` → `POST /api/billing/checkout` assigns plan + credits and returns `mock:true`. Empty secret alone no longer free-upgrades (security harden). **Note:** Mock checkout on Local Demo Co temporarily moved the tenant off Enterprise (e.g. to Starter). Enterprise was **restored** afterward so demo stays on ~1M credits / Unlimited. Re-run `go run ./cmd/seed-demo` (or admin AssignPlan Enterprise) if mock Checkout is used again on the demo company. Enterprise stays sales-led (no self-serve price). UI: `/plans`, `/billing`. Full guide: [stripe-setup.md](stripe-setup.md). --- ## 4. WooCommerce **Verdict:** Product push + orders/reviews pull + campaign audiences work. Live shop optional; **seed path** covers demo without WordPress. ### Configure for real 1. WooCommerce → Settings → Advanced → REST API → Read/Write key. 2. Descrybe `/woocommerce` → enable sync → Store URL + `ck_…` / `cs_…` → Save → Test Connection. 3. Queue product sync, then Orders / Reviews; keep **worker** running. 4. Optional env (or UI only): `WOO_STORE_URL`, `WOO_CONSUMER_KEY`, `WOO_CONSUMER_SECRET` (aliases `WOOCOMMERCE_*` also work). Encrypt at rest needs `CREDENTIALS_ENCRYPTION_KEY`. 5. SSRF: `http` allowed only for localhost/loopback; remote shops need https (or tunnel). ### No live shop? ```powershell cd apps/api $env:DATABASE_URL = "postgres://descrybe:descrybe@localhost:5433/descrybe?sslmode=disable" go run ./cmd/seed-woo-demo -postgres $env:DATABASE_URL -company "Local Demo Co" # or: make seed-woo ``` Seeds Demo Electronics products, 4 orders, 3 reviews, draft “purchased” campaign. Details: [woocommerce-demo.md](woocommerce-demo.md). --- ## 5. Tutorial **Verdict:** Forced action tour with Skip / Pause / Resume / Restart. No free Continue on action steps. - Progress: `localStorage` key `descrybe.tutorial.v2` - 15 steps: welcome → standard fields → feeds map/auto-map/save/sync-sample → products → export → campaigns → done - Advance only on real clicks (`data-tour`) or successful `tutorial.reportAction(…)` - Header + dashboard: Start / Resume / Restart Manual checklist: [tutorial.md](tutorial.md). --- ## 6. E2E results ### Feeds → map → process → export — **PASS** | Check | Result | |-------|--------| | 12 input feeds mapped | PASS | | Process job 5/5 steps | PASS (worker required) | | Export XML/CSV generate | PASS (~4322 products) | | Public token downloads | PASS | | Mapping PUT nested `{key,mapping}` | Fixed this wave | FTP/FTPS supplier sync still **400** (not supported). Migrated data already present so process/export work without re-sync. Details: [e2e-feeds-process-export.md](e2e-feeds-process-export.md). ### Marketing (brand / SEO / campaign / email / calendar) — **PASS** (14/14) Against Green Chat `overloaded-local` @ `192.168.50.181:8767`: | Step | Result | |------|--------| | Brand kit PUT/GET + tips | PASS | | SEO `mode=ai` | PASS (~1s, brand terms, credits charged) | | Campaign `mode=ai` + template | PASS | | Email / campaign `dry_run` | PASS | | Calendar prepare Black Friday | PASS (path fix: `/api/marketing/calendar/prepare`) | Details: [e2e-marketing.md](e2e-marketing.md). ### Processing UI — **PASS** Create → cancel (steps marked cancelled) → enhance_only via Green Chat (~1s). `/tasks` → 307 `/processing`. Details: [e2e-processing.md](e2e-processing.md). ### Free tier (while demo is Enterprise) — **PASS** New register → Free / 0 credits / `can_use_ai=false`; AI-only jobs → **402**; normalize/full still runs with AI/EPREL skipped. Demo remains Enterprise. Details: [free-tier-verify.md](free-tier-verify.md). ### Admin / health / public API — **PASS** 20/20 Platform admin routes, `/healthz` `/readyz`, RapiDoc docs, OpenAPI, v1 CRUD smoke + 401 without key. Details: [api-surface-smoke.md](api-surface-smoke.md). --- ## 7. Other sibling fixes (this wave) | Area | Outcome | |------|---------| | **Security** | Stripe webhook always verified when `STRIPE_WEBHOOK_SECRET` set; mock purchases need explicit `STRIPE_MOCK`; generic webhook errors; SSRF notes in [security-notes.md](security-notes.md) | | **Billing / Plans UI** | Enterprise shows Unlimited; Checkout CTAs; no false Free out-of-credits banner | | **Dashboard** | Clear CTAs (tutorial / process / feeds / BF); clickable credit widgets; dead upload section removed | | **SEO / copy** | Plain ecommerce voice; SeoHead on privacy/terms/features; clearer empty states | | **Products** | Search includes processed/raw names; CSV ingest via Feeds only; server-side sort | | **Categories / attributes** | Attributes default `roots=1`; formula 404 UX fixed | | **Settings** | API keys CRUD, invites, email link; Copy Key no longer copies useless prefix | | **Export feeds** | Public routes mounted before session auth (401→404/405); absolute public URLs in UI | | **Nav / a11y / tutorial targets** | `data-tour` preserved for tour | --- ## 8. Known gaps | Gap | Notes | |-----|-------| | No daily **credit** ledger | Usage range filters products/tokens only; credit burn is wallet-level | | FTP/FTPS feed sync | Not supported; sync returns 400 for those supplier URLs | | Stripe live untested in this wave | Mock + unit/integration coverage; real Checkout needs keys above | | Live Woo optional | Seed covers audiences; live sync needs a real store + worker | | Email delivery | Prefer `force_dry_run` / `EMAIL_DRY_RUN`; stub SMTP test fails without a real listener | | Cutover / production | Staging data OK; production cutover still blocked — see [cutover.md](cutover.md) · [go-live-checklist.md](go-live-checklist.md) | | Migrated tenants | May hold legacy wallets; entitlements treat empty plan as Free-with-leftover-credits | | Campaign/SEO charge after LLM | Wallet gated first; mid-flight race can still burn tokens then fail debit | | Structured-descriptions / vector-categories | UI shells exist; backend still partial ([status-and-gaps.md](status-and-gaps.md)) | | CI | Makefile + unit tests; no GitHub Actions yet | --- ## 9. How to re-verify locally ```powershell # Postgres cd f:\laragon\www\_MY\descrybe-v2 docker compose up -d # :5433 # Build gates cd apps\api; go build ./... cd ..\web; npm run check # Runtime (separate terminals) # scripts\run-api.ps1 → API :8080 # make worker / go run ./cmd/worker # make web → :5174 # Login # demo@descrybe.local / DemoPass123! ``` Spot-check: 1. `/billing` → 1M remaining, usage matches wallet 2. `/plans` → Upgrade (mock Checkout if `STRIPE_MOCK=true`) 3. Start tutorial → action steps have no Continue 4. `/feeds` → map → `/processing` → `/export-feeds` generate 5. `/campaigns` + `/seo` AI (needs Green Chat + `OPENAI_*`) 6. `/woocommerce` or `make seed-woo` → orders/reviews + audience --- ## 10. Related docs index | Doc | Topic | |-----|-------| | [billing-credits-audit.md](billing-credits-audit.md) | Credits correctness | | [analytics-audit.md](analytics-audit.md) | Usage / admin analytics | | [stripe-setup.md](stripe-setup.md) | Stripe real + mock | | [woocommerce-demo.md](woocommerce-demo.md) | Woo live + seed | | [tutorial.md](tutorial.md) | Guided tour | | [e2e-feeds-process-export.md](e2e-feeds-process-export.md) | Catalog pipeline E2E | | [e2e-marketing.md](e2e-marketing.md) | Marketing AI E2E | | [e2e-processing.md](e2e-processing.md) | Background tasks | | [free-tier-verify.md](free-tier-verify.md) | Free vs Enterprise proof | | [api-surface-smoke.md](api-surface-smoke.md) | Admin / docs / v1 | | [demo-user.md](demo-user.md) | Credentials | | [security-notes.md](security-notes.md) | AuthZ / webhooks / SSRF | | [status-and-gaps.md](status-and-gaps.md) | Broader product gaps | | [ai-full-smoke.md](ai-full-smoke.md) | Green Chat AI matrix |