# Mobile API Implementation Plan — System Side

This is the step-by-step work to prepare the Laravel backend for the Flutter mobile app.
Each step is a vertical slice: migration → model → service → controller → test in browser/Postman.

---

## Phase 1: Foundation (API scaffold + auth)

### Step 1.1: API Route File + Versioned Prefix

**File:** `routes/api.php`

```php
Route::prefix('v1')->group(function () {
    // Public (no auth)
    Route::get('app/config', [AppConfigController::class, 'index']);

    // Auth
    Route::prefix('auth')->group(function () {
        Route::post('otp/request', [AuthOtpController::class, 'request']);
        Route::post('otp/verify', [AuthOtpController::class, 'verify']);
        Route::post('logout', [AuthOtpController::class, 'logout'])->middleware('auth:sanctum');
        Route::get('me', [AuthOtpController::class, 'me'])->middleware('auth:sanctum');
    });

    // Protected
    Route::middleware('auth:sanctum')->group(function () {
        // ... all other endpoints
    });
});
```

**Work:**
- Create `routes/api.php` (Laravel already has it scaffolded, just empty)
- Add `api` middleware group in `bootstrap/app.php` if not present
- Ensure Sanctum is configured for token auth (stateless, no cookies)
- Add `HasApiTokens` trait to User model

---

### Step 1.2: App Config Endpoint

**Controller:** `app/Http/Controllers/Api/V1/AppConfigController.php`

**Logic:**
- Read current academy (from host header or default)
- Return: name, logo URL, colors, feature flags, auth mode, min version

