# ECOIN v3 - User Journeys & State Machines (Comprehensive)

**Version:** 2.0  
**Date:** 2026-01-07  
**Languages:** العربية (RTL) + Français (LTR)

---

## 1. Assumptions & Policies

### TTL (Time-To-Live) Policies
| Context | TTL | Action on Expiry |
|---------|-----|------------------|
| Seat Reservation (Electronic Payment) | 60 minutes | Release seat, notify student |
| Seat Reservation (Cash Option) | 48 hours | Cancel draft, notify student |
| Receipt Review SLA | 24 hours (business) | Escalate to admin backlog |
| Session Flash Messages | 5 seconds | Auto-dismiss |

### Payment Rules
- **Minimum Payment**: Full amount required (no partial for MVP)
- **Payment Methods**: BaridiMob, CCP, Cash at Office
- **Anti-Duplicate**: Unique TRX ref per student per course
- **Audit**: All payment state changes logged with reviewer ID + timestamp

### Security Policies
- **File Upload**: JPG/PNG/PDF only, max 4MB, stored in private disk
- **IDOR Prevention**: Model-based policies on all resources
- **Rate Limiting**: 5 attempts per minute on registration/upload endpoints

---

## 2. User Journeys

### J1: Visitor → Browse Courses → Start Registration

**Persona**: Visitor (زائر غير مسجل)  
**Goal**: Discover a course and begin enrollment  
**Trigger**: Marketing link, search engine, referral

```mermaid
flowchart LR
    A[Landing Page] --> B[Browse Catalog]
    B --> C{Search/Filter}
    C --> D[View Course List]
    D --> E[Click Course Card]
    E --> F[Course Details Page]
    F --> G{Available Seats?}
    G -->|Yes| H[Click 'Register Now']
    G -->|No| I[Show Waitlist Option]
    H --> J[Registration Wizard Step 1]
```

**Microcopy**:
| Element | العربية | Français |
|---------|--------|----------|
| CTA Primary | احجز مقعدك الآن | Réservez votre place |
| No Seats | الفوج مكتمل حالياً | Groupe complet |
| Low Stock | ⚠️ 3 مقاعد متبقية فقط | ⚠️ Seulement 3 places restantes |

**Edge Cases**:
- **No Results**: Show empty state with filter reset suggestion
- **Session Error**: Show friendly error with retry button

---

### J2: Registration Step 1 (Personal Data)

**Persona**: Visitor  
**Goal**: Submit valid personal information  
**Precondition**: Clicked "Register Now" from course page

**Steps**:
1. Display form: Name, Phone, DOB, Gender
2. If guest: Show Email + Password fields
3. Real-time validation on phone format (05/06/07)
4. Click "Next" → Validate all fields
5. If valid → Proceed to Step 2
6. If invalid → Highlight errors inline

**Validation Rules**:
| Field | Rule | Error (AR) | Error (FR) |
|-------|------|------------|------------|
| Name | min:3 | الاسم قصير جداً | Nom trop court |
| Phone | regex:05/06/07... | رقم الهاتف غير صحيح | Numéro invalide |
| Email | unique | البريد مستخدم مسبقاً | Email déjà utilisé |
| Password | min:8 | كلمة المرور ضعيفة | Mot de passe faible |

**Pain Points & Solutions**:
| Pain Point | UX Solution |
|------------|-------------|
| Forget to fill field | Auto-scroll to first error |
| Lose data on back | Persist in session storage |
| Phone format confusion | Placeholder: 05xxxxxxxx |

---

### J3: Registration Step 2 (Choose Cohort)

**Persona**: Student  
**Goal**: Select an available session  
**Precondition**: Completed Step 1 validation

**Steps**:
1. Show available sessions for selected course
2. Display: Name, Start Date, Time, Location, Seats
3. Full sessions disabled with "Full" badge
4. Student selects session via radio
5. System reserves seat temporarily (TTL starts)
6. Click "Next" → Proceed to Step 3

**Microcopy**:
| State | العربية | Français |
|-------|--------|----------|
| Available | 🟢 متاح (12 مقعد) | 🟢 Disponible (12 places) |
| Low | 🟠 آخر 3 مقاعد | 🟠 3 dernières places |
| Full | 🔴 مكتمل | 🔴 Complet |
| No Sessions | لا توجد أفواج متاحة حالياً | Aucun groupe disponible |

---

### J4: Payment (BaridiMob) → Upload Receipt

