# Item 2: Credits, Billing, Pricing Logic, and Usage Limits

**Priority:** Critical  
**Target:** Production Launch  
**Last Updated:** December 11, 2025

---

## Overview

Define and implement a comprehensive credit cost system, plan-based usage limits, and billing logic for all AI operations. This includes setting credit costs per function, establishing plan tiers with limits, and implementing enforcement mechanisms across backend and frontend.

---

## Current Implementation Analysis

### Credit System Architecture

**Location:** `backend/igny8_core/business/billing/`

#### Credit Models

| Model | Purpose | Key Fields |
|-------|---------|------------|
| **CreditTransaction** | Tracks all credit additions/deductions | `transaction_type`, `amount`, `balance_after`, `description` |
| **CreditUsageLog** | Detailed log per AI operation | `operation_type`, `credits_used`, `cost_usd`, `model_used`, `tokens_input`, `tokens_output` |
| **CreditCostConfig** | Admin-configurable credit costs | `operation_type`, `credits_cost`, `unit`, `display_name` |

**Credit Transaction Types:**
- `purchase` - Credit purchase
- `subscription` - Monthly subscription renewal
- `refund` - Credit refund
- `deduction` - Usage deduction (AI operations)
- `adjustment` - Manual admin adjustment

#### Credit Service

**Location:** `backend/igny8_core/business/billing/services/credit_service.py`

**Methods:**
- `get_credit_cost(operation_type, amount)` - Calculate cost for operation
- `check_credits(account, operation_type, amount)` - Validate sufficient credits
- `deduct_credits(account, amount, operation_type, ...)` - Deduct and log
- `deduct_credits_for_operation(...)` - Convenience method with auto-calculation

**Logic:**
1. Checks database `CreditCostConfig` first
2. Falls back to hardcoded `CREDIT_COSTS` constants
3. Applies unit-based calculation (per 100 words, per image, etc.)
4. Validates sufficient balance before deduction
5. Creates both `CreditTransaction` and `CreditUsageLog` records

---

### Current Credit Costs

**Location:** `backend/igny8_core/business/billing/constants.py`

| Operation | Current Cost | Unit | Notes |
|-----------|--------------|------|-------|
| `clustering` | 10 credits | per request | Clusters all submitted keywords |
| `idea_generation` | 15 credits | per request | Ideas for one cluster |
| `content_generation` | 1 credit | per 100 words | Word-count based |
| `image_prompt_extraction` | 2 credits | per content | Extract prompts from content |
| `image_generation` | 5 credits | per image | Generate single image |
| `linking` | 8 credits | per content | Internal linking (NEW) |
| `optimization` | 1 credit | per 200 words | Content optimization (NEW) |
| `site_structure_generation` | 50 credits | per site | Site blueprint (Phase 7) |
| `site_page_generation` | 20 credits | per page | Page generation (Phase 7) |

**Legacy Aliases:**
- `ideas` → `idea_generation`
- `content` → 3 credits fixed (legacy)
- `images` → `image_generation`
- `reparse` → 1 credit

**Issues with Current Costs:**
1. **Not optimized for profitability** - Costs may not reflect actual AI provider costs
2. **Arbitrary values** - No clear formula based on model costs, processing time, or value
3. **Inconsistent granularity** - Some per-request, some per-word, some per-item
4. **No differentiation by quality** - Same cost regardless of model quality (GPT-4 vs GPT-3.5)

---

### Plan Model and Limits

**Location:** `backend/igny8_core/auth/models.py` - `Plan` model

#### Current Plan Structure

| Field | Purpose | Current State |
|-------|---------|---------------|
| `name`, `slug` | Plan identification | ✅ Implemented |
| `price`, `billing_cycle` | Pricing | ✅ Monthly/Annual support |
| `included_credits` | Monthly credit allocation | ✅ Implemented |
| `extra_credit_price` | Per-credit overage cost | ✅ Default $0.01 |
| `allow_credit_topup` | Can buy more credits | ✅ Boolean flag |
| `auto_credit_topup_threshold` | Auto-buy trigger | ✅ Optional |
| `auto_credit_topup_amount` | Auto-buy amount | ✅ Optional |
| `max_users` | Users per account | ✅ Implemented |
| `max_sites` | Sites per account | ✅ Implemented |
| `max_industries` | Industries/sectors limit | ✅ Optional |
| `max_author_profiles` | Writing styles limit | ✅ Default 5 |

