# أنور بي آي — web Next.js 15 + Payload 3 app: Arabic RTL paywalled content site. ## Stack - Next.js 15.4 (App Router, standalone output) + React 19 - Payload 3.90 (Postgres adapter, Lexical richText) - Stripe subscriptions (35 SAR/month, test mode) ## Collections - **users** — auth; `role: free | vip | admin`, `stripeCustomerId` - **posts** — `title, slug, dek, access (free|vip), teaser, content (lexical), category, cover, readingMinutes, publishedAt, status (draft|published)` - **media** — image uploads Access control: public REST reads only `status=published`; VIP `content` is withheld from non-VIP (field-level read rule + paywall page never selects it for them). ## Env vars | Var | Purpose | |---|---| | `DATABASE_URI` | Postgres connection string | | `PAYLOAD_SECRET` | Payload encryption/ signing secret | | `AGENT_API_KEY` | Agent API auth (`x-agent-key` header) | | `NEXT_PUBLIC_SITE_URL` | Public URL (baked at build) | | `STRIPE_SECRET_KEY` / `STRIPE_PUBLISHABLE_KEY` / `STRIPE_PRICE_ID` / `STRIPE_WEBHOOK_SECRET` | Optional — unset = "pending" mode | ## Agent API ```sh # create draft (always draft; publish via /admin only) curl -X POST http://localhost:3000/api/agent/posts \ -H "x-agent-key: $AGENT_API_KEY" -H 'Content-Type: application/json' \ -d '{"title":"...","slug":"my-post","dek":"...","category":"وكلاء","access":"vip","teaser":"...","readingMinutes":8,"content":{"root":{"type":"root","children":[],"format":"","indent":0,"version":1}}}' # update (cannot change status) curl -X PUT http://localhost:3000/api/agent/posts/my-post -H "x-agent-key: $AGENT_API_KEY" ... ``` Omitting `cover` assigns the per-category default cover. ## Seed ```sh npm run covers # regenerate public/covers/*.jpg npx payload migrate npx payload run src/seed.ts # idempotent (skips existing slugs) ``` Seeds: 12 posts (6 free + 6 VIP, 6 categories), admin `admin@anwarpi.ai` / `AnwarPi-Admin-2026!`, test users `free@test.anwarpi.ai` / `Free1234!`, `vip@test.anwarpi.ai` / `Vip1234!`. ## Docker deploy ```sh cp .env.production.example .env.production # fill secrets docker compose --env-file .env.production up -d --build docker compose exec web npx payload run src/seed.ts # first time ``` ## Stripe webhook (testing on LAN) ```sh stripe listen --forward-to localhost:3000/api/stripe/webhook ``` On production (Railway), point the webhook endpoint at the public URL.