Files
igny8/docs/40-WORKFLOWS/CREDIT-SYSTEM.md
IGNY8 VPS (Salman) c777e5ccb2 dos updates
2026-01-20 14:45:21 +00:00

14 KiB
Raw Blame History

Credit System

Last Verified: January 20, 2026
Version: 1.8.4
Status: Complete (v1.8.3 - Two-Pool Credit System)


Overview

IGNY8 uses a two-pool credit system (v1.8.3):

  • Plan Credits (account.credits): From subscription, reset on renewal
  • Bonus Credits (account.bonus_credits): Purchased, NEVER expire

Usage Priority: Plan credits consumed first, bonus credits only when plan = 0.


Credit Flow (Verified Architecture)

┌─────────────────────────────────────────────────────────────────┐
│                    CREDIT FLOW                                   │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  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

Simplified Limits (v1.5.0)

Hard Limits (Never Reset)

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

Monthly Limits (Reset on Billing Cycle)

Limit Plan Field Account Field Description
Ahrefs Queries max_ahrefs_queries usage_ahrefs_queries Live Ahrefs API queries per month

Removed Limits (Now Credit-Based)

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

Plan Tiers

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

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) - v1.7.1 Complete

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

Image Generation Credit Flow (v1.7.1):

  1. Pre-check: CreditService.check_credits_for_image() verifies sufficient credits
  2. Generation: Images generated via process_image_generation_queue Celery task
  3. Post-deduct: CreditService.deduct_credits_for_image() called per successful image
  4. Logging: CreditUsageLog + CreditTransaction + AITaskLog entries created
  5. Notifications: NotificationService.notify_images_complete/failed() called

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

Account (Credit Balance)

class Account(models.Model):
    credits = models.IntegerField(default=0)  # Current balance
    usage_ahrefs_queries = models.IntegerField(default=0)  # Monthly Ahrefs usage

Plan (Allocations)

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)

class CreditTransaction(models.Model):
    account = models.ForeignKey(Account)
    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)

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()

Business Logic

CreditService

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

Key Methods:

class CreditService:
    @staticmethod
    def check_credits(account, required_credits):
        """Check if sufficient credits available, raises InsufficientCreditsError if not"""
        
    @staticmethod
    def deduct_credits_for_operation(account, operation_type, model, tokens_in, tokens_out, metadata=None):
        """Deduct credits and log usage after AI operation"""
        
    @staticmethod
    def add_credits(account, amount, transaction_type, description):
        """Add credits (admin/purchase/subscription)"""
        
    @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:

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"""
        
    @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

# In content generation service
def generate_content(task, user):
    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, usage = ai_engine.generate_content(task)
    
    # 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

API Responses

Successful Operation

{
  "success": true,
  "data": { ... },
  "credits_used": 15,
  "balance": 9985
}

Insufficient Credits

HTTP 402 Payment Required

{
  "success": false,
  "error": "Insufficient credits",
  "code": "INSUFFICIENT_CREDITS",
  "required": 50,
  "available": 25
}

Limit Exceeded

HTTP 402 Payment Required

{
  "success": false,
  "error": "Keyword limit reached",
  "code": "HARD_LIMIT_EXCEEDED",
  "limit": "keywords",
  "current": 500,
  "max": 500
}

Frontend Handling

Credit Balance Display

  • Header shows current credit balance
  • Updates after each operation
  • Warning at low balance (< 10%)

Pre-Operation Check

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;
    }
    
    // Proceed with generation
    await generateContent();
  };
}

API Endpoints

Credit Balance

GET /api/v1/billing/balance/

Response:

{
  "credits": 9500,
  "plan_credits_per_month": 10000,
  "credits_used_this_month": 500,
  "credits_remaining": 9500
}

Usage Limits

GET /api/v1/billing/usage/limits/

Response:

{
  "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" }
  },
  "days_until_reset": 18
}

Usage Analytics

GET /api/v1/account/usage/analytics/?days=30

Response:

{
  "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 Allocation

Credits are added to Account.credits when:

  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

Monthly Reset

Monthly limits (Ahrefs queries) reset on billing cycle:

# 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'])

Admin Operations

Manual Credit Adjustment

Via Django Admin or API:

from igny8_core.business.billing.services.credit_service import CreditService

# Add credits
CreditService.add_credits(
    account=account,
    amount=1000,
    transaction_type='adjustment',
    description='Customer support adjustment'
)

Usage Audit

All credit changes logged in CreditTransaction with:

  • Timestamp
  • Transaction type
  • Amount (positive or negative)
  • Balance after transaction
  • Description

All AI operations logged in CreditUsageLog with:

  • Operation type
  • Credits used
  • Model used
  • Token counts
  • Related object metadata

Image Generation Credit System (v1.7.1)

Implementation Details

Files:

Credit Flow for Image Generation

1. User triggers image generation
   ↓
2. CreditService.check_credits_for_image(account, model, num_images)
   - Calculates: credits_per_image × num_images
   - Raises InsufficientCreditsError if balance < required
   ↓
3. process_image_generation_queue() processes each image
   ↓
4. For each successful image:
   CreditService.deduct_credits_for_image()
   - Creates CreditUsageLog entry
   - Creates CreditTransaction entry
   - Updates account.credits balance
   ↓
5. After all images processed:
   - AITaskLog entry created
   - Notification created (success or failure)

Logging Locations

Table What's Logged When
CreditTransaction Credit deduction (financial ledger) Per image
CreditUsageLog Usage details (model, cost, credits) Per image
AITaskLog Task execution summary After batch
Notification User notification After batch

Automation Compatibility

Image generation credits work identically for:

  • Manual image generation (from UI)
  • Automation Stage 6 (scheduled/manual automation runs)

Both call process_image_generation_queue which handles:

  • Credit checking before generation
  • Credit deduction after each successful image
  • Proper logging to all tables