**What's MISSING:**
- ❌ Max keywords limit
- ❌ Max clusters limit
- ❌ Max ideas limit
- ❌ Max content pieces limit
- ❌ Max images limit
- ❌ Max tasks in queue limit
- ❌ Daily/monthly usage caps (beyond credits)
- ❌ Per-user vs per-account limits distinction

---

### Account Credit Balance

**Location:** `backend/igny8_core/auth/models.py` - `Account` model

**Field:** `credits` (IntegerField with MinValueValidator(0))

**Current Behavior:**
- Credits deducted on AI operation completion
- Credit balance checked before operation starts
- `InsufficientCreditsError` raised if balance < required

**No Implementation For:**
- Credit expiration dates
- Credit rollover rules (monthly vs annual)
- Negative balance prevention (hard stop vs warning)
- Credit reserve for pending operations

---

## Pricing Plan Requirements

### Recommended Plan Tiers

Based on industry standards and target market:

| Plan | Monthly Price | Annual Price | Included Credits | Target User |
|------|---------------|--------------|------------------|-------------|
| **Free** | $0 | $0 | 50 | Trial users, hobbyists |
| **Starter** | $29 | $299 (15% off) | 500 | Solo creators, small blogs |
| **Growth** | $99 | $1,019 (15% off) | 2,000 | Growing sites, agencies |
| **Pro** | $299 | $3,077 (15% off) | 7,500 | Power users, large agencies |
| **Enterprise** | Custom | Custom | Custom | Enterprise clients |

**Free Plan Considerations:**
- Should be marked `is_internal=True` to hide from public pricing
- Limits should be strict enough to encourage upgrade
- Should not include advanced features (automation, API access)

---

### Usage Limits Per Plan

**Proposed Limits** (to be finalized):

| Limit Type | Free | Starter | Growth | Pro | Enterprise |
|------------|------|---------|--------|-----|------------|
| **Monthly Credits** | 50 | 500 | 2,000 | 7,500 | Custom |
| **Max Users** | 1 | 2 | 5 | 15 | Unlimited |
| **Max Sites** | 1 | 3 | 10 | 50 | Unlimited |
| **Max Keywords (saved)** | 100 | 1,000 | 5,000 | 25,000 | Unlimited |
| **Max Clusters** | 20 | 100 | 500 | 2,500 | Unlimited |
| **Max Ideas (saved)** | 50 | 500 | 2,500 | 12,500 | Unlimited |
| **Max Content Pieces** | 25 | 250 | 1,250 | 6,250 | Unlimited |
| **Max Images** | 25 | 250 | 1,250 | 6,250 | Unlimited |
| **Max Queue Size** | 5 | 20 | 50 | 200 | Unlimited |
| **Automation Enabled** | ❌ | ❌ | ✅ | ✅ | ✅ |
| **API Access** | ❌ | ❌ | ✅ | ✅ | ✅ |
| **Priority Support** | ❌ | ❌ | ❌ | ✅ | ✅ |

**Notes:**
- "Unlimited" means no hard limit, but still subject to fair use policy
- Limits apply per account (across all sites in account)
- Deleted items don't count toward limits (soft-delete system)

---

### Credit Cost Optimization Strategy

**Goal:** Define credit costs that:
1. Cover AI provider costs + margin
2. Are competitive with market rates
3. Encourage usage without abuse
4. Scale predictably with usage

#### Recommended Credit Cost Revisions

**Analysis Required:**
- [ ] Calculate actual AI provider costs per operation (OpenAI, Runware, etc.)
- [ ] Add 30-50% margin for infrastructure, support, and profit
- [ ] Compare with competitor pricing (Jasper, Copy.ai, Writesonic)
- [ ] Test with sample use cases to ensure plan value

**Proposed Adjustments** (pending analysis):

| Operation | Current | Proposed | Reasoning |
|-----------|---------|----------|-----------|
| Clustering | 10 | **8** | Lower barrier for discovery phase |
| Idea Generation | 15 | **12** | Encourage ideation before writing |
| Content (100 words) | 1 | **1.5** | Reflect actual GPT-4 costs |
| Image Prompts | 2 | **3** | More complex extraction logic |
| Image Generation | 5 | **6** | Runware/DALL-E costs increasing |
| Optimization | 1 per 200 words | **0.5 per 100 words** | Encourage optimization usage |

