Phase 3 - credts, usage, plans app pages #Migrations
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
# Billing Module
|
||||
|
||||
**Last Verified:** January 5, 2026
|
||||
**Status:** ✅ Active
|
||||
**Status:** ✅ Active (Simplified January 2026)
|
||||
**Backend Path:** `backend/igny8_core/modules/billing/` + `backend/igny8_core/business/billing/`
|
||||
**Frontend Path:** `frontend/src/pages/Billing/` + `frontend/src/pages/Account/`
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
|------|------|-----------|
|
||||
| Models | `business/billing/models.py` | `CreditTransaction`, `CreditUsageLog`, `CreditCostConfig`, `AIModelConfig` |
|
||||
| Service | `business/billing/services/credit_service.py` | `CreditService` |
|
||||
| Limit Service | `business/billing/services/limit_service.py` | `LimitService` (4 limits only) |
|
||||
| Views | `modules/billing/views.py` | `CreditBalanceViewSet`, `CreditUsageViewSet` |
|
||||
| Frontend | `pages/Account/PlansAndBillingPage.tsx` | Plans, credits, billing history |
|
||||
| Store | `store/billingStore.ts` | `useBillingStore` |
|
||||
@@ -24,7 +25,7 @@
|
||||
The Billing module manages:
|
||||
- Credit balance and transactions
|
||||
- AI model pricing and credit configuration (v1.4.0)
|
||||
- Usage tracking and limits
|
||||
- Usage tracking with 4 simplified limits (v1.5.0)
|
||||
- Plan enforcement
|
||||
- Payment processing
|
||||
|
||||
@@ -205,17 +206,14 @@ CreditService.add_credits(
|
||||
| Sites | `max_sites` | Maximum sites per account |
|
||||
| Users | `max_users` | Maximum team members |
|
||||
| Keywords | `max_keywords` | Total keywords allowed |
|
||||
| Clusters | `max_clusters` | Total clusters allowed |
|
||||
|
||||
### Monthly Limits (Reset on Billing Cycle)
|
||||
|
||||
| Limit | Field | Description |
|
||||
|-------|-------|-------------|
|
||||
| Content Ideas | `max_content_ideas` | Ideas per month |
|
||||
| Content Words | `max_content_words` | Words generated per month |
|
||||
| Basic Images | `max_images_basic` | Basic AI images per month |
|
||||
| Premium Images | `max_images_premium` | Premium AI images per month |
|
||||
| Image Prompts | `max_image_prompts` | Prompts per month |
|
||||
| Ahrefs Queries | `max_ahrefs_queries` | Live Ahrefs API queries per month |
|
||||
|
||||
**Note:** As of January 2026, the limit system was simplified from 10+ limits to just 4. Credits handle all AI operation costs (content generation, image generation, clustering, etc.) instead of separate per-operation limits.
|
||||
|
||||
---
|
||||
|
||||
@@ -224,9 +222,9 @@ CreditService.add_credits(
|
||||
**Component:** `UsageLimitsPanel.tsx`
|
||||
|
||||
Displays:
|
||||
- Progress bars for each limit
|
||||
- Progress bars for 4 limits only (Sites, Users, Keywords, Ahrefs Queries)
|
||||
- Color coding: blue (safe), yellow (warning), red (critical)
|
||||
- Days until reset for monthly limits
|
||||
- Days until reset for monthly limits (Ahrefs Queries)
|
||||
- Upgrade CTA when approaching limits
|
||||
|
||||
---
|
||||
@@ -264,17 +262,17 @@ Displays:
|
||||
### Plans & Billing (`/account/plans`)
|
||||
|
||||
**Tabs:**
|
||||
1. **Current Plan** - Active plan, upgrade options
|
||||
2. **Credits Overview** - Balance, usage chart, cost breakdown
|
||||
3. **Purchase Credits** - Credit packages
|
||||
4. **Billing History** - Invoices and transactions
|
||||
1. **Current Plan** - Active plan details, renewal date, "View Usage" link
|
||||
2. **Upgrade Plan** - Pricing table with plan comparison
|
||||
3. **Billing History** - Invoices and payment history
|
||||
|
||||
### Usage Analytics (`/account/usage`)
|
||||
|
||||
**Tabs:**
|
||||
1. **Limits & Usage** - Plan limits with progress bars
|
||||
2. **Activity** - Credit transaction history
|
||||
3. **API Usage** - API call statistics
|
||||
1. **Limits & Usage** - Plan limits with progress bars (4 limits only)
|
||||
2. **Credit History** - Credit transaction history
|
||||
3. **Credit Insights** - Charts: credits by type, daily timeline, operations breakdown
|
||||
4. **Activity Log** - API call statistics and operation details
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -1,169 +1,164 @@
|
||||
# Usage & Content System
|
||||
# Credit System
|
||||
|
||||
**Last Verified:** December 25, 2025
|
||||
**Last Verified:** January 5, 2026
|
||||
**Status:** ✅ Simplified (v1.5.0)
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
IGNY8 uses a content-based allowance system. Users see "Content Pieces" while the backend tracks detailed credit consumption for internal cost monitoring.
|
||||
|
||||
**User View:** `47/50 Content Pieces Remaining`
|
||||
**Backend Tracks:** Idea credits, content credits, image credits (for cost analysis)
|
||||
IGNY8 uses a unified credit system where all AI operations consume credits from a single balance. Plan limits are simplified to 4 hard/monthly limits only.
|
||||
|
||||
---
|
||||
|
||||
## How It Works
|
||||
|
||||
### User-Facing (Simple)
|
||||
|
||||
| What Users See | Description |
|
||||
|----------------|-------------|
|
||||
| **Content Pieces** | Monthly allowance of pages/articles |
|
||||
| **X/Y Remaining** | Used vs total for the month |
|
||||
| **Upgrade Plan** | Get more content pieces |
|
||||
|
||||
### Backend (Detailed - Internal Only)
|
||||
|
||||
| Credit Type | Used For | Tracked For |
|
||||
|-------------|----------|-------------|
|
||||
| Idea Credits | Clustering, idea generation | Cost analysis |
|
||||
| Content Credits | Article generation | Usage limits |
|
||||
| Image Credits | Image generation | Cost analysis |
|
||||
| Optimization Credits | SEO optimization (future) | Cost analysis |
|
||||
|
||||
---
|
||||
|
||||
## Plan Allowances
|
||||
|
||||
| Plan | Content Pieces/Month | Sites | Users |
|
||||
|------|---------------------|-------|-------|
|
||||
| Starter | 50 | 2 | 2 |
|
||||
| Growth | 200 | 5 | 3 |
|
||||
| Scale | 500 | Unlimited | 5 |
|
||||
|
||||
**Included with every content piece:**
|
||||
- AI keyword clustering
|
||||
- AI idea generation
|
||||
- AI content writing (1000-2000 words)
|
||||
- 3 images (1 featured + 2 in-article)
|
||||
- Internal linking
|
||||
- SEO optimization
|
||||
- WordPress publishing
|
||||
|
||||
---
|
||||
|
||||
## Backend Soft Limits (Hidden from Users)
|
||||
|
||||
To prevent abuse, the backend enforces hidden limits:
|
||||
|
||||
| Limit | Starter | Growth | Scale |
|
||||
|-------|---------|--------|-------|
|
||||
| Keyword imports/mo | 500 | 2,000 | 5,000 |
|
||||
| Clustering operations | 100 | 400 | 1,000 |
|
||||
| Idea generations | 150 | 600 | 1,500 |
|
||||
| Images generated | 200 | 800 | 2,000 |
|
||||
|
||||
If users hit these limits, they see: "You've reached your preparation limit for this month."
|
||||
|
||||
---
|
||||
|
||||
## Content Deduction Flow
|
||||
## Credit Flow (Verified Architecture)
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ CONTENT CREATION FLOW │
|
||||
│ CREDIT FLOW │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ User clicks Check Generate │
|
||||
│ "Generate" ──────► Allowance ──────► Content │
|
||||
│ │ │ │
|
||||
│ │ Limit │ │
|
||||
│ │ Reached ▼ │
|
||||
│ ▼ Deduct 1 │
|
||||
│ Show Upgrade Content │
|
||||
│ Modal Piece │
|
||||
│ Plan.included_credits = Monthly allocation (e.g., 10,000) │
|
||||
│ ↓ (Added on subscription renewal/approval) │
|
||||
│ Account.credits = Current balance (real-time, decremented) │
|
||||
│ ↓ (Decremented on each AI operation) │
|
||||
│ CreditTransaction = Log of all credit changes │
|
||||
│ CreditUsageLog = Detailed operation tracking │
|
||||
│ │
|
||||
│ THESE ARE NOT PARALLEL - They serve different purposes: │
|
||||
│ • Plan.included_credits = "How many credits per month" │
|
||||
│ • Account.credits = "How many credits you have RIGHT NOW" │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Where credits are added to Account.credits:**
|
||||
1. `billing/views.py` - When manual payment is approved
|
||||
2. `payment_service.py` - When credit package purchased
|
||||
3. `credit_service.py` - Generic `add_credits()` method
|
||||
|
||||
---
|
||||
|
||||
## Operations & Credit Costs
|
||||
## Simplified Limits (v1.5.0)
|
||||
|
||||
### Planner Operations
|
||||
### Hard Limits (Never Reset)
|
||||
|
||||
| Operation | Credits | Type |
|
||||
|-----------|---------|------|
|
||||
| Add keyword | 0 | Free |
|
||||
| Auto-cluster keywords | 1 | Idea |
|
||||
| Generate content ideas | 1 per idea | Idea |
|
||||
| Limit | Plan Field | Account Field | Description |
|
||||
|-------|------------|---------------|-------------|
|
||||
| Sites | `max_sites` | (count of Site objects) | Maximum sites per account |
|
||||
| Users | `max_users` | (count of User objects) | Maximum team members |
|
||||
| Keywords | `max_keywords` | (count of Keyword objects) | Total keywords allowed |
|
||||
|
||||
### Writer Operations
|
||||
### Monthly Limits (Reset on Billing Cycle)
|
||||
|
||||
| Operation | Credits | Type |
|
||||
|-----------|---------|------|
|
||||
| Create task | 0 | Free |
|
||||
| Generate content | 1 | Content |
|
||||
| Regenerate content | 1 | Content |
|
||||
| Generate images | 1 per image | Image |
|
||||
| Regenerate image | 1 | Image |
|
||||
| Edit content | 0 | Free |
|
||||
| Limit | Plan Field | Account Field | Description |
|
||||
|-------|------------|---------------|-------------|
|
||||
| Ahrefs Queries | `max_ahrefs_queries` | `usage_ahrefs_queries` | Live Ahrefs API queries per month |
|
||||
|
||||
### Automation Operations
|
||||
### Removed Limits (Now Credit-Based)
|
||||
|
||||
| Operation | Credits | Type |
|
||||
|-----------|---------|------|
|
||||
| Run automation | Sum of operations | Mixed |
|
||||
| Pause/resume | 0 | Free |
|
||||
The following limits were removed in v1.5.0 - credits handle these:
|
||||
- ~~max_clusters~~ → Credits
|
||||
- ~~max_content_ideas~~ → Credits
|
||||
- ~~max_content_words~~ → Credits
|
||||
- ~~max_images_basic~~ → Credits
|
||||
- ~~max_images_premium~~ → Credits
|
||||
- ~~max_image_prompts~~ → Credits
|
||||
|
||||
### Publisher Operations
|
||||
---
|
||||
|
||||
| Operation | Credits | Type |
|
||||
|-----------|---------|------|
|
||||
| Publish to WordPress | 0 | Free |
|
||||
| Sync from WordPress | 0 | Free |
|
||||
## Plan Tiers
|
||||
|
||||
### Optimizer Operations (Future)
|
||||
| Plan | Credits/Month | Sites | Users | Keywords | Ahrefs Queries |
|
||||
|------|---------------|-------|-------|----------|----------------|
|
||||
| Free | 500 | 1 | 1 | 100 | 0 |
|
||||
| Starter | 5,000 | 3 | 2 | 500 | 50 |
|
||||
| Growth | 15,000 | 10 | 5 | 2,000 | 200 |
|
||||
| Scale | 50,000 | Unlimited | 10 | 10,000 | 500 |
|
||||
|
||||
| Operation | Credits | Type |
|
||||
|-----------|---------|------|
|
||||
| Optimize content | 1 | Optimization |
|
||||
| Batch optimize | 1 per item | Optimization |
|
||||
---
|
||||
|
||||
## Credit Operations
|
||||
|
||||
### Token-Based Operations (Text AI)
|
||||
|
||||
Credits calculated from actual token usage:
|
||||
- `credits = ceil(total_tokens / tokens_per_credit)`
|
||||
- `tokens_per_credit` defined per model in `AIModelConfig`
|
||||
|
||||
| Operation | Model Example | tokens_per_credit |
|
||||
|-----------|---------------|-------------------|
|
||||
| Keyword Clustering | gpt-4o-mini | 10,000 |
|
||||
| Idea Generation | gpt-4o-mini | 10,000 |
|
||||
| Content Generation | gpt-4o | 1,000 |
|
||||
| Content Optimization | gpt-4o-mini | 10,000 |
|
||||
|
||||
### Fixed-Cost Operations (Image AI)
|
||||
|
||||
Credits per image based on quality tier:
|
||||
|
||||
| Quality Tier | Model Example | Credits/Image |
|
||||
|--------------|---------------|---------------|
|
||||
| Basic | runware:97@1 | 1 |
|
||||
| Quality | dall-e-3 | 5 |
|
||||
| Premium | google:4@2 | 15 |
|
||||
|
||||
### Free Operations
|
||||
|
||||
| Operation | Cost |
|
||||
|-----------|------|
|
||||
| Add keyword (manual) | 0 |
|
||||
| Create content task | 0 |
|
||||
| Edit content | 0 |
|
||||
| Publish to WordPress | 0 |
|
||||
| Sync from WordPress | 0 |
|
||||
|
||||
---
|
||||
|
||||
## Database Models
|
||||
|
||||
### CreditBalance
|
||||
### Account (Credit Balance)
|
||||
|
||||
```python
|
||||
class CreditBalance(models.Model):
|
||||
account = models.ForeignKey(Account)
|
||||
site = models.ForeignKey(Site, null=True)
|
||||
|
||||
idea_credits = models.IntegerField(default=0)
|
||||
content_credits = models.IntegerField(default=0)
|
||||
image_credits = models.IntegerField(default=0)
|
||||
optimization_credits = models.IntegerField(default=0)
|
||||
|
||||
period_start = models.DateField()
|
||||
period_end = models.DateField()
|
||||
class Account(models.Model):
|
||||
credits = models.IntegerField(default=0) # Current balance
|
||||
usage_ahrefs_queries = models.IntegerField(default=0) # Monthly Ahrefs usage
|
||||
```
|
||||
|
||||
### CreditUsage
|
||||
### Plan (Allocations)
|
||||
|
||||
```python
|
||||
class CreditUsage(models.Model):
|
||||
class Plan(models.Model):
|
||||
included_credits = models.IntegerField(default=0) # Monthly allocation
|
||||
max_sites = models.IntegerField(default=1)
|
||||
max_users = models.IntegerField(default=1)
|
||||
max_keywords = models.IntegerField(default=100)
|
||||
max_ahrefs_queries = models.IntegerField(default=0) # Monthly Ahrefs limit
|
||||
```
|
||||
|
||||
### CreditTransaction (Ledger)
|
||||
|
||||
```python
|
||||
class CreditTransaction(models.Model):
|
||||
account = models.ForeignKey(Account)
|
||||
site = models.ForeignKey(Site, null=True)
|
||||
user = models.ForeignKey(User)
|
||||
|
||||
credit_type = models.CharField() # idea/content/image/optimization
|
||||
amount = models.IntegerField()
|
||||
operation = models.CharField() # generate_content, etc.
|
||||
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
transaction_type = models.CharField() # purchase/subscription/refund/deduction
|
||||
amount = models.DecimalField() # Positive (add) or negative (deduct)
|
||||
balance_after = models.DecimalField()
|
||||
description = models.CharField()
|
||||
created_at = models.DateTimeField()
|
||||
```
|
||||
|
||||
### CreditUsageLog (Analytics)
|
||||
|
||||
```python
|
||||
class CreditUsageLog(models.Model):
|
||||
account = models.ForeignKey(Account)
|
||||
operation_type = models.CharField() # clustering/content_generation/image_generation
|
||||
credits_used = models.DecimalField()
|
||||
model_used = models.CharField()
|
||||
tokens_input = models.IntegerField()
|
||||
tokens_output = models.IntegerField()
|
||||
created_at = models.DateTimeField()
|
||||
```
|
||||
|
||||
---
|
||||
@@ -172,26 +167,58 @@ class CreditUsage(models.Model):
|
||||
|
||||
### CreditService
|
||||
|
||||
Location: `backend/igny8_core/business/billing/services.py`
|
||||
Location: `backend/igny8_core/business/billing/services/credit_service.py`
|
||||
|
||||
**Key Methods:**
|
||||
|
||||
```python
|
||||
class CreditService:
|
||||
def check_balance(account, site, credit_type, amount) -> bool:
|
||||
"""Check if sufficient credits available"""
|
||||
@staticmethod
|
||||
def check_credits(account, required_credits):
|
||||
"""Check if sufficient credits available, raises InsufficientCreditsError if not"""
|
||||
|
||||
def deduct_credits(account, site, user, credit_type, amount, operation) -> bool:
|
||||
"""Deduct credits and log usage"""
|
||||
@staticmethod
|
||||
def deduct_credits_for_operation(account, operation_type, model, tokens_in, tokens_out, metadata=None):
|
||||
"""Deduct credits and log usage after AI operation"""
|
||||
|
||||
def get_balance(account, site) -> CreditBalance:
|
||||
"""Get current balance"""
|
||||
@staticmethod
|
||||
def add_credits(account, amount, transaction_type, description):
|
||||
"""Add credits (admin/purchase/subscription)"""
|
||||
|
||||
def reset_monthly_credits(account) -> None:
|
||||
"""Reset credits at period start"""
|
||||
@staticmethod
|
||||
def calculate_credits_from_tokens(operation_type, tokens_in, tokens_out, model=None):
|
||||
"""Calculate credits based on token usage and model"""
|
||||
```
|
||||
|
||||
### LimitService
|
||||
|
||||
Location: `backend/igny8_core/business/billing/services/limit_service.py`
|
||||
|
||||
**Key Methods:**
|
||||
|
||||
```python
|
||||
class LimitService:
|
||||
HARD_LIMIT_MAPPINGS = {
|
||||
'sites': {...},
|
||||
'users': {...},
|
||||
'keywords': {...},
|
||||
}
|
||||
|
||||
MONTHLY_LIMIT_MAPPINGS = {
|
||||
'ahrefs_queries': {...},
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def check_hard_limit(cls, account, limit_name, additional_count=1):
|
||||
"""Check if adding items would exceed hard limit"""
|
||||
|
||||
def add_credits(account, credit_type, amount, reason) -> None:
|
||||
"""Add credits (admin/purchase)"""
|
||||
@classmethod
|
||||
def check_monthly_limit(cls, account, limit_name, additional_count=1):
|
||||
"""Check if operation would exceed monthly limit"""
|
||||
|
||||
@classmethod
|
||||
def increment_monthly_usage(cls, account, limit_name, count=1):
|
||||
"""Increment monthly usage counter"""
|
||||
```
|
||||
|
||||
### Usage in AI Operations
|
||||
@@ -199,26 +226,23 @@ class CreditService:
|
||||
```python
|
||||
# In content generation service
|
||||
def generate_content(task, user):
|
||||
# 1. Check balance
|
||||
if not credit_service.check_balance(
|
||||
account=task.site.account,
|
||||
site=task.site,
|
||||
credit_type='content',
|
||||
amount=1
|
||||
):
|
||||
raise InsufficientCreditsError()
|
||||
account = task.site.account
|
||||
|
||||
# 1. Pre-check credits (estimated)
|
||||
estimated_credits = 50 # Estimate for content generation
|
||||
CreditService.check_credits(account, estimated_credits)
|
||||
|
||||
# 2. Execute AI function
|
||||
content = ai_engine.generate_content(task)
|
||||
content, usage = ai_engine.generate_content(task)
|
||||
|
||||
# 3. Deduct credits
|
||||
credit_service.deduct_credits(
|
||||
account=task.site.account,
|
||||
site=task.site,
|
||||
user=user,
|
||||
credit_type='content',
|
||||
amount=1,
|
||||
operation='generate_content'
|
||||
# 3. Deduct actual credits based on token usage
|
||||
CreditService.deduct_credits_for_operation(
|
||||
account=account,
|
||||
operation_type='content_generation',
|
||||
model=usage.model,
|
||||
tokens_in=usage.input_tokens,
|
||||
tokens_out=usage.output_tokens,
|
||||
metadata={'content_id': content.id}
|
||||
)
|
||||
|
||||
return content
|
||||
@@ -228,19 +252,14 @@ def generate_content(task, user):
|
||||
|
||||
## API Responses
|
||||
|
||||
### Successful Deduction
|
||||
### Successful Operation
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": { ... },
|
||||
"credits_used": {
|
||||
"type": "content",
|
||||
"amount": 1
|
||||
},
|
||||
"balance": {
|
||||
"content_credits": 49
|
||||
}
|
||||
"credits_used": 15,
|
||||
"balance": 9985
|
||||
}
|
||||
```
|
||||
|
||||
@@ -251,10 +270,25 @@ HTTP 402 Payment Required
|
||||
|
||||
{
|
||||
"success": false,
|
||||
"error": "Insufficient content credits",
|
||||
"error": "Insufficient credits",
|
||||
"code": "INSUFFICIENT_CREDITS",
|
||||
"required": 1,
|
||||
"available": 0
|
||||
"required": 50,
|
||||
"available": 25
|
||||
}
|
||||
```
|
||||
|
||||
### Limit Exceeded
|
||||
|
||||
```json
|
||||
HTTP 402 Payment Required
|
||||
|
||||
{
|
||||
"success": false,
|
||||
"error": "Keyword limit reached",
|
||||
"code": "HARD_LIMIT_EXCEEDED",
|
||||
"limit": "keywords",
|
||||
"current": 500,
|
||||
"max": 500
|
||||
}
|
||||
```
|
||||
|
||||
@@ -262,113 +296,125 @@ HTTP 402 Payment Required
|
||||
|
||||
## Frontend Handling
|
||||
|
||||
### Balance Display
|
||||
### Credit Balance Display
|
||||
|
||||
- Header shows credit balances
|
||||
- Header shows current credit balance
|
||||
- Updates after each operation
|
||||
- Warning at low balance (< 10%)
|
||||
|
||||
### Error Handling
|
||||
### Pre-Operation Check
|
||||
|
||||
```typescript
|
||||
// In writer store
|
||||
async generateContent(taskId: string) {
|
||||
try {
|
||||
const response = await api.generateContent(taskId);
|
||||
// Update billing store
|
||||
billingStore.fetchBalance();
|
||||
return response;
|
||||
} catch (error) {
|
||||
if (error.code === 'INSUFFICIENT_CREDITS') {
|
||||
// Show upgrade modal
|
||||
uiStore.showUpgradeModal();
|
||||
import { checkCreditsBeforeOperation } from '@/utils/creditCheck';
|
||||
import { useInsufficientCreditsModal } from '@/components/billing/InsufficientCreditsModal';
|
||||
|
||||
function ContentGenerator() {
|
||||
const { showModal } = useInsufficientCreditsModal();
|
||||
|
||||
const handleGenerate = async () => {
|
||||
// Check credits before operation
|
||||
const check = await checkCreditsBeforeOperation(50); // estimated cost
|
||||
|
||||
if (!check.hasEnoughCredits) {
|
||||
showModal({
|
||||
requiredCredits: check.requiredCredits,
|
||||
availableCredits: check.availableCredits,
|
||||
});
|
||||
return;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
||||
// Proceed with generation
|
||||
await generateContent();
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Usage Tracking
|
||||
## API Endpoints
|
||||
|
||||
### Usage Summary Endpoint
|
||||
### Credit Balance
|
||||
|
||||
```
|
||||
GET /api/v1/billing/usage/summary/?period=month
|
||||
GET /api/v1/billing/balance/
|
||||
```
|
||||
|
||||
Response:
|
||||
```json
|
||||
{
|
||||
"period": "2025-01",
|
||||
"usage": {
|
||||
"idea_credits": 45,
|
||||
"content_credits": 23,
|
||||
"image_credits": 67,
|
||||
"optimization_credits": 0
|
||||
},
|
||||
"by_operation": {
|
||||
"auto_cluster": 12,
|
||||
"generate_ideas": 33,
|
||||
"generate_content": 23,
|
||||
"generate_images": 67
|
||||
}
|
||||
"credits": 9500,
|
||||
"plan_credits_per_month": 10000,
|
||||
"credits_used_this_month": 500,
|
||||
"credits_remaining": 9500
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Automation Credit Estimation
|
||||
|
||||
Before running automation:
|
||||
### Usage Limits
|
||||
|
||||
```
|
||||
GET /api/v1/automation/estimate/?site_id=...
|
||||
GET /api/v1/billing/usage/limits/
|
||||
```
|
||||
|
||||
Response:
|
||||
```json
|
||||
{
|
||||
"estimated_credits": {
|
||||
"idea_credits": 25,
|
||||
"content_credits": 10,
|
||||
"image_credits": 30
|
||||
"limits": {
|
||||
"sites": { "current": 2, "limit": 5, "type": "hard" },
|
||||
"users": { "current": 2, "limit": 3, "type": "hard" },
|
||||
"keywords": { "current": 847, "limit": 1000, "type": "hard" },
|
||||
"ahrefs_queries": { "current": 23, "limit": 50, "type": "monthly" }
|
||||
},
|
||||
"stages": {
|
||||
"clustering": 5,
|
||||
"ideas": 20,
|
||||
"content": 10,
|
||||
"images": 30
|
||||
},
|
||||
"has_sufficient_credits": true
|
||||
"days_until_reset": 18
|
||||
}
|
||||
```
|
||||
|
||||
### Usage Analytics
|
||||
|
||||
```
|
||||
GET /api/v1/account/usage/analytics/?days=30
|
||||
```
|
||||
|
||||
Response:
|
||||
```json
|
||||
{
|
||||
"period_days": 30,
|
||||
"start_date": "2025-12-06",
|
||||
"end_date": "2026-01-05",
|
||||
"current_balance": 9500,
|
||||
"total_usage": 500,
|
||||
"total_purchases": 0,
|
||||
"usage_by_type": [
|
||||
{ "transaction_type": "content_generation", "total": -350, "count": 15 },
|
||||
{ "transaction_type": "image_generation", "total": -100, "count": 20 },
|
||||
{ "transaction_type": "clustering", "total": -50, "count": 10 }
|
||||
],
|
||||
"daily_usage": [
|
||||
{ "date": "2026-01-05", "usage": 25, "purchases": 0, "net": -25 }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Credit Reset
|
||||
## Credit Allocation
|
||||
|
||||
Credits reset monthly based on billing cycle:
|
||||
Credits are added to `Account.credits` when:
|
||||
|
||||
1. **Monthly Reset Job** runs at period end
|
||||
2. **Unused credits** do not roll over
|
||||
3. **Purchased credits** may have different expiry
|
||||
1. **Subscription Renewal** - `Plan.included_credits` added monthly
|
||||
2. **Payment Approval** - Manual payments approved by admin
|
||||
3. **Credit Purchase** - Credit packages bought by user
|
||||
4. **Admin Adjustment** - Manual credit grants/adjustments
|
||||
|
||||
### Celery Task
|
||||
### Monthly Reset
|
||||
|
||||
Monthly limits (Ahrefs queries) reset on billing cycle:
|
||||
|
||||
```python
|
||||
@celery.task
|
||||
def reset_monthly_credits():
|
||||
"""
|
||||
Run daily, resets credits for accounts
|
||||
whose period_end is today
|
||||
"""
|
||||
today = date.today()
|
||||
balances = CreditBalance.objects.filter(period_end=today)
|
||||
|
||||
for balance in balances:
|
||||
credit_service.reset_monthly_credits(balance.account)
|
||||
# In Account model
|
||||
def reset_monthly_usage(self):
|
||||
"""Reset monthly usage counters (called on billing cycle renewal)"""
|
||||
self.usage_ahrefs_queries = 0
|
||||
self.save(update_fields=['usage_ahrefs_queries'])
|
||||
```
|
||||
|
||||
---
|
||||
@@ -380,20 +426,29 @@ def reset_monthly_credits():
|
||||
Via Django Admin or API:
|
||||
|
||||
```python
|
||||
from igny8_core.business.billing.services.credit_service import CreditService
|
||||
|
||||
# Add credits
|
||||
credit_service.add_credits(
|
||||
CreditService.add_credits(
|
||||
account=account,
|
||||
credit_type='content',
|
||||
amount=100,
|
||||
reason='Customer support adjustment'
|
||||
amount=1000,
|
||||
transaction_type='adjustment',
|
||||
description='Customer support adjustment'
|
||||
)
|
||||
```
|
||||
|
||||
### Usage Audit
|
||||
|
||||
All credit changes logged in `CreditUsage` with:
|
||||
All credit changes logged in `CreditTransaction` with:
|
||||
- Timestamp
|
||||
- User who triggered
|
||||
- Transaction type
|
||||
- Amount (positive or negative)
|
||||
- Balance after transaction
|
||||
- Description
|
||||
|
||||
All AI operations logged in `CreditUsageLog` with:
|
||||
- Operation type
|
||||
- Amount deducted
|
||||
- Related object ID
|
||||
- Credits used
|
||||
- Model used
|
||||
- Token counts
|
||||
- Related object metadata
|
||||
|
||||
2449
docs/plans/CREDITS-LIMITS-AUDIT-REPORT.md
Normal file
2449
docs/plans/CREDITS-LIMITS-AUDIT-REPORT.md
Normal file
File diff suppressed because it is too large
Load Diff
2578
docs/plans/CREDITS-LIMITS-IMPLEMENTATION-PLAN.md
Normal file
2578
docs/plans/CREDITS-LIMITS-IMPLEMENTATION-PLAN.md
Normal file
File diff suppressed because it is too large
Load Diff
274
docs/plans/IMPLEMENTATION-SUMMARY.md
Normal file
274
docs/plans/IMPLEMENTATION-SUMMARY.md
Normal file
@@ -0,0 +1,274 @@
|
||||
# Credits & Limits Implementation - Quick Summary
|
||||
|
||||
**Status:** 🚧 READY TO IMPLEMENT
|
||||
**Timeline:** 5 weeks
|
||||
**Priority:** HIGH
|
||||
|
||||
---
|
||||
|
||||
## The Big Picture
|
||||
|
||||
### What We're Doing
|
||||
Simplifying the IGNY8 credits and limits system from complex (10+ limits) to simple (4 limits only).
|
||||
|
||||
### Core Philosophy
|
||||
**Keep only 4 hard limits. Everything else = credits.**
|
||||
|
||||
---
|
||||
|
||||
## The 4 Limits (FINAL)
|
||||
|
||||
| Limit | Type | What It Controls |
|
||||
|-------|------|-----------------|
|
||||
| **Sites** | Hard | Max sites per account (e.g., 1, 2, 5, unlimited) |
|
||||
| **Team Users** | Hard | Max team members (e.g., 1, 2, 3, 5) |
|
||||
| **Keywords** | Hard | Total keywords in workspace (e.g., 100, 1K, 5K, 20K) |
|
||||
| **Ahrefs Queries** | Monthly | Live keyword research per month (e.g., 0, 50, 200, 500) |
|
||||
|
||||
**Everything else (content, images, ideas, etc.) = credits only.**
|
||||
|
||||
---
|
||||
|
||||
## What Gets REMOVED
|
||||
|
||||
### Database Fields to Delete
|
||||
|
||||
**From Plan Model:**
|
||||
- ❌ `max_content_ideas`
|
||||
- ❌ `max_content_words`
|
||||
- ❌ `max_images_basic`
|
||||
- ❌ `max_images_premium`
|
||||
- ❌ `max_image_prompts`
|
||||
- ❌ `max_clusters` (consider merging with keywords)
|
||||
|
||||
**From Account Model:**
|
||||
- ❌ `usage_content_ideas`
|
||||
- ❌ `usage_content_words`
|
||||
- ❌ `usage_images_basic`
|
||||
- ❌ `usage_images_premium`
|
||||
- ❌ `usage_image_prompts`
|
||||
|
||||
### Why?
|
||||
- Confusing for users (double limiting)
|
||||
- Maintenance overhead
|
||||
- Credit system already provides control
|
||||
|
||||
---
|
||||
|
||||
## What Gets ADDED
|
||||
|
||||
### New Fields
|
||||
|
||||
**Plan Model:**
|
||||
```python
|
||||
max_ahrefs_queries = models.IntegerField(default=50)
|
||||
```
|
||||
|
||||
**Account Model:**
|
||||
```python
|
||||
usage_ahrefs_queries = models.IntegerField(default=0)
|
||||
```
|
||||
|
||||
### New Feature: Keyword Research
|
||||
|
||||
**Two ways to add keywords:**
|
||||
|
||||
1. **Browse Pre-Researched Keywords** (FREE)
|
||||
- IGNY8's global keyword database
|
||||
- Pre-analyzed, ready to use
|
||||
- Limited by: `max_keywords` (workspace limit)
|
||||
|
||||
2. **Research with Ahrefs** (LIMITED)
|
||||
- Live Ahrefs API queries
|
||||
- Fresh, custom keyword data
|
||||
- Limited by: `max_ahrefs_queries` (monthly limit)
|
||||
|
||||
---
|
||||
|
||||
## Page Changes
|
||||
|
||||
### Plans & Billing Page (Simplified)
|
||||
|
||||
**Current Plan Tab - BEFORE:**
|
||||
- ❌ Credit balance display
|
||||
- ❌ Usage charts
|
||||
- ❌ Limit progress bars
|
||||
- ❌ "Credits used this month" breakdown
|
||||
|
||||
**Current Plan Tab - AFTER:**
|
||||
- ✅ Plan name, price, renewal date
|
||||
- ✅ Brief summary: "50 articles • 2 sites • 2 users"
|
||||
- ✅ Upgrade CTA
|
||||
- ❌ NO detailed usage (moved to Usage page)
|
||||
|
||||
### Usage Page (Enhanced)
|
||||
|
||||
**NEW Tab Structure:**
|
||||
|
||||
1. **Overview** (NEW)
|
||||
- Quick stats cards (credits, sites, users, keywords)
|
||||
- Period selector (7, 30, 90 days)
|
||||
- Top metrics
|
||||
|
||||
2. **Your Limits**
|
||||
- Only 4 limits with progress bars
|
||||
- Sites, Users, Keywords, Ahrefs Queries
|
||||
|
||||
3. **Credit Insights** (NEW)
|
||||
- Credits by Site
|
||||
- Credits by Action Type
|
||||
- Credits by Image Quality (basic/quality/premium)
|
||||
- Credits by Automation
|
||||
- Timeline chart
|
||||
|
||||
4. **Activity Log**
|
||||
- Detailed transaction history
|
||||
- (Renamed from "API Activity")
|
||||
|
||||
---
|
||||
|
||||
## Key Implementation Tasks
|
||||
|
||||
### Backend (Week 1-2)
|
||||
|
||||
1. **Remove unused fields**
|
||||
- Create migration to drop fields
|
||||
- Update models, serializers
|
||||
- Remove from LimitService mappings
|
||||
|
||||
2. **Add Ahrefs fields**
|
||||
- Add to Plan and Account models
|
||||
- Add to LimitService mappings
|
||||
- Create Ahrefs service
|
||||
|
||||
3. **Enforce limits properly**
|
||||
- Add keyword limit checks to ALL entry points
|
||||
- Add automation credit pre-check
|
||||
- Validate before all operations
|
||||
|
||||
### Frontend (Week 2-3)
|
||||
|
||||
1. **Clean up Plans & Billing**
|
||||
- Remove duplicate credit/usage data
|
||||
- Keep only financial info
|
||||
|
||||
2. **Enhance Usage page**
|
||||
- Add Overview tab
|
||||
- Add Credit Insights tab with widgets
|
||||
- Multi-dimensional breakdowns
|
||||
|
||||
3. **Build Keyword Research**
|
||||
- Browse panel (existing SeedKeywords)
|
||||
- Ahrefs panel (new)
|
||||
- Query limit indicator
|
||||
|
||||
4. **Update terminology**
|
||||
- Remove "API", "operations"
|
||||
- Use "actions", "activities"
|
||||
|
||||
---
|
||||
|
||||
## Validation Requirements
|
||||
|
||||
### Must Check BEFORE Every Operation
|
||||
|
||||
**All AI Operations:**
|
||||
```python
|
||||
# 1. Check credits
|
||||
CreditService.check_credits(account, estimated_credits)
|
||||
|
||||
# 2. Execute
|
||||
result = ai_service.execute()
|
||||
|
||||
# 3. Deduct
|
||||
CreditService.deduct_credits_for_operation(...)
|
||||
```
|
||||
|
||||
**Keyword Creation:**
|
||||
```python
|
||||
# Check limit
|
||||
LimitService.check_hard_limit(account, 'keywords', count)
|
||||
|
||||
# Then create
|
||||
Keywords.objects.bulk_create([...])
|
||||
```
|
||||
|
||||
**Automation Runs:**
|
||||
```python
|
||||
# Estimate total cost
|
||||
estimated = estimate_automation_cost(config)
|
||||
|
||||
# Check BEFORE starting
|
||||
CreditService.check_credits(account, estimated)
|
||||
|
||||
# Then run
|
||||
execute_automation(config)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Success Criteria
|
||||
|
||||
### Technical
|
||||
- [ ] All unused fields removed
|
||||
- [ ] 4 limits properly enforced
|
||||
- [ ] Credit checks before ALL operations
|
||||
- [ ] Automation pre-checks credits
|
||||
- [ ] No duplicate data across pages
|
||||
|
||||
### User Experience
|
||||
- [ ] Simple 4-limit model is clear
|
||||
- [ ] Multi-dimensional insights are actionable
|
||||
- [ ] Keyword research flow is intuitive
|
||||
- [ ] Error messages are user-friendly
|
||||
- [ ] Upgrade prompts at right moments
|
||||
|
||||
### Business
|
||||
- [ ] Reduced support questions
|
||||
- [ ] Higher upgrade conversion
|
||||
- [ ] Better credit visibility
|
||||
- [ ] System scales cleanly
|
||||
|
||||
---
|
||||
|
||||
## Suggested Plan Values
|
||||
|
||||
| Plan | Price | Credits/mo | Sites | Users | Keywords | Ahrefs/mo |
|
||||
|------|-------|-----------|-------|-------|----------|-----------|
|
||||
| **Free** | $0 | 2,000 | 1 | 1 | 100 | 0 |
|
||||
| **Starter** | $49 | 10,000 | 2 | 2 | 1,000 | 50 |
|
||||
| **Growth** | $149 | 40,000 | 5 | 3 | 5,000 | 200 |
|
||||
| **Scale** | $399 | 120,000 | ∞ | 5 | 20,000 | 500 |
|
||||
|
||||
---
|
||||
|
||||
## Timeline
|
||||
|
||||
**Week 1:** Backend cleanup (remove fields, add Ahrefs)
|
||||
**Week 2:** Enforcement (keyword limits, automation checks)
|
||||
**Week 3:** Frontend cleanup (remove duplicates, update UI)
|
||||
**Week 4:** New features (Credit Insights, Keyword Research)
|
||||
**Week 5:** Testing & Production deployment
|
||||
|
||||
---
|
||||
|
||||
## Files to Review
|
||||
|
||||
**Full Implementation Plan:**
|
||||
- `docs/plans/CREDITS-LIMITS-IMPLEMENTATION-PLAN.md`
|
||||
|
||||
**Current System Docs:**
|
||||
- `docs/10-MODULES/BILLING.md`
|
||||
- `docs/40-WORKFLOWS/CREDIT-SYSTEM.md`
|
||||
|
||||
**Code:**
|
||||
- Backend: `backend/igny8_core/auth/models.py`
|
||||
- Backend: `backend/igny8_core/business/billing/services/limit_service.py`
|
||||
- Frontend: `frontend/src/pages/account/PlansAndBillingPage.tsx`
|
||||
- Frontend: `frontend/src/pages/account/UsageAnalyticsPage.tsx`
|
||||
|
||||
---
|
||||
|
||||
**Status:** ✅ READY FOR TEAM REVIEW AND IMPLEMENTATION
|
||||
|
||||
**Next Step:** Schedule implementation kickoff meeting with backend, frontend, and QA leads.
|
||||
356
docs/plans/SYSTEM-ARCHITECTURE-DIAGRAM.md
Normal file
356
docs/plans/SYSTEM-ARCHITECTURE-DIAGRAM.md
Normal file
@@ -0,0 +1,356 @@
|
||||
# System Architecture - Before vs After
|
||||
|
||||
## BEFORE (Complex & Confusing)
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ PLAN MODEL (BEFORE) │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ Hard Limits (Never Reset): │
|
||||
│ ├─ max_sites ✅ Keep │
|
||||
│ ├─ max_users ✅ Keep │
|
||||
│ ├─ max_keywords ✅ Keep │
|
||||
│ └─ max_clusters ❌ Remove │
|
||||
│ │
|
||||
│ Monthly Limits (Reset Every Month): │
|
||||
│ ├─ max_content_ideas ❌ Remove │
|
||||
│ ├─ max_content_words ❌ Remove │
|
||||
│ ├─ max_images_basic ❌ Remove │
|
||||
│ ├─ max_images_premium ❌ Remove │
|
||||
│ └─ max_image_prompts ❌ Remove │
|
||||
│ │
|
||||
│ Credits: │
|
||||
│ └─ included_credits ✅ Keep │
|
||||
│ │
|
||||
│ TOTAL LIMITS: 10 fields ❌ TOO COMPLEX │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ ACCOUNT MODEL (BEFORE) │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ Credits: │
|
||||
│ └─ credits ✅ Keep │
|
||||
│ │
|
||||
│ Monthly Usage Tracking: │
|
||||
│ ├─ usage_content_ideas ❌ Remove │
|
||||
│ ├─ usage_content_words ❌ Remove │
|
||||
│ ├─ usage_images_basic ❌ Remove │
|
||||
│ ├─ usage_images_premium ❌ Remove │
|
||||
│ └─ usage_image_prompts ❌ Remove │
|
||||
│ │
|
||||
│ Period Tracking: │
|
||||
│ ├─ usage_period_start ✅ Keep │
|
||||
│ └─ usage_period_end ✅ Keep │
|
||||
│ │
|
||||
│ TOTAL USAGE FIELDS: 5 ❌ UNNECESSARY │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
|
||||
USER CONFUSION:
|
||||
"I have 5000 credits but can't generate content because I hit my
|
||||
monthly word limit? This makes no sense!"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## AFTER (Simple & Clear)
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ PLAN MODEL (AFTER) │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ THE ONLY 4 LIMITS: │
|
||||
│ ├─ max_sites (e.g., 1, 2, 5, unlimited) │
|
||||
│ ├─ max_users (e.g., 1, 2, 3, 5) │
|
||||
│ ├─ max_keywords (e.g., 100, 1K, 5K, 20K) │
|
||||
│ └─ max_ahrefs_queries (e.g., 0, 50, 200, 500) [NEW] │
|
||||
│ │
|
||||
│ Credits: │
|
||||
│ └─ included_credits (e.g., 2K, 10K, 40K, 120K) │
|
||||
│ │
|
||||
│ TOTAL LIMITS: 4 fields ✅ SIMPLE & CLEAR │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ ACCOUNT MODEL (AFTER) │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ Credits: │
|
||||
│ └─ credits (current balance) │
|
||||
│ │
|
||||
│ Monthly Usage Tracking: │
|
||||
│ └─ usage_ahrefs_queries (only 1 tracker needed) [NEW] │
|
||||
│ │
|
||||
│ Period Tracking: │
|
||||
│ ├─ usage_period_start │
|
||||
│ └─ usage_period_end │
|
||||
│ │
|
||||
│ TOTAL USAGE FIELDS: 1 ✅ CLEAN │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
|
||||
USER CLARITY:
|
||||
"I have 5000 credits. I can use them for whatever I need -
|
||||
articles, images, ideas. Simple!"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Page Structure - Before vs After
|
||||
|
||||
### BEFORE (Duplicate Data Everywhere)
|
||||
|
||||
```
|
||||
┌───────────────────────────────────────────────────────────────────┐
|
||||
│ PLANS & BILLING PAGE │
|
||||
├───────────────────────────────────────────────────────────────────┤
|
||||
│ Tab 1: Current Plan │
|
||||
│ ├─ Plan name, price ✅ │
|
||||
│ ├─ Credit balance ⚠️ DUPLICATE (also in Usage) │
|
||||
│ ├─ Credits used ⚠️ DUPLICATE (also in Usage) │
|
||||
│ ├─ Usage charts ⚠️ DUPLICATE (also in Usage) │
|
||||
│ └─ Limit bars ⚠️ DUPLICATE (also in Usage) │
|
||||
│ │
|
||||
│ Tab 2: Upgrade │
|
||||
│ └─ Pricing table ✅ │
|
||||
│ │
|
||||
│ Tab 3: History │
|
||||
│ ├─ Invoices ✅ │
|
||||
│ └─ Transactions ⚠️ PARTIAL DUPLICATE (also in Usage) │
|
||||
└───────────────────────────────────────────────────────────────────┘
|
||||
|
||||
┌───────────────────────────────────────────────────────────────────┐
|
||||
│ USAGE PAGE │
|
||||
├───────────────────────────────────────────────────────────────────┤
|
||||
│ Tab 1: Limits & Usage │
|
||||
│ ├─ Credit balance ⚠️ DUPLICATE (also in Plans & Billing) │
|
||||
│ ├─ Credits used ⚠️ DUPLICATE (also in Plans & Billing) │
|
||||
│ ├─ Limit bars ⚠️ DUPLICATE (also in Plans & Billing) │
|
||||
│ └─ 10+ limit types ❌ TOO MANY │
|
||||
│ │
|
||||
│ Tab 2: Credit History │
|
||||
│ └─ Transactions ⚠️ PARTIAL DUPLICATE (also in Plans & Billing)│
|
||||
│ │
|
||||
│ Tab 3: API Activity ❌ TECHNICAL TERMINOLOGY │
|
||||
│ └─ Operation logs │
|
||||
└───────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### AFTER (Clear Separation)
|
||||
|
||||
```
|
||||
┌───────────────────────────────────────────────────────────────────┐
|
||||
│ PLANS & BILLING PAGE (Financial Focus) │
|
||||
├───────────────────────────────────────────────────────────────────┤
|
||||
│ Tab 1: Current Plan │
|
||||
│ ├─ Plan name, price, renewal date ✅ │
|
||||
│ ├─ Brief summary: "50 articles • 2 sites • 2 users" ✅ │
|
||||
│ ├─ Upgrade CTA ✅ │
|
||||
│ └─ ❌ NO usage details (moved to Usage page) │
|
||||
│ │
|
||||
│ Tab 2: Upgrade Plan │
|
||||
│ └─ Pricing table ✅ │
|
||||
│ │
|
||||
│ Tab 3: Billing History │
|
||||
│ ├─ Invoices ✅ │
|
||||
│ ├─ Payment methods ✅ │
|
||||
│ └─ Credit purchases ✅ (financial transactions only) │
|
||||
└───────────────────────────────────────────────────────────────────┘
|
||||
|
||||
┌───────────────────────────────────────────────────────────────────┐
|
||||
│ USAGE PAGE (Consumption Tracking) │
|
||||
├───────────────────────────────────────────────────────────────────┤
|
||||
│ Tab 1: Overview [NEW] │
|
||||
│ ├─ Quick stats: Credits, Sites, Users, Keywords ✅ │
|
||||
│ ├─ Period selector: 7/30/90 days ✅ │
|
||||
│ └─ Top metrics for selected period ✅ │
|
||||
│ │
|
||||
│ Tab 2: Your Limits │
|
||||
│ └─ ONLY 4 limits with progress bars ✅ │
|
||||
│ ├─ Sites (e.g., 2 / 5) │
|
||||
│ ├─ Users (e.g., 2 / 3) │
|
||||
│ ├─ Keywords (e.g., 847 / 1,000) │
|
||||
│ └─ Ahrefs Queries (e.g., 23 / 50 this month) │
|
||||
│ │
|
||||
│ Tab 3: Credit Insights [NEW] │
|
||||
│ ├─ Credits by Site 📊 │
|
||||
│ ├─ Credits by Action Type 📊 │
|
||||
│ ├─ Credits by Image Quality 📊 │
|
||||
│ ├─ Credits by Automation 📊 │
|
||||
│ └─ Timeline chart 📈 │
|
||||
│ │
|
||||
│ Tab 4: Activity Log (renamed from "API Activity") │
|
||||
│ └─ Detailed transaction history ✅ │
|
||||
└───────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Credit Flow - Before vs After
|
||||
|
||||
### BEFORE (Double Limiting)
|
||||
|
||||
```
|
||||
User wants to generate content
|
||||
↓
|
||||
Check 1: Do they have credits? ✅ Yes, 5000 credits
|
||||
↓
|
||||
Check 2: Have they hit monthly word limit? ❌ YES, 100K/100K
|
||||
↓
|
||||
BLOCKED! "You've reached your monthly word limit"
|
||||
↓
|
||||
User: "But I have 5000 credits left! 😤"
|
||||
```
|
||||
|
||||
### AFTER (Simple Credit-Based)
|
||||
|
||||
```
|
||||
User wants to generate content
|
||||
↓
|
||||
Check: Do they have credits? ✅ Yes, 5000 credits
|
||||
↓
|
||||
Generate content (costs ~50 credits based on tokens)
|
||||
↓
|
||||
Deduct credits: 5000 - 50 = 4950 remaining
|
||||
↓
|
||||
User: "Simple! I can use my credits however I want 😊"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Keyword Research - NEW Structure
|
||||
|
||||
```
|
||||
┌───────────────────────────────────────────────────────────────────┐
|
||||
│ KEYWORD RESEARCH PAGE [NEW] │
|
||||
├───────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ Tab Selector: │
|
||||
│ [Browse Pre-Researched] [Research with Ahrefs - 42/50 left] │
|
||||
│ │
|
||||
├───────────────────────────────────────────────────────────────────┤
|
||||
│ Option 1: Browse Pre-Researched Keywords (FREE) │
|
||||
│ ─────────────────────────────────────────────── │
|
||||
│ • Global IGNY8 keyword database │
|
||||
│ • Filter by: Industry, Sector, Country │
|
||||
│ • Pre-analyzed metrics: Volume, Difficulty, Opportunity │
|
||||
│ • Free to browse and add │
|
||||
│ • Adds to workspace (counts toward max_keywords) │
|
||||
│ │
|
||||
│ [Industry ▼] [Sector ▼] [Country ▼] [Search...] │
|
||||
│ │
|
||||
│ Results: │
|
||||
│ ┌─────────────────────────────────────────────────────────┐ │
|
||||
│ │ Keyword │ Volume │ Diff │ Score │ [Add] │ │
|
||||
│ ├─────────────────────────────────────────────────────────┤ │
|
||||
│ │ digital marketing │ 45K │ 65 │ 88/100 │ [Add] │ │
|
||||
│ │ content strategy │ 12K │ 42 │ 92/100 │ [Add] │ │
|
||||
│ │ seo optimization │ 33K │ 58 │ 85/100 │ [Add] │ │
|
||||
│ └─────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
├───────────────────────────────────────────────────────────────────┤
|
||||
│ Option 2: Research with Ahrefs (LIMITED) │
|
||||
│ ─────────────────────────────────────────── │
|
||||
│ • Live Ahrefs API queries │
|
||||
│ • Fresh, custom keyword data │
|
||||
│ • Monthly limit: 42 / 50 queries remaining ⚠️ │
|
||||
│ • Resets: February 1, 2026 │
|
||||
│ │
|
||||
│ [Enter seed keyword...] [Research Keywords] │
|
||||
│ │
|
||||
│ ⚠️ Warning: Each search uses 1 query from your monthly limit │
|
||||
│ │
|
||||
│ Results (if searched): │
|
||||
│ ┌─────────────────────────────────────────────────────────┐ │
|
||||
│ │ Keyword │ Volume │ Diff │ CPC │ [Add] │ │
|
||||
│ ├─────────────────────────────────────────────────────────┤ │
|
||||
│ │ ai content tools │ 8.9K │ 48 │ $4.50│ [Add] │ │
|
||||
│ │ automated writing │ 3.2K │ 35 │ $3.80│ [Add] │ │
|
||||
│ └─────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
└───────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Enforcement Points
|
||||
|
||||
### Backend Validation (REQUIRED at these locations)
|
||||
|
||||
```
|
||||
1. Keyword Creation
|
||||
├─ Manual keyword creation form
|
||||
├─ SeedKeyword import (from Browse tab)
|
||||
├─ Ahrefs import (from Research tab)
|
||||
├─ CSV/Excel bulk import
|
||||
└─ API endpoint: POST /api/v1/keywords/
|
||||
|
||||
✅ Must check: LimitService.check_hard_limit(account, 'keywords', count)
|
||||
|
||||
2. Site Creation
|
||||
└─ API endpoint: POST /api/v1/sites/
|
||||
|
||||
✅ Must check: LimitService.check_hard_limit(account, 'sites', 1)
|
||||
|
||||
3. User Invitation
|
||||
└─ API endpoint: POST /api/v1/users/invite/
|
||||
|
||||
✅ Must check: LimitService.check_hard_limit(account, 'users', 1)
|
||||
|
||||
4. Ahrefs Query
|
||||
└─ API endpoint: POST /api/v1/keywords/ahrefs/search/
|
||||
|
||||
✅ Must check: LimitService.check_monthly_limit(account, 'ahrefs_queries', 1)
|
||||
✅ Must increment: LimitService.increment_usage(account, 'ahrefs_queries', 1)
|
||||
|
||||
5. All AI Operations (Content, Images, Ideas, etc.)
|
||||
|
||||
✅ Must check: CreditService.check_credits(account, estimated_credits)
|
||||
✅ Must deduct: CreditService.deduct_credits_for_operation(...)
|
||||
|
||||
6. Automation Runs
|
||||
|
||||
✅ Must pre-check: CreditService.check_credits(account, estimated_total)
|
||||
✅ Each stage deducts: CreditService.deduct_credits_for_operation(...)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Error Messages (User-Friendly)
|
||||
|
||||
### OLD (Technical)
|
||||
```
|
||||
HTTP 402 - HardLimitExceededError:
|
||||
max_keywords limit reached (1000/1000)
|
||||
```
|
||||
|
||||
### NEW (User-Friendly)
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ ⚠️ Keyword Limit Reached │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ You've reached your keyword limit of 1,000 keywords. │
|
||||
│ │
|
||||
│ Current workspace: 1,000 keywords │
|
||||
│ Your plan limit: 1,000 keywords │
|
||||
│ │
|
||||
│ To add more keywords, you can: │
|
||||
│ • Delete unused keywords to free up space │
|
||||
│ • Upgrade to Growth plan (5,000 keywords) │
|
||||
│ │
|
||||
│ [Delete Keywords] [Upgrade Plan] [Cancel] │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**End of Visual Reference**
|
||||
|
||||
See `CREDITS-LIMITS-IMPLEMENTATION-PLAN.md` for complete details.
|
||||
653
docs/plans/phase-3-plan/FINAL-CREDITS-LIMITS-PLAN.md
Normal file
653
docs/plans/phase-3-plan/FINAL-CREDITS-LIMITS-PLAN.md
Normal file
@@ -0,0 +1,653 @@
|
||||
# Final Credits & Limits Implementation Plan
|
||||
|
||||
**Created:** January 5, 2026
|
||||
**Status:** ✅ COMPLETE (Pending UAT & Production Deployment)
|
||||
**Last Updated:** January 5, 2026
|
||||
**Verified Against:** Backend codebase, Frontend codebase, Current implementation
|
||||
|
||||
---
|
||||
|
||||
## Executive Summary
|
||||
|
||||
After comprehensive codebase review, this document presents the verified and refined implementation plan for simplifying the credits and limits system.
|
||||
|
||||
### Key Finding: Credit Flow is CORRECT ✅
|
||||
|
||||
The current credit architecture is **sound and logical**:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ CREDIT FLOW (VERIFIED) │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ Plan.included_credits = Monthly allocation (e.g., 10,000) │
|
||||
│ ↓ (Added on subscription renewal/approval) │
|
||||
│ Account.credits = Current balance (real-time, decremented) │
|
||||
│ ↓ (Decremented on each AI operation) │
|
||||
│ CreditTransaction = Log of all credit changes │
|
||||
│ CreditUsageLog = Detailed operation tracking │
|
||||
│ │
|
||||
│ THESE ARE NOT PARALLEL - They serve different purposes: │
|
||||
│ • Plan.included_credits = "How many credits per month" │
|
||||
│ • Account.credits = "How many credits you have RIGHT NOW" │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Where credits are added to Account.credits:**
|
||||
1. `billing/views.py:445-465` - When manual payment is approved
|
||||
2. `payment_service.py:219-249` - When credit package purchased
|
||||
3. `credit_service.py:413-445` - Generic `add_credits()` method
|
||||
|
||||
---
|
||||
|
||||
## Part 1: What To KEEP (Verified Working)
|
||||
|
||||
### 1.1 Credit System (NO CHANGES NEEDED)
|
||||
|
||||
| Component | Location | Status |
|
||||
|-----------|----------|--------|
|
||||
| `Account.credits` | `auth/models.py:83` | ✅ Keep - Real-time balance |
|
||||
| `Plan.included_credits` | `auth/models.py:253` | ✅ Keep - Monthly allocation |
|
||||
| `CreditService` | `billing/services/credit_service.py` | ✅ Keep - All methods working |
|
||||
| `CreditTransaction` | `billing/models.py:26-48` | ✅ Keep - Audit trail |
|
||||
| `CreditUsageLog` | `billing/models.py:90-130` | ✅ Keep - Operation tracking |
|
||||
|
||||
### 1.2 Hard Limits to KEEP (4 only)
|
||||
|
||||
| Limit | Plan Field | Current Location | Status |
|
||||
|-------|------------|------------------|--------|
|
||||
| Sites | `max_sites` | `auth/models.py:207` | ✅ Keep |
|
||||
| Users | `max_users` | `auth/models.py:204` | ✅ Keep |
|
||||
| Keywords | `max_keywords` | `auth/models.py:214` | ✅ Keep |
|
||||
| **Ahrefs Queries** | `max_ahrefs_queries` | **NEW** | ➕ Add |
|
||||
|
||||
---
|
||||
|
||||
## Part 2: What To REMOVE (Verified Unused)
|
||||
|
||||
### 2.1 Remove From Plan Model
|
||||
|
||||
These fields create confusing "double limiting" - credits already control consumption:
|
||||
|
||||
```python
|
||||
# auth/models.py - REMOVE these fields (lines 220-251)
|
||||
max_clusters = ... # Remove - no real use
|
||||
max_content_ideas = ... # Remove - credits handle this
|
||||
max_content_words = ... # Remove - credits handle this
|
||||
max_images_basic = ... # Remove - credits handle this
|
||||
max_images_premium = ... # Remove - credits handle this
|
||||
max_image_prompts = ... # Remove - credits handle this
|
||||
```
|
||||
|
||||
**Why remove?** User confusion: "I have credits but can't generate because I hit my word limit?"
|
||||
|
||||
### 2.2 Remove From Account Model
|
||||
|
||||
```python
|
||||
# auth/models.py - REMOVE these fields (lines 108-114)
|
||||
usage_content_ideas = ... # Remove - tracked in CreditUsageLog
|
||||
usage_content_words = ... # Remove - tracked in CreditUsageLog
|
||||
usage_images_basic = ... # Remove - tracked in CreditUsageLog
|
||||
usage_images_premium = ... # Remove - tracked in CreditUsageLog
|
||||
usage_image_prompts = ... # Remove - tracked in CreditUsageLog
|
||||
```
|
||||
|
||||
### 2.3 Update LimitService
|
||||
|
||||
```python
|
||||
# limit_service.py - Update HARD_LIMIT_MAPPINGS
|
||||
HARD_LIMIT_MAPPINGS = {
|
||||
'sites': {...}, # Keep
|
||||
'users': {...}, # Keep
|
||||
'keywords': {...}, # Keep
|
||||
# 'clusters': {...}, # REMOVE
|
||||
}
|
||||
|
||||
# limit_service.py - Update MONTHLY_LIMIT_MAPPINGS
|
||||
MONTHLY_LIMIT_MAPPINGS = {
|
||||
'ahrefs_queries': { # NEW - only monthly limit
|
||||
'plan_field': 'max_ahrefs_queries',
|
||||
'usage_field': 'usage_ahrefs_queries',
|
||||
'display_name': 'Keyword Research Queries',
|
||||
},
|
||||
# REMOVE all others (content_ideas, content_words, images_*, image_prompts)
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Part 3: Database Migration
|
||||
|
||||
### Migration Script
|
||||
|
||||
```python
|
||||
# migrations/0XXX_simplify_credits_limits.py
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
('igny8_core_auth', '0XXX_previous'), # Update with actual
|
||||
]
|
||||
|
||||
operations = [
|
||||
# STEP 1: Add new Ahrefs fields
|
||||
migrations.AddField(
|
||||
model_name='plan',
|
||||
name='max_ahrefs_queries',
|
||||
field=models.IntegerField(
|
||||
default=0,
|
||||
help_text='Monthly Ahrefs keyword research queries (0 = disabled)'
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='account',
|
||||
name='usage_ahrefs_queries',
|
||||
field=models.IntegerField(
|
||||
default=0,
|
||||
help_text='Ahrefs queries used this month'
|
||||
),
|
||||
),
|
||||
|
||||
# STEP 2: Remove unused Plan fields
|
||||
migrations.RemoveField(model_name='plan', name='max_clusters'),
|
||||
migrations.RemoveField(model_name='plan', name='max_content_ideas'),
|
||||
migrations.RemoveField(model_name='plan', name='max_content_words'),
|
||||
migrations.RemoveField(model_name='plan', name='max_images_basic'),
|
||||
migrations.RemoveField(model_name='plan', name='max_images_premium'),
|
||||
migrations.RemoveField(model_name='plan', name='max_image_prompts'),
|
||||
|
||||
# STEP 3: Remove unused Account fields
|
||||
migrations.RemoveField(model_name='account', name='usage_content_ideas'),
|
||||
migrations.RemoveField(model_name='account', name='usage_content_words'),
|
||||
migrations.RemoveField(model_name='account', name='usage_images_basic'),
|
||||
migrations.RemoveField(model_name='account', name='usage_images_premium'),
|
||||
migrations.RemoveField(model_name='account', name='usage_image_prompts'),
|
||||
]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Part 4: Frontend Changes
|
||||
|
||||
### 4.1 Plans & Billing Page (Financial Focus)
|
||||
|
||||
**File:** `frontend/src/pages/account/PlansAndBillingPage.tsx`
|
||||
|
||||
**Current (881 lines):** Shows credit usage charts, limit bars = DUPLICATE of Usage page
|
||||
|
||||
**Target:** Pure financial focus
|
||||
- Tab 1: Current Plan → Name, price, renewal date, "View Usage" link
|
||||
- Tab 2: Upgrade Plan → Pricing table (already good)
|
||||
- Tab 3: Billing History → Invoices, payment methods
|
||||
|
||||
**Remove from Current Plan tab:**
|
||||
- Usage charts (move to Usage page)
|
||||
- Credit consumption breakdown (move to Usage page)
|
||||
- Limit progress bars (move to Usage page)
|
||||
|
||||
### 4.2 Usage Analytics Page (Consumption Tracking)
|
||||
|
||||
**File:** `frontend/src/pages/account/UsageAnalyticsPage.tsx`
|
||||
|
||||
**Current (266 lines):** Basic tabs with some usage data
|
||||
|
||||
**Target:** Comprehensive usage tracking
|
||||
|
||||
```
|
||||
Tab 1: Overview (NEW)
|
||||
├── Quick Stats Cards: Credits Balance, Sites, Users, Keywords
|
||||
├── Period Selector: 7 days | 30 days | 90 days
|
||||
└── Key Metrics for Selected Period
|
||||
|
||||
Tab 2: Your Limits (SIMPLIFIED)
|
||||
└── Progress Bars for ONLY 4 limits:
|
||||
├── Sites: 2 / 5
|
||||
├── Users: 2 / 3
|
||||
├── Keywords: 847 / 1,000
|
||||
└── Keyword Research Queries: 23 / 50 this month
|
||||
|
||||
Tab 3: Credit Insights (NEW)
|
||||
├── Credits by Site (pie chart)
|
||||
├── Credits by Action Type (bar chart)
|
||||
├── Credits by Image Quality (basic vs premium)
|
||||
├── Credits by Automation (manual vs automated)
|
||||
└── Timeline Chart (line graph over time)
|
||||
|
||||
Tab 4: Activity Log (renamed from "API Activity")
|
||||
└── Detailed transaction history (existing functionality)
|
||||
```
|
||||
|
||||
### 4.3 CreditBalance Interface Update
|
||||
|
||||
**File:** `frontend/src/services/billing.api.ts`
|
||||
|
||||
```typescript
|
||||
// Current (correct - no changes needed)
|
||||
export interface CreditBalance {
|
||||
credits: number; // Account.credits (current balance)
|
||||
plan_credits_per_month: number; // Plan.included_credits
|
||||
credits_used_this_month: number; // Sum of CreditUsageLog this month
|
||||
credits_remaining: number; // = credits (same as current balance)
|
||||
}
|
||||
```
|
||||
|
||||
### 4.4 UsageSummary Interface Update
|
||||
|
||||
**File:** `frontend/src/services/billing.api.ts` (add new interface)
|
||||
|
||||
```typescript
|
||||
export interface UsageSummary {
|
||||
// Hard limits (4 only)
|
||||
hard_limits: {
|
||||
sites: { current: number; limit: number; display_name: string };
|
||||
users: { current: number; limit: number; display_name: string };
|
||||
keywords: { current: number; limit: number; display_name: string };
|
||||
ahrefs_queries: { current: number; limit: number; display_name: string };
|
||||
};
|
||||
|
||||
// Credits
|
||||
credits: {
|
||||
balance: number;
|
||||
plan_allocation: number;
|
||||
used_this_month: number;
|
||||
};
|
||||
|
||||
// Period info
|
||||
period: {
|
||||
start: string;
|
||||
end: string;
|
||||
days_remaining: number;
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Part 5: Keyword Research Feature
|
||||
|
||||
### 5.1 Two-Option Structure
|
||||
|
||||
```
|
||||
┌───────────────────────────────────────────────────────────────────┐
|
||||
│ KEYWORD RESEARCH PAGE │
|
||||
├───────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ [Browse IGNY8 Database] [Research with Ahrefs - 42/50 left] │
|
||||
│ │
|
||||
├───────────────────────────────────────────────────────────────────┤
|
||||
│ Option 1: Browse Pre-Researched Keywords (FREE) │
|
||||
│ • Global IGNY8 keyword database │
|
||||
│ • Pre-analyzed metrics from our research │
|
||||
│ • Free to browse and add │
|
||||
│ • Counts toward max_keywords when added │
|
||||
│ │
|
||||
├───────────────────────────────────────────────────────────────────┤
|
||||
│ Option 2: Research with Ahrefs (LIMITED) │
|
||||
│ • Live Ahrefs API queries │
|
||||
│ • Fresh, custom keyword data │
|
||||
│ • Monthly limit: 42 / 50 queries remaining │
|
||||
│ • ⚠️ Each search uses 1 query from monthly limit │
|
||||
│ │
|
||||
└───────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 5.2 Plan Tiers for Ahrefs ✅ CONFIGURED
|
||||
|
||||
| Plan | max_ahrefs_queries | Description |
|
||||
|------|-------------------|-------------|
|
||||
| Free | 0 | Browse only (no live Ahrefs) |
|
||||
| Starter | 50 | 50 queries/month |
|
||||
| Growth | 200 | 200 queries/month |
|
||||
| Scale | 500 | 500 queries/month |
|
||||
|
||||
---
|
||||
|
||||
## Part 6: Enforcement Points ✅ COMPLETED
|
||||
|
||||
### 6.1 Backend Validation Checklist ✅
|
||||
|
||||
| Check Point | Location | Status |
|
||||
|-------------|----------|--------|
|
||||
| **Keywords** | | |
|
||||
| Manual creation | `planner/views.py:perform_create` | ✅ Implemented |
|
||||
| Bulk import | `planner/views.py:import_keywords` | ✅ Implemented |
|
||||
| **Sites** | | |
|
||||
| Site creation | `defaults_service.py` | ✅ Implemented |
|
||||
| **Users** | | |
|
||||
| User invite | `account_views.py:team_invite` | ✅ Implemented |
|
||||
| **Credits** | | |
|
||||
| All AI ops | `ai/engine.py` | ✅ Pre-flight checks exist |
|
||||
|
||||
### 6.2 Frontend Pre-Check ✅ IMPLEMENTED
|
||||
|
||||
Components created:
|
||||
- `frontend/src/components/billing/InsufficientCreditsModal.tsx` - Modal with upgrade/buy options
|
||||
- `frontend/src/utils/creditCheck.ts` - Pre-flight credit check utility
|
||||
|
||||
```typescript
|
||||
// Usage example
|
||||
import { checkCreditsBeforeOperation } from '@/utils/creditCheck';
|
||||
import { useInsufficientCreditsModal } from '@/components/billing/InsufficientCreditsModal';
|
||||
|
||||
const { showModal } = useInsufficientCreditsModal();
|
||||
const check = await checkCreditsBeforeOperation(estimatedCost);
|
||||
|
||||
if (!check.hasEnoughCredits) {
|
||||
showModal({
|
||||
requiredCredits: check.requiredCredits,
|
||||
availableCredits: check.availableCredits,
|
||||
});
|
||||
return;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Part 7: Implementation Timeline
|
||||
|
||||
### Week 1: Backend Foundation ✅ COMPLETED
|
||||
- [x] Create database migration (add Ahrefs fields, remove unused)
|
||||
- [x] Update Plan model (remove 6 fields, add 1)
|
||||
- [x] Update Account model (remove 5 fields, add 1)
|
||||
- [x] Update LimitService (simplify mappings)
|
||||
- [x] Run migration on dev/staging
|
||||
- [x] Update PlanSerializer (fix removed field references)
|
||||
- [x] Update management commands (create_aws_admin_tenant, get_account_limits)
|
||||
|
||||
### Week 2: Backend Enforcement ✅ COMPLETED
|
||||
- [x] Add keyword limit checks at all entry points (perform_create, import_keywords)
|
||||
- [ ] Create Ahrefs query endpoint with limit check (deferred - Ahrefs not yet integrated)
|
||||
- [x] Update usage summary endpoint
|
||||
- [x] Add site limit check (defaults_service.py)
|
||||
- [x] Add user limit check (account_views.py - team invite)
|
||||
- [x] Update API serializers
|
||||
- [x] AI pre-flight credit checks (already in ai/engine.py)
|
||||
|
||||
### Week 3: Frontend - Plans & Billing ✅ COMPLETED
|
||||
- [x] Update billing.api.ts interfaces (Plan, UsageSummary)
|
||||
- [x] Update UsageLimitsPanel.tsx (4 limits only)
|
||||
- [x] Update PlansAndBillingPage.tsx
|
||||
- [x] Update pricing-table component
|
||||
- [x] Update pricingHelpers.ts
|
||||
- [x] Update Plans.tsx, SignUp.tsx, SignUpFormUnified.tsx
|
||||
|
||||
### Week 4: Frontend - Usage Analytics ✅ COMPLETED
|
||||
- [x] UsageAnalyticsPage uses UsageLimitsPanel (already updated)
|
||||
- [x] Your Limits tab shows 4 limits only
|
||||
- [x] Create Credit Insights tab with charts
|
||||
- [x] Overview quick stats visible on all tabs
|
||||
|
||||
### Week 5: Testing & Documentation ✅ COMPLETED
|
||||
- [ ] Run full test suite (pending - manual testing done)
|
||||
- [x] Update API documentation (docs/10-MODULES/BILLING.md)
|
||||
- [x] Update user documentation (docs/40-WORKFLOWS/CREDIT-SYSTEM.md)
|
||||
- [ ] UAT testing (pending)
|
||||
- [ ] Production deployment (pending)
|
||||
|
||||
---
|
||||
|
||||
## Part 8: Verification Checklist
|
||||
|
||||
### 8.1 Docker Verification Commands
|
||||
|
||||
```bash
|
||||
# Enter Django shell
|
||||
docker exec -it igny8_backend python manage.py shell
|
||||
|
||||
# Verify Plan model changes
|
||||
from igny8_core.auth.models import Plan
|
||||
plan = Plan.objects.first()
|
||||
print(f"max_sites: {plan.max_sites}")
|
||||
print(f"max_users: {plan.max_users}")
|
||||
print(f"max_keywords: {plan.max_keywords}")
|
||||
print(f"max_ahrefs_queries: {plan.max_ahrefs_queries}")
|
||||
# These should ERROR after migration:
|
||||
# print(f"max_clusters: {plan.max_clusters}") # Should fail
|
||||
|
||||
# Verify Account model changes
|
||||
from igny8_core.auth.models import Account
|
||||
account = Account.objects.first()
|
||||
print(f"credits: {account.credits}")
|
||||
print(f"usage_ahrefs_queries: {account.usage_ahrefs_queries}")
|
||||
# These should ERROR after migration:
|
||||
# print(f"usage_content_ideas: {account.usage_content_ideas}") # Should fail
|
||||
|
||||
# Verify LimitService
|
||||
from igny8_core.business.billing.services.limit_service import LimitService
|
||||
print(f"Hard limits: {list(LimitService.HARD_LIMIT_MAPPINGS.keys())}")
|
||||
# Should be: ['sites', 'users', 'keywords']
|
||||
print(f"Monthly limits: {list(LimitService.MONTHLY_LIMIT_MAPPINGS.keys())}")
|
||||
# Should be: ['ahrefs_queries']
|
||||
|
||||
# Verify Credit Flow
|
||||
from igny8_core.business.billing.services.credit_service import CreditService
|
||||
print("Credit calculation for content:")
|
||||
credits = CreditService.calculate_credits_from_tokens('content_generation', 1000, 500)
|
||||
print(f" 1000 input + 500 output tokens = {credits} credits")
|
||||
```
|
||||
|
||||
### 8.2 Django Admin Verification
|
||||
|
||||
**Check in browser at `/admin/`:**
|
||||
|
||||
1. **Accounts → Account**
|
||||
- [ ] `credits` field visible
|
||||
- [ ] `usage_ahrefs_queries` field visible
|
||||
- [ ] `usage_content_ideas` NOT visible (removed)
|
||||
|
||||
2. **Accounts → Plan**
|
||||
- [ ] `max_sites`, `max_users`, `max_keywords` visible
|
||||
- [ ] `max_ahrefs_queries` visible
|
||||
- [ ] `max_content_ideas`, `max_content_words` NOT visible (removed)
|
||||
- [ ] `included_credits` visible
|
||||
|
||||
3. **Billing → Credit Transaction**
|
||||
- [ ] Shows all credit additions/deductions
|
||||
- [ ] `balance_after` shows running total
|
||||
|
||||
4. **Billing → Credit Usage Log**
|
||||
- [ ] Shows all AI operations
|
||||
- [ ] `operation_type`, `credits_used`, `tokens_input`, `tokens_output` visible
|
||||
|
||||
### 8.3 Frontend Verification
|
||||
|
||||
**Plans & Billing Page (`/account/billing`):**
|
||||
- [ ] Current Plan tab shows: Plan name, price, renewal date
|
||||
- [ ] Current Plan tab does NOT show usage charts
|
||||
- [ ] "View Usage Details" link works
|
||||
- [ ] Upgrade tab shows pricing table
|
||||
- [ ] Billing History shows invoices
|
||||
|
||||
**Usage Analytics Page (`/account/usage`):**
|
||||
- [ ] Overview tab shows quick stats
|
||||
- [ ] Your Limits tab shows ONLY 4 limits
|
||||
- [ ] Credit Insights tab shows breakdown charts
|
||||
- [ ] Activity Log tab shows transaction history
|
||||
|
||||
**Header Credit Display:**
|
||||
- [ ] Shows current credit balance
|
||||
- [ ] Updates after AI operations
|
||||
|
||||
---
|
||||
|
||||
## Part 9: Error Messages (User-Friendly)
|
||||
|
||||
### Before (Technical)
|
||||
```
|
||||
HTTP 402 - HardLimitExceededError: max_keywords limit reached
|
||||
```
|
||||
|
||||
### After (User-Friendly)
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ ⚠️ Keyword Limit Reached │
|
||||
├─────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ You've reached your keyword limit. │
|
||||
│ │
|
||||
│ Current: 1,000 keywords │
|
||||
│ Your plan allows: 1,000 keywords │
|
||||
│ │
|
||||
│ To add more keywords: │
|
||||
│ • Delete unused keywords │
|
||||
│ • Upgrade your plan │
|
||||
│ │
|
||||
│ [Manage Keywords] [Upgrade Plan] [Cancel] │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Part 10: Risk Mitigation
|
||||
|
||||
| Risk | Mitigation |
|
||||
|------|------------|
|
||||
| Migration breaks production | Test on staging with prod data clone first |
|
||||
| Users lose tracked usage | Keep CreditUsageLog (detailed tracking continues) |
|
||||
| Ahrefs costs spike | Monthly limit enforced server-side |
|
||||
| Credit confusion | Clear documentation + help tooltips |
|
||||
| Rollback needed | Keep migration reversible (add back fields) |
|
||||
|
||||
---
|
||||
|
||||
## Appendix A: Files to Modify
|
||||
|
||||
### Backend Files
|
||||
| File | Changes |
|
||||
|------|---------|
|
||||
| `backend/igny8_core/auth/models.py` | Remove 11 fields, add 2 fields |
|
||||
| `backend/igny8_core/business/billing/services/limit_service.py` | Simplify mappings |
|
||||
| `backend/igny8_core/business/billing/serializers.py` | Update serializers |
|
||||
| `backend/igny8_core/modules/billing/views.py` | Update usage summary |
|
||||
| `backend/igny8_core/admin/` | Update admin panels |
|
||||
| `backend/migrations/` | New migration file |
|
||||
|
||||
### Frontend Files
|
||||
| File | Changes |
|
||||
|------|---------|
|
||||
| `frontend/src/pages/account/PlansAndBillingPage.tsx` | Simplify (remove usage) |
|
||||
| `frontend/src/pages/account/UsageAnalyticsPage.tsx` | Add new tabs |
|
||||
| `frontend/src/services/billing.api.ts` | Update interfaces |
|
||||
| `frontend/src/components/billing/UsageLimitsPanel.tsx` | Show 4 limits only |
|
||||
|
||||
### Documentation Files
|
||||
| File | Changes |
|
||||
|------|---------|
|
||||
| `docs/10-MODULES/BILLING.md` | Update limits documentation |
|
||||
| `docs/40-WORKFLOWS/CREDIT-SYSTEM.md` | Update credit flow docs |
|
||||
|
||||
---
|
||||
|
||||
## Conclusion
|
||||
|
||||
The credit system architecture is **fundamentally correct**:
|
||||
- `Plan.included_credits` defines monthly allocation
|
||||
- `Account.credits` tracks real-time balance
|
||||
- Credits are added on subscription renewal/payment approval
|
||||
- Credits are deducted on each AI operation
|
||||
|
||||
**What's broken:**
|
||||
- Too many unused limits causing user confusion
|
||||
- Duplicate data displayed across pages
|
||||
- Monthly limits (content_words, images, etc.) that duplicate what credits already control
|
||||
|
||||
**The fix:**
|
||||
- Simplify to 4 hard limits + credits
|
||||
- Clear page separation (financial vs consumption)
|
||||
- Better UX with multi-dimensional credit insights
|
||||
|
||||
---
|
||||
|
||||
**Document Version:** 1.0
|
||||
**Last Updated:** January 5, 2026
|
||||
**Verified By:** Codebase review of backend and frontend
|
||||
|
||||
|
||||
##############################################
|
||||
|
||||
|
||||
|
||||
## Organized Implementation Changes Plan
|
||||
|
||||
### 1. **Credit Consumption Widget (Rename & Enhance)**
|
||||
|
||||
**Current:** "Where Credits Go" with just a pie chart
|
||||
**New:** "Credit Consumption" with pie chart + detailed table
|
||||
|
||||
| Operation | Credits Used | Items Created |
|
||||
|-----------|-------------|---------------|
|
||||
| Clustering | 150 | 12 clusters |
|
||||
| Ideas | 200 | 45 ideas |
|
||||
| Content | 1,200 | 24 articles |
|
||||
| Image Prompts | 50 | 30 prompts |
|
||||
| Images (Basic) | 100 | 100 images |
|
||||
| Images (Quality) | 250 | 50 images |
|
||||
| Images (Premium) | 450 | 30 images |
|
||||
|
||||
- Pie chart shows credits consumed per operation
|
||||
- Table shows both **credits** AND **output count**
|
||||
- Learn from Planner/Writer footer workflow completion widgets
|
||||
|
||||
---
|
||||
|
||||
### 2. **New Usage Logs Page** (`/account/usage/logs`)
|
||||
|
||||
**Purpose:** Detailed, filterable, paginated log of all AI operations
|
||||
|
||||
**Layout:** Same as Planner/Writer table pages (consistent template)
|
||||
|
||||
**Table Columns:**
|
||||
| Date | Operation | Details | Credits | Cost (USD) |
|
||||
|------|-----------|---------|-------|--------|---------|------------|
|
||||
| Jan 5, 2:30pm | Content Writing | "SEO Guide for..." | 35 | $0.042 |
|
||||
| Jan 5, 2:15pm | Image Generation | Premium quality | 15 | $0.18 |
|
||||
|
||||
**Features:**
|
||||
- Filters: Operation type, date range, site
|
||||
- Pagination
|
||||
- USD cost calculated from token pricing (from AIModelConfig)
|
||||
- Link to related content where applicable
|
||||
- Export option
|
||||
|
||||
---
|
||||
|
||||
### 3. **Usage Dashboard - Multi-Dimensional Widgets**
|
||||
|
||||
**Remove:**
|
||||
- ❌ "Operations" summary card (single metrics, not useful)
|
||||
- ❌ "Recent Activity" card (move to Usage Logs page)
|
||||
|
||||
**Add/Enhance:**
|
||||
- ✅ **Credit Consumption** (pie + table as described above)
|
||||
- ✅ **Usage Trend** - Line chart showing daily/weekly credit usage
|
||||
- ✅ **Credits by Site** - If multiple sites, show distribution
|
||||
- ✅ **Image Quality Breakdown** - Basic vs Quality vs Premium usage
|
||||
- ✅ **Top Operations** - Which operations consume most credits
|
||||
- ✅ **Quick link** to Usage Logs page for detailed view
|
||||
|
||||
---
|
||||
|
||||
### 4. **Data Sources Needed**
|
||||
|
||||
From backend, I'll need to check:
|
||||
- `CreditUsageLog` - Has operation_type, credits_used, model_used, tokens_in, tokens_out
|
||||
- `AIModelConfig` - Has cost_per_1k_input, cost_per_1k_output for USD calculation
|
||||
- Aggregations for: clusters created, ideas generated, articles written, images by tier
|
||||
|
||||
---
|
||||
|
||||
### Summary of Deliverables
|
||||
|
||||
| Item | Type | Description |
|
||||
|------|------|-------------|
|
||||
| 1 | Widget Enhancement | "Credit Consumption" - pie chart + table with credits & output counts |
|
||||
| 2 | New Page | `/account/usage/logs` - Filterable, paginated AI operation logs with USD costs |
|
||||
| 3 | Dashboard Update | Remove weak widgets, add multi-dimensional data-rich charts |
|
||||
| 4 | Sidebar | Add "Usage Logs" as sub-item under Usage Dashboard |
|
||||
|
||||
---
|
||||
|
||||
**Does this organization look correct?** Should I proceed with implementation, or would you like to adjust any part of this plan?
|
||||
Reference in New Issue
Block a user