# RongBerong — Auth, Guest Mode, Free Trial & Coin Economy

> **Tech Lead Spec** — For agentic AI implementation
> Last updated: June 21, 2026

---

## 1. Business Model Summary

| Item | Price to User | Cost to Us (USD) | Cost to Us (BDT) | Margin |
|------|--------------|-----------------|-----------------|--------|
| 1 Image | **10 BDT** (10 coins) | ~$0.067 | ~8 ৳ | **20%** |
| 1 Video (Veo 3.0 Fast) | **200 BDT** (200 coins) | ~$1.20 | ~144 ৳ | **28%** |
| 1 Video (Veo 3.1 Fast*) | **200 BDT** (200 coins) | ~$0.15 | ~18 ৳ | **91%** |

*\*Recommended — `veo-3.0-fast-generate-001` deprecates June 30, 2026*

**Free trial per user:** 2 images + 1 video (guest or registered)
**After trial:** must buy coins to generate

---

## 2. User States & Flow

```
                    ┌─────────────────┐
                    │  Visits Site     │
                    └────────┬────────┘
                             │
                    ┌────────▼────────┐
                    │  Guest User     │
                    │ (session/cookie)│
                    └────────┬────────┘
                             │
                    ┌────────▼────────┐
                    │ Free Trial?     │
                    │ (2 img + 1 vid) │
                    └───┬────────┬────┘
                   Yes │        │ No
              ┌────────▼──┐  ┌──▼───────────┐
              │ Generate   │  │ Login/Register│
              │ (track via │  │ Modal forced  │
              │  session)  │  └──────┬───────┘
              └─────┬──────┘         │
                    │ trial done     │
                    └──┬─────────────┘
                       │
              ┌────────▼────────┐
              │  Logged In      │
              │  (mobile + OTP) │
              └────────┬────────┘
                       │
              ┌────────▼────────┐
              │ Coins > 0?      │
              └───┬────────┬────┘
             Yes │        │ No
        ┌────────▼──┐  ┌──▼───────────┐
        │ Generate   │  │ Show Buy UI  │
        │ Deduct     │  │ (coin packs) │
        │ coins      │  └──────┬───────┘
        └────────────┘         │
                       ┌──────▼──────┐
                       │ SSLCommerz  │
                       │ Payment     │
                       └──────┬──────┘
                              │
                       ┌──────▼──────┐
                       │ Coins Added │
                       │ to Wallet   │
                       └─────────────┘
```

---

## 3. Guest Mode

**Key rules:**
- Guest identified by `session_id` stored in session cookie
- Guest usage tracked in `guest_usage` table (session_id → counts)
- Free trial consumption tracked SEPARATELY for guest vs registered
- When guest tries to generate after trial ends → show auth modal with message: *"Sign in to continue. Your 2 free images & 1 free video are used."*
- If guest logs in, their guest usage does NOT transfer — they get fresh free trial as a registered user
- **Guest cannot purchase coins** — must register first

**Guest usage table:**
```sql
CREATE TABLE guest_usage (
    session_id   VARCHAR(64) PRIMARY KEY,
    images_used  INT DEFAULT 0,
    videos_used  INT DEFAULT 0,
    created_at   TIMESTAMP,
    updated_at   TIMESTAMP
);
```

---

## 4. OTP Authentication

### ENV Config
```env
APP_ENV=local
OTP_DEFAULT=123456           # Used when APP_ENV=local
FREE_IMAGES_LIMIT=2
FREE_VIDEOS_LIMIT=1
COST_IMAGE=10                # coins per image
COST_VIDEO=200               # coins per video
```

### Flow
1. User enters mobile number → `POST /auth/send-otp`
2. Validate mobile (Bangladeshi: 01XXXXXXXXX or +8801XXXXXXXXX)
3. Generate 6-digit OTP:
   - `APP_ENV=local` → OTP = `env('OTP_DEFAULT', '123456')`
   - `APP_ENV=production` → random 6 digits, send via SMS (SmsService)
4. Store OTP in `users.otp` + `users.otp_expires_at` (5 min expiry)
5. If user doesn't exist → auto-create with mobile only
6. User submits OTP → `POST /auth/verify-otp`
7. Validate OTP + check expiry → `Auth::login()`
8. Return user data + JS session

### Database Migration — `users` table changes
```php
Schema::table('users', function (Blueprint $table) {
    $table->string('mobile', 15)->unique()->nullable()->after('id');
    $table->string('otp', 6)->nullable();
    $table->timestamp('otp_expires_at')->nullable();
    $table->timestamp('mobile_verified_at')->nullable();
    $table->integer('images_used')->default(0);     // paid + free total
    $table->integer('videos_used')->default(0);
    $table->integer('free_images_used')->default(0);
    $table->integer('free_videos_used')->default(0);
    $table->integer('coin_balance')->default(0);

    // Make name & email nullable
    $table->string('name')->nullable()->change();
    $table->string('email')->nullable()->change();
    $table->string('password')->nullable()->change();
});
```