**Variable Costs by Model Quality:**
- **Option:** Charge more for GPT-4 vs GPT-3.5, DALL-E 3 vs DALL-E 2
- **Implementation:** Add `model_tier` multiplier in `get_credit_cost()`

---

## Required Implementation

### A. Expand Plan Model with Usage Limits

**File:** `backend/igny8_core/auth/models.py` - `Plan` model

**Add Fields:**

```python
# Content Creation Limits (NULL = unlimited)
max_keywords = models.IntegerField(
    null=True, blank=True,
    validators=[MinValueValidator(1)],
    help_text="Maximum keywords saved per account"
)
max_clusters = models.IntegerField(
    null=True, blank=True,
    validators=[MinValueValidator(1)],
    help_text="Maximum clusters per account"
)
max_ideas = models.IntegerField(
    null=True, blank=True,
    validators=[MinValueValidator(1)],
    help_text="Maximum content ideas saved per account"
)
max_content = models.IntegerField(
    null=True, blank=True,
    validators=[MinValueValidator(1)],
    help_text="Maximum content pieces per account"
)
max_images = models.IntegerField(
    null=True, blank=True,
    validators=[MinValueValidator(1)],
    help_text="Maximum images per account"
)

# Queue and Rate Limits
max_queue_size = models.IntegerField(
    default=10,
    validators=[MinValueValidator(1)],
    help_text="Maximum concurrent items in queue"
)
max_daily_ai_requests = models.IntegerField(
    null=True, blank=True,
    validators=[MinValueValidator(1)],
    help_text="Maximum AI requests per day (prevents abuse)"
)
max_monthly_content_generated = models.IntegerField(
    null=True, blank=True,
    validators=[MinValueValidator(1)],
    help_text="Maximum content pieces generated per month"
)

# Feature Access Flags
allow_automation = models.BooleanField(
    default=False,
    help_text="Enable automation wizard"
)
allow_api_access = models.BooleanField(
    default=False,
    help_text="Enable API access"
)
allow_bulk_operations = models.BooleanField(
    default=True,
    help_text="Enable bulk actions (delete, export, etc.)"
)
```

**Migration:** Create Django migration to add these fields with default NULL values

---

### B. Create Limit Enforcement Service

**File:** `backend/igny8_core/business/billing/services/limit_service.py` (NEW)

**Service Class:** `LimitService`

**Methods to Implement:**

| Method | Purpose | Returns |
|--------|---------|---------|
| `check_keyword_limit(account)` | Check if can add more keywords | `bool` or raises `LimitExceededError` |
| `check_cluster_limit(account)` | Check if can add more clusters | `bool` or raises `LimitExceededError` |
| `check_idea_limit(account)` | Check if can add more ideas | `bool` or raises `LimitExceededError` |
| `check_content_limit(account)` | Check if can add more content | `bool` or raises `LimitExceededError` |
| `check_image_limit(account)` | Check if can add more images | `bool` or raises `LimitExceededError` |
| `check_queue_limit(account)` | Check queue capacity | `bool` or raises `LimitExceededError` |
| `check_daily_request_limit(account)` | Check daily AI request quota | `bool` or raises `LimitExceededError` |
| `get_usage_stats(account)` | Get current usage counts | `dict` with all counters |
| `get_limit_stats(account)` | Get limits and remaining capacity | `dict` with limits |

**Implementation Logic:**

```python
def check_keyword_limit(account):
    plan = account.plan
    if plan.max_keywords is None:
        return True  # Unlimited
    
    current_count = Keywords.objects.filter(
        account=account,
        deleted_at__isnull=True  # Exclude soft-deleted
    ).count()
    
    if current_count >= plan.max_keywords:
        raise LimitExceededError(
            f"Keyword limit reached ({plan.max_keywords}). Upgrade your plan."
        )
    
    return True
```

**Exception:** `LimitExceededError` (inherit from `BillingException`)

---

### C. Integrate Limit Checks in API Views

**Files to Update:**

- `backend/igny8_core/modules/planner/views.py` - KeywordsViewSet
- `backend/igny8_core/modules/planner/views.py` - ClustersViewSet
- `backend/igny8_core/modules/planner/views.py` - ContentIdeasViewSet
- `backend/igny8_core/modules/writer/views.py` - TasksViewSet
- `backend/igny8_core/modules/writer/views.py` - ContentViewSet

