# markatty_api — Standalone Laravel 11 Mobile API

## Purpose

Standalone reimplementation of all `mobikulhttp/v3/` mobile API endpoints from the Bagisto-based Markatty project. Designed to be **performant**, **maintainable**, and **independently deployable** via Docker while connecting to the same MySQL database and Redis instance.

---

## Quick Start

```bash
# Copy env and fill in values
cp .env.example .env

# Start Docker stack
make up

# Run tests
make test

# Lint code
make lint

# Static analysis
make analyze

# Full CI check (lint + analyze + test)
make check
```

---

## Architecture

```
markatty_api/
├── app/
│   ├── Exceptions/          # Single exception handler (no try/catch in controllers)
│   │   ├── Handler.php               — Exception rendering (registered in bootstrap/app.php)
│   │   ├── DomainNotFoundException.php
│   │   ├── ApiAuthenticationException.php
│   │   └── InvalidApiHeaderException.php
│   ├── Http/
│   │   ├── Controllers/     # Thin controllers — delegate to Services only
│   │   │   ├── Catalog/     # HomePage, Settings, Category, Product, Branches, Compare, Rating
│   │   │   ├── Customer/    # Account, Saas, Address, Order, Review, Wishlist, Download
│   │   │   ├── Checkout/    # Cart, Checkout
│   │   │   ├── Extra/       # MobikulController (CMS, Device, Logout, Search, Domain)
│   │   │   ├── Sales/       # SalesController
│   │   │   ├── Contact/     # ContactController
│   │   │   ├── Download/    # DownloadController
│   │   │   ├── Index/       # ProfileController (file uploads)
│   │   │   ├── ProductAlert/# ProductAlertController
│   │   │   ├── Profile/     # EditController
│   │   │   └── HealthController.php
│   │   ├── Middleware/
│   │   │   ├── SetTraceId.php        — UUID per request, injected into log context
│   │   │   ├── ResolveDomain.php     — Resolves domain → channel/branch/locale
│   │   │   ├── ValidateApiHeader.php — Validates api_token header
│   │   │   ├── RequestLogger.php     — Structured JSON access log per request
│   │   │   └── Authenticate.php      — JWT token validation (optional or required)
│   │   └── Requests/        # Form Requests per endpoint group
│   ├── Models/              # Eloquent models — no migrations, read/write existing DB
│   │   Company, Channel, Branch, Locale, Currency, Customer, CustomerAddress,
│   │   Product, ProductFlat, ProductImage, ProductReview, ProductAttributeValue,
│   │   Category, CategoryTranslation, Attribute, AttributeOption, AttributeTranslation,
│   │   AttributeOptionTranslation, BannerImage, BannerImageTranslation,
│   │   Order, OrderItem, OrderPayment, OrderAddress, OrderShipment, OrderShipmentItem,
│   │   OrderInvoice, OrderInvoiceItem, Cart, CartItem, CartAddress, CartPayment,
│   │   Wishlist, CoreConfig, CmsPage, CmsPageTranslation, Country, CountryState
│   ├── Repositories/        # One per aggregate — all DB queries, eager loading enforced
│   │   CatalogRepository, CustomerRepository, CartRepository
│   ├── Services/            # Business logic layer
│   │   DomainDetailsService, AuthService, CatalogService, CustomerService, CartService, ExtraService
│   └── Support/
│       └── ApiResponse.php  — success() and error() static helpers — all controllers use this
├── routes/api.php           # All ~65 endpoints under /mobikulhttp/v3/
├── config/
│   ├── auth.php             # api guard → JWT → Customer model
│   ├── jwt.php              # tymon/jwt-auth config
│   ├── logging.php          # api channel → daily JSON logs
│   └── mobikul.php          # api_token, CDN_URL, cache TTLs
├── openapi.yaml             # OpenAPI 3.0 contract for all response shapes
├── tests/
│   ├── Feature/             # PHPUnit feature tests per group
│   │   ├── Catalog/         # HomePageTest, SettingsTest, …
│   │   ├── Customer/        # LoginTest, …
│   │   ├── Checkout/        # …
│   │   └── HealthTest.php
│   └── Fixtures/            # JSON fixtures matching Postman examples
├── docker/
│   ├── nginx/conf.d/default.conf   — Gzip, static cache headers
│   └── supervisord.conf            — PHP-FPM + Nginx
├── Dockerfile               # PHP 8.2-FPM + Nginx + OPcache
├── docker-compose.yml       # Joins existing markatty_markatty-network, port 8090
├── Makefile                 # make up/down/test/lint/analyze/check/coverage
├── phpstan.neon             # Larastan level 6 config
└── pint.json                # Laravel Pint (PSR-12 + strict_types)
```