### Auth Routes
```
POST /auth/send-otp       → AuthController@sendOtp
POST /auth/verify-otp     → AuthController@verifyOtp
POST /auth/logout         → AuthController@logout
GET  /auth/me             → AuthController@me     (returns user + balance)
```

### SmsService (provider-agnostic)
```php
interface SmsProvider {
    public function send(string $to, string $message): bool;
}

class LogSmsProvider implements SmsProvider { ... }    // dev: logs to file
// Future: class BulkSmsBDProvider implements SmsProvider { ... }
// Future: class TwilioProvider implements SmsProvider { ... }
```

---

## 5. Free Trial System

### Middleware: `CheckGenerationLimit`

Applied to `/generate` and `/generate-video`:

```
If user is guest:
    Check guest_usage.images_used < FREE_IMAGES_LIMIT
    OR guest_usage.videos_used < FREE_VIDEOS_LIMIT
    → allow + increment
    → else: return error "FREE_TRIAL_EXHAUSTED" + prompt login

If user is registered:
    Check user.free_images_used < FREE_IMAGES_LIMIT
    OR user.free_videos_used < FREE_VIDEOS_LIMIT
    → allow + increment
    → else: check user.coin_balance >= COST_IMAGE or COST_VIDEO
    → if insufficient: return error "INSUFFICIENT_COINS" + show buy UI
```

### Controller changes in `RongBerongController@generate` & `@generateVideo`
```php
// After successful generation:
if ($user->free_images_used < FREE_IMAGES_LIMIT) {
    $user->increment('free_images_used');
} else {
    $user->decrement('coin_balance', COST_IMAGE);
    Transaction::create([
        'user_id' => $user->id,
        'type' => 'debit',
        'amount' => COST_IMAGE,
        'description' => 'Image generation',
    ]);
}
```

---

## 6. Coin Economy

### Coin to BDT mapping

**1 coin = 1 BDT at base rate.**

Per-generation cost in coins:

| Action | Coins | BDT Equivalent |
|--------|-------|---------------|
| 1 Image | 10 | 10 ৳ |
| 1 Video | 200 | 200 ৳ |

### Coin Packs (volume discounted)

| Pack | Coins | Price (BDT) | Per Coin | You Get |
|------|-------|------------|----------|---------|
| 🪙 Starter | 50 | 50 ৳ | 1.00 ৳ | 5 images or ¼ video |
| 🪙 Basic | 200 | 170 ৳ | 0.85 ৳ | 20 images or 1 video |
| 🪙 Popular | 500 | 400 ৳ | 0.80 ৳ | 50 images or 2.5 videos |
| 🪙 Pro | 1,500 | 1,050 ৳ | 0.70 ৳ | 150 images or 7.5 videos |
| 🪙 Enterprise | 5,000 | 3,000 ৳ | 0.60 ৳ | 500 images or 25 videos |

### Profit Margin per pack (at Starter pack purchase)

| Action | Revenue | Cost (BDT) | Profit | Margin |
|--------|---------|-----------|--------|--------|
| 1 Image (10 coins) | 10.00 ৳ | 8.04 ৳ | 1.96 ৳ | **20%** |
| 1 Video (3.0 Fast, 200 coins) | 200.00 ৳ | 144.00 ৳ | 56.00 ৳ | **28%** |
| 1 Video (3.1 Fast, 200 coins) | 200.00 ৳ | 18.00 ৳ | 182.00 ৳ | **91%** |

### Profit Margin per pack (at Enterprise pack — worst case)

| Action | Revenue (Enterprise rate) | Cost (BDT) | Profit | Margin |
|--------|--------------------------|-----------|--------|--------|
| 1 Image (10 coins × 0.60) | 6.00 ৳ | 8.04 ৳ | -2.04 ৳ | **-34% (loss)** |
| 1 Video (3.0 Fast, 200 × 0.60) | 120.00 ৳ | 144.00 ৳ | -24.00 ৳ | **-20% (loss)** |
| 1 Video (3.1 Fast, 200 × 0.60) | 120.00 ৳ | 18.00 ৳ | 102.00 ৳ | **85%** |

> **Key insight:** With Veo 3.0 Fast, Enterprise pack loses money on both images and videos. **Migrating to Veo 3.1 Fast is critical** — it makes video highly profitable at every pack level. Images are thin margin / minor loss leader at discounted packs, accepted as cost of customer acquisition.

### Revenue projection (100 users/month, Popular pack)

| Metric | Value |
|--------|-------|
| Users buying 500-coin Popular pack | 100 |
| Revenue | 100 × 400 ৳ = **40,000 ৳** |
| Avg usage: 30 images + 2 videos per user | |
| Gemini cost (3.1 Fast): 100 × (30×8 + 2×18) | 100 × (240 + 36) = **27,600 ৳** |
| Gateway fees (~2% + 5 ৳) | ~1,300 ৳ |
| **Net profit** | **~11,100 ৳ / month** |

---

## 7. Database Schema

