Files
igny8/final-tenancy-accounts-payments/tenancy-implementation-plan.md
IGNY8 VPS (Salman) 191287829f asdasd
2025-12-08 05:53:32 +00:00

339 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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.