tahamajs/IE / Projects /docs /Subscription&UsageSystem.md
tahamajs's picture
|
download
raw
12.1 kB

📘 Subscription & Usage System – Complete Documentation

1. Overview

The IE Research Platform offers a freemium subscription model with two tiers:

Tier Plan ID Description
Free FREE Basic features for individual researchers
Professional Monthly PRO_MONTHLY Advanced features, billed monthly
Professional Yearly PRO_YEARLY Advanced features, billed annually (2 months free)

All plans include monthly usage quotas that reset on the 1st of each month.


2. Subscription Plans – Detailed Comparison

Feature Free Pro Monthly Pro Yearly
Monthly Price $0 $29.99 $24.99 (billed $299.99/year)
Articles per month 5 100 100
Storage space 100 MB 5 GB 5 GB
API calls per month 100 5,000 5,000
AI generations per month 10 500 500
Team members 1 10 10
AI‑powered search ❌ ✅ ✅
Advanced analytics ❌ ✅ ✅
API access ❌ ✅ ✅
Priority support ❌ ✅ ✅
Collaboration tools ❌ ✅ ✅
Export citations (BibTeX, RIS, etc.) ❌ ✅ ✅
Free trial – 14 days 14 days
Auto‑renewal – Optional Optional

3. Usage Tracking & Limits

Each user’s monthly usage is tracked per calendar month (UTC). Counters reset automatically on the 1st day of each month.

3.1 Tracked Metrics

Metric Description Free Limit Pro Limit
articles_created Number of published articles 5 100
storage_used_mb Total uploaded files size 100 MB 5,120 MB (5 GB)
api_calls REST API requests 100 5,000
ai_generations AI‑powered features (summaries, grammar check, etc.) 10 500
team_members Number of users in collaborative projects 1 10

3.2 Usage Enforcement

  • When a user attempts to create a new article, the system checks articles_created < limit. If exceeded, the request is rejected with 429 Too Many Requests.
  • File uploads check storage_used_mb + file_size_mb ≤ limit.
  • Each AI endpoint call consumes one AI generation token.
  • API calls are counted per request (excluding public/static endpoints).

3.3 Usage Reset & Pro‑rating

  • Counters reset to zero on the 1st day of every month at 00:00 UTC.
  • When a user upgrades mid‑month, the remaining quota for that month is not prorated – they immediately receive the full Pro limit. However, downgrading to Free mid‑month does not revoke already‑used quota – the user will be blocked from creating new content if they exceed the Free limit.

4. Subscription API Endpoints

Base URL: https://yourdomain.com/api/subscription

4.1 Get Available Plans

GET /api/subscription/plans

Response example:

{
  "success": true,
  "plans": [
    {
      "id": "FREE",
      "name": "Free",
      "priceMonthly": 0.0,
      "priceYearly": 0.0,
      "currency": "USD",
      "features": ["Up to 5 articles", "100 MB storage", "100 API calls per month", ...],
      "limits": { "articles": 5, "storageMB": 100, "apiCalls": 100, "aiGenerations": 10, "teamMembers": 1 },
      "popular": false,
      "recommended": false,
      "trialDays": 0
    },
    {
      "id": "PRO_MONTHLY",
      "name": "Professional",
      "priceMonthly": 29.99,
      "priceYearly": 299.99,
      "description": "Advanced features for professional researchers",
      "features": ["Up to 100 articles", "5 GB storage", "5000 API calls", "500 AI generations", ...],
      "limits": { "articles": 100, "storageMB": 5120, "apiCalls": 5000, "aiGenerations": 500, "teamMembers": 10 },
      "popular": true,
      "trialDays": 14
    },
    {
      "id": "PRO_YEARLY",
      "name": "Professional (Yearly)",
      "priceMonthly": 24.99,
      "priceYearly": 299.99,
      "recommended": true,
      "trialDays": 14
    }
  ]
}

4.2 Get Single Plan

GET /api/subscription/plan/{planId}

Path parameters: planId = FREE, PRO_MONTHLY, or PRO_YEARLY

4.3 Get Current User’s Subscription

GET /api/subscription/me