**Persona**: Student  
**Goal**: Complete payment via BaridiMob and upload proof

**Steps**:
1. Select "BaridiMob" payment method
2. System displays RIP number + amount
3. Student performs transfer externally
4. Return to app, click upload area
5. Select image/PDF of receipt
6. Show upload progress + filename
7. Click "Confirm Registration"
8. System creates: Registration + Payment + Receipt
9. Show "Pending Review" status page

**Microcopy**:
| Element | العربية | Français |
|---------|--------|----------|
| Instructions | قم بالتحويل ثم ارفع صورة الوصل | Effectuez le virement puis téléchargez le reçu |
| Upload Idle | اضغط لرفع الوصل | Cliquez pour télécharger |
| Upload Success | ✓ تم رفع الملف: receipt.jpg | ✓ Fichier téléchargé: receipt.jpg |
| File Too Large | الملف كبير جداً (الحد: 4MB) | Fichier trop volumineux (max: 4Mo) |
| Wrong Type | يرجى رفع صورة أو PDF | Veuillez télécharger une image ou PDF |

---

### J5: Payment (CCP) → Upload Receipt

Same as J4 with different payment instructions:

**Microcopy**:
| Element | العربية | Français |
|---------|--------|----------|
| Instructions | حوّل المبلغ إلى الحساب البريدي | Transférez le montant au compte CCP |
| Account | الحساب: CCP 00799999... | Compte: CCP 00799999... |

---

### J6: Payment (Cash at Office)

**Persona**: Student  
**Goal**: Reserve seat with pay-at-desk option

**Steps**:
1. Select "Cash at Office" payment method
2. System shows instructions + deadline (48h)
3. Click "Confirm Registration"
4. System creates: Registration (status: draft) + Payment (status: due)
5. Show confirmation page with reservation code
6. Student visits office, pays, admin confirms

**Microcopy**:
| Element | العربية | Français |
|---------|--------|----------|
| Info Box | مقعدك محجوز لمدة 48 ساعة. يرجى زيارة المكتب للدفع. | Votre place est réservée 48h. Venez payer au bureau. |
| Deadline | ينتهي الحجز: {date} | La réservation expire: {date} |
| Code | رمز الحجز: ECOIN-2026-0042 | Code de réservation: ECOIN-2026-0042 |

---

### J7: Student Dashboard Daily Use

**Persona**: Student  
**Goal**: Check schedule, see alerts, take action

**Steps**:
1. Login → Dashboard
2. See: Active Courses, Upcoming Schedule, Payment Alerts
3. If payment due: Show alert card with "Pay Now" CTA
4. Click course → See session details
5. Click schedule item → See time/location

**Microcopy**:
| Element | العربية | Français |
|---------|--------|----------|
| Welcome | مرحباً، {name} 👋 | Bienvenue, {name} 👋 |
| Due Alert | ⚠️ لديك دفعة مستحقة | ⚠️ Vous avez un paiement dû |
| No Courses | لم تسجل في أي دورة بعد | Aucune inscription |
| Next Class | الحصة القادمة: {date} | Prochain cours: {date} |

---

### J8: Student Payments → Pay Due Installment

**Persona**: Student  
**Goal**: Clear a due payment

**Steps**:
1. Dashboard → Payments section
2. See list of payments with status badges
3. Click "Pay Now" on Due item
4. Redirect to upload receipt flow
5. Upload + Submit → Returns to Pending Review

---

### J9: Finance Reviewer → Review & Decide

**Persona**: Finance Reviewer  
**Goal**: Process pending payment queue efficiently

**Steps**:
1. Admin Dashboard → "5 Pending Reviews" badge
2. Click → Registrations list filtered by pending
3. Click registration → Payment Review modal
4. Left: Receipt image (zoomable), Right: Transaction details
5. Compare amount, date, name
6. **Decision A**: Click "Verify & Confirm"
   - Payment → verified, Registration → confirmed
   - Student notified, Audit logged
7. **Decision B**: Click "Reject"
   - Reason required (dropdown + text)
   - Payment → rejected, Student notified with reason
   - Student can reupload

**Microcopy (Admin)**:
| Element | العربية | Français |
|---------|--------|----------|
| Verify Button | ✓ تأكيد الدفع | ✓ Confirmer le paiement |
| Reject Button | ✗ رفض | ✗ Rejeter |
| Reason Placeholder | سبب الرفض... (مطلوب) | Motif du refus... (requis) |
| Success Toast | تم تأكيد الدفع بنجاح | Paiement confirmé avec succès |

