From 191287829f7657b61458f7460a8e981c65debcc8 Mon Sep 17 00:00:00 2001 From: "IGNY8 VPS (Salman)" Date: Mon, 8 Dec 2025 05:53:32 +0000 Subject: [PATCH] asdasd --- .../Final_Flow_Tenancy.md | 0 .../Tenancy_Audit_Report.md | 0 .../audit_fixes.md | 0 .../tenancy-implementation-plan.md | 338 ++++++++++++++++++ 4 files changed, 338 insertions(+) rename Final_Flow_Tenancy.md => final-tenancy-accounts-payments/Final_Flow_Tenancy.md (100%) rename Tenancy_Audit_Report.md => final-tenancy-accounts-payments/Tenancy_Audit_Report.md (100%) rename audit_fixes.md => final-tenancy-accounts-payments/audit_fixes.md (100%) create mode 100644 final-tenancy-accounts-payments/tenancy-implementation-plan.md diff --git a/Final_Flow_Tenancy.md b/final-tenancy-accounts-payments/Final_Flow_Tenancy.md similarity index 100% rename from Final_Flow_Tenancy.md rename to final-tenancy-accounts-payments/Final_Flow_Tenancy.md diff --git a/Tenancy_Audit_Report.md b/final-tenancy-accounts-payments/Tenancy_Audit_Report.md similarity index 100% rename from Tenancy_Audit_Report.md rename to final-tenancy-accounts-payments/Tenancy_Audit_Report.md diff --git a/audit_fixes.md b/final-tenancy-accounts-payments/audit_fixes.md similarity index 100% rename from audit_fixes.md rename to final-tenancy-accounts-payments/audit_fixes.md diff --git a/final-tenancy-accounts-payments/tenancy-implementation-plan.md b/final-tenancy-accounts-payments/tenancy-implementation-plan.md new file mode 100644 index 00000000..52a8657f --- /dev/null +++ b/final-tenancy-accounts-payments/tenancy-implementation-plan.md @@ -0,0 +1,338 @@ +Got you. Let’s 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.