Complete business logic, database schema, admin panel features, and system architecture documentation for the Play Store app and backend platform.
A monetized Spoken English training app for Indian learners, delivered via a Flutter Android app on the Play Store. Users browse structured course modules, watch video classes, read Google Drive materials, submit Google Form assignments, and practice sentence correction powered by AI — all gated by subscription tier.
A Module is the top-level grouping — e.g., "Foundation Level", "Intermediate Conversations", "Advanced Business English". Each module has a title, description, display order, and thumbnail image.
Each class inside a module is a YouTube video rendered via WebView in the app. A class also carries an access tag controlling who can play it.
Each class can have one or more PDF / document attachments hosted on Google Drive. Rendered via WebView or opened with the Drive viewer.
Each class can link to a Google Form. Rendered inside the app via WebView so users submit without leaving the app. Admin can track responses via Google Sheets integration.
When a user taps a class, the app compares the class tag against the user's active subscription status:
| Class Tag | FREE User | BEGINNER Subscriber | PRO Subscriber |
|---|---|---|---|
| FREE | ✅ Accessible | ✅ Accessible | ✅ Accessible |
| BEGINNER | 🔒 Locked | ✅ Accessible | ✅ Accessible |
| PRO | 🔒 Locked | 🔒 Locked | ✅ Accessible |
User picks Beginner or Pro plan in the app. Coupon code optionally applied for discount.
Payment processed via Razorpay. On success, webhook fires to backend.
user_subscriptions row created/updated with plan type, start date, expiry date.
App checks subscription status on each class open. Locked classes show upgrade prompt.
On expiry, user reverts to FREE status. Renewal uses the same payment flow.
Grammar and spelling corrections. Quick fix for simple sentences. Powered by Grok xAI API (or Gemini fallback).
Grammar + tone + fluency + vocabulary improvement. Detailed explanation of changes. Full rewrite with rationale.
| Event | Credit Change | Notes |
|---|---|---|
| New user signup (FREE) | +5 credits | Starter credits |
| BEGINNER plan active | +30 credits/month | Auto-top-up on renewal |
| PRO plan active | +80 credits/month | Auto-top-up on renewal |
| Basic correction used | −1 credit | Per request |
| Advanced correction used | −2 credits | Per request |
| Referral bonus (referred user pays) | +5 credits | One-time per referral |
Admin configures Razorpay keys from the admin panel. The keys are stored securely in the backend environment (not hard-coded in the app). The app fetches order details from the backend before opening the Razorpay checkout.
POST /api/orders/createCreated manually in the admin panel for promotions, launch offers, or event discounts. Can have usage limits and expiry dates.
Auto-generated for every registered user. Shared via WhatsApp/Telegram along with the Play Store link. Gives a discount to the new user who applies it.
| Check | Rule |
|---|---|
| Expiry | Current date must be ≤ expire_date |
| Usage Limit | times_used < max_uses (if max_uses is set) |
| Active Status | Coupon must be is_active = true |
| Self-Referral | User cannot use their own referral coupon |
| Duplicate Use | Referral coupon can only be used once per new user |
When a new user registers with a referral code at payment time, the backend records the referral in the referrals table linking referrer_user_id → referred_user_id. Credit bonus to the referrer is applied on the referred user's first successful payment.
| Column | Type | Notes |
|---|---|---|
| id | INT PK AUTO_INCREMENT | Primary key |
| name | VARCHAR(100) | |
VARCHAR(150) UNIQUE | ||
| phone | VARCHAR(15) | |
| password_hash | VARCHAR(255) | Bcrypt hashed |
| referral_code | VARCHAR(20) UNIQUE | Auto-generated on signup |
| ai_credits | INT DEFAULT 5 | Current credit balance |
| subscription_status | ENUM('free','beginner','pro') | Denormalized for fast access checks |
| created_at | TIMESTAMP | |
| updated_at | TIMESTAMP |
| Column | Type | Notes |
|---|---|---|
| id | INT PK AUTO_INCREMENT | |
| title | VARCHAR(200) | |
| description | TEXT | |
| thumbnail_url | VARCHAR(500) | |
| display_order | INT | Controls app listing order |
| is_active | TINYINT(1) DEFAULT 1 | |
| created_at | TIMESTAMP |
| Column | Type | Notes |
|---|---|---|
| id | INT PK AUTO_INCREMENT | |
| module_id | INT FK → modules.id | |
| title | VARCHAR(200) | |
| youtube_url | VARCHAR(500) | YouTube embed/watch URL |
| description | TEXT | |
| access_tag | ENUM('free','beginner','pro') | Access control |
| display_order | INT | |
| is_active | TINYINT(1) DEFAULT 1 | |
| created_at | TIMESTAMP |
| Column | Type | Notes |
|---|---|---|
| id | INT PK AUTO_INCREMENT | |
| class_id | INT FK → classes.id | |
| type | ENUM('material','assignment') | Material = Drive link, Assignment = Google Form |
| title | VARCHAR(200) | |
| url | VARCHAR(500) | Google Drive or Google Form URL |
| display_order | INT | |
| created_at | TIMESTAMP |
| Column | Type | Notes |
|---|---|---|
| id | INT PK AUTO_INCREMENT | |
| user_id | INT FK → users.id | |
| plan | ENUM('beginner','pro') | |
| status | ENUM('active','expired','cancelled') | |
| started_at | TIMESTAMP | |
| expires_at | TIMESTAMP | |
| coupon_id | INT FK → coupons.id NULL | Coupon applied at payment |
| amount_paid | DECIMAL(10,2) | After discount |
| razorpay_order_id | VARCHAR(100) | |
| razorpay_payment_id | VARCHAR(100) | |
| created_at | TIMESTAMP |
| Column | Type | Notes |
|---|---|---|
| id | INT PK AUTO_INCREMENT | |
| user_id | INT FK → users.id | |
| correction_type | ENUM('basic','advanced') | |
| credits_used | INT | 1 or 2 |
| input_text | TEXT | User's original sentence |
| output_text | TEXT | AI-corrected sentence |
| created_at | TIMESTAMP |
| Column | Type | Notes |
|---|---|---|
| id | INT PK AUTO_INCREMENT | |
| code | VARCHAR(30) UNIQUE | e.g. LAUNCH30, RAVI2024 |
| type | ENUM('admin','referral') | Admin-created or user referral |
| owner_user_id | INT FK → users.id NULL | For referral coupons only |
| discount_percent | DECIMAL(5,2) | e.g. 20.00 = 20% |
| max_uses | INT NULL | NULL = unlimited |
| times_used | INT DEFAULT 0 | |
| expire_date | DATE NULL | NULL = never expires |
| is_active | TINYINT(1) DEFAULT 1 | |
| created_at | TIMESTAMP |
| Column | Type | Notes |
|---|---|---|
| id | INT PK AUTO_INCREMENT | |
| referrer_user_id | INT FK → users.id | Who shared the code |
| referred_user_id | INT FK → users.id | New user who used the code |
| coupon_id | INT FK → coupons.id | Referral coupon used |
| bonus_credited | TINYINT(1) DEFAULT 0 | Whether referrer got AI credit bonus |
| bonus_credited_at | TIMESTAMP NULL | When first payment triggered bonus |
| created_at | TIMESTAMP | When referral was recorded |
| Column | Type | Notes |
|---|---|---|
| key | VARCHAR(100) PK | |
| value | TEXT | |
| updated_at | TIMESTAMP |
Stores: razorpay_key_id, razorpay_key_secret, razorpay_webhook_secret, razorpay_mode (test/live), plan prices, credit allocations per plan, referral bonus credits, etc.
/api/admin/* routes.