| # 📘 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:** | |
| ```json | |
| { | |
| "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:** | |
| ```json | |
| { | |
| "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:** | |
| ```json | |
| { | |
| "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:** | |
| ```json | |
| { | |
| "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:** | |
| ```json | |
| { | |
| "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:** | |
| ```json | |
| { | |
| "success": true, | |
| "remaining": 477 | |
| } | |
| ``` | |
| ### 4.10 List Invoices (paginated) | |
| ``` | |
| GET /api/subscription/invoices?page=0&limit=20 | |
| ``` | |
| **Response:** | |
| ```json | |
| { | |
| "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:** | |
| ```json | |
| { | |
| "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:** | |
| ```json | |
| { | |
| "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) | |
| ```sql | |
| -- 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 | |
| ```javascript | |
| 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 | |
| ```javascript | |
| 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.