# GOLIVE — press-zone-backend

## Money, billing & credit integrity
- [ ] AC-01 [UNVERIFIED] — Production PayPal checkout credentials and plan mappings are configured. verify: start API with production environment and confirm `validatePayPalConfig()` reports `ok: true`.
- [ ] AC-02 [UNVERIFIED] — Subscription checkout creates a PayPal approval flow bound to a server-side checkout session. verify: complete a live checkout and poll `/v1/subscriptions/checkout-sessions/:id` for one-time license and API-key retrieval.
- [ ] AC-03 [BROKEN] — PayPal webhook signature verification uses original request bytes. Evidence: `api/src/server.ts:createServer` parses JSON before `api/src/routes/webhooks.ts` verifies `JSON.stringify(req.body)`.
- [ ] AC-04 [BROKEN] — Failed PayPal webhook handlers remain retryable. Evidence: `api/src/utils/webhookIdempotency.ts:withIdempotency` inserts event before handler, while `api/src/routes/webhooks.ts` catches failure and returns HTTP 200.
- [ ] AC-05 [WORKS] — Duplicate PayPal event IDs do not execute handlers twice. Evidence: `api/src/utils/webhookIdempotency.ts:withIdempotency` uses unique `PayPalEvent.paypal_event_id` and handles Prisma P2002.
- [ ] AC-06 [UNVERIFIED] — Admin refund requests reach PayPal and persist payment state. verify: refund a sandbox payment through `POST /v1/admin/transactions/payments/:id/refund` and reconcile PayPal plus database status.
- [ ] AC-07 [BROKEN] — Concurrent credit deductions cannot overspend an account. Evidence: `api/src/services/creditService.ts:deductCredits` reads balance through global `prisma` inside its transaction rather than transaction client or row lock.
- [ ] AC-08 [WORKS] — Payment-completion webhook creates payment, invoice, and credit allocation atomically. Evidence: `api/src/routes/webhooks.ts:handlePaymentCompleted` uses `prisma.$transaction` including `tx.invoice.create`.
- [ ] AC-09 [WORKS] — Orphan PayPal disputes receive hourly reconciliation. Evidence: `api/src/worker.ts` registers Bull repeat job `reconcile-orphan-disputes` with cron `17 * * * *`.

## Authentication & authorization
- [ ] AC-10 [WORKS] — API-key requests reject inactive keys, suspended users, and users without an active subscription. Evidence: `api/src/middleware/auth.ts:authenticateApiKey` loads key/user/subscriptions and returns 401 or 403 before `next()`.
- [ ] AC-11 [BROKEN] — `X-Plugin` access requires an active subscription for that exact plugin. Evidence: `api/src/middleware/auth.ts:authenticateApiKey` falls back to any active subscription when requested plugin has none.
- [ ] AC-12 [WORKS] — Customer JWTs are verified against active database users on protected account routes. Evidence: `api/src/middleware/auth.ts:authenticateJWT` verifies token then loads user/status before `next()`.
- [ ] AC-13 [BROKEN] — Email verification prevents unverified accounts from receiving authenticated access. Evidence: `api/src/routes/auth.ts:/register` returns JWTs immediately and `authenticateJWT` never checks `email_verified`.
- [ ] AC-14 [MISSING] — Email-verification tokens expire. Evidence: `User` has no verification-expiry field and `api/src/routes/auth.ts:/verify-email` accepts any stored token.
- [ ] AC-15 [BROKEN] — Password-reset and verification secrets never enter application logs. Evidence: `api/src/services/emailService.ts` logs tokens when SendGrid is absent.
- [ ] AC-16 [BROKEN] — Admin mutations enforce distinct administrator and support roles. Evidence: `api/src/middleware/auth.ts:authenticateAdmin` attaches `role`, but no `req.admin.role` authorization check exists.
- [ ] AC-17 [BROKEN] — Admin login is brute-force rate limited. Evidence: `api/src/routes/admin/auth.ts:/login` has validation only; no rate-limiter middleware.
- [ ] AC-18 [WORKS] — Admin routes require a separately signed active-admin JWT. Evidence: every non-auth route under `api/src/routes/admin/` uses `authenticateAdmin`, which verifies `JWT_ADMIN_SECRET` and `is_active`.

## Translation API & callback delivery
- [ ] AC-19 [UNVERIFIED] — Authenticated synchronous and queued translations complete through Gemini. verify: run `/v1/translate` and `/v1/jobs` against production Gemini credentials, Redis worker, and a funded API key.
- [ ] AC-20 [WORKS] — Translation request bodies are schema-validated and bounded before service execution. Evidence: `api/src/routes/translate.ts` and `api/src/routes/jobs.ts` apply Zod `validate(...)` with content limits.
- [ ] AC-21 [BROKEN] — Customer callback URLs cannot target internal network services. Evidence: `api/src/routes/jobs.ts:jobSubmitSchema` accepts any URL and `api/src/services/webhookService.ts:deliverWebhook` posts to it without SSRF filtering.
- [ ] AC-22 [BROKEN] — Failed customer webhooks receive configured automatic retries. Evidence: `api/src/services/webhookService.ts:retryFailedWebhook` exists but no call site schedules or invokes it.
- [ ] AC-23 [WORKS] — Translation completion callbacks include an HMAC signature and persisted delivery result. Evidence: `api/src/services/webhookService.ts:deliverWebhook` sends `X-TPZ-Signature` and creates `WebhookDelivery`.
- [ ] AC-24 [WORKS] — Registered WordPress sites are scoped to their owning API-key user. Evidence: `api/src/routes/sites.ts` queries site records with both `id` and `user_id`.