**Rejection Reasons**:
| Code | العربية | Français |
|------|--------|----------|
| blurry | صورة غير واضحة | Image floue |
| wrong_amount | المبلغ غير مطابق | Montant incorrect |
| duplicate | وصل مستخدم مسبقاً | Reçu déjà utilisé |
| invalid | وصل غير صالح | Reçu invalide |

---

### J10: Attendance Manager → Mark Attendance

**Persona**: Attendance Manager  
**Goal**: Record daily attendance for a session

**Steps**:
1. Navigate to Attendance section
2. Select: Date, Course, Session
3. System loads registered students
4. For each student: Select Present/Absent/Late/Excused
5. Bulk action: "Mark All Present"
6. Add notes if needed
7. Click "Save Changes"
8. Click "Print" for physical record

**Microcopy**:
| Element | العربية | Français |
|---------|--------|----------|
| Save Button | حفظ الحضور | Enregistrer la présence |
| Mark All | تحديد الكل حاضر | Marquer tous présents |
| Print | طباعة القائمة | Imprimer la liste |
| Saved Toast | ✓ تم حفظ الحضور | ✓ Présence enregistrée |

---

### J11: Student Certificate Journey

**Persona**: Student  
**Goal**: Obtain course completion certificate

**Preconditions**:
- Payment fully confirmed
- Attendance >= 80% threshold
- Course completed

**Steps**:
1. Dashboard → My Certificates
2. Certificate status shown per course
3. If Locked: Show reason (payment/attendance)
4. If Issued: Show "Download PDF" button
5. Click → Download signed certificate

**Microcopy**:
| State | العربية | Français |
|-------|--------|----------|
| Locked | 🔒 غير متاحة (الأسباب أدناه) | 🔒 Non disponible |
| Processing | ⏳ قيد التجهيز | ⏳ En cours de traitement |
| Issued | ✓ جاهزة للتحميل | ✓ Prête à télécharger |
| Reason: Payment | الدفع غير مكتمل | Paiement incomplet |
| Reason: Attendance | نسبة الحضور أقل من 80% | Présence < 80% |

---

### J12: Support Journey

**Persona**: Any User  
**Goal**: Get help when stuck

**Trigger**: Friction at any point (payment issue, error, confusion)

**Steps**:
1. Click floating WhatsApp button (bottom-right)
2. Opens WhatsApp with pre-filled context
3. Support receives: Registration ID, Course, Issue context
4. User explains issue
5. Support resolves or escalates

**Pre-filled Message Template**:
```
مرحباً، أحتاج مساعدة بخصوص تسجيلي.
الدورة: {course_name}
رقم التسجيل: {registration_id}

------
Bonjour, j'ai besoin d'aide pour mon inscription.
Cours: {course_name}
N° inscription: {registration_id}
```

---

## 3. State Machines

### 3.1 Registration State Machine

```mermaid
stateDiagram-v2
    [*] --> Draft: create
    Draft --> PendingPayment: select_session
    Draft --> Cancelled: cancel
    Draft --> Expired: timeout_48h
    
    PendingPayment --> PendingReview: upload_receipt
    PendingPayment --> Cancelled: cancel
    PendingPayment --> Expired: timeout_60min
    
    PendingReview --> Confirmed: admin_approve
    PendingReview --> Rejected: admin_reject
    
    Rejected --> PendingReview: reupload_receipt
    Rejected --> Cancelled: cancel
    
    Confirmed --> [*]
    Cancelled --> [*]
    Expired --> [*]
```

**Transition Table**:
| From | Event | To | Guard | Side Effects |
|------|-------|----|-------|--------------|
| Draft | select_session | PendingPayment | session.has_seats | Reserve seat (TTL) |
| PendingPayment | upload_receipt | PendingReview | file.valid | Store file |
| PendingReview | admin_approve | Confirmed | payment.matches | Confirm seat, notify |
| PendingReview | admin_reject | Rejected | reason.provided | Notify with reason |
| Rejected | reupload_receipt | PendingReview | file.valid | Replace file |
| * | timeout | Expired | ttl.exceeded | Release seat |

---

### 3.2 Seat/Enrollment State Machine

```mermaid
stateDiagram-v2
    [*] --> Available: init
    Available --> Reserved: student_selects
    Reserved --> Confirmed: payment_verified
    Reserved --> Available: timeout
    Reserved --> Waitlisted: capacity_exceeded
    Confirmed --> Cancelled: admin_cancel
    Waitlisted --> Reserved: seat_freed
    Cancelled --> [*]
```