**System settings keys needed:**
- `app_primary_color` (default: academy's website primary color)
- `app_accent_color`
- `app_features_shop` (bool)
- `app_features_events` (bool)
- `app_features_chat` (bool)
- `app_min_version` (string, e.g. "1.0.0")
- `app_maintenance_mode` (bool)
- `app_maintenance_message` (text)
- `auth_otp_mode` ('demo' | 'sms')

**Migration:** Add these keys to `system_settings` seeder (or insert-if-not-exists migration).

---

### Step 1.3: OTP Auth Flow

**Controller:** `app/Http/Controllers/Api/V1/AuthOtpController.php`

**Endpoints:**

`POST /api/v1/auth/otp/request`
```json
// Request
{"phone": "+201012345678"}

// Response (demo mode)
{"sent": true, "mode": "demo", "expires_in": 300}

// Response (sms mode)
{"sent": true, "mode": "sms", "expires_in": 300}

// Error: phone not found
{"error": "phone_not_found", "message": "هذا الرقم غير مسجل في النظام"}
```

`POST /api/v1/auth/otp/verify`
```json
// Request
{"phone": "+201012345678", "otp": "123456"}

// Response
{
  "token": "1|abc123...",
  "user": {"id": 1, "name_ar": "أحمد محمد", "phone": "+201012345678"},
  "participants": [
    {"uuid": "abc-123", "name_ar": "محمد أحمد", "program": "فريق 2016", "photo_url": null}
  ]
}

// Error: wrong OTP
{"error": "invalid_otp", "message": "رمز التحقق غير صحيح"}
```

**Logic:**
```php
public function request(Request $request)
{
    $phone = $this->normalizePhone($request->phone);
    $user = User::where('phone', $phone)->first();
    if (!$user) return error('phone_not_found');

    $mode = SystemSetting::get('auth_otp_mode', 'demo');

    if ($mode === 'demo') {
        Cache::put("otp:{$phone}", '123456', 300);
    } else {
        $otp = str_pad(random_int(0, 999999), 6, '0', STR_PAD_LEFT);
        Cache::put("otp:{$phone}", $otp, 300);
        SmsService::send($phone, "رمز التحقق: {$otp}");
    }

    return response()->json(['sent' => true, 'mode' => $mode, 'expires_in' => 300]);
}

public function verify(Request $request)
{
    $phone = $this->normalizePhone($request->phone);
    $cached = Cache::get("otp:{$phone}");

    if (!$cached || $cached !== $request->otp) {
        return error('invalid_otp');
    }

    Cache::forget("otp:{$phone}");
    $user = User::where('phone', $phone)->firstOrFail();
    $token = $user->createToken('mobile', ['mobile:*'])->plainTextToken;

    $participants = $this->getLinkedParticipants($user);

    return response()->json([
        'token' => $token,
        'user' => UserResource::make($user),
        'participants' => ParticipantResource::collection($participants),
    ]);
}
```

**Rate limiting:** 3 OTP requests per phone per 10 minutes.

---

### Step 1.4: Device Token Registration

**Migration:** `create_device_tokens_table`

```php
Schema::create('device_tokens', function (Blueprint $table) {
    $table->id();
    $table->foreignId('academy_id')->constrained('academies');
    $table->foreignId('user_id')->constrained('users');
    $table->string('device_token', 255)->unique();
    $table->string('platform', 10); // android, ios
    $table->string('device_name', 100)->nullable();
    $table->string('app_version', 20)->nullable();
    $table->boolean('is_active')->default(true);
    $table->timestamp('last_used_at')->nullable();
    $table->timestamps();

    $table->index(['user_id', 'is_active']);
});
DB::statement("ALTER TABLE device_tokens ADD CONSTRAINT device_tokens_platform_check CHECK (platform IN ('android', 'ios'))");
```

**Controller:** `app/Http/Controllers/Api/V1/DeviceController.php`

```
POST /api/v1/devices/register   {"token": "fcm_xxx", "platform": "android", "device_name": "Samsung S24", "app_version": "1.0.0"}
PATCH /api/v1/devices/refresh   {"old_token": "fcm_old", "new_token": "fcm_new"}
DELETE /api/v1/devices/{token}   (on logout)
```

---

### Step 1.5: Participant Data Endpoints

**Controller:** `app/Http/Controllers/Api/V1/ParticipantController.php`

```
GET /api/v1/participants/{uuid}           → ParticipantResource (full profile)
GET /api/v1/participants/{uuid}/summary   → dashboard data
GET /api/v1/participants/{uuid}/schedule  → upcoming sessions (next 7 days)
GET /api/v1/participants/{uuid}/attendance → attendance history + rate
GET /api/v1/participants/{uuid}/invoices  → outstanding + recent paid
GET /api/v1/participants/{uuid}/enrollments → active enrollments
GET /api/v1/guardian/children              → all linked participants
```

**Authorization:** User can only access participants linked to them (own children or self).

**Resources:**
```
app/Http/Resources/Api/V1/
├── ParticipantResource.php
├── ParticipantSummaryResource.php
├── SessionResource.php
├── AttendanceResource.php
├── InvoiceResource.php
├── InvoiceItemResource.php
├── EnrollmentResource.php
└── UserResource.php
```

---

## Phase 2: Push Notifications

### Step 2.1: Install Firebase PHP SDK

```bash
composer require kreait/firebase-php
```

---

### Step 2.2: PushNotificationService

**File:** `app/Domain/Shared/Services/PushNotificationService.php`

```php
class PushNotificationService
{
    public function sendToUser(User $user, string $title, string $body, array $data = []): void
    public function sendToParticipantGuardians(Participant $participant, string $title, string $body, array $data = []): void
    public function sendToGroup(TrainingGroup $group, string $title, string $body, array $data = []): void
    public function sendBulk(array $userIds, string $title, string $body, array $data = []): void
}
```

**Credentials loading:**
1. Check `system_settings` for `firebase_service_account_json`
2. If not found, check `storage/app/firebase/service-account.json`
3. If not found, check env `FIREBASE_CREDENTIALS`
4. If nothing → log warning, don't crash (graceful degradation)

---

### Step 2.3: Event Listeners → Push

Extend existing event listeners to also dispatch push notifications:

| Event | Push Target | Title | Body |
|-------|-------------|-------|------|
| `AttendanceMarked` | participant's guardians | تسجيل حضور | تم تسجيل حضور {name} في جلسة اليوم |
| `AttendanceMarked` (absent) | participant's guardians | تسجيل غياب | تم تسجيل غياب {name} في جلسة اليوم |
| `InvoiceCreated` | invoice billable's user | فاتورة جديدة | فاتورة بقيمة {amount} ج.م بانتظار الدفع |
| `PaymentConfirmed` | payer | تأكيد الدفع | تم تأكيد دفع {amount} ج.م بنجاح |
| `SessionCancelled` | group participants' guardians | إلغاء جلسة | تم إلغاء جلسة {group} يوم {date} |
| `EnrollmentCreated` | participant's guardians | تأكيد الاشتراك | تم تسجيل {name} في {program} |

---

### Step 2.4: Scheduled Push Jobs

**Session Reminder:**
```php
// Runs every minute
// Find sessions starting in exactly 30 minutes
// Push to all participants' guardians in those sessions
```

**Installment Due Reminder:**
```php
// Runs daily at 9am
// Find installments with due_date = tomorrow
// Push to the participant's guardian
```

**Medical Certificate Expiry:**
```php
// Runs daily
// Find approved medical certs expiring in 7 days
// Push to participant's guardian
```

---

### Step 2.5: Notification History Endpoint

**Controller:** `app/Http/Controllers/Api/V1/NotificationController.php`

```
GET /api/v1/notifications              → paginated list (20 per page)
PATCH /api/v1/notifications/{id}/read  → mark as read
POST /api/v1/notifications/read-all    → mark all as read
POST /api/v1/notifications/preferences → update per-type toggles
GET /api/v1/notifications/preferences  → current preferences
```

Uses existing `notification_logs` table, filtered by user.

---

## Phase 3: Academy Explore + Shop

### Step 3.1: Academy Info/News/Gallery

**Controller:** `app/Http/Controllers/Api/V1/AcademyController.php`

```
GET /api/v1/academy/news      → paginated news articles (from website_news table)
GET /api/v1/academy/gallery   → paginated gallery items (from website sections)
GET /api/v1/academy/programs  → list of active programs with descriptions
GET /api/v1/academy/events    → upcoming events
POST /api/v1/events/{uuid}/register → join event
```

---

### Step 3.2: Product Shop API

**Controller:** `app/Http/Controllers/Api/V1/ShopController.php`

```
GET /api/v1/products → essential products list
```

Response includes `already_purchased_at` field per product (same logic as POS warning):
```json
{
  "data": [
    {
      "id": 1,
      "name_ar": "الزي الرسمي",
      "price": 65000,
      "image_url": "...",
      "installment_plans": [...],
      "already_purchased_at": "2026-03-15"  // null if not purchased this year
    }
  ]
}
```

```
POST /api/v1/orders/create    → create order + initiate payment
GET /api/v1/orders            → order history
GET /api/v1/orders/{uuid}     → order details + delivery status
```

---

## Phase 4: Financial API

### Step 4.1: Invoice & Payment Endpoints

**Controller:** `app/Http/Controllers/Api/V1/FinancialController.php`

```
GET /api/v1/participants/{uuid}/invoices → outstanding + paid (paginated)
GET /api/v1/invoices/{uuid}             → invoice detail with items
POST /api/v1/payments/initiate          → start online payment
POST /api/v1/payments/callback          → webhook from payment gateway (no auth)
```

**Payment initiation response:**
```json
{
  "payment_key": "ZXlKaGJ...",      // Paymob iframe key
  "iframe_url": "https://accept.paymob.com/api/acceptance/iframes/...",
  "order_id": "ORD-123"
}
```

---

### Step 4.2: Payment Gateway Integration (Paymob)

**Service:** `app/Domain/Financial/Services/PaymobService.php`

```php
class PaymobService
{
    public function createPaymentIntention(Invoice $invoice, User $payer): array
    public function verifyCallback(Request $request): bool
    public function processSuccessfulPayment(array $callbackData): Payment
}
```

**System settings:**
- `paymob_api_key`
- `paymob_integration_id`
- `paymob_iframe_id`
- `paymob_hmac_secret`

---

## Phase 5: Communication + Polish

### Step 5.1: Report Absence

**Controller:** `app/Http/Controllers/Api/V1/AbsenceController.php`

```
POST /api/v1/absences/report
{"participant_uuid": "abc", "session_id": 123, "reason": "مرض"}
```

Creates an `excused` attendance record for a future session.

---

### Step 5.2: Contact Academy

**Controller:** `app/Http/Controllers/Api/V1/MessageController.php`

```
POST /api/v1/messages/send
{"subject": "استفسار", "body": "...", "participant_uuid": "abc"}
```

Creates a record in a new `contact_messages` table and notifies academy admin.

---

### Step 5.3: Rate Limiting + Error Format

**Middleware:** `app/Http/Middleware/ApiRateLimit.php`

```
OTP requests: 3 per phone per 10 min
General API: 120 requests per minute per token
Payment: 5 per minute per token
```

**Standard error response:**
```json
{
  "error": "error_code",
  "message": "رسالة باللغة العربية",
  "details": {}  // optional validation errors
}
```

---

### Step 5.4: System Settings UI for Mobile App

Add a new tab/section in the existing System Settings Livewire component:

- **Mobile App** section with:
  - OTP mode toggle (demo / sms)
  - Firebase service account JSON upload
  - Paymob credentials (API key, integration ID, HMAC)
  - Feature toggles (shop, events, chat, online payment)
  - Minimum app version
  - Maintenance mode toggle + message

---

## File Structure (new files)

```
app/Http/Controllers/Api/V1/
├── AppConfigController.php
├── AuthOtpController.php
├── DeviceController.php
├── ParticipantController.php
├── FinancialController.php
├── NotificationController.php
├── AcademyController.php
├── ShopController.php
├── AbsenceController.php
└── MessageController.php

app/Http/Resources/Api/V1/
├── UserResource.php
├── ParticipantResource.php
├── ParticipantSummaryResource.php
├── SessionResource.php
├── AttendanceResource.php
├── InvoiceResource.php
├── InvoiceItemResource.php
├── EnrollmentResource.php
├── NotificationResource.php
├── ProductResource.php
├── OrderResource.php
├── EventResource.php
└── NewsResource.php

app/Http/Middleware/
└── ApiRateLimit.php

app/Domain/Shared/Services/
├── PushNotificationService.php
└── SmsService.php

app/Domain/Financial/Services/
└── PaymobService.php

database/migrations/
├── xxxx_create_device_tokens_table.php
├── xxxx_create_contact_messages_table.php
└── xxxx_add_mobile_app_system_settings.php

routes/
└── api.php
```

---

## Implementation Order (what to build first)

| # | What | Why First |
|---|------|-----------|
| 1 | `routes/api.php` + Sanctum setup | Everything depends on this |
| 2 | `GET /api/v1/app/config` | Flutter app needs this to boot |
| 3 | OTP auth (demo mode) | Flutter app needs login to work |
| 4 | Device token registration | Need this before push works |
| 5 | `GET /api/v1/guardian/children` | Home screen needs participant list |
| 6 | `GET /api/v1/participants/{uuid}/summary` | Dashboard data |
| 7 | Schedule + Attendance endpoints | Core daily-use screens |
| 8 | Invoice + Payment endpoints | Financial screens |
| 9 | `PushNotificationService` + Firebase | THE killer feature |
| 10 | Push event listeners | Attendance/invoice/session pushes |
| 11 | Scheduled reminder jobs | Session/installment/cert reminders |
| 12 | Academy explore (news/gallery/events) | Content screens |
| 13 | Shop API | Product purchasing |
| 14 | Paymob integration | Online payment |
| 15 | Communication (absence/messages) | Nice-to-have actions |
| 16 | System settings UI | Admin manages mobile config |

Steps 1-8 = **MVP** (app is usable for viewing data)
Steps 9-11 = **Core value** (push notifications work)
Steps 12-16 = **Full feature set**