### `users` (modified)
```php
id                  bigint PK
mobile              varchar(15) unique nullable
otp                 varchar(6) nullable
otp_expires_at      timestamp nullable
mobile_verified_at  timestamp nullable
name                varchar nullable (was NOT NULL)
email               varchar unique nullable (was NOT NULL)
password            varchar nullable (was NOT NULL)
free_images_used    int default 0
free_videos_used    int default 0
images_used         int default 0
videos_used         int default 0
coin_balance        int default 0
remember_token      varchar nullable
timestamps
```

### `guest_usage` (new)
```php
session_id          varchar(64) primary key
images_used         int default 0
videos_used         int default 0
created_at          timestamp
updated_at          timestamp
```

### `transactions` (new)
```php
id                  bigint PK
user_id             bigint FK → users
type                enum('credit','debit')
amount              int (coins)
balance_after       int (running balance)
description         varchar(255)
reference           varchar(100) nullable (payment txn ID)
created_at          timestamp
index: (user_id, created_at)
```

### `payments` (new)
```php
id                  bigint PK
user_id             bigint FK → users
amount              decimal(10,2) (BDT)
coins               int
transaction_id      varchar(100) unique (SSLCommerz txn ID)
status              enum('pending','success','failed','refunded')
gateway_response    json nullable
created_at          timestamp
updated_at          timestamp
```

---

## 8. API Routes

### Auth
| Method | URI | Purpose |
|--------|-----|---------|
| POST | `/auth/send-otp` | Send OTP to mobile |
| POST | `/auth/verify-otp` | Verify OTP & login |
| POST | `/auth/logout` | Logout |
| GET | `/auth/me` | Get current user + balance |

### Coins
| Method | URI | Purpose |
|--------|-----|---------|
| GET | `/coins/balance` | Get current balance |
| GET | `/coins/packages` | List available coin packs |
| POST | `/coins/purchase` | Initiate purchase → redirect to SSLCommerz |
| POST | `/coins/ipn` | SSLCommerz IPN webhook |
| GET | `/coins/history` | Transaction history |

### Existing (add auth middleware)
| Method | URI | Change |
|--------|-----|--------|
| POST | `/generate` | Add auth + coin deduction |
| POST | `/generate-video` | Add auth + coin deduction |
| (others) | | No change |

---

## 9. Frontend Components (Vanilla JS)

### Auth Modal
- Overlay with backdrop blur on the SPA
- Two steps (no page reload):
  - **Step 1:** Mobile input with +880 prefix → "Send OTP" button
  - **Step 2:** 6-digit OTP input → "Verify" button
- "Continue as Guest" link at bottom
- On success: dismiss, update topbar with mobile + coin balance

### Topbar Changes (in `topbar.blade.php`)
```
[Logo] [Badge]    [EN/বাংলা] [Guide] [Download] [⋮] | [📱 01XXXXXXXXX] [🪙 42] [Buy]
```

### Coin Balance Badge
- Shows current balance in topbar
- Click → dropdown with quick actions: "Buy more", "Transaction history"

### Buy Coins Modal
- Shows 5 coin pack options as cards
- Each card: pack name, coin count, price (BDT), per-coin rate, "Buy" button
- Click "Buy" → redirects to SSLCommerz
- On success callback → update balance

### Generation Guard
- When user hits "Generate" and has insufficient coins:
  - Show toast/inline error: *"Not enough coins. You need 10 coins for an image. [Buy coins]"*
  - "Buy coins" link opens Buy Modal

---

## 10. Implementation Order

```
Phase 1 — Auth (2 days)
├── Migration: modify users table + create guest_usage table
├── AuthController (sendOtp, verifyOtp, logout, me)
├── SmsService (Log provider for dev)
├── Auth routes
├── Frontend: Auth modal (2-step: mobile → OTP)
└── Topbar: show mobile when logged in

Phase 2 — Guest Mode (1 day)
├── Guest usage tracking (session-based)
├── Guest generation limit check
├── Auth modal prompt when guest trial exhausted
└── Merge guest → registered flow (fresh trial)

Phase 3 — Free Trial + Generation Guard (1 day)
├── CheckGenerationLimit middleware
├── Modify generate() & generateVideo() controllers
├── Deduct free_trial or coins after generation
└── Error responses for exhausted trial / insufficient coins

Phase 4 — Coin Economy (2 days)
├── Migration: transactions + payments tables
├── CoinController (balance, packages, purchase, history, ipn)
├── SSLCommerz integration in PaymentService
├── Coin deduction after generation
├── Frontend: Buy coins modal with pack cards
├── Frontend: Coin balance badge in topbar
└── Transaction history UI
```

---

## 11. ENV Reference

```env
# Auth
OTP_DEFAULT=123456

# Free Trial
FREE_IMAGES_LIMIT=2
FREE_VIDEOS_LIMIT=1

# Coin Costs
COST_IMAGE=10
COST_VIDEO=200

# SSLCommerz
SSLCOMMERZ_STORE_ID=
SSLCOMMERZ_STORE_PASSWORD=
SSLCOMMERZ_SANDBOX=true

# Future SMS Provider
SMS_PROVIDER=log
SMS_API_KEY=

# Model Migration (future)
GEMINI_VIDEO_MODEL=veo-3.1-fast-generate-001    # after deprecation
```