Headers: Authorization: Bearer <token>

Response example:

{
  "success": true,
  "subscription": {
    "userId": 123,
    "username": "researcher1",
    "plan": "PRO_MONTHLY",
    "status": "active",
    "startDate": "2025-01-15T10:00:00",
    "endDate": "2025-02-15T10:00:00",
    "autoRenew": true,
    "cancelled": false,
    "isTrial": false,
    "trialDaysRemaining": 0,
    "daysRemaining": 28,
    "articleLimit": 100,
    "storageLimitMB": 5120,
    "apiCallsLimit": 5000,
    "aiGenerationsLimit": 500,
    "teamMembersLimit": 10,
    "articlesUsed": 12,
    "storageUsedMB": 245,
    "apiCallsUsed": 387,
    "aiGenerationsUsed": 23,
    "teamMembersUsed": 3,
    "features": {
      "aiSearch": true,
      "advancedAnalytics": true,
      "apiAccess": true,
      "prioritySupport": true,
      "collaboration": true,
      "exportCitations": true
    },
    "paymentMethod": "credit_card",
    "paymentProvider": "stripe",
    "price": 29.99,
    "currency": "USD",
    "lastInvoiceId": "INV-1737123456789-ABC12345",
    "lastInvoiceDate": "2025-01-15T10:00:00",
    "lastInvoiceAmount": 29.99
  }
}

4.4 Subscribe to a Plan

POST /api/subscription/subscribe

Headers: Authorization: Bearer <token>, Content-Type: application/json

Request body:

{
  "planId": "PRO_MONTHLY",
  "paymentMethod": "credit_card",
  "paymentToken": "pm_xxx",          // from Stripe/PayPal
  "couponCode": "WELCOME10",         // optional
  "autoRenew": true
}

Response: same as GET /api/subscription/me

4.5 Upgrade to Pro (convenience)

POST /api/subscription/upgrade?yearly=false

Query parameter: yearly – false for monthly, true for yearly.

Response: same as GET /api/subscription/me

4.6 Cancel Subscription (at period end)

POST /api/subscription/cancel

Effect: sets cancelled = true and autoRenew = false. Access continues until endDate.

Response:

{
  "success": true,
  "message": "Subscription cancelled. You will have access until the end of the billing period."
}

4.7 Downgrade to Free Immediately

POST /api/subscription/downgrade

Effect: immediately sets plan to FREE and removes future expiry. Warning: any over‑usage of Free limits will block further actions.

4.8 Get Current Usage (without consuming)

GET /api/subscription/usage

Response:

{
  "success": true,
  "usage": {
    "remainingGenerations": 477,
    "canCreateArticle": 1
  }
}

4.9 Consume an AI Generation

POST /api/subscription/consume

Effect: increments ai_generations counter. Returns 429 if limit reached.

Response:

{
  "success": true,
  "remaining": 477
}

4.10 List Invoices (paginated)

GET /api/subscription/invoices?page=0&limit=20

Response:

{
  "success": true,
  "invoices": [
    {
      "invoiceNumber": "INV-...",
      "amount": 29.99,
      "currency": "USD",
      "status": "paid",
      "issuedAt": "2025-01-15T10:00:00",
      "pdfUrl": "/api/subscription/invoices/INV-.../pdf"
    }
  ]
}

4.11 Admin Endpoints

Endpoint Method Description Role
/api/subscription/admin/all?page=0&limit=20 GET List all users’ subscriptions (with usage) ADMIN
/api/subscription/admin/stats GET Aggregated subscription statistics ADMIN

Admin stats response:

{
  "success": true,
  "statistics": {
    "totalUsers": 1250,
    "freeUsers": 980,
    "proMonthly": 200,
    "proYearly": 70,
    "activePro": 270,
    "conversionRate": 21.6,
    "monthlyRecurringRevenue": 12998.50
  }
}

5. Webhook Integration (Payment Providers)

To handle subscription events (payment success, cancellation, renewal), configure a webhook endpoint:

POST /api/subscription/webhook/{provider}

Supported providers: stripe, paypal

Example Stripe webhook payload handling:

