191 lines
19 KiB
Markdown
191 lines
19 KiB
Markdown
Nice, this is exactly the sort of thing that cleans up the whole mental model.
|
|
|
|
Below is a single structured view of flows, split into tight tables and mini text diagrams so you can literally walk them in frontend and API console and tick them off.
|
|
|
|
I am describing how it is intended to work, aligned with current models and views.
|
|
|
|
---
|
|
|
|
## 0. Key objects involved
|
|
|
|
| Area | Model / Endpoint | Notes |
|
|
| ------------------------- | ---------------------------------------------------------------------------------------------------------- | --------------------------------------------------- |
|
|
| Plans | `Plan` model with `price`, `billing_cycle`, `included_credits`, `stripe_*` fields | Defines included monthly credits and Stripe product |
|
|
| Accounts | `Account` with `plan`, `credits`, `status` (active, trial, suspended, cancelled) | Tenant root, holds current credits |
|
|
| Subscription | `Subscription` with `current_period_start`, `current_period_end`, `status`, `cancel_at_period_end` | Mirrors Stripe subscription state |
|
|
| Auth endpoints | `/api/v1/auth/register/`, `/api/v1/auth/login/`, `/api/v1/auth/plans/` allowed without auth | Used before tenant context exists |
|
|
| Account/Subscription APIs | `AccountsViewSet`, `SubscriptionsViewSet` under `/api/v1/auth/accounts/` and `/api/v1/auth/subscriptions/` | Manage accounts and subscriptions |
|
|
|
|
### Payment methods (current state)
|
|
|
|
- Options shown: Stripe (placeholder), PayPal (placeholder), Bank Transfer (active).
|
|
- Stripe/PayPal: keep UI/API options but mark as "coming soon"; no charge attempts yet.
|
|
- Bank Transfer: the only active path; capture payer details + plan, mark subscription/account pending payment until confirmed manually.
|
|
- No country-specific rules for any method; keep global defaults.
|
|
|
|
---
|
|
|
|
## Flow F1 - Sign up, plan selection, initial credits
|
|
|
|
### Text diagram
|
|
|
|
```text
|
|
Marketing Site → App Auth
|
|
1) User chooses plan on pricing page
|
|
2) Frontend loads plans from API
|
|
3) User fills register form with plan
|
|
4) Backend creates User + Account + (optionally) Subscription
|
|
5) Account gets plan and starting credits
|
|
```
|
|
|
|
### Step table
|
|
|
|
| Step | Frontend surface | Backend call | Backend effect (DB) | What to verify |
|
|
| ---- | ------------------------------- | ----------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
|
|
| 1 | Pricing page (public) | `GET /api/v1/auth/plans/` | Reads active `Plan` records via `PlanSerializer` | Plans visible match Plan rows (name, price, billing_cycle, included_credits) |
|
|
| 2 | Register page shows chosen plan | no call or same `GET /auth/plans/` | - | Selected plan id matches an active Plan row |
|
|
| 3 | Register submit | `POST /api/v1/auth/register/` (body contains user + account name + `plan_id`) | Creates `User`, `Account(plan=Plan)` and possibly `Subscription` if Stripe created immediately | In DB check: new User, new Account with `plan_id` set and `status` = `trial` or `active` as per your rule; `account.owner` = user |
|
|
| 4 | Initial credits seed | same register flow or follow up billing hook | Account.credits should be set to `plan.get_effective_credits_per_month()` (from `included_credits` or legacy `credits_per_month`) | After signup, check `Account.credits` equals the plan credits for this plan |
|
|
| 5 | Payment method choice | UI shows Stripe (placeholder), PayPal (placeholder), Bank Transfer (active) | Record `payment_method` on Account/Subscription. For now only Bank Transfer can be marked paid; Stripe/PayPal stay "coming soon" | UI labels make clear Stripe/PayPal are not yet active; bank transfer selection stores choice and blocks entry until marked paid |
|
|
|
|
For validation you can drive this entirely with Postman and Django admin plus the app UI.
|
|
|
|
---
|
|
|
|
## Flow F2 - Login, tenant context, subscription gate
|
|
|
|
This validates that a user can only enter the app when account and plan are healthy.
|
|
|
|
### Diagram
|
|
|
|
```text
|
|
Login form
|
|
→ POST /auth/login/
|
|
→ Validate password
|
|
→ Ensure User has Account
|
|
→ Ensure Account has active Plan
|
|
→ Issue JWT with account_id
|
|
→ All subsequent requests:
|
|
→ Middleware sets request.account from token
|
|
→ Permissions enforce tenant access
|
|
```
|
|
|
|
### Step table
|
|
|
|
| Step | Frontend surface | API | Backend logic | Verify |
|
|
| ---- | -------------------------- | --------------------------- | ------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------ |
|
|
| 1 | Login form | `POST /api/v1/auth/login/` | Finds user by email, checks password | Wrong password gives 401 with "Invalid credentials" |
|
|
| 2 | Account gate | same login handler | If `user.account` is missing, returns 403 "Account not configured" | Create a user without account and confirm it cannot log in |
|
|
| 3 | Plan gate | same | If `account.plan` is missing or inactive, returns 402 "Active subscription required" | Mark a plan inactive, try login, see 402 |
|
|
| 4 | Token issue | same | Generates access and refresh tokens embedding `user_id` and `account_id` in JWT, and returns them with expiry timestamps | Decode JWT and check `account_id` == Account.id |
|
|
| 5 | Tenant context per request | Any app request after login | `AccountContextMiddleware` reads JWT, fetches User and Account, sets `request.account` and blocks suspended / invalid accounts | Hit a random endpoint, log `request.account.id` and compare to user.account.id |
|
|
|
|
If this flow passes, your base tenancy gate is sound.
|
|
|
|
---
|
|
|
|
## Flow F3 - Normal app workflow and credits consumption
|
|
|
|
Think of this as the day to day life of a tenant.
|
|
|
|
### Subflow F3a - Site and sector inside an account
|
|
|
|
| Step | UI | API | DB objects | Verify tenancy |
|
|
| ---- | ----------- | -------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- |
|
|
| 1 | Sites page | `GET /api/v1/auth/sites/` | Returns `Site` filtered by `account` via `AccountModelViewSet` and `SiteSerializer` | Confirm only sites with `site.account_id = request.account.id` show |
|
|
| 2 | Create site | `POST /api/v1/auth/sites/` | Creates `Site` inheriting `account` from `request.account`, counts against `plan.max_sites` | Try creating more sites than `plan.max_sites` and confirm it fails as per your rule |
|
|
| 3 | Add sectors | `POST /api/v1/auth/sectors/` or site detail → add sector | Sector belongs to Site, and Site belongs to Account. `Site.get_max_sectors_limit()` uses `account.plan.max_industries` to enforce limit | Adding sectors beyond limit returns validation error; each sector.site.account_id matches account |
|
|
|
|
This sets the container for Planner / Writer.
|
|
|
|
---
|
|
|
|
### Subflow F3b - Planner credits usage
|
|
|
|
Based on the Planner technical guide.
|
|
|
|
#### Diagram
|
|
|
|
```text
|
|
Keywords → Auto cluster → Ideas
|
|
|
|
Each AI operation:
|
|
- Calculate credits needed
|
|
- Call CreditService to deduct from Account.credits
|
|
- Call AICore with account context
|
|
- Store results (clusters, ideas, tasks)
|
|
```
|
|
|
|
#### Table
|
|
|
|
| Stage | Frontend action | Endpoint | Backend behavior | Credits check |
|
|
| ------------------ | ------------------------------- | --------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
| 1. Attach keyword | Keywords page, "Add keyword" | `POST /v1/planner/keywords/` | Validates seed keyword, site, sector, then creates Keyword using `SiteSectorBaseModel` so `account` is set from site | No or minimal credits used, depending on your rule |
|
|
| 2. Auto cluster | Select keywords, "Auto Cluster" | `POST /v1/planner/keywords/auto_cluster/` | `auto_cluster` loads keywords, instantiates `ClusteringService`, calls `cluster_keywords(keyword_ids, account, sector_id)` | In `ClusteringService` step 2, it "Check credits - Calculate credits needed and deduct credits from account" . Validate that Account.credits decreases accordingly after clustering |
|
|
| 3. Idea generation | Clusters page, "Generate ideas" | Usually `POST /v1/planner/clusters/generate_ideas/` | Service collects cluster data, calls AI via `AICore(account=request.account)` and creates `ContentIdeas` | Confirm one AI call consumes the planned credits and created ideas are tied to same site/sector/account |
|
|
|
|
To test: run each step for a known account and watch `Account.credits` in admin before and after.
|
|
|
|
---
|
|
|
|
### Subflow F3c - Writer credits usage
|
|
|
|
Pattern mirrors Planner.
|
|
|
|
| Stage | Frontend | Endpoint | Backend behavior | Credits check |
|
|
| -------------------------- | -------------------------------------- | -------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- |
|
|
| 1. Create task from idea | Ideas page, "Send to Writer" | `POST /v1/writer/tasks/` | Creates Writer Task linked to idea, site, sector, account | No credits yet or minimal |
|
|
| 2. Generate draft | Writer task detail, "Generate Article" | `POST /v1/writer/content/generate/` or similar | Calls AI via `AICore(account=request.account)`, passes structured prompt, receives content, stores `Content` row | Check that for each generation, Account.credits decreases by the expected credit cost per 1k tokens |
|
|
| 3. Regenerate sections etc | Same page | e.g. `POST /v1/writer/content/regenerate_section/` | Same AI pipeline, smaller requests | Verify smaller credit deductions and that usage is logged in any Usage / Billing tables if present |
|
|
|
|
`AICore` always initialises with an account and logs cost per request based on tokens and `MODEL_RATES` . You can cross check AI cost vs credit deduction.
|
|
|
|
---
|
|
|
|
## Flow F4 - Subscription renewal and monthly or annual cycle
|
|
|
|
The renewal flow is Stripe driven, mirrored in your `Subscription` and `Account` rows.
|
|
|
|
### Diagram
|
|
|
|
```text
|
|
Stripe billing cycle
|
|
→ Stripe renews subscription (card charged)
|
|
→ Stripe sends webhook to backend
|
|
→ Backend updates Subscription row
|
|
→ Backend resets/adjusts Account.credits based on Plan.included_credits
|
|
```
|
|
|
|
### Step table
|
|
|
|
| Step | Source | Backend effect | What to verify | |
|
|
| ---- | ------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------- |
|
|
| 1 | Stripe billing engine / bank transfer confirmation | Stripe (later) marks subscription renewed; today bank transfer confirmation is recorded manually against the subscription | For bank transfer, admin marks payment received and sets renewal date; Stripe dashboard flows remain future work | |
|
|
| 2 | Stripe webhook or admin update | Webhook (future) or admin update finds `Subscription` by payment method and updates `status`, `current_period_start`, `current_period_end`, `cancel_at_period_end` | Subscription dates/status match payment confirmation; Stripe webhook path is stubbed until integration | |
|
|
| 3 | Credits reset or top up | Same handler/job | Sets `Account.credits` to `plan.get_effective_credits_per_month()` or adds included credits, depending on your policy | At renewal/confirmation, credits jump to intended value and usage across the new period subtracts from that |
|
|
| 4 | Account status update | Same billing service | Ensures `Account.status` switches from `trial` or `past_due` back to `active` | After payment, login is allowed again and middleware does not block due to account status |
|
|
|
|
You can simulate this by editing Subscription dates and status in admin if webhooks are not wired yet, then running the same code path as webhook.
|
|
|
|
---
|
|
|
|
## Flow F5 - Cancellation and expiry
|
|
|
|
Short but necessary to validate health.
|
|
|
|
| Scenario | Backend state | Effects to validate |
|
|
| ---------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
|
|
| User cancels at period end | `Subscription.cancel_at_period_end=True` while `status` remains `active` until `current_period_end` | App continues working, credits used until period end; no new credits after end date |
|
|
| Subscription ends or payment fails | `Subscription.status` becomes `canceled` or `past_due`. Billing job sets `Account.status` to `suspended` or `cancelled` | Login returns 402 or 403 according to your rule, Planner/Writer calls fail with proper error and do not consume credits |
|
|
|
|
---
|
|
|
|
## How to actually use this
|
|
|
|
You can turn this into a verification pass like:
|
|
|
|
1. Pick a clean test user and Stripe test customer.
|
|
2. Walk F1 then F2 in sequence and confirm all DB fields line up.
|
|
3. Run through F3a, F3b, F3c and log credits before and after each AI-heavy action.
|
|
4. Simulate renewal and cancellation via Stripe or DB and walk F4 and F5.
|