# CarsBH — API Architecture & Development Structure

**Version:** 1.0  
**Stack:** Laravel 12 · PHP 8.2+ · Sanctum · MySQL  
**Clients:** Admin (Vue.js) · Customer App (Flutter)  
**References:** [CarsBH FSD v1.1](/Users/sigosoftpvtltd/Downloads/CarsBH_FSD_v1.1.docx), [Admin design](https://ourworks.co.in/carshbh-admin-design), [Mobile UI (Figma)](https://www.figma.com/design/rFWzWDeeySmmmNiCokN98J/CarsBh1?node-id=513-1448), [Database Design](./DATABASE_DESIGN.md)  
**Architecture references:** `norbiz-lotto-backend`, `saimpex-backend`, `airotrack-backend`

---

## 1. Executive Summary

CarsBH backend is a **single Laravel API** serving the admin panel (Vue.js) and the customer mobile app (Flutter). It follows the Sigosoft multi-route-file pattern:

- One `routes/api.php` entry point
- Separate route files per audience (`admin`, `customer`)
- Shared services for listings, packages, wallets, chat, and payments
- Consistent JSON response envelope via `ResponseSender`
- Role-based access via `users.role_id` + Sanctum tokens

**Two roles only:** Admin (`role_id = 1`) and Customer (`role_id = 2`). Customers use one account to **buy and sell** — browse, chat, wishlist, create listings, and purchase packages. There is no separate vendor/seller role.

**Currency:** BHD (Bahraini Dinar, 3 decimal places).  
**Locales:** English (`en`) and Arabic (`ar`, RTL on mobile). Admin panel is English-only in v1.0.

---

## 2. High-Level Architecture

```
┌─────────────────────────────────────────────────────────────────────────┐
│                         CLIENT APPLICATIONS                              │
├──────────────────────┬──────────────────────┐
│ Admin Panel (Vue.js) │ Customer App (Flutter)│
│                      │ Buy + sell on one role │
└──────────┬───────────┴──────────┬───────────┘
           │                      │
           └──────────────────────┘
                          │
                 HTTPS  /api/*
                          │
┌─────────────────────────▼───────────────────────────────────────────────┐
│                    Laravel 12 API (carsbh-backend)                       │
│  ┌─────────┐ ┌──────────┐ ┌────────────┐ ┌──────────────────────────┐  │
│  │ Routes  │ │Middleware│ │Controllers │ │ Services                 │  │
│  └────┬────┘ └────┬─────┘ └─────┬──────┘ └────────────┬─────────────┘  │
│       └───────────┴─────────────┴─────────────────────┘                 │
│                          │                                                │
│  ┌───────────────────────▼────────────────────────────────────────────┐  │
│  │ Services: ListingService, PackageService, WalletService,           │  │
│  │ ChatService, OtpService, PaymentGatewayManager, NotificationService  │  │
│  └───────────────────────┬────────────────────────────────────────────┘  │
└──────────────────────────┬──────────────────────────────────────────────┘
                           │
        ┌──────────────────┼──────────────────┐
        │                  │                  │
   MySQL DB           Redis/Cache         Queue Workers
        │                  │                  │
        │            ┌─────▼─────┐    ┌───────▼────────┐
        │            │ OTP TTL   │    │ SendPushJob    │
        │            │ Rate limit│    │ ExpireListings │
        │            │ Search    │    │ ProcessPayment │
        │            └───────────┘    └────────────────┘
        │
   External: Benefit Pay, Tap Payments, Twilio SMS, SendGrid, FCM, Google Maps, AWS S3
```

---

## 3. Project Directory Structure

```
app/
├── Http/
│   ├── Controllers/
│   │   ├── Controller.php                 # Shared base with validation/transaction helpers
│   │   ├── LoginController.php              # Admin auth
│   │   ├── CustomerAuthController.php       # Customer register/login/OTP/social
│   │   ├── GeneralController.php            # Public lookups (country codes, splash, governorates)
│   │   ├── admin/
│   │   │   ├── DashboardController.php
│   │   │   ├── UserController.php
│   │   │   ├── ListingController.php
│   │   │   ├── CategoryController.php
│   │   │   ├── AttributeController.php
│   │   │   ├── PackageController.php
│   │   │   ├── TransactionController.php
│   │   │   ├── ChatController.php
│   │   │   ├── CMSController.php
│   │   │   ├── NotificationController.php
│   │   │   ├── ReportController.php
│   │   │   ├── SettingsController.php
│   │   │   ├── SupportController.php
│   │   │   ├── AdminUserController.php
│   │   │   └── AuditLogController.php
│   │   └── customer/
│   │       ├── HomeController.php
│   │       ├── ListingController.php
│   │       ├── SearchController.php
│   │       ├── ProfileController.php
│   │       ├── WishlistController.php
│   │       ├── ChatController.php
│   │       ├── PackageController.php
│   │       ├── WalletController.php
│   │       ├── NotificationController.php
│   │       └── SupportController.php
│   ├── Middleware/
│   │   ├── EnsureUserRole.php
│   │   ├── EnsureAdminPermission.php
│   │   ├── SetLocale.php
│   │   ├── LogAdminActivity.php
│   │   └── CheckMaintenanceMode.php
│   └── Requests/                            # Optional Form Requests as app grows
├── Models/
│   ├── User.php
│   ├── Role.php, CountryCodes.php, Governorates.php
│   ├── Modules.php, AdminModulePrivileges.php, AdminProfile.php
│   ├── OtpVerification.php, SocialAccount.php, LoginLog.php, AuditLog.php
│   ├── CategoryType.php, Category.php, SubCategory.php
│   ├── Attribute.php, AttributeOption.php
│   ├── Package.php, CustomerWallet.php, Setting.php, General.php
│   ├── service/
│   │   └── ResponseSender.php
│   ├── Admin/                             # Admin query-layer stubs (plural names)
│   └── Customer/                          # Customer query-layer stubs (plural names)
├── Services/
│   ├── MultilingualResponse.php
│   ├── OtpService.php
│   ├── ListingService.php
│   ├── PackageService.php
│   ├── WalletService.php
│   ├── ChatService.php
│   ├── NotificationService.php
│   ├── Payment/
│   │   ├── BenefitPayGateway.php
│   │   └── TapPaymentsGateway.php
│   └── ReportService.php
├── Jobs/
│   ├── SendPushNotificationJob.php
│   ├── ExpireListingsJob.php
│   └── ProcessPaymentWebhookJob.php
└── Support/
    └── AuditAttributes.php

routes/
├── api.php                                  # Entry — requires sub-files
├── admin.php
├── customer.php
├── web.php                                  # Payment redirect pages only
└── console.php                              # Scheduled commands

resources/lang/
├── en/
│   ├── app-success.php                      # Customer-facing messages
│   ├── app-error.php
│   ├── success.php                          # Admin-facing messages
│   ├── error.php
│   └── validation.php
└── ar/
    └── (same structure)

docs/
├── DATABASE_DESIGN.md
└── API_ARCHITECTURE.md
```

---

## 4. Routing Strategy

### 4.1 `bootstrap/app.php`

```php
->withRouting(
    web: __DIR__.'/../routes/web.php',
    api: __DIR__.'/../routes/api.php',
    commands: __DIR__.'/../routes/console.php',
    health: '/up',
)
->withMiddleware(function (Middleware $middleware): void {
    $middleware->alias([
        'role' => \App\Http\Middleware\EnsureUserRole::class,
        'admin.permission' => \App\Http\Middleware\EnsureAdminPermission::class,
        'locale' => \App\Http\Middleware\SetLocale::class,
        'admin.log' => \App\Http\Middleware\LogAdminActivity::class,
        'maintenance' => \App\Http\Middleware\CheckMaintenanceMode::class,
    ]);
});
```

### 4.2 `routes/api.php`

```php
<?php

use Illuminate\Support\Facades\Route;

require base_path('routes/customer.php');
require base_path('routes/admin.php');
```

### 4.3 Route Prefixes & Auth

| File | Prefix | Auth | Role |
|------|--------|------|------|
| `customer.php` | `customer/` | Sanctum on protected routes | `role_id = 2` |
| `admin.php` | `admin/` | Sanctum on protected routes | `role_id = 1` |

**Base URL:** `https://api.carsbh.bh/api/` (configure per environment)

### 4.4 Route Conventions

- **Kebab-case** paths: `forgot-password/send-otp`, `listing-detail`
- **POST for mutations** even when REST might use PUT/PATCH: `post('update-listing')`, `post('add-banner')`
- **Query params for detail** where appropriate: `GET admin/user-detail?user_id=`, `GET customer/listings/show?listing_id=`
- **Public browse endpoints** under `customer/` without auth middleware (home, search, listing detail, categories)
- **No URL versioning** (`/v1/`) in Phase 1 — audience prefix separates clients. App force-update uses `general_settings` version keys.

---

## 5. Standard API Response Format

Following `norbiz-lotto-backend` / `ResponseSender` pattern.

**Rule:** `message` is **always** a bilingual object — never a plain string. Every response includes `en` and `ar` keys. Clients pick the string for the active locale.

Supported locale suffixes: `_en`, `_ar`.

### Success

```json
{
  "status": "true",
  "data": { },
  "message": {
    "message_en": ["Listing published successfully"],
    "message_ar": ["تم نشر الإعلان بنجاح"]
  }
}
```

### Validation / field error

```json
{
  "status": "false",
  "data": [],
  "message": {
    "mobile_en": ["Phone number is required"],
    "mobile_ar": ["رقم الهاتف مطلوب"]
  }
}
```

### Business / auth error

```json
{
  "status": "false",
  "data": [],
  "message": {
    "message_en": ["Insufficient wallet balance"],
    "message_ar": ["رصيد المحفظة غير كافٍ"]
  }
}
```

### HTTP Status Codes

| Code | Usage |
|------|-------|
| `200` | Success |
| `401` | Unauthenticated / invalid token |
| `403` | Wrong role, blocked user, or insufficient admin permission |
| `422` | Validation / business rule failure |
| `429` | OTP or rate limit exceeded |
| `503` | Maintenance mode enabled |

### `status` field

String `"true"` / `"false"` (not boolean) — consistent with existing Sigosoft client libraries.

### Pagination envelope

List endpoints return paginated data inside `data`:

```json
{
  "status": "true",
  "data": {
    "items": [ ],
    "pagination": {
      "current_page": 1,
      "per_page": 20,
      "total": 150,
      "last_page": 8
    }
  },
  "message": {
    "message_en": ["Success"],
    "message_ar": ["نجاح"]
  }
}
```

---

## 6. Authentication

### 6.1 Role Model

| `role_id` | Role | Clients |
|-----------|------|---------|
| `1` | Admin | Vue.js admin panel |
| `2` | Customer | Flutter mobile app (buy and sell) |

Both roles authenticate against the single `users` table. Authorization is enforced via `EnsureUserRole` middleware (`role:1`, `role:2`).

### 6.2 Admin (`role_id = 1`)

| Endpoint | Method | Auth | Description |
|----------|--------|------|-------------|
| `admin/login` | POST | Public | Email/username + password |
| `admin/verify-totp` | POST | Public | TOTP code after password step |
| `admin/logout` | POST | Sanctum | Revoke token |
| `admin/me` | GET | Sanctum | Current admin + module privileges |

**Login flow:**
1. `POST admin/login` with `email` (or `username`) + `password`
2. If 2FA enabled → return `requires_totp: true` + temporary session token
3. `POST admin/verify-totp` with `totp_code` → issue Sanctum token
4. Return: user details, `admin_role`, `admin_module_privileges`, Sanctum token

**Security:**
- TOTP (Google Authenticator) mandatory for all admin accounts
- Session expires after 4 hours of inactivity
- 5 failed login attempts → 30-minute account lock
- All admin mutations logged to `audit_logs`

**Payload (login):**
```json
{
  "email": "admin@carsbh.bh",
  "password": "********"
}
```

**Payload (TOTP):**
```json
{
  "totp_code": "123456",
  "temp_token": "..."
}
```

### 6.3 Customer (`role_id = 2`)

| Endpoint | Method | Auth | Description |
|----------|--------|------|-------------|
| `customer/send-otp` | POST | Public | Send OTP to phone |
| `customer/verify-otp` | POST | Public | Verify OTP → login/register |
| `customer/register` | POST | Public | Full registration with password |
| `customer/login` | POST | Public | Phone/email + password |
| `customer/forgot-password/send-otp` | POST | Public | Password reset OTP |
| `customer/forgot-password/verify-otp` | POST | Public | Verify reset OTP |
| `customer/forgot-password/reset` | POST | Public | Set new password |
| `customer/social-login` | POST | Public | Apple / Google OAuth |
| `customer/refresh-token` | POST | Public | Issue new access token |
| `customer/logout` | POST | Sanctum | Revoke tokens |

**OTP flow:**
1. `POST customer/send-otp` with `country_code_id` + `mobile`
2. 6-digit OTP sent via SMS (Twilio/Unifonic), 120s expiry
3. `POST customer/verify-otp` with `mobile` + `otp` → create user if new, issue tokens
4. 3 consecutive failures → 15-minute lock (`users.otp_locked_until`)
5. Resend available after 60 seconds; max 3 requests per 10 minutes per phone

**Social login:**
- `POST customer/social-login` with `provider` (`apple`/`google`), `id_token`, optional `full_name`
- Maps to existing account by email or creates new account

**Token response:**
```json
{
  "status": "true",
  "data": {
    "access_token": "1|...",
    "refresh_token": "...",
    "expires_in": 86400,
    "user": {
      "id": 1,
      "name": "Ahmed Ali",
      "mobile": "33123456",
      "email": null,
      "preferred_locale": "ar",
      "wallet_balance_bhd": "25.000"
    }
  },
  "message": { "message_en": ["Login successful"], "message_ar": ["تم تسجيل الدخول بنجاح"] }
}
```

---

## 7. Middleware Stack

| Middleware | Applied to | Purpose |
|------------|-----------|---------|
| `auth:sanctum` | Protected routes | Token validation |
| `role:1` | Admin routes | Admin only |
| `role:2` | Customer protected routes | Customer only |
| `admin.permission:{module}` | Admin mutations | Check `admin_module_privileges` |
| `locale` | All API | Set locale from `Accept-Language` or `?lang=` |
| `throttle:otp` | OTP endpoints | 3 per 10 min per phone |
| `throttle:api` | All API | 100 req/min per IP on auth |
| `admin.log` | Admin mutations | Write `audit_logs` |
| `maintenance` | Customer routes | Return 503 if maintenance mode on |

---

## 8. API Modules — Endpoint Map

### 8.1 Public / Customer Browse — `routes/customer.php` (no auth)

Prefix: `customer/`. No authentication required.

| Group | Endpoint | Method | Description |
|-------|----------|--------|-------------|
| **General** | `customer/country-codes` | GET | Dial codes list |
| | `customer/governorates` | GET | Bahrain governorates |
| | `customer/splash-screens` | GET | Onboarding splash config |
| | `customer/app-version` | GET | Min iOS/Android version for force-update |
| | `customer/maintenance-status` | GET | Maintenance mode check |
| **Home** | `customer/home` | GET | Slider ads, categories, featured, latest listings |
| **Categories** | `customer/category-types` | GET | All active category types |
| | `customer/categories` | GET | Categories with sub-categories (`?category_type_id=`) |
| | `customer/categories/{id}/attributes` | GET | Attributes + options for listing form |
| | `customer/attribute-options` | GET | Filtered options (`?attribute_id=&parent_option_id=`) |
| **Listings** | `customer/listings` | GET | Paginated browse + filters |
| | `customer/listings/show` | GET | Full detail (`?listing_id=`) |
| | `customer/listings/featured` | GET | Active featured listings (max 10) |
| | `customer/listings/slider` | GET | Active slider ad listings (max 5) |
| | `customer/sellers/show` | GET | Public seller profile (`?user_id=`) |
| | `customer/sellers/listings` | GET | Seller's active listings |
| | `customer/sellers/reviews` | GET | Seller reviews |
| **CMS** | `customer/cms/{slug}` | GET | Static pages: about, terms, privacy, contact |
| | `customer/faqs` | GET | FAQ list |
| | `customer/announcements` | GET | Active platform announcements |
| **Packages** | `customer/packages` | GET | Active packages (`?type=regular\|featured\|slider`) |

---

### 8.2 Customer Authenticated — `routes/customer.php`

Prefix: `customer/`. Auth: Sanctum + `role:2`.

| Group | Endpoint | Method | Description |
|-------|----------|--------|-------------|
| **Profile** | `customer/profile` | GET | Full user profile |
| | `customer/update-profile` | POST | Edit name, email, photo |
| | `customer/change-phone/send-otp` | POST | OTP for phone change |
| | `customer/change-phone/verify` | POST | Confirm new phone |
| | `customer/update-fcm-token` | POST | Update push token |
| | `customer/update-settings` | POST | Notification prefs, locale |
| | `customer/delete-account` | POST | Account deletion request |
| **Listings (Sell)** | `customer/listings/create` | POST | Create listing (multipart) |
| | `customer/listings/update` | POST | Edit own listing |
| | `customer/listings/delete` | POST | Soft delete own listing |
| | `customer/listings/mark-sold` | POST | Mark as sold |
| | `customer/my-listings` | GET | Active/sold tabs (`?status=`) |
| | `customer/listing-drafts/save` | POST | Auto-save sell flow |
| | `customer/listing-drafts/get` | GET | Resume draft |
| | `customer/listings/report` | POST | Report a listing |
| **Wishlist** | `customer/wishlist` | GET | Saved listings |
| | `customer/wishlist/add` | POST | Save listing (`listing_id`) |
| | `customer/wishlist/remove` | POST | Remove listing |
| **Chat** | `customer/conversations` | GET | Chat inbox |
| | `customer/conversations/create` | POST | Start conversation (`listing_id`, `message`) |
| | `customer/conversations/messages` | GET | Thread messages (`?conversation_id=`, cursor) |
| | `customer/conversations/send-message` | POST | Send message |
| | `customer/conversations/mark-read` | POST | Mark thread read |
| | `customer/conversations/block` | POST | Block conversation |
| **Packages** | `customer/packages/purchase` | POST | Buy package (`package_id`, `listing_id?`) |
| | `customer/my-subscriptions` | GET | Active/historical subscriptions |
| | `customer/subscription-history` | GET | Purchase history |
| **Wallet** | `customer/wallet/balance` | GET | Current balance |
| | `customer/wallet/topup` | POST | Initiate top-up (`amount_bhd`, `payment_method`) |
| | `customer/wallet/history` | GET | Transaction ledger |
| | `customer/wallet/payment-callback` | POST | Gateway webhook/callback |
| **Notifications** | `customer/notifications` | GET | Notification list |
| | `customer/notifications/mark-read` | POST | Mark one/all read |
| | `customer/notifications/unread-count` | GET | Badge count |
| **Reviews** | `customer/reviews/submit` | POST | Rate listing owner (`seller_id`, `rating`, `comment?`) |
| **Support** | `customer/support/tickets` | GET | My support tickets |
| | `customer/support/create-ticket` | POST | Submit ticket |
| | `customer/support/reply` | POST | Reply to ticket |

---

### 8.3 Admin — `routes/admin.php`

Prefix: `admin/`. Auth: Sanctum + `role:1` on protected routes.

#### Public

| Endpoint | Method | Description |
|----------|--------|-------------|
| `admin/login` | POST | Email + password |
| `admin/verify-totp` | POST | TOTP verification |
| `admin/forgot-password` | POST | Admin password reset email |

#### Dashboard

| Endpoint | Method | Permission | Description |
|----------|--------|------------|-------------|
| `admin/dashboard` | GET | dashboard | KPI cards, charts, quick actions, alerts |
| `admin/dashboard/recent-activity` | GET | dashboard | Last 20 platform events |

#### Users

| Endpoint | Method | Permission | Description |
|----------|--------|------------|-------------|
| `admin/users` | GET | users | Paginated user list + filters |
| `admin/user-detail` | GET | users | Full per-user profile (tabs data) |
| `admin/user-listings` | GET | users | User's listings by status |
| `admin/user-conversations` | GET | users | User's chat history (read-only) |
| `admin/user-wishlist` | GET | users | User's saved listings |
| `admin/user-subscriptions` | GET | users | Package purchase history |
| `admin/user-wallet-transactions` | GET | users | Wallet ledger |
| `admin/user-reviews` | GET | users | Reviews received/given |
| `admin/user-reports` | GET | users | Reports against user |
| `admin/user-login-history` | GET | users | Last 20 logins |
| `admin/suspend-user` | POST | users | Suspend with reason |
| `admin/ban-user` | POST | users | Ban with reason |
| `admin/unban-user` | POST | users | Unban with reason |
| `admin/wallet-adjustment` | POST | users | Credit/debit wallet |
| `admin/send-user-notification` | POST | users | Direct push to user |
| `admin/reported-users` | GET | users | Reported users queue |
| `admin/banned-users` | GET | users | Banned users list |
| `admin/top-sellers` | GET | users | Ranked sellers report |
| `admin/active-subscriptions` | GET | users | Users with active packages |
| `admin/export-users` | GET | users | CSV export |

#### Listings

| Endpoint | Method | Permission | Description |
|----------|--------|------------|-------------|
| `admin/listings` | GET | listings | All listings + status tabs |
| `admin/listing-detail` | GET | listings | Full listing + moderation history |
| `admin/approve-listing` | POST | listings | Approve pending listing |
| `admin/reject-listing` | POST | listings | Reject with reason |
| `admin/edit-listing` | POST | listings | Admin edit any field |
| `admin/delete-listing` | POST | listings | Soft delete with reason |
| `admin/feature-listing` | POST | listings | Manual featured (bypass payment) |
| `admin/slider-listing` | POST | listings | Manual slider ad |
| `admin/revoke-featured` | POST | listings | Remove featured status |
| `admin/revoke-slider` | POST | listings | Remove slider ad |
| `admin/reorder-slider` | POST | listings | Drag-reorder slider positions |
| `admin/featured-listings` | GET | listings | All featured listings |
| `admin/slider-listings` | GET | listings | All slider ads |
| `admin/reported-listings` | GET | listings | Reported listings queue |
| `admin/expiring-listings` | GET | listings | Expiring in 7 days |
| `admin/bulk-listing-action` | POST | listings | Bulk approve/reject/delete |
| `admin/send-renewal-reminder` | POST | listings | Batch expiry reminder push |
| `admin/export-listings` | GET | listings | CSV export |

#### Categories & Attributes

| Endpoint | Method | Permission | Description |
|----------|--------|------------|-------------|
| `admin/category-types` | GET/POST | categories | List / add category types |
| `admin/update-category-type` | POST | categories | Edit category type |
| `admin/categories` | GET/POST | categories | List / add categories |
| `admin/update-category` | POST | categories | Edit category |
| `admin/sub-categories` | GET/POST | categories | List / add sub-categories |
| `admin/update-sub-category` | POST | categories | Edit sub-category |
| `admin/attributes` | GET/POST | categories | List / add attributes per category |
| `admin/update-attribute` | POST | categories | Edit attribute |
| `admin/attribute-options` | GET/POST | categories | List / add options |
| `admin/update-attribute-option` | POST | categories | Edit option (value, image, parent) |
| `admin/bulk-import-options` | POST | categories | CSV bulk import |

#### Packages

| Endpoint | Method | Permission | Description |
|----------|--------|------------|-------------|
| `admin/packages` | GET/POST | packages | List / create packages |
| `admin/update-package` | POST | packages | Edit package |
| `admin/deactivate-package` | POST | packages | Deactivate package |
| `admin/subscriptions` | GET | packages | All active subscriptions |
| `admin/expired-subscriptions` | GET | packages | Expired subscriptions |
| `admin/grant-subscription` | POST | packages | Manual grant (zero-value) |
| `admin/package-analytics` | GET | packages | Best-selling, revenue split |

#### Transactions

| Endpoint | Method | Permission | Description |
|----------|--------|------------|-------------|
| `admin/transactions` | GET | transactions | Full transaction ledger |
| `admin/transaction-detail` | GET | transactions | Transaction detail view |
| `admin/gateway-logs` | GET | transactions | Payment gateway logs |
| `admin/refund-requests` | GET | transactions | Refund queue |
| `admin/approve-refund` | POST | transactions | Approve refund |
| `admin/reject-refund` | POST | transactions | Reject refund |
| `admin/resend-invoice` | POST | transactions | Re-send invoice email |
| `admin/earnings-report` | GET | transactions | Aggregated earnings |

#### Chat

| Endpoint | Method | Permission | Description |
|----------|--------|------------|-------------|
| `admin/conversations` | GET | chat | All conversations |
| `admin/conversation-detail` | GET | chat | Read-only thread view |
| `admin/reported-conversations` | GET | chat | Flagged conversations |
| `admin/block-conversation` | POST | chat | Block thread |
| `admin/chat-analytics` | GET | chat | Chat stats |

#### CMS

| Endpoint | Method | Permission | Description |
|----------|--------|------------|-------------|
| `admin/banners` | GET/POST | cms | Category banners CRUD |
| `admin/update-banner` | POST | cms | Edit banner |
| `admin/sliders` | GET/POST | cms | Homepage sliders CRUD |
| `admin/update-slider` | POST | cms | Edit slider |
| `admin/cms-pages` | GET/POST | cms | Static pages |
| `admin/update-cms-page` | POST | cms | Edit + publish page |
| `admin/faqs` | GET/POST | cms | FAQ CRUD |
| `admin/update-faq` | POST | cms | Edit FAQ |
| `admin/announcements` | GET/POST | cms | Platform announcements |

#### Notifications (Admin Broadcast)

| Endpoint | Method | Permission | Description |
|----------|--------|------------|-------------|
| `admin/notifications/send-push` | POST | cms | Compose FCM push |
| `admin/notifications/send-email` | POST | cms | Compose email |
| `admin/notifications/send-sms` | POST | cms | Compose SMS |
| `admin/notifications/history` | GET | cms | Sent notification log |
| `admin/notifications/schedule` | POST | cms | Schedule future send |

#### Reports

| Endpoint | Method | Permission | Description |
|----------|--------|------------|-------------|
| `admin/reports/ads-performance` | GET | reports | Views, inquiries per listing |
| `admin/reports/user-activity` | GET | reports | DAU, MAU, registrations |
| `admin/reports/top-sellers` | GET | reports | Ranked sellers |
| `admin/reports/package-sales` | GET | reports | Package sales trends |
| `admin/reports/financial-summary` | GET | reports | Revenue breakdown |
| `admin/reports/listings-by-governorate` | GET | reports | Governorate distribution |
| `admin/reports/export` | GET | reports | CSV/XLSX/PDF export |

#### System Settings

| Endpoint | Method | Permission | Description |
|----------|--------|------------|-------------|
| `admin/settings/general` | GET/POST | settings | Site name, logo, maintenance |
| `admin/settings/regional` | GET/POST | settings | Languages, currency, governorates |
| `admin/settings/payment` | GET/POST | settings | Gateway keys, min top-up |
| `admin/settings/security` | GET/POST | settings | 2FA, rate limits, IP blocklist |
| `admin/settings/integrations` | GET/POST | settings | FCM, Twilio, SendGrid, Maps |
| `admin/settings/integration-health` | GET | settings | Integration status widget |
| `admin/ip-blocklist` | GET/POST | settings | Manage blocked IPs |
| `admin/logout` | POST | — | Revoke token |

#### Support

| Endpoint | Method | Permission | Description |
|----------|--------|------------|-------------|
| `admin/support/tickets` | GET | support | Support ticket queue |
| `admin/support/ticket-detail` | GET | support | Ticket thread |
| `admin/support/reply` | POST | support | Admin reply |
| `admin/support/update-status` | POST | support | Change ticket status |
| `admin/support/bug-reports` | GET | support | Bug reports queue |

#### Admin Users

| Endpoint | Method | Permission | Description |
|----------|--------|------------|-------------|
| `admin/admin-users` | GET/POST | settings | List / create admin accounts |
| `admin/update-admin-user` | POST | settings | Edit role, deactivate |
| `admin/audit-logs` | GET | settings | Admin action audit trail |

---

## 9. Key Request/Response Examples

### 9.1 Customer Home

**`GET /api/customer/home`**

Query: `?lang=ar`

```json
{
  "status": "true",
  "data": {
    "slider_ads": [
      {
        "listing_id": 42,
        "image_url": "https://cdn.carsbh.bh/...",
        "make": "Toyota",
        "model": "Camry",
        "year": 2022,
        "price_bhd": "4500.000",
        "governorate": { "name_en": "Capital Governorate", "name_ar": "محافظة العاصمة" }
      }
    ],
    "category_types": [
      { "id": 1, "name_en": "Vehicles", "name_ar": "مركبات", "icon_url": "..." }
    ],
    "featured_listings": [ ],
    "latest_listings": [ ]
  },
  "message": { "message_en": ["Success"], "message_ar": ["نجاح"] }
}
```

### 9.2 Listing Search

**`GET /api/customer/listings`**

Query params:

| Param | Type | Description |
|-------|------|-------------|
| `keyword` | string | Min 2 chars |
| `category_id` | int | |
| `sub_category_id` | int | |
| `make` | string | |
| `model` | string | |
| `year_min` / `year_max` | int | |
| `price_min` / `price_max` | decimal | BHD |
| `mileage_min` / `mileage_max` | int | km |
| `governorate_id` | int | |
| `condition` | int | `1-new, 2-used` |
| `transmission` | int | |
| `fuel_type` | int | |
| `sort` | string | `newest`, `price_asc`, `price_desc`, `popular` |
| `page` | int | Default 1 |
| `per_page` | int | Default 20, max 50 |

### 9.3 Create Listing

**`POST /api/customer/listings/create`**

Content-Type: `multipart/form-data`

| Field | Required | Notes |
|-------|----------|-------|
| `category_id` | Yes | |
| `sub_category_id` | No | |
| `make` | Yes | From attribute options |
| `model` | Yes | Dependent on make |
| `year` | Yes | 1970 – current+1 |
| `mileage_km` | Yes | |
| `price_bhd` | Yes | Min 1.000 |
| `condition` | Yes | `1-new, 2-used` |
| `colour` | Yes | |
| `transmission` | Yes | |
| `fuel_type` | Yes | |
| `description` | Yes | 20–2000 chars |
| `governorate_id` | Yes | |
| `latitude` | No | |
| `longitude` | No | |
| `photos[]` | Yes | Min 3, max 20 images |
| `video` | No | Max 60s, 50MB, MP4 |
| `attributes` | No | JSON array of `{attribute_id, option_id?, text_value?}` |

### 9.4 Package Purchase

**`POST /api/customer/packages/purchase`**

```json
{
  "package_id": 3,
  "listing_id": 42
}
```

Business rules:
- Deduct `price_bhd` from wallet
- If insufficient balance → return 422 with `insufficient_balance: true` and `topup_needed_bhd`
- Create `user_subscriptions` row with snapshots
- For featured/slider: set `listings.is_featured` / `is_slider_ad` + expiry
- For regular: increment `ads_used` on active subscription or create new

### 9.5 Admin User Detail

**`GET /api/admin/user-detail?user_id=15`**

Returns aggregated data for all tabs:

```json
{
  "status": "true",
  "data": {
    "profile": { },
    "listings_summary": { "active": 3, "sold": 1, "rejected": 0 },
    "wallet": { "balance_bhd": "150.000" },
    "active_subscriptions": [ ],
    "report_count": 0,
    "average_rating": "4.50"
  },
  "message": { "message_en": ["Success"], "message_ar": ["نجاح"] }
}
```

Tab data loaded via separate endpoints (`user-listings`, `user-conversations`, etc.) for performance.

---

## 10. Service Layer

| Service | Responsibility |
|---------|----------------|
| `OtpService` | Generate, hash, verify OTP; rate limiting; lockout |
| `ListingService` | CRUD, publish workflow, moderation, expiry, view count |
| `PackageService` | Purchase, subscription management, featured/slider activation |
| `WalletService` | Credit/debit with ledger; row locking; balance checks |
| `ChatService` | Conversations, messages, read receipts, block |
| `NotificationService` | FCM push, email (SendGrid), SMS (Twilio) |
| `PaymentGatewayManager` | Benefit Pay + Tap Payments integration |
| `CategoryService` | Category tree, dynamic attributes |
| `ReportService` | Dashboard KPIs, analytics queries |
| `MultilingualResponse` | Build bilingual message objects from lang files |

### Wallet debit pattern (from norbiz-lotto)

```php
// Always paired: balance update + wallet_transactions row
WalletService::debit($user, $amount, $type, $subscriptionId);
// Creates row with balance_before, balance_after
// Uses SELECT ... FOR UPDATE on customer_wallets
```

### Audit columns on create/update

All main mutable models use Laravel `SoftDeletes` plus `added_by` / `updated_by` (FK → `users.id`). Base `Controller` helpers (aligned with norbiz-lotto):

```php
// On create
$data = $this->withAuditCreate($validated);

// On update
$data = $this->withAuditUpdate($validated);

// Soft delete — never hard-delete business rows
$listing->delete(); // sets deleted_at; preserve row for audit
```

Set from `auth('sanctum')->id()` on every authenticated write. Admin actions also write to `audit_logs` via `admin.log` middleware.

**Exceptions:** `wallet_transactions` (immutable, `added_by` only), `login_logs`, `audit_logs`, `listing_views`, `messages`, `otp_verifications` — see [DATABASE_DESIGN.md §1.1](./DATABASE_DESIGN.md#11-standard-audit-columns).

---

## 11. Real-Time Chat

**Phase 1 approach:** Polling + FCM push (no WebSocket dependency).

| Event | Mechanism |
|-------|-----------|
| New message sent | Store in `messages`, update `conversations.last_message_at`, increment unread count |
| Recipient offline | FCM push via `SendPushNotificationJob` |
| Read receipt | `POST customer/conversations/mark-read` sets `messages.read_at` |
| Admin moderation | Read-only via admin endpoints; no admin send |

**Future:** Laravel Reverb / Socket.io for sub-500ms delivery.

---

## 12. File Uploads

| Type | Rules | Storage |
|------|-------|---------|
| Listing photos | JPEG/PNG/WebP, max 5MB each, 3–20 per listing | S3/DO Spaces → CDN |
| Listing video | MP4, max 50MB, max 60s | S3/DO Spaces → CDN |
| Profile photo | JPEG/PNG, max 2MB | S3 → CDN |
| Brand logos | PNG/SVG, max 500KB | S3 → CDN |
| CMS images | JPEG/PNG, max 5MB | S3 → CDN |

**Flow:**
1. Client uploads via `multipart/form-data` to listing create/update endpoint
2. Server validates MIME, scans for malware (ClamAV)
3. Store to S3 with path: `{env}/listings/{listing_id}/{uuid}.{ext}`
4. Return CDN URL stored in `listing_media.url`

**Alternative:** Pre-signed URL upload for large videos (future optimization).

---

## 13. Scheduled Commands

Registered in `routes/console.php`:

| Command | Schedule | Description |
|---------|----------|-------------|
| `listings:expire` | Daily 00:05 Asia/Bahrain | Expire listings past `expires_at` |
| `featured:expire` | Hourly | Clear expired featured status |
| `slider:expire` | Hourly | Clear expired slider ads |
| `subscriptions:expire` | Daily 00:10 | Mark subscriptions expired; send push |
| `notifications:send-scheduled` | Every minute | Process scheduled campaigns |
| `drafts:cleanup` | Weekly | Remove old listing drafts |
| `otp:cleanup` | Daily | Purge expired OTP records |

---

## 14. Environment Variables

| Variable | Description |
|----------|-------------|
| `APP_NAME` | `CarsBH` |
| `APP_URL` | API base URL |
| `APP_TIMEZONE` | `Asia/Bahrain` |
| `DB_*` | MySQL connection |
| `REDIS_*` | Cache, OTP TTL, rate limiting |
| `AWS_*` | S3 file storage |
| `FCM_SERVER_KEY` | Firebase push |
| `TWILIO_SID` / `TWILIO_TOKEN` | SMS OTP |
| `SENDGRID_API_KEY` | Transactional email |
| `BENEFIT_PAY_*` | Benefit Pay gateway |
| `TAP_PAYMENTS_*` | Tap Payments gateway |
| `GOOGLE_MAPS_API_KEY` | Geolocation |
| `SANCTUM_TOKEN_EXPIRATION` | Access token TTL (minutes) |

Remove orphaned lottery keys from `.env.example` (`LOTTERY_RESULTS_*`) — they are not applicable to CarsBH.

---

## 15. Error Codes & Business Rules

| Rule | Enforcement |
|------|-------------|
| OTP expires in 120s | `otp_verifications.expires_at` |
| 3 OTP failures → 15min lock | `users.otp_locked_until` |
| Min 3 photos to publish | Validation in `ListingService` |
| Description 20–2000 chars | Validator |
| Guest cannot chat/wishlist/sell | Middleware checks `users.is_guest` |
| Suspended user cannot login | `users.status = 2` check on auth |
| Banned user cannot login | `users.status = 3` |
| Listing moderation | New listings → `status = 4` (pending_review) if moderation enabled |
| Wallet insufficient balance | `WalletService` throws business error before debit |
| Admin TOTP required | `admin_profiles.two_factor_enabled = 1` |
| Finance role cannot edit listings | `EnsureAdminPermission` middleware |

---

## 16. Logging

Audience-specific log channels in `config/logging.php`:

```php
'admin' => [
    'driver' => 'daily',
    'path' => storage_path('logs/admin/error.log'),
],
'customer' => [
    'driver' => 'daily',
    'path' => storage_path('logs/customer/error.log'),
],
```

Usage: `Log::channel('admin')->error(...)`, `Log::channel('customer')->error(...)`.

---

## 17. Testing Strategy

| Type | Scope |
|------|-------|
| **Unit** | `WalletService`, `OtpService`, `PackageService`, attribute cascade filtering |
| **Feature** | Auth flows, listing create-to-publish, package purchase, wallet top-up |
| **Integration** | Payment gateway callbacks, FCM push delivery |
| **Admin** | Role-based access (Moderator blocked from Transactions) |

Critical test cases from FSD:
- Valid OTP → session created
- 3 wrong OTPs → 15-min lock
- Create listing with < 3 photos → validation error
- Featured purchase with insufficient wallet → blocked with top-up amount
- Brand selected → model dropdown filtered by `parent_option_id`
- Admin suspend user → login blocked, listings hidden
- Featured expiry → `is_featured` cleared automatically

---

## 18. Implementation Phases

### Phase 1 (MVP)

1. Project scaffold (ResponseSender, middleware, route files, base Controller)
2. Auth: admin login + TOTP, customer OTP + password + social
3. Categories, attributes, governorates (seed + admin CRUD)
4. Listings: create, browse, search, detail, moderation
5. Packages + wallet + payment gateway (Benefit Pay primary)
6. Chat (polling + FCM)
7. Wishlist, reviews, notifications
8. Admin: all 11 modules
9. CMS pages, FAQs, announcements
10. Scheduled jobs (expiry, cleanup)

### Phase 2 (Future)

- WebSocket real-time chat
- Pre-signed URL uploads
- API versioning (`/v1/`) if third-party consumers
- Laravel Scribe / OpenAPI auto-docs

---

## 19. Quick Reference — Key Files to Create

| Purpose | Path |
|---------|------|
| Route entry | `routes/api.php` |
| Admin routes | `routes/admin.php` |
| Customer routes | `routes/customer.php` |
| Role middleware | `app/Http/Middleware/EnsureUserRole.php` |
| Admin permission middleware | `app/Http/Middleware/EnsureAdminPermission.php` |
| Response envelope | `app/Models/service/ResponseSender.php` |
| Bilingual messages | `app/Services/MultilingualResponse.php` |
| Base controller | `app/Http/Controllers/Controller.php` |
| Admin auth | `app/Http/Controllers/LoginController.php` |
| Customer auth | `app/Http/Controllers/CustomerAuthController.php` |
| Database doc | `docs/DATABASE_DESIGN.md` |
| API doc | `docs/API_ARCHITECTURE.md` |

---

*This document is version-controlled. Changes require sign-off from the Project Lead before implementation on affected modules.*
