This commit is contained in:
IGNY8 VPS (Salman)
2025-12-08 05:53:32 +00:00
parent 69e88432c5
commit 191287829f
4 changed files with 338 additions and 0 deletions

View File

@@ -0,0 +1,338 @@
Got you. Lets lock the design first, then walk a very concrete implementation plan.
---
## 1. Design baseline
### 1.1 What is actually enforced vs just marketing
| Area | Enforced in backend | Marketing only / derived text |
| ------------------------- | ----------------------------------------------------------------------------------------------------------------- | --------------------------------------------------- |
| Sites per account | Yes, via `Plan.max_sites` and `Site.get_max_sectors_limit()` for industries/sectors | Text on pricing page rephrases this, no extra logic |
| Users per account | Yes, via per plan user limit if present in Plan model (or enforced through Account → User count) | Pricing shows same number, no separate enforcement |
| Credits per month | Yes, `Plan.included_credits` / `Plan.get_effective_credits_per_month()``Account.credits` on signup and renewal | Words per month etc derived from credits only |
| Payment method | Yes, new `payment_method` on Account and Subscription (stripe, paypal, bank_transfer) | UI labels like “Bank transfer” or “Coming soon” |
| Keywords, clusters, ideas | Not enforced for now, only advisory copy | Keep in pricing copy, treat as soft guidance |
| Words per month | Not enforced directly | Approx = credits * X words, shown only on frontend |
| Daily content tasks | Not enforced directly | Approx, shown only on frontend |
| Images per month | Optional: either map to credits or leave as copy | Pricing text only for now |
Decision: backend hard limits are Sites, (Industries/Sectors), Users, Credits. Everything else is just marketing descriptions driven from those hard limits.
---
## 2. Target user flows (one view)
### 2.1 High level text diagram
```text
Pricing page (marketing)
→ Choose plan (Starter / Growth / Scale)
→ Click "Start" → App Register (with plan_id preselected)
Register
→ POST /api/v1/auth/register/ with plan_id + payment_method=bank_transfer
→ Create User + Account(plan) + Subscription(payment_method, status='pending_payment')
→ Seed Account.credits = plan.get_effective_credits_per_month()
→ User can log in and use app while account.status in ['trial', 'active', 'pending_payment'] if you allow pre-payment use
App usage (monthly)
→ Login → JWT → AccountContextMiddleware sets request.account
→ User works in Planner / Writer
→ Every AI call:
- CreditService.check_credits()
- AICore runs model
- CreditService.deduct_credits_for_operation()
Renewal
→ Admin confirms bank transfer for next period
→ Or Stripe webhook in future
→ Subscription period dates updated
→ Account.credits reset to plan monthly credits
→ Account.status set to 'active'
Expiry / non payment
→ Subscription.status becomes 'past_due'/'canceled'
→ Account.status set to 'suspended'
→ Login / AI calls blocked with clear message
```
---
## 3. Phase A Plans and pricing alignment
### A1. Make Plan the single source of truth for limits
**Implementation**
* In `Plan` model ensure we have at least: `slug`, `name`, `price`, `billing_cycle`, `included_credits`, `max_sites`, `max_users`, `max_industries` (or current field names).
* Add helper: `Plan.to_public_dict()` that returns exactly what pricing needs:
* `name`, `slug`, `price`, `billing_cycle`
* `max_sites`, `max_users`, `included_credits`
* optional: `tagline`, `recommended`, `order_index` (pure marketing fields stored on Plan to avoid hardcoded frontend).
* Update `/api/v1/auth/plans/` to return only these fields plus computed soft fields:
* `approx_words_per_month = included_credits * WORDS_PER_CREDIT` (constant, eg 120).
* `approx_images_per_month = included_credits / CREDITS_PER_IMAGE` (if you want).
**Verification**
* Hit `GET /api/v1/auth/plans/` and confirm:
* Each plan matches DB row and has only canonical hard limit fields plus derived estimates.
* No legacy fields leaking like “keywords_limit” unless they truly exist.
* Update pricing page to build cards from this response instead of static JSON.
### A2. Simplify pricing UI to reflect hard limits
**Implementation**
* On `igny8.com/pricing`:
* Show for each plan:
* Sites, users, AI credits per month (clear).
* A line like “Up to ~120K AI generated words per month” using API derived field.
* Keep keywords/clusters/ideas as descriptive bullet points, but not used anywhere in backend.
* Change CTA text from “Start free trial” to “Get started” or keep label but functionally treat as plan signup.
**Verification**
* Disable JS and verify pricing still renders correct values from API.
* Check for each plan: numbers on page match `Plan` entries and included_credits.
---
## 4. Phase B Signup, subscription and payment methods
### B1. Extend models for payment_method and external_payment_id
**Implementation**
* Add migrations as per audit:
* `Account.payment_method` (choices: stripe, paypal, bank_transfer, default stripe).
* `Subscription.payment_method` same choices.
* `Subscription.external_payment_id` nullable.
* Make `stripe_subscription_id` nullable and drop unique if necessary.
* Backfill existing rows: set `payment_method='stripe'` where `stripe_subscription_id` is not null.
**Verification**
* Run migrations in staging:
* Confirm existing subscriptions untouched but now have `payment_method='stripe'`.
* New columns visible in Django admin.
### B2. Wire pricing CTA to register with plan
**Implementation**
* Pricing card button:
* On click, route to `app.igny8.com/register?plan=starter` etc.
* Register page in app:
* On mount, call `/api/v1/auth/plans/` and validate that `plan` query param is a valid `slug`.
* Show selected plan summary in the form.
* Registration form posts:
* `POST /api/v1/auth/register/` with body:
```json
{
"email": "...",
"password": "...",
"account_name": "...",
"plan_slug": "starter",
"payment_method": "bank_transfer"
}
```
* Backend resolves `plan_slug` to `Plan`, sets on Account and Subscription, sets `payment_method`, sets `Subscription.status='pending_payment'` and `Account.status='pending_payment'` or `trial`, and seeds `Account.credits = plan.get_effective_credits_per_month()`.
* Wrap creation in a database transaction as per audit.
**Verification**
* From pricing go through full register flow:
* Check DB: new User, Account with correct Plan, Subscription linked, `payment_method='bank_transfer'`, credits set to plan value.
* Decode JWT after login and verify `account_id` matches created account and account.status is as expected.
### B3. Bank transfer confirmation flow
**Implementation**
* Create admin endpoint `POST /api/v1/billing/confirm-bank-transfer/`:
* Guarded by `IsAdminOrOwner` or separate `IsStaffAdmin`.
* Accepts `subscription_id`, `external_payment_id`, amount, payer details, optional proof URL.
* Inside a transaction:
* Validate subscription belongs to account.
* Set `Subscription.payment_method='bank_transfer'`, `external_payment_id=...`, `status='active'`, and new `current_period_start` and `current_period_end`.
* Set `Account.status='active'`.
* Reset `Account.credits = plan.get_effective_credits_per_month()` and create a `CreditTransaction` log entry.
**Verification**
* In staging, create an account via bank transfer path, then hit admin endpoint:
* Confirm status flips from pending to active.
* Credits reset correctly.
* User can login and use AI features without seeing payment required errors.
---
## 5. Phase C Auth, tenancy and API key correctness
### C1. Central account+plan validation
**Implementation**
* Extract helper `validate_account_and_plan(account)` from `AccountContextMiddleware`.
* Checks:
* `account` exists and not deleted.
* `account.status in allowed set` (trial, active, pending_payment if you allow usage).
* `account.plan` exists and plan active.
* Use this helper:
* In middleware for JWT/session requests.
* In `APIKeyAuthentication.authenticate()` for API key requests.
**Verification**
* Test matrix:
* Account active → both JWT and API key get access.
* Account suspended or plan inactive → both JWT and API key requests receive consistent 402/403.
### C2. Throttling and tenant scoping
**Implementation**
* Update `DebugScopedRateThrottle`:
* Keyed by `(scope, account.id)` instead of user only.
* Only bypass when `DEBUG=True` or a special flag is set.
**Verification**
* Use a script to hit AI endpoints many times:
* Same account should start getting 429 after configured limit.
* Different accounts are throttled independently.
---
## 6. Phase D Credits as single limiting mechanism
### D1. Standardize credit operations
**Implementation**
* In `CreditService` define per operation costs:
* `CLUSTER_COST_PER_KEYWORD`, `IDEA_COST_PER_CLUSTER`, `CONTENT_COST_PER_1K_TOKENS`, `IMAGE_COST_PER_IMAGE`, etc.
* For each AI entry point in Planner and Writer:
* Before calling any AI engine, call `CreditService.check_credits(account, operation_type, units)` and fail fast with a friendly error if credits insufficient.
* After success, call `deduct_credits_for_operation`.
* Ensure `AIEngine.execute` never calls external API without passing through CreditService.
**Verification**
* With a test account:
* Set credits small (for example 10 credits).
* Run a sequence of operations until credits exactly hit zero.
* Verify next AI attempt fails with “not enough credits” and no external API call is made.
### D2. Monthly credits reset on renewal
**Implementation**
* In renewal handler (Stripe webhook or bank transfer confirm):
* Always:
* Set `Account.credits = plan.get_effective_credits_per_month()` as new monthly allowance (no rollover), or apply your chosen policy.
**Verification**
* Manipulate subscription dates to simulate new period, run renewal handler:
* Compare previous credits to new credits and confirm reset logic is correct.
---
## 7. Phase E End to end user flows and QA checklists
For each flow from Final_Flow_Tenancy plus the new marketing connection, create a short verification table. Example:
### E1. Flow P Pricing to first login
| Step | Action | Expected backend state |
| ---- | ----------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| P1 | Open pricing page | `GET /auth/plans/` called, numbers match Plan rows |
| P2 | Click “Start” on Growth | Browser navigates to `/register?plan=growth` |
| P3 | Submit registration | User, Account(plan=growth), Subscription(payment_method='bank_transfer', status='pending_payment'), Account.credits set |
| P4 | Login | JWT issued, `request.account` set to new account |
| P5 | Visit Sites page | Only default site or none, request.account matches account.id |
### E2. Flow C Credits and AI usage
| Step | Action | Expected result |
| ---- | ---------------------------------- | ------------------------------------------------------------------------------------------------ |
| C1 | Note current Account.credits | Shows value N from Plan |
| C2 | Run auto cluster for small batch | Credits drop by expected amount, clusters created |
| C3 | Generate content draft | Credits drop again, Content row created |
| C4 | Keep triggering until credits zero | Last allowed request succeeds, next request returns “insufficient credits” without external call |
### E3. Flow R Renewal
| Step | Action | Expected result |
| ---- | --------------------------------------- | ---------------------------------------------------------------------------------- |
| R1 | Admin confirms next month bank transfer | Subscription dates advanced, status active, Account.credits reset to monthly quota |
| R2 | User logs in and runs AI | Requests succeed and credits decrement from new monthly total |
### E4. Flow X Expiry
| Step | Action | Expected result |
| ---- | ----------------------------------------------------------------- | ---------------------------------------------------------------- |
| X1 | Set Subscription.status='past_due' and Account.status='suspended' | Login shows “Active subscription required”, AI endpoints blocked |
| X2 | Confirm payment again | Account.status back to active, login and AI work |
---
## 8. Phase F Tests and CI
Wrap everything with tests so this stays correct.
* Unit tests for:
* Plan API response shape vs Plan model.
* Registration transaction: no partial user or account on failure.
* validate_account_and_plan for different statuses.
* CreditService check and deduct behavior for each operation.
* Integration tests for:
* Pricing → register → login → run AI until credits exhausted.
* Bank transfer confirmation and renewal.
* API key access for active vs suspended accounts.
Once these phases are implemented and the flow tables all pass in staging, you have a fully connected, credit based, monthly subscription system where marketing, signup, tenancy, billing, and AI usage all match the single Final_Flow_Tenancy spec plus the two audits.