---

## Key Design Decisions

### 1. Single Exception Handler
All exceptions bubble from controllers/services to `bootstrap/app.php` `withExceptions()`. 
**Zero try/catch blocks in controllers or services.**
Every error returns:
```json
{
  "success": false,
  "message": "Human-readable message",
  "error_code": "DOMAIN_NOT_FOUND",
  "trace_id": "uuid-per-request"
}
```

### 2. Domain Resolution (critical middleware)
Every v3 request passes through `ResolveDomain` middleware:
- `domain` is **required** — missing domain → 422 `DOMAIN_NOT_FOUND`
- `locale` falls back to channel `default_locale` if not sent
- `branchId` falls back to company's first branch if not sent
- Result cached in Redis for 24 hours: `mobikul:domain_details:{domain}`

### 3. Structured JSON Logging
Every request writes a JSON log entry to `storage/logs/markatty_api-YYYY-MM-DD.log`:
```json
{
  "message": "request",
  "context": {
    "trace_id": "uuid",
    "endpoint": "GET /mobikulhttp/v3/catalog/homepage",
    "domain": "mystore.markatty.com",
    "customer_id": null,
    "duration_ms": 42,
    "status": 200
  }
}
```

### 4. Performance
- All relationships **eager loaded** with `->with([...])`
- `->select([...])` on every query — no `SELECT *`
- Homepage, settings, categories cached in Redis (TTL configurable via `.env`)
- ETag header support for Flutter client caching
- OPcache + PHP preloading enabled in production Docker image
- Nginx gzip compression + proxy cache headers

### 5. JWT Auth
- Uses same `JWT_SECRET` as existing Bagisto project → tokens are cross-compatible
- `AuthService::authenticateOptional()` → returns Customer or null for guests
- `AuthService::authenticate()` → returns Customer or throws `ApiAuthenticationException`
- Guard: `api` → `jwt` driver → `Customer` model

### 6. Data Types Contract
All API Resources must return the **exact same field names and types** as the existing project (verified against Postman collection and Flutter client) to prevent crashes.

---

## Environment Variables

| Variable | Required | Description |
|----------|----------|-------------|
| `APP_KEY` | ✓ | Laravel app key |
| `DB_HOST` | ✓ | MySQL host (existing Bagisto container: `markatty_db`) |
| `DB_DATABASE` | ✓ | Database name |
| `DB_USERNAME` | ✓ | DB username |
| `DB_PASSWORD` | ✓ | DB password |
| `REDIS_HOST` | ✓ | Redis host |
| `JWT_SECRET` | ✓ | **Must match existing project's JWT_SECRET** |
| `MOBIKUL_API_TOKEN` | ✓ | md5(username:password) from Mobikul admin |
| `CDN_URL` | — | CDN base URL for media assets |
| `CACHE_HOMEPAGE_TTL` | — | Homepage cache TTL (default: 3600s) |
| `CACHE_DOMAIN_TTL` | — | Domain cache TTL (default: 86400s) |

---

## Docker

```bash
# Build and start
make up

# Connect to shell
make shell

# View logs
make logs
```

The container joins the external `markatty_markatty-network` Docker network and connects to the existing database and Redis containers.

Port: **8090**

---

## Testing

```bash
# Run all tests
make test

# With coverage (requires Xdebug or PCOV)
make coverage
```

Tests use mocked `DomainDetailsService` and `CatalogService`/`CustomerService` — **no real DB hits** in unit tests.

Test structure mirrors the controller groups:
```
tests/Feature/
├── Catalog/    — HomePageTest, SettingsTest
├── Customer/   — LoginTest
├── Checkout/   — (add CartTest etc.)
└── HealthTest.php
```

---

## Adding a New Endpoint

1. Add route to `routes/api.php`
2. Create Form Request in `app/Http/Requests/{Group}/`
3. Add method to Controller (thin — call Service)
4. Add method to Service (business logic)
5. Add query to Repository (DB access)
6. Add test to `tests/Feature/{Group}/`
7. Update `openapi.yaml`

---

## TODO / Remaining Work

- [ ] Implement full product page data (attributes, options, related products, reviews)
- [ ] Implement layered navigation for category page
- [ ] Implement full cart/checkout flow (shipping methods, payment methods, place order)
- [ ] Implement full address save/update with country/state lookup
- [ ] Add ETag/If-None-Match response caching middleware
- [ ] Add push notification device registration (FCM integration)
- [ ] Social login (Google/Apple)
- [ ] Add test coverage for all endpoints (target: 100% route coverage)
- [ ] Add GitHub Actions CI pipeline using `make check`
- [ ] Review and validate response shapes against Flutter client integration tests