## Licensing & account data
- [ ] AC-25 [WORKS] — License keys are stored only as SHA-256 hashes and plugin scope is checked during activation. Evidence: `api/src/services/multilingualLicenseService.ts:hashLicenseKey` and `activateLicense`.
- [ ] AC-26 [BROKEN] — Concurrent license activations cannot exceed a finite site limit. Evidence: `api/src/services/multilingualLicenseService.ts:activateLicense` counts active sites before non-transactional upsert.
- [ ] AC-27 [WORKS] — Prisma schema defines relational users, subscriptions, keys, jobs, payments, licenses, invoices, disputes, and webhook-event records. Evidence: `api/prisma/schema.prisma` models and constraints.
- [ ] AC-28 [UNVERIFIED] — Production database migrations are current. verify: run `npx prisma migrate status` using production `DATABASE_URL` and require no pending migrations.

## Operations & observability
- [ ] AC-29 [WORKS] — Health, readiness, liveness, and authenticated Prometheus metrics endpoints are wired. Evidence: `api/src/routes/health.ts` and `api/src/server.ts:/metrics`.
- [ ] AC-30 [MISSING] — Sentry error reporting and Slack alert delivery are initialized. Evidence: `SENTRY_DSN` and `SLACK_WEBHOOK_URL` exist only in `api/src/config/index.ts`; no integration call exists.
- [ ] AC-31 [UNVERIFIED] — Container deployment starts API, worker, PostgreSQL, Redis, and migrations with production secrets. verify: deploy `podman-compose.yml` on target host and require all service health checks plus `/health/ready`.
- [ ] AC-32 [UNVERIFIED] — Backups are scheduled and restorable in production. verify: run `backup/backup.sh`, restore into isolated PostgreSQL, and confirm scheduler installs `backup/crontab.example`.
- [ ] AC-33 [BROKEN] — Static lint gate executes successfully. Evidence: `api/package.json:lint` invokes ESLint 9 without an `eslint.config.*`, producing configuration failure.
- [ ] AC-34 [WORKS] — TypeScript build and Jest suite execute successfully. Evidence: `api/package.json` build/test scripts completed successfully during this audit.

## Scoreboard
**13 WORKS / 12 BROKEN / 2 MISSING / 7 UNVERIFIED** (34 criteria)

---

# Migration readiness — CF go-live (added 2026-07-19)

Scope: cutover from this backend (VPS, expiring) to product repo `alexcodeplace/press.zone` → `backend/` (CF Workers; platform repo supplies `@platform-modules/*` only). Go-live client = `international-press-zone` plugin only. Methodology + phases: `docs/plans/2026-07-19-cf-golive-test-plan.md`. AC-xx above audit the OLD backend and are NOT cutover gates; known-broken old behaviors are not parity targets.

Rule: every MR criterion is satisfied ONLY by a passing automated test against a live deployment — never by code inspection.

## Source of truth (no spec exists — these become it)
- [x] MR-01 — Client-demand contract extracted from `international-press-zone` client code (endpoints, headers, consumed response fields, handled errors) → `docs/contracts/plugin-client-contract.md`. Done 2026-07-19: 23 outbound endpoints + 3 inbound webhook contracts.
- [x] MR-02 — Golden behavioral fixtures captured from live `api.press.zone` (happy, invalid-auth, insufficient-credits, rate-limit, malformed per endpoint) BEFORE VPS expiry → `docs/contracts/fixtures/`. Done 2026-07-19: 79 fixtures, 23/23 endpoints; non-triggerable scenarios documented in fixtures README (402 not safely triggerable; checkout happy paths skipped by design).
- [x] MR-03 — Capability parity matrix complete: every plugin-consumed old capability → new-app equivalent + test ID, or explicit `DROPPED:<reason>` → `docs/contracts/parity-matrix.md`. Done 2026-07-19: 18 capabilities — 9 GAP / 6 EXISTS-UNTESTED / 3 DROPPED.
- [x] MR-04 — L1 capability suite passes in oracle mode against old prod (harness validated before judging new app). Done 2026-07-20 (commit `46837c3e`): `tests/capability/`, 18 capabilities — 8 PASS / 7 NOT-IMPLEMENTED / 3 DROPPED / 0 FAIL. Every capability old prod genuinely implements passes, including live async job → signed callback delivery (HMAC verified over a cloudflared tunnel) and exact-debit + 402 `INSUFFICIENT_CREDITS`. NOT-IMPLEMENTED rows are documented old-prod defects/absences, not parity targets: CAP-05 sites/register 500, CAP-06 heartbeat depends on it, CAP-10 no plugin-contract webhook-idempotency surface, CAP-12 `ipz_license_key` format, CAP-13 updates/check 404, CAP-18 no rate limiting, and CAP-16 wrong-plugin entitlement (live behavior confirms AC-11 — legacy falls back to any active subscription).