**Integration Points:**

| ViewSet | Action | Check to Add |
|---------|--------|--------------|
| KeywordsViewSet | `create()` | `LimitService.check_keyword_limit(account)` |
| KeywordsViewSet | `bulk_create()` | Check limit with proposed count |
| ClustersViewSet | `create()` | `LimitService.check_cluster_limit(account)` |
| ContentIdeasViewSet | `create()` | `LimitService.check_idea_limit(account)` |
| TasksViewSet | `create()` | `LimitService.check_content_limit(account)` + `check_queue_limit()` |
| ContentViewSet | `create()` | `LimitService.check_content_limit(account)` |

**Example Integration:**

```python
def create(self, request, *args, **kwargs):
    account = request.user.account
    
    # Check limit before creating
    try:
        LimitService.check_keyword_limit(account)
    except LimitExceededError as e:
        return Response({
            'error': str(e),
            'error_code': 'LIMIT_EXCEEDED',
            'upgrade_url': '/pricing'
        }, status=403)
    
    # Proceed with creation
    return super().create(request, *args, **kwargs)
```

---

### D. Add Usage Tracking and Counter Cache

**Optimization:** Instead of counting records on every request, cache counts

**Implementation Options:**

#### Option 1: Add Counter Fields to Account Model

```python
# Add to Account model
keyword_count = models.IntegerField(default=0)
cluster_count = models.IntegerField(default=0)
idea_count = models.IntegerField(default=0)
content_count = models.IntegerField(default=0)
image_count = models.IntegerField(default=0)
```

**Update counters in signals:**
- `post_save` signal: increment counter
- `post_delete` signal: decrement counter
- Periodic reconciliation task to fix drift

#### Option 2: Cache Usage Stats (Recommended)

Use Django cache with 5-minute TTL:

```python
def get_cached_usage_stats(account):
    cache_key = f'usage_stats_{account.id}'
    stats = cache.get(cache_key)
    
    if stats is None:
        stats = {
            'keywords': Keywords.objects.filter(account=account, deleted_at__isnull=True).count(),
            'clusters': Clusters.objects.filter(account=account, deleted_at__isnull=True).count(),
            # ... etc
        }
        cache.set(cache_key, stats, 300)  # 5 minutes
    
    return stats
```

**Invalidate cache on:**
- Create operations
- Delete operations
- Soft-delete operations

---

### E. Frontend Limit Display

#### 1. Usage Dashboard Widget

**Location:** `frontend/src/components/dashboard/UsageLimitsWidget.tsx` (NEW)

**Display:**
- Current usage vs limit for each resource
- Progress bars with color coding:
  - Green: < 70% used
  - Yellow: 70-90% used
  - Red: > 90% used
- "Upgrade Plan" button when approaching limits

**Example UI:**

```
Usage & Limits
━━━━━━━━━━━━━━━━━━━━━━━━━━━
Keywords: 750 / 1,000 ████████░░ 75%
Clusters: 45 / 100    ████░░░░░░ 45%
Ideas: 380 / 500      ███████░░░ 76%
Content: 120 / 250    ████░░░░░░ 48%

[Upgrade Plan]
```

#### 2. Inline Warnings

**Show warnings when approaching limits:**

- At 80%: Yellow badge "Approaching limit"
- At 90%: Orange warning "Near limit - Upgrade recommended"
- At 100%: Red error "Limit reached - Upgrade required"

**Display in:**
- Header metrics
- Page headers
- Before bulk operations
- In forms (disable submit if limit reached)

#### 3. Create/Import Dialogs

**Add limit check before showing form:**

```typescript
const handleCreateKeyword = () => {
  const stats = usageStats; // from API
  const limit = account.plan.max_keywords;
  
  if (limit && stats.keywords >= limit) {
    toast.error('Keyword limit reached. Upgrade your plan.');
    navigate('/settings/billing');
    return;
  }
  
  setShowCreateModal(true);
};
```

#### 4. Upgrade Prompts

**When limit error occurs:**
- Show modal with:
  - Current plan
  - Current limit
  - Recommended plan
  - Benefits of upgrading
  - "Upgrade Now" CTA

---

### F. Credit Cost Configuration UI (Admin)

