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.