Skip to content

Latest commit

 

History

History
202 lines (161 loc) · 6.63 KB

File metadata and controls

202 lines (161 loc) · 6.63 KB

Usage Limits Implementation Plan

Overview

Subscription plans are now differentiated by check limits per month rather than features:

  • Personal (£9.99/month): 5 checks/month
  • Trade (£29.99/month): 100 checks/month
  • Business (£99.99/month): Unlimited checks

All plans include: Dashboard access, PDF downloads, check history

Database Schema

New Table: monthly_usage

CREATE TABLE monthly_usage (
    id VARCHAR(36) PRIMARY KEY,
    user_id VARCHAR(36) REFERENCES users(id) ON DELETE CASCADE,
    subscription_id VARCHAR(36) REFERENCES subscriptions(id) ON DELETE CASCADE,
    month DATE NOT NULL,  -- First day of the month (e.g., 2025-01-01)
    checks_used INTEGER DEFAULT 0 NOT NULL,
    UNIQUE(user_id, subscription_id, month),
    INDEX idx_monthly_usage_user_month (user_id, month),
    INDEX idx_monthly_usage_subscription (subscription_id)
);

Implementation Components

1. Usage Tracking Model (/backend/app/models/usage_tracking.py) ✅ CREATED

  • MonthlyUsage model with relationships to User and Subscription
  • Properties to calculate check limits, remaining checks, and validation
  • Plan limits hardcoded in check_limit property:
    • Personal: 5 checks
    • Trade: 100 checks
    • Business: -1 (unlimited)

2. Usage CRUD Operations (NEEDS TO BE CREATED)

File: /backend/app/crud/usage_tracking.py

async def get_or_create_current_usage(db, user_id, subscription_id):
    """Get or create usage record for current month."""

async def increment_usage(db, user_id):
    """Increment check count for current month."""

async def reset_monthly_usage(db, subscription_id):
    """Reset usage at billing cycle (called by Stripe webhooks)."""

async def get_remaining_checks(db, user_id):
    """Get remaining checks for user's active subscription."""

3. Check Creation Validation (NEEDS UPDATE)

File: /backend/app/api/checks.py

Before creating a check:

  1. Check if user is authenticated
  2. If authenticated, check if they have an active subscription
  3. If subscription exists, validate usage limits:
    usage = await usage_tracking_crud.get_or_create_current_usage(db, user_id, sub_id)
    if not usage.can_run_check:
        raise HTTPException(
            status_code=429,
            detail=f"Monthly check limit reached. You have {usage.check_limit} checks per month."
        )
  4. After check creation, increment usage counter

4. Dashboard Usage Display (NEEDS UPDATE)

File: /backend/app/api/v1/endpoints/dashboard.py

Add new endpoint:

@router.get("/usage")
async def get_usage_stats(current_user: User, db: AsyncSession):
    """Get current month's usage statistics."""
    subscription = await subscription_crud.get_active_subscription(db, current_user.id)
    if not subscription:
        return {"has_subscription": False}

    usage = await usage_tracking_crud.get_or_create_current_usage(
        db, current_user.id, subscription.id
    )

    return {
        "plan_type": subscription.plan_type.value,
        "check_limit": usage.check_limit,
        "checks_used": usage.checks_used,
        "remaining_checks": usage.remaining_checks,
        "is_unlimited": usage.is_unlimited,
        "resets_at": subscription.current_period_end
    }

5. Billing Cycle Reset (NEEDS UPDATE)

File: /backend/app/api/v1/payments.py

In Stripe webhook handler for customer.subscription.updated:

async def handle_subscription_updated(subscription, db):
    # ... existing code ...

    # If new billing period started, reset usage
    if subscription.current_period_start changed:
        await usage_tracking_crud.reset_monthly_usage(db, subscription_id)

6. Frontend Dashboard Update (NEEDS UPDATE)

File: /frontend/app/dashboard/page.tsx

Add usage stats display:

<div className="bg-white rounded-lg shadow p-6">
  <h3 className="font-semibold mb-2">Usage This Month</h3>
  {usageStats.is_unlimited ? (
    <p className="text-2xl font-bold text-green-600">Unlimited</p>
  ) : (
    <>
      <div className="flex items-baseline gap-2 mb-2">
        <span className="text-3xl font-bold">{usageStats.remaining_checks}</span>
        <span className="text-gray-600">/ {usageStats.check_limit} remaining</span>
      </div>
      <div className="w-full bg-gray-200 rounded-full h-2">
        <div
          className="bg-blue-600 h-2 rounded-full"
          style={{width: `${(usageStats.checks_used / usageStats.check_limit) * 100}%`}}
        />
      </div>
    </>
  )}
  <p className="text-sm text-gray-500 mt-2">
    Resets on {new Date(usageStats.resets_at).toLocaleDateString()}
  </p>
</div>

Migration Steps

Step 1: Create Database Migration

cd /root/clone-check/backend
poetry run alembic revision --autogenerate -m "add_monthly_usage_table"
poetry run alembic upgrade head

Step 2: Create Usage CRUD

Create /backend/app/crud/usage_tracking.py with CRUD operations

Step 3: Update Check Creation

Modify /backend/app/api/checks.py to validate limits before creating checks

Step 4: Update Webhooks

Modify /backend/app/api/v1/payments.py to reset usage on billing cycle

Step 5: Update Dashboard API

Add usage endpoint to /backend/app/api/v1/endpoints/dashboard.py

Step 6: Update Dashboard UI

Modify /frontend/app/dashboard/page.tsx to display usage stats

Stripe Integration

The Stripe price IDs remain the same. The mapping works like this:

  1. User subscribes to a plan (Personal/Trade/Business)
  2. Webhook creates Subscription record in database
  3. First check creates MonthlyUsage record for current month
  4. Each check increments checks_used
  5. Validation happens before each check creation
  6. When customer.subscription.updated webhook fires with new billing period:
    • Create new MonthlyUsage record for new month
    • Old month's data is preserved for history

Testing Plan

  1. Manual Test Subscription Creation:

    • Create test user
    • Manually insert subscription record with Personal plan
    • Manually insert MonthlyUsage record
    • Try to run 6 checks (should fail on 6th)
  2. Test Usage Reset:

    • Update month field to previous month
    • Create new MonthlyUsage for current month
    • Verify checks work again
  3. Test Unlimited:

    • Change plan_type to BUSINESS_MONTHLY
    • Verify can run unlimited checks

Next Steps

You need to decide if you want me to:

  1. Continue with full implementation now - Create all the CRUD, update endpoints, run migrations
  2. Test first - Give you SQL to manually create a subscription and test the basic flow
  3. Pause and review - Review this plan and suggest changes before proceeding

The pricing page is already updated to show the new structure clearly!