**Location:** Django Admin or custom Admin Panel page

**Feature:** Allow superusers to edit credit costs without code changes

**Admin Interface:**
- List all operations with current costs
- Edit cost, unit, and display name
- Track change history (previous_cost field)
- Enable/disable operations
- Preview impact on sample use cases

**Models Used:**
- `CreditCostConfig` - Admin-editable costs
- Falls back to `CREDIT_COSTS` constants if not configured

---

## Testing Requirements

### Limit Enforcement Tests

| Test Case | Expected Result |
|-----------|-----------------|
| Create keyword at limit | Error: "Keyword limit reached" |
| Create keyword below limit | Success |
| Create 10 keywords via bulk import at limit | Error with count blocked |
| Delete keyword then create | Success (count decremented) |
| Soft-delete keyword then restore | Counts update correctly |
| Upgrade plan mid-session | New limits apply immediately |

### Credit Deduction Tests

| Test Case | Expected Result |
|-----------|-----------------|
| Generate content with sufficient credits | Content created, credits deducted |
| Generate content with insufficient credits | Error: "Insufficient credits" |
| Generate content at exact credit balance | Success, balance = 0 |
| Generate multiple items in queue | Each deducts credits sequentially |
| Credit deduction failure mid-operation | Transaction rolled back, no partial deduction |

### Plan Limit Tests

| Plan | Test Case | Expected Result |
|------|-----------|-----------------|
| Free | Create 101st keyword (limit: 100) | Blocked |
| Starter | Create 6 queue items (limit: 5) | Blocked |
| Growth | Enable automation | Success (has access) |
| Pro | Create unlimited keywords | Success (no limit) |
| Enterprise | All operations | No limits enforced |

---

## Pricing Page Updates

**Location:** `frontend/src/pages/marketing/Pricing.tsx`

### Required Elements

1. **Plan Comparison Table**
   - All tiers side-by-side
   - Feature checkmarks
   - Highlight "Most Popular" plan
   - Monthly/Annual toggle with savings badge

2. **Usage Limits Display**
   - Show key limits per plan
   - Use "Unlimited" label for null limits
   - Tooltip explanations for complex limits

3. **Credit System Explanation**
   - What credits are
   - How they're consumed
   - How to buy more
   - Credit rollover rules

4. **FAQ Section**
   - "What happens when I run out of credits?"
   - "Can I change plans mid-month?"
   - "Do unused credits roll over?"
   - "What's included in Enterprise?"

5. **Calculator Widget** (Optional)
   - Estimate monthly usage
   - Recommend plan based on needs
   - Show credit consumption breakdown

---

## Success Metrics

- ✅ All AI operations enforce credit checks
- ✅ All create operations enforce limit checks
- ✅ Credit costs reflect actual provider costs + margin
- ✅ Plans are competitively priced
- ✅ Usage dashboard shows accurate counts
- ✅ Limit warnings prevent user frustration
- ✅ Upgrade flow is clear and frictionless
- ✅ Admin can adjust costs without code changes
- ✅ All tests pass

---

## Related Files Reference

### Backend
- `backend/igny8_core/auth/models.py` - Account, Plan models
- `backend/igny8_core/business/billing/models.py` - Credit models
- `backend/igny8_core/business/billing/constants.py` - Credit costs
- `backend/igny8_core/business/billing/services/credit_service.py` - Credit logic
- `backend/igny8_core/business/billing/services/limit_service.py` - **NEW** Limit enforcement
- `backend/igny8_core/modules/planner/views.py` - Planner API views
- `backend/igny8_core/modules/writer/views.py` - Writer API views

### Frontend
- `frontend/src/components/dashboard/UsageLimitsWidget.tsx` - **NEW** Usage display
- `frontend/src/pages/marketing/Pricing.tsx` - Pricing page
- `frontend/src/pages/Planner/*.tsx` - Planner pages (add limit checks)
- `frontend/src/pages/Writer/*.tsx` - Writer pages (add limit checks)
- `frontend/src/services/api.ts` - API service (handle limit errors)

---

## Notes

- Limits should be enforced at API level, not just UI level
- Consider "soft limits" with warnings vs "hard limits" with blocks
- Credit expiration and rollover rules need business decision
- Enterprise pricing needs custom quote system
- Monitor actual usage patterns to optimize costs and limits
- A/B test different pricing tiers to maximize conversion