{
  "type": "invoice.payment_succeeded",
  "data": {
    "object": {
      "customer": "cus_xxx",
      "subscription": "sub_xxx",
      "amount_paid": 2999,
      "currency": "usd"
    }
  }
}

The service will update the user’s subscription expiry date and create a new invoice.


6. Usage Middleware / Interceptors

To enforce limits, the following filters/interceptors should be applied:

Limitation Interceptor
Article creation ArticleService.create() checks articles_used < limit before saving
File upload FileUploadService checks storage limit before writing file
API calls A RateLimitFilter or UsageInterceptor increments api_calls per authenticated request
AI features Each AI endpoint calls SubscriptionService.consumeGeneration()
Team member invitations ProjectService.addMember() checks team member limit

7. Database Schema (Summary)

-- Users table (extended)
ALTER TABLE users ADD COLUMN subscription_plan VARCHAR(20) DEFAULT 'FREE';
ALTER TABLE users ADD COLUMN subscription_expiry TIMESTAMP;
ALTER TABLE users ADD COLUMN subscription_start TIMESTAMP;
ALTER TABLE users ADD COLUMN subscription_cancelled BOOLEAN DEFAULT 0;
ALTER TABLE users ADD COLUMN subscription_auto_renew BOOLEAN DEFAULT 1;

-- Monthly usage
CREATE TABLE subscription_usage (
    id INTEGER PRIMARY KEY,
    user_id INTEGER NOT NULL,
    year_month VARCHAR(7) NOT NULL,
    articles_created INTEGER DEFAULT 0,
    api_calls INTEGER DEFAULT 0,
    storage_used_mb INTEGER DEFAULT 0,
    ai_generations INTEGER DEFAULT 0,
    UNIQUE(user_id, year_month)
);

-- Invoices
CREATE TABLE invoices (
    id INTEGER PRIMARY KEY,
    user_id INTEGER NOT NULL,
    invoice_number VARCHAR(50) UNIQUE NOT NULL,
    plan VARCHAR(20) NOT NULL,
    amount DECIMAL(10,2) NOT NULL,
    currency VARCHAR(3) DEFAULT 'USD',
    status VARCHAR(20) DEFAULT 'pending',
    payment_method VARCHAR(50),
    payment_provider VARCHAR(50),
    payment_provider_id VARCHAR(100),
    issued_at TIMESTAMP,
    paid_at TIMESTAMP
);

8. Error Codes

HTTP Status Code Description
429 LIMIT_REACHED Monthly quota exceeded (articles, storage, API calls, or AI generations)
402 PAYMENT_REQUIRED Subscription expired or missing payment
400 SUBSCRIPTION_ERROR Invalid plan ID or coupon code
403 FORBIDDEN Admin endpoint accessed by non‑admin

9. Example Frontend Integration

Show current plan and usage bar

fetch('/api/subscription/me', { headers: { 'Authorization': `Bearer ${token}` } })
  .then(res => res.json())
  .then(data => {
    const sub = data.subscription;
    document.getElementById('planName').innerText = sub.plan;
    document.getElementById('articlesUsed').innerText = `${sub.articlesUsed}/${sub.articleLimit}`;
    const percent = (sub.articlesUsed / sub.articleLimit) * 100;
    document.getElementById('usageFill').style.width = `${percent}%`;
  });

Upgrade button

document.getElementById('upgradeBtn').addEventListener('click', async () => {
  const res = await fetch('/api/subscription/upgrade?yearly=false', {
    method: 'POST',
    headers: { 'Authorization': `Bearer ${token}` }
  });
  if (res.ok) location.reload();
  else alert('Upgrade failed');
});

10. Automated Renewal & Expiry Check

A scheduled job (cron 0 0 0 * * *) runs daily to:

  • Expire subscriptions where endDate < now() and autoRenew == false
  • For autoRenew == true, attempt to charge the saved payment method and extend endDate by one billing period
  • Send expiry notifications 7, 3, and 1 day before expiry

Xet Storage Details

Size:
12.1 kB
·
Xet hash:
bd73d9d3e40d2b50aa10a04aa2bd94d85867b22891f4df1026e41453738d307c

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.