## Connectivity, auth, rate limiting (L1 vs CF staging)
- [ ] MR-05 — Auth: valid credential accepted; invalid, revoked, and wrong-plugin-entitlement credentials rejected 401/403.
- [ ] MR-06 — Rate limiting live-verified: per-IP unauth burst → 429; per-credential burst → 429 with Retry-After honored; failed-login backoff engages. Bulk-burst tolerance: simulated site-wide translation (≥200 batched authed job submissions within 5 min + status reads) completes with ZERO failed operations — 429s permitted only if absorbed transparently by client backoff/queue (slower is acceptable; lost work or user-visible errors are not).
- [ ] MR-07 — Credits: metered debit equals exact char count; over-quota → 402 `INSUFFICIENT_CREDITS` hard block; period-key rollover resets allocation.
- [ ] MR-08 — Async jobs: submit → process → callback delivered with valid HMAC signature; failed callback retried.
- [ ] MR-09 — Licensing/seats: activation within cap succeeds; over cap → 409 `SITE_LIMIT_REACHED`; deactivate idempotent and revivable.
- [ ] MR-10 — Webhook idempotency: duplicate PayPal event (new event-id, same chargeKey) grants credits exactly once.

## Plugin integration (L2, real WordPress vs CF staging)
- [ ] MR-11 — `international-press-zone` on live WP completes: connect/auth → estimate → sync translate → bulk async → callback applied to posts → credits reflected in UI.
- [ ] MR-12 — Error UX: 402 and 429 surfaced visibly in wp-admin; no silent failures.

## Staff admin dashboard (L3, data seeded via API only)
- [ ] MR-13 — Staff journeys green: login (incl. rate-limited login), account lookup, subscriptions + usage/ledger views show real seeded data, refund (PayPal sandbox + Morning credit note), manual credit, suspend, password reset, exception management (view + edit); each action writes audit row.
- [ ] MR-14 — Per-page UI matrix tests green (every control renders and enables/disables correctly per role) across the FULL old-panel parity surface: jobs, licenses, tiers, webhook deliveries, analytics/churn, payment filter + CSV export, credit transactions, platform settings (UJ-108..UJ-117 all MUST-HAVE per user decision 2026-07-20).

## Customer dashboard (L3, data provisioned via public/dev API only)
- [ ] MR-22 — Customer journeys green against live DEV: account signup/login (incl. rate-limited login and password reset), site + API-key/OAuth credential lifecycle (issue, list, revoke — revoked credential immediately rejected 401), plan catalog → PayPal-sandbox checkout → credits granted exactly once, usage/ledger view reconciles to the debits the plugin actually incurred, invoice + credit-note documents resolve from Morning sandbox, and over-quota state surfaces 402 with an upgrade path. Every mutating action writes an audit row scoped to the acting account, and NO customer request may read or mutate another account's data (object-level authorization asserted per route, not per page). Fixtures MUST be provisioned through public/dev APIs — direct database seeding invalidates this criterion.

## Nonfunctional gates
- [ ] MR-15 — Observability drill: forced error appears in Sentry, alert delivered; Logpush delivering structured logs.
- [ ] MR-16 — Security sweep of new app surface: no open P0/P1.
- [ ] MR-17 — Load sanity: sustained modest mixed-RPS run with zero 5xx and acceptable p95; Neon/Hyperdrive connections stable.
- [ ] MR-18 — Backup: Neon point-in-time restore drill performed successfully.

## Multi-plugin platform readiness
- [ ] MR-21 — Entitlement + metering is plugin-generic, proven by test: a second plugin (`forum`) with a DIFFERENT metered unit and limit is provisioned through the same account/subscription/ledger surface with ZERO translation-specific code paths — entitlement check, quota debit, 402 over-quota, and staff ledger view all work for it. Translation-only meters at go-live; the model must accept a new plugin meter by configuration, never by schema change.

## Cutover
- [ ] MR-19 — Cutover rehearsal + rollback plan tested; `@smoke` L4 subset passes against production after DNS move; synthetic canary cron enabled.
- [ ] MR-20 — 72h production soak clean; final VPS DB snapshot archived; VPS decommissioned.

## Migration scoreboard
**4 / 22 MR criteria verified** (MR-01..03 done 2026-07-19 — Phase 0 exit; MR-04 done 2026-07-20 — Phase 1 harness validated in oracle mode. MR-21 added 2026-07-20 with user's multi-plugin directive; staff scope widened to full old-panel parity, so MR-13/14 grew. MR-22 added 2026-08-05 — the customer dashboard was covered by no criterion; MR-13/14 gate staff surfaces only.)
