Files
igny8/docs/PRE-LAUNCH/ITEM-2-CREDITS-BILLING-PRICING.md
2025-12-11 07:20:21 +00:00

20 KiB

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:

  • ideasidea_generation
  • content → 3 credits fixed (legacy)
  • imagesimage_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

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

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:

# 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:

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:

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

# 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

Use Django cache with 5-minute TTL:

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:

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

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