**Rules**:
- Reserved has TTL (60min electronic / 48h cash)
- Confirmed only after payment verified
- Waitlist processed FIFO when seat frees

---

### 3.3 Payment State Machine

```mermaid
stateDiagram-v2
    [*] --> Due: create_cash
    [*] --> Pending: create_with_receipt
    
    Pending --> Verified: admin_approve
    Pending --> Rejected: admin_reject
    
    Rejected --> Pending: reupload
    Rejected --> Due: reset_to_due
    
    Due --> Pending: upload_receipt
    Due --> Verified: cash_confirmed
    
    Verified --> [*]
```

**Audit Events**: Every transition creates `PaymentReview` record.

---

### 3.4 Receipt State Machine

```mermaid
stateDiagram-v2
    [*] --> NotProvided: init
    NotProvided --> Uploaded: upload
    Uploaded --> InReview: submit
    InReview --> Accepted: approve
    InReview --> NeedsMoreInfo: request_info
    InReview --> Rejected: reject
    NeedsMoreInfo --> Uploaded: reupload
    Rejected --> Uploaded: reupload
```

---

### 3.5 Attendance Eligibility

```mermaid
stateDiagram-v2
    [*] --> Eligible: payment_confirmed
    Eligible --> BlockedDuePayment: payment_overdue
    Eligible --> BlockedByAdmin: admin_block
    BlockedDuePayment --> EligibleWithWarning: grace_period
    EligibleWithWarning --> Eligible: payment_cleared
    BlockedByAdmin --> Eligible: admin_unblock
```

---

### 3.6 Certificate State Machine

```mermaid
stateDiagram-v2
    [*] --> Locked: init
    Locked --> Processing: eligibility_met
    Processing --> Issued: generate_complete
    Issued --> Revoked: admin_revoke
    Revoked --> [*]
```

**Eligibility Criteria**:
- Payment.status = verified
- Attendance >= 80%
- Course.end_date passed

---

### 3.7 Notification State Machine

```mermaid
stateDiagram-v2
    [*] --> Queued: create
    Queued --> Sent: dispatch
    Sent --> Delivered: confirm
    Sent --> Failed: error
    Failed --> Queued: retry
    Failed --> [*]: max_retries
    Delivered --> [*]
```

**Retry Policy**: Max 3 attempts, exponential backoff.

---

## 4. UX Copy Library

### Payment Status Pages

#### 4.1 Pending Review (جاري التحقق)

| Element | العربية | Français |
|---------|--------|----------|
| Title | جاري مراجعة طلبك | Demande en cours de vérification |
| Description | تم استلام وصل الدفع. سيتم التحقق خلال 24 ساعة. | Reçu reçu. Vérification sous 24h. |
| Next Step | ستصلك إشعار فور التأكيد | Vous serez notifié dès confirmation |
| CTA Primary | العودة للوحة التحكم | Retour au tableau de bord |
| CTA Secondary | التواصل مع الدعم | Contacter le support |
| SLA Note | متوسط المعالجة: 4 ساعات | Délai moyen: 4 heures |

#### 4.2 Success (تمت العملية بنجاح)

| Element | العربية | Français |
|---------|--------|----------|
| Title | 🎉 تم تأكيد تسجيلك | 🎉 Inscription confirmée |
| Description | مقعدك مؤكد في {course}. | Votre place est confirmée dans {course}. |
| Next Step | ستصلك تفاصيل الجدول قريباً | Vous recevrez les détails bientôt |
| CTA Primary | عرض جدولي | Voir mon emploi du temps |
| CTA Secondary | تحميل التأكيد | Télécharger la confirmation |

#### 4.3 Rejected (مرفوض)

| Element | العربية | Français |
|---------|--------|----------|
| Title | ⚠️ الوصل مرفوض | ⚠️ Reçu refusé |
| Description | السبب: {reason} | Motif: {reason} |
| Next Step | يرجى إعادة رفع وصل صحيح | Veuillez re-télécharger un reçu valide |
| CTA Primary | إعادة رفع الوصل | Re-télécharger le reçu |
| CTA Secondary | التواصل مع الدعم | Contacter le support |

### Empty States

| Screen | العربية | Français |
|--------|--------|----------|
| No Courses | لم تسجل في أي دورة بعد. [تصفح الدورات] | Aucune inscription. [Parcourir les cours] |
| No Payments | لا توجد مدفوعات | Aucun paiement |
| No Certificates | لا توجد شهادات حالياً | Aucun certificat pour le moment |
| No Schedule | لا توجد حصص قادمة | Aucun cours prévu |

### Error States

| Error | العربية | Français |
|-------|--------|----------|
| Upload Failed | فشل رفع الملف. حاول مجدداً. | Échec du téléchargement. Réessayez. |
| Session Expired | انتهت مهلة الحجز. يرجى البدء من جديد. | Réservation expirée. Veuillez recommencer. |
| Network Error | اتصال ضعيف. تحقق من الإنترنت. | Connexion faible. Vérifiez votre connexion. |
| Duplicate TRX | هذا الوصل مستخدم مسبقاً | Ce reçu a déjà été utilisé |

---

## 5. Event Tracking Plan

| Event Name | Screen | Properties | KPI |
|------------|--------|------------|-----|
| `course_list_viewed` | Catalog | category, search_query, result_count | CVR-1 |
| `course_details_viewed` | Course Detail | course_id, course_name, seats_available | CVR-2 |
| `registration_started` | Wizard Step 1 | course_id, session_id, source | CVR-3 |
| `step_completed` | Wizard | step_number (1/2/3), course_id | Drop-off |
| `payment_method_selected` | Wizard Step 3 | method (ccp/baridimob/cash) | Method Split |
| `receipt_upload_started` | Wizard Step 3 | course_id, file_type | Upload Start |
| `receipt_upload_completed` | Wizard Step 3 | course_id, file_size, duration_ms | Upload Success |
| `receipt_upload_failed` | Wizard Step 3 | error_type, file_size | Upload Errors |
| `registration_submitted` | Success Page | course_id, payment_method, amount | CVR-3 |
| `payment_review_opened` | Admin Review | payment_id, reviewer_id | Admin Activity |
| `payment_confirmed` | Admin Review | payment_id, reviewer_id, duration_ms | CVR-4 |
| `payment_rejected` | Admin Review | payment_id, reason_code | Rejection Rate |
| `dashboard_viewed` | Student Dashboard | user_id, courses_count, dues_count | Engagement |
| `due_alert_clicked` | Dashboard | payment_id, due_amount | Due Follow-up |
| `attendance_saved` | Attendance Sheet | session_id, present_count, absent_count | Attendance Rate |
| `certificate_downloaded` | Certificates | certificate_id, course_id | Completion Rate |

---

## 6. Security & Abuse Scenarios

### Scenarios & Mitigations

| Scenario | Risk | Mitigation |
|----------|------|------------|
| **IDOR on Receipts** | User A views User B's receipt | Model Policy: `user_id === auth.id || isAdmin` |
| **Duplicate TRX Fraud** | Same receipt used twice | Unique constraint on `transaction_ref` + admin flag |
| **Rate Limit Bypass** | Bot spam registrations | Rate limit: 5 registrations per IP per hour |
| **Fake File Upload** | .php disguised as .jpg | MIME validation + magic number check |
| **Mass Seat Reservation** | Hold seats without paying | TTL on Reserved status (60min) |
| **Payment Status Tampering** | Student changes status | Status column not in `$fillable`, only via methods |
| **Admin Impersonation** | Access admin routes | Middleware: `role === admin`, no role elevation via form |

### Audit Log Structure

```php
[
    'action' => 'payment.verified',
    'causer_id' => 5,  // Admin who did it
    'subject_type' => 'Payment',
    'subject_id' => 123,
    'properties' => [
        'previous_status' => 'pending',
        'new_status' => 'verified',
        'reviewed_at' => '2026-01-07T09:00:00Z'
    ]
]
```

---

## RTL/LTR Considerations

| Element | RTL (Arabic) | LTR (French) |
|---------|--------------|--------------|
| Text Direction | `dir="rtl"` | `dir="ltr"` |
| Arrow Icons | Rotate 180° | Default |
| Progress Bar | Right to Left | Left to Right |
| Number Format | 15,000 DA | 15 000 DA |
| Date Format | 07/01/2026 | 07/01/2026 |
| Phone Placeholder | 05xxxxxxxx | 05xxxxxxxx |

### Font Stack
- **Arabic**: Tajawal, system-ui
- **French**: Inter, system-ui

---

*Document generated from ECOIN v3 PRD specifications.*
