# Search Engine

# Search API

**Author**: Josh S. Sakweli, Backend Lead Team  
**Last Updated**: 2025-12-23  
**Version**: v1.0

**Base URL**: `https://api.nextgate.com/api/v1`

**Short Description**: Public search endpoints for discovering users and shops on the Nexgate platform. These endpoints power the @mention and $shop autocomplete features across the application.

**Hints**:

- Use `@` prefix for user search (automatically stripped)
- Use `$` prefix for shop search (automatically stripped)
- Results are ordered by relevance: exact match → starts with → contains
- All endpoints are public and do not require authentication
- Rate limiting applies to prevent abuse

---

## Standard Response Format

All API responses follow a consistent structure using our Globe Response Builder pattern:

### Success Response Structure

```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Operation completed successfully",
  "action_time": "2025-12-23T10:30:45",
  "data": {
    // Actual response data goes here
  }
}

```

### Error Response Structure

```json
{
  "success": false,
  "httpStatus": "BAD_REQUEST",
  "message": "Error description",
  "action_time": "2025-12-23T10:30:45",
  "data": "Error description"
}

```

---

## Endpoints

## 1. Search Users

**Purpose**: Search for users by username, first name, or last name for @mentions and user discovery.

**Endpoint**: <span style="background-color: #28a745; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">GET</span> `{base_url}/users/search`

**Access Level**: 🌐 Public (Rate limited)

**Authentication**: None

**Query Parameters**:

<table id="bkmrk-parameter-type-requi"><thead><tr><th>Parameter</th><th>Type</th><th>Required</th><th>Description</th><th>Validation</th><th>Default</th></tr></thead><tbody><tr><td>q</td><td>string</td><td>No</td><td>Search query (username, first name, or last name). `@` prefix is automatically removed</td><td>Min: 1 character after trimming</td><td>-</td></tr><tr><td>page</td><td>integer</td><td>No</td><td>Page number (1-based)</td><td>Min: 1</td><td>1</td></tr><tr><td>size</td><td>integer</td><td>No</td><td>Number of results per page</td><td>Min: 1, Max: 50</td><td>10</td></tr></tbody></table>

**Success Response JSON Sample**:

```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Users retrieved successfully",
  "action_time": "2025-12-23T11:14:05.293524449",
  "data": {
    "content": [
      {
        "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
        "userName": "john_doe",
        "displayName": "John Doe",
        "avatarUrl": "https://cdn.nextgate.com/avatars/john_doe.jpg",
        "isVerified": true
      },
      {
        "id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
        "userName": "johnny_b",
        "displayName": "Johnny Brown",
        "avatarUrl": null,
        "isVerified": false
      }
    ],
    "pageable": {
      "pageNumber": 0,
      "pageSize": 10,
      "sort": {
        "empty": true,
        "sorted": false,
        "unsorted": true
      },
      "offset": 0,
      "paged": true,
      "unpaged": false
    },
    "last": false,
    "totalPages": 3,
    "totalElements": 25,
    "first": true,
    "size": 10,
    "number": 0,
    "numberOfElements": 10,
    "sort": {
      "empty": true,
      "sorted": false,
      "unsorted": true
    },
    "empty": false
  }
}

```

**Success Response Fields**:

<table id="bkmrk-field-description-co"><thead><tr><th>Field</th><th>Description</th></tr></thead><tbody><tr><td>content</td><td>Array of user objects matching the search query</td></tr><tr><td>content\[\].id</td><td>Unique identifier of the user</td></tr><tr><td>content\[\].userName</td><td>Username of the user</td></tr><tr><td>content\[\].displayName</td><td>Full display name (firstName + lastName, or userName if not available)</td></tr><tr><td>content\[\].avatarUrl</td><td>URL to user's profile picture (null if not set)</td></tr><tr><td>content\[\].isVerified</td><td>Whether the user account is verified</td></tr><tr><td>totalElements</td><td>Total number of users matching the query</td></tr><tr><td>totalPages</td><td>Total number of pages available</td></tr><tr><td>first</td><td>Whether this is the first page</td></tr><tr><td>last</td><td>Whether this is the last page</td></tr></tbody></table>

**Empty Result Response JSON Sample**:

```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Users retrieved successfully",
  "action_time": "2025-12-23T11:14:05.293524449",
  "data": {
    "content": [],
    "pageable": {
      "pageNumber": 0,
      "pageSize": 10,
      "sort": {
        "empty": true,
        "sorted": false,
        "unsorted": true
      },
      "offset": 0,
      "paged": true,
      "unpaged": false
    },
    "last": true,
    "totalPages": 0,
    "totalElements": 0,
    "first": true,
    "size": 10,
    "number": 0,
    "numberOfElements": 0,
    "sort": {
      "empty": true,
      "sorted": false,
      "unsorted": true
    },
    "empty": true
  }
}

```

**Example Requests**:

<table id="bkmrk-use-case-request-bas"><thead><tr><th>Use Case</th><th>Request</th></tr></thead><tbody><tr><td>Basic search</td><td>`/users/search?q=john`</td></tr><tr><td>Search with @ prefix</td><td>`/users/search?q=@john`</td></tr><tr><td>Paginated search</td><td>`/users/search?q=john&page=2&size=20`</td></tr></tbody></table>

---

## 2. Search Shops

**Purpose**: Search for shops by name, slug, or tagline for $mentions and shop discovery.

**Endpoint**: <span style="background-color: #28a745; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">GET</span> `{base_url}/shops/search`

**Access Level**: 🌐 Public (Rate limited)

**Authentication**: None

**Query Parameters**:

<table id="bkmrk-parameter-type-requi-1"><thead><tr><th>Parameter</th><th>Type</th><th>Required</th><th>Description</th><th>Validation</th><th>Default</th></tr></thead><tbody><tr><td>q</td><td>string</td><td>No</td><td>Search query (shop name, slug, or tagline). `$` prefix is automatically removed</td><td>Min: 1 character after trimming</td><td>-</td></tr><tr><td>page</td><td>integer</td><td>No</td><td>Page number (1-based)</td><td>Min: 1</td><td>1</td></tr><tr><td>size</td><td>integer</td><td>No</td><td>Number of results per page</td><td>Min: 1, Max: 50</td><td>10</td></tr></tbody></table>

**Success Response JSON Sample**:

```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Shops retrieved successfully",
  "action_time": "2025-12-23T11:20:30.123456789",
  "data": {
    "content": [
      {
        "shopId": "c3d4e5f6-a7b8-9012-cdef-123456789012",
        "shopName": "Mama's Kitchen",
        "shopSlug": "mamas-kitchen",
        "tagline": "Best local food in town",
        "logoUrl": "https://cdn.nextgate.com/shops/mamas-kitchen/logo.jpg",
        "isVerified": true,
        "verificationBadge": "GOLD",
        "city": "Dar es Salaam"
      },
      {
        "shopId": "d4e5f6a7-b8c9-0123-def0-234567890123",
        "shopName": "Mama Lishe",
        "shopSlug": "mama-lishe",
        "tagline": "Authentic Tanzanian cuisine",
        "logoUrl": null,
        "isVerified": false,
        "verificationBadge": null,
        "city": "Arusha"
      }
    ],
    "pageable": {
      "pageNumber": 0,
      "pageSize": 10,
      "sort": {
        "empty": true,
        "sorted": false,
        "unsorted": true
      },
      "offset": 0,
      "paged": true,
      "unpaged": false
    },
    "last": false,
    "totalPages": 2,
    "totalElements": 15,
    "first": true,
    "size": 10,
    "number": 0,
    "numberOfElements": 10,
    "sort": {
      "empty": true,
      "sorted": false,
      "unsorted": true
    },
    "empty": false
  }
}

```

**Success Response Fields**:

<table id="bkmrk-field-description-co-1"><thead><tr><th>Field</th><th>Description</th></tr></thead><tbody><tr><td>content</td><td>Array of shop objects matching the search query</td></tr><tr><td>content\[\].shopId</td><td>Unique identifier of the shop</td></tr><tr><td>content\[\].shopName</td><td>Display name of the shop</td></tr><tr><td>content\[\].shopSlug</td><td>URL-friendly unique identifier</td></tr><tr><td>content\[\].tagline</td><td>Short promotional text for the shop</td></tr><tr><td>content\[\].logoUrl</td><td>URL to shop's logo image (null if not set)</td></tr><tr><td>content\[\].isVerified</td><td>Whether the shop is verified</td></tr><tr><td>content\[\].verificationBadge</td><td>Verification badge level (GOLD, SILVER, BRONZE, or null)</td></tr><tr><td>content\[\].city</td><td>City where the shop is located</td></tr><tr><td>totalElements</td><td>Total number of shops matching the query</td></tr><tr><td>totalPages</td><td>Total number of pages available</td></tr><tr><td>first</td><td>Whether this is the first page</td></tr><tr><td>last</td><td>Whether this is the last page</td></tr></tbody></table>

**Empty Result Response JSON Sample**:

```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Shops retrieved successfully",
  "action_time": "2025-12-23T11:20:30.123456789",
  "data": {
    "content": [],
    "pageable": {
      "pageNumber": 0,
      "pageSize": 10,
      "sort": {
        "empty": true,
        "sorted": false,
        "unsorted": true
      },
      "offset": 0,
      "paged": true,
      "unpaged": false
    },
    "last": true,
    "totalPages": 0,
    "totalElements": 0,
    "first": true,
    "size": 10,
    "number": 0,
    "numberOfElements": 0,
    "sort": {
      "empty": true,
      "sorted": false,
      "unsorted": true
    },
    "empty": true
  }
}

```

**Example Requests**:

<table id="bkmrk-use-case-request-bas-1"><thead><tr><th>Use Case</th><th>Request</th></tr></thead><tbody><tr><td>Basic search</td><td>`/shops/search?q=mama`</td></tr><tr><td>Search with $ prefix</td><td>`/shops/search?q=$mama`</td></tr><tr><td>Paginated search</td><td>`/shops/search?q=kitchen&page=2&size=20`</td></tr></tbody></table>

---

## Search Ordering Logic

Both search endpoints use smart ordering to return the most relevant results first:

<table id="bkmrk-priority-match-type-"><thead><tr><th>Priority</th><th>Match Type</th><th>Example (query: "john")</th></tr></thead><tbody><tr><td>1</td><td>Exact match</td><td>`john`</td></tr><tr><td>2</td><td>Starts with</td><td>`johnny`, `john_doe`</td></tr><tr><td>3</td><td>Contains</td><td>`big_john`, `_john_`</td></tr></tbody></table>

Results with the same priority are sorted alphabetically.

---

## Error Responses

**Standard Error Types**:

### Application-Level Exceptions (400-499)

- `400 BAD_REQUEST`: Invalid request parameters
- `429 TOO_MANY_REQUESTS`: Rate limit exceeded

**Error Response Examples**:

*Rate Limit Exceeded (429):*

```json
{
  "success": false,
  "httpStatus": "TOO_MANY_REQUESTS",
  "message": "Rate limit exceeded. Please try again later.",
  "action_time": "2025-12-23T11:30:45",
  "data": "Rate limit exceeded. Please try again later."
}

```

---

## Quick Reference

<table id="bkmrk-feature-user-search-"><thead><tr><th>Feature</th><th>User Search</th><th>Shop Search</th></tr></thead><tbody><tr><td>Endpoint</td><td>`/users/search`</td><td>`/shops/search`</td></tr><tr><td>Prefix</td><td>`@` (optional)</td><td>`$` (optional)</td></tr><tr><td>Searches</td><td>userName, firstName, lastName</td><td>shopName, shopSlug, tagline</td></tr><tr><td>Filters</td><td>Non-locked users only</td><td>Active, non-deleted shops only</td></tr><tr><td>Auth</td><td>None</td><td>None</td></tr></tbody></table>

---

## Future Scaling Strategy

As Nexgate grows, the search system will evolve to handle millions of users and shops while maintaining fast response times. Here's how we'll scale like Instagram and GitHub.

---

### Phase 1: Current Implementation (MVP)

**Scale**: Up to 100K users/shops

**How it works**:

- Direct PostgreSQL queries with pattern matching
- Smart ordering: exact match → starts with → contains
- Simple and effective for early-stage growth

**Limitations**:

- Slows down with large datasets
- No typo tolerance
- No personalization

---

### Phase 2: Database Optimization

**Scale**: 100K - 1M users/shops

**What we'll add**:

- **Trigram Indexing (pg\_trgm)**: PostgreSQL extension that enables fast fuzzy search and handles typos (e.g., "jonh" finds "john")
- **Redis Caching**: Cache popular search queries and recent searches per user
- **Optimized Indexes**: GIN indexes for faster text matching

**Benefits**:

- 3-5x faster queries
- Typo tolerance
- Reduced database load

---

### Phase 3: Dedicated Search Engine

**Scale**: 1M - 100M users/shops

**What we'll add**:

- **Elasticsearch Cluster**: Dedicated search infrastructure separate from main database
- **Real-time Sync**: Changes in PostgreSQL automatically sync to Elasticsearch
- **Advanced Features**: Autocomplete suggestions, fuzzy matching, relevance scoring

**Benefits**:

- Sub-10ms response times
- Horizontal scaling (add more nodes as needed)
- Rich search features without impacting main database

---

### Phase 4: Personalized Search (Instagram/GitHub Style)

**Scale**: 100M+ users/shops

**How Instagram does it**: They don't search all 2 billion users. Instead, they search within YOUR world first.

**Tiered Search Approach**:

<table id="bkmrk-priority-who-gets-se"><thead><tr><th>Priority</th><th>Who Gets Searched First</th><th>Why</th></tr></thead><tbody><tr><td>1st</td><td>People you message frequently</td><td>Strongest signal of relationship</td></tr><tr><td>2nd</td><td>People you interact with (likes, comments)</td><td>Active engagement</td></tr><tr><td>3rd</td><td>People you follow</td><td>Explicit interest</td></tr><tr><td>4th</td><td>People who follow you</td><td>They know you</td></tr><tr><td>5th</td><td>Global search</td><td>Only if above tiers don't have enough results</td></tr></tbody></table>

**What we'll add**:

- **Interaction Graph**: Track who talks to whom, who views whose profile, who likes whose posts
- **Pre-computed Candidates**: For each user, maintain a list of likely search targets
- **ML Ranking**: Machine learning model to predict who you're most likely searching for
- **Prefix Caching**: Pre-compute results for common prefixes (typing "j" instantly shows your friend "John")

**Relevance Scoring**:

- 40% - How often you interact with this person/shop
- 30% - How well the query matches their name
- 20% - How recently you interacted
- 10% - Their popularity (followers, verification)

---

### Performance Targets

<table id="bkmrk-phase-scale-average-"><thead><tr><th>Phase</th><th>Scale</th><th>Average Response Time</th></tr></thead><tbody><tr><td>Phase 1 (Current)</td><td>&lt;100K</td><td>~50ms</td></tr><tr><td>Phase 2 (Optimized DB)</td><td>&lt;1M</td><td>~30ms</td></tr><tr><td>Phase 3 (Elasticsearch)</td><td>&lt;100M</td><td>~10ms</td></tr><tr><td>Phase 4 (Personalized)</td><td>100M+</td><td>~5ms</td></tr></tbody></table>

---

### Migration Triggers

<table id="bkmrk-when-to-move-action-"><thead><tr><th>When to Move</th><th>Action</th></tr></thead><tbody><tr><td>50K users</td><td>Add pg\_trgm + Redis caching</td></tr><tr><td>500K users</td><td>Introduce Elasticsearch</td></tr><tr><td>5M users</td><td>Build interaction graph + personalization</td></tr></tbody></table>

**Key Principle**: The API stays the same. Users won't notice any changes except faster, smarter results.

# E-Commerce Search Engine

**Author**: Josh S. Sakweli, Backend Lead Team **Last Updated**: 2025-12-30 **Version**: v1.0

**Base URL**: `https://api.nextgate.com/api/v1`

**Short Description**: Public search endpoints for discovering shops, products, and active purchase groups on the Nexgate e-commerce platform. These endpoints power the main search functionality, product discovery, and group buying features across the application.

**Hints**:

- Search query must be at least 2 characters
- Results are ordered alphabetically by name
- Filter by type using the `type` parameter: `SHOP`, `PRODUCT`, or `PURCHASE_GROUP`
- Purchase groups are searched by both name and group code (e.g., `GP-ABC123`)
- All endpoints are public and do not require authentication
- Maximum page size is 50 results
- Rate limiting applies to prevent abuse

---

## Standard Response Format

All API responses follow a consistent structure using our Globe Response Builder pattern:

### Success Response Structure

```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Operation completed successfully",
  "action_time": "2025-12-30T10:30:45",
  "data": {
    // Actual response data goes here
  }
}

```

### Error Response Structure

```json
{
  "success": false,
  "httpStatus": "BAD_REQUEST",
  "message": "Error description",
  "action_time": "2025-12-30T10:30:45",
  "data": null
}

```

---

## Search Result Types

Each search result contains a `type` field indicating the result category:

<table id="bkmrk-type-description-spe"><thead><tr><th>Type</th><th>Description</th><th>Specific Fields</th></tr></thead><tbody><tr><td>`SHOP`</td><td>Active shop/store</td><td>`shop` object</td></tr><tr><td>`PRODUCT`</td><td>Active product</td><td>`product` object</td></tr><tr><td>`PURCHASE_GROUP`</td><td>Open group purchase</td><td>`purchaseGroup` object</td></tr></tbody></table>

---

## Endpoints

---

## 1. Global Search

**Purpose**: Search across all types (shops, products, and purchase groups) with optional type filtering

**Endpoint**: <span style="background-color: #61affe; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">GET</span> `{base_url}/api/v1/e-commerce/search`

**Access Level**: 🌐 Public (No Authentication Required)

**Query Parameters**:

<table id="bkmrk-parameter-type-requi"><thead><tr><th>Parameter</th><th>Type</th><th>Required</th><th>Default</th><th>Description</th><th>Validation</th></tr></thead><tbody><tr><td>query</td><td>string</td><td>Yes</td><td>-</td><td>Search term</td><td>Min 2 characters</td></tr><tr><td>type</td><td>string</td><td>No</td><td>null (all)</td><td>Filter by type</td><td>SHOP, PRODUCT, PURCHASE\_GROUP</td></tr><tr><td>page</td><td>int</td><td>No</td><td>1</td><td>Page number</td><td>Min 1</td></tr><tr><td>size</td><td>int</td><td>No</td><td>20</td><td>Results per page</td><td>Max 50</td></tr></tbody></table>

**Request Examples**:

```bash
# Search all types
GET /api/v1/e-commerce/search?query=iphone&page=1&size=20

# Search shops only
GET /api/v1/e-commerce/search?query=fashion&type=SHOP

# Search products only
GET /api/v1/e-commerce/search?query=dress&type=PRODUCT

# Search purchase groups only
GET /api/v1/e-commerce/search?query=GP-ABC123&type=PURCHASE_GROUP

```

**Success Response JSON Sample**:

```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Search results retrieved successfully",
  "action_time": "2025-12-30T10:30:45",
  "data": {
    "content": [
      {
        "type": "SHOP",
        "id": "550e8400-e29b-41d4-a716-446655440000",
        "name": "Fashion Hub TZ",
        "imageUrl": "https://storage.example.com/shops/logo1.jpg",
        "shop": {
          "slug": "fashion-hub-tz",
          "isVerified": true,
          "rating": 4.8,
          "productsCount": 156,
          "location": "Dar es Salaam"
        }
      },
      {
        "type": "PRODUCT",
        "id": "660e8400-e29b-41d4-a716-446655440001",
        "name": "Summer Fashion Dress",
        "imageUrl": "https://storage.example.com/products/dress1.jpg",
        "product": {
          "slug": "summer-fashion-dress",
          "price": 45000.00,
          "discountPrice": 38000.00,
          "currency": "TZS",
          "inStock": true,
          "rating": 4.5,
          "shopName": "Fashion Hub TZ",
          "shopIsVerified": true
        }
      },
      {
        "type": "PURCHASE_GROUP",
        "id": "770e8400-e29b-41d4-a716-446655440002",
        "name": "Fashion Week Special Deal",
        "imageUrl": "https://storage.example.com/products/bundle.jpg",
        "purchaseGroup": {
          "groupCode": "GP-FWS123",
          "regularPrice": 150000.00,
          "groupPrice": 120000.00,
          "savingsPercentage": 20,
          "seatsRemaining": 3,
          "totalSeats": 10,
          "expiresAt": "2025-12-31T23:59:59",
          "shopName": "Fashion Hub TZ"
        }
      }
    ],
    "page": 0,
    "size": 20,
    "totalElements": 3,
    "totalPages": 1,
    "hasNext": false,
    "hasPrevious": false,
    "shopsCount": 1,
    "productsCount": 1,
    "purchaseGroupsCount": 1
  }
}

```

**Response Fields**:

<table id="bkmrk-field-type-descripti"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>content</td><td>array</td><td>List of search result items</td></tr><tr><td>page</td><td>int</td><td>Current page number (0-indexed)</td></tr><tr><td>size</td><td>int</td><td>Page size</td></tr><tr><td>totalElements</td><td>long</td><td>Total number of results</td></tr><tr><td>totalPages</td><td>int</td><td>Total number of pages</td></tr><tr><td>hasNext</td><td>boolean</td><td>Whether more pages exist</td></tr><tr><td>hasPrevious</td><td>boolean</td><td>Whether previous pages exist</td></tr><tr><td>shopsCount</td><td>long</td><td>Total shops matching query</td></tr><tr><td>productsCount</td><td>long</td><td>Total products matching query</td></tr><tr><td>purchaseGroupsCount</td><td>long</td><td>Total purchase groups matching query</td></tr></tbody></table>

**Error Response JSON Samples**:

*Query too short:*

```json
{
  "success": false,
  "httpStatus": "BAD_REQUEST",
  "message": "Search query must be at least 2 characters",
  "action_time": "2025-12-30T10:30:45",
  "data": null
}

```

*Invalid type:*

```json
{
  "success": false,
  "httpStatus": "BAD_REQUEST",
  "message": "Invalid type. Must be: SHOP, PRODUCT, or PURCHASE_GROUP",
  "action_time": "2025-12-30T10:30:45",
  "data": null
}

```

---

## 2. Search Shops

**Purpose**: Search for active shops by name

**Endpoint**: <span style="background-color: #61affe; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">GET</span> `{base_url}/api/v1/e-commerce/search/shops`

**Access Level**: 🌐 Public (No Authentication Required)

**Query Parameters**:

<table id="bkmrk-parameter-type-requi-1"><thead><tr><th>Parameter</th><th>Type</th><th>Required</th><th>Default</th><th>Description</th><th>Validation</th></tr></thead><tbody><tr><td>query</td><td>string</td><td>Yes</td><td>-</td><td>Shop name search term</td><td>Min 2 characters</td></tr><tr><td>page</td><td>int</td><td>No</td><td>1</td><td>Page number</td><td>Min 1</td></tr><tr><td>size</td><td>int</td><td>No</td><td>20</td><td>Results per page</td><td>Max 50</td></tr></tbody></table>

**Request Example**:

```bash
GET /api/v1/e-commerce/search/shops?query=fashion&page=1&size=20

```

**Success Response JSON Sample**:

```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Shops retrieved successfully",
  "action_time": "2025-12-30T10:30:45",
  "data": {
    "content": [
      {
        "type": "SHOP",
        "id": "550e8400-e29b-41d4-a716-446655440000",
        "name": "Fashion Hub TZ",
        "imageUrl": "https://storage.example.com/shops/logo1.jpg",
        "shop": {
          "slug": "fashion-hub-tz",
          "isVerified": true,
          "rating": 4.8,
          "productsCount": 156,
          "location": "Dar es Salaam"
        }
      },
      {
        "type": "SHOP",
        "id": "660e8400-e29b-41d4-a716-446655440001",
        "name": "Fashion Zone",
        "imageUrl": "https://storage.example.com/shops/logo2.jpg",
        "shop": {
          "slug": "fashion-zone",
          "isVerified": false,
          "rating": 4.2,
          "productsCount": 89,
          "location": "Arusha"
        }
      }
    ],
    "page": 0,
    "size": 20,
    "totalElements": 2,
    "totalPages": 1,
    "hasNext": false,
    "hasPrevious": false,
    "shopsCount": 2,
    "productsCount": 0,
    "purchaseGroupsCount": 0
  }
}

```

**Shop Info Fields**:

<table id="bkmrk-field-type-descripti-1"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>slug</td><td>string</td><td>URL-friendly shop identifier</td></tr><tr><td>isVerified</td><td>boolean</td><td>Whether shop is verified</td></tr><tr><td>rating</td><td>double</td><td>Shop rating (0-5)</td></tr><tr><td>productsCount</td><td>int</td><td>Number of active products</td></tr><tr><td>location</td><td>string</td><td>Shop location</td></tr></tbody></table>

---

## 3. Search Products

**Purpose**: Search for active products by name

**Endpoint**: <span style="background-color: #61affe; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">GET</span> `{base_url}/api/v1/e-commerce/search/products`

**Access Level**: 🌐 Public (No Authentication Required)

**Query Parameters**:

<table id="bkmrk-parameter-type-requi-2"><thead><tr><th>Parameter</th><th>Type</th><th>Required</th><th>Default</th><th>Description</th><th>Validation</th></tr></thead><tbody><tr><td>query</td><td>string</td><td>Yes</td><td>-</td><td>Product name search term</td><td>Min 2 characters</td></tr><tr><td>page</td><td>int</td><td>No</td><td>1</td><td>Page number</td><td>Min 1</td></tr><tr><td>size</td><td>int</td><td>No</td><td>20</td><td>Results per page</td><td>Max 50</td></tr></tbody></table>

**Request Example**:

```bash
GET /api/v1/e-commerce/search/products?query=dress&page=1&size=20

```

**Success Response JSON Sample**:

```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Products retrieved successfully",
  "action_time": "2025-12-30T10:30:45",
  "data": {
    "content": [
      {
        "type": "PRODUCT",
        "id": "550e8400-e29b-41d4-a716-446655440000",
        "name": "Summer Dress Collection",
        "imageUrl": "https://storage.example.com/products/dress1.jpg",
        "product": {
          "slug": "summer-dress-collection",
          "price": 45000.00,
          "discountPrice": 38000.00,
          "currency": "TZS",
          "inStock": true,
          "rating": 4.5,
          "shopName": "Fashion Hub TZ",
          "shopIsVerified": true
        }
      },
      {
        "type": "PRODUCT",
        "id": "660e8400-e29b-41d4-a716-446655440001",
        "name": "Evening Dress",
        "imageUrl": "https://storage.example.com/products/dress2.jpg",
        "product": {
          "slug": "evening-dress",
          "price": 85000.00,
          "discountPrice": null,
          "currency": "TZS",
          "inStock": true,
          "rating": 4.8,
          "shopName": "Elegance Store",
          "shopIsVerified": true
        }
      },
      {
        "type": "PRODUCT",
        "id": "770e8400-e29b-41d4-a716-446655440002",
        "name": "Casual Dress",
        "imageUrl": "https://storage.example.com/products/dress3.jpg",
        "product": {
          "slug": "casual-dress",
          "price": 35000.00,
          "discountPrice": 30000.00,
          "currency": "TZS",
          "inStock": false,
          "rating": 4.0,
          "shopName": "Budget Fashion",
          "shopIsVerified": false
        }
      }
    ],
    "page": 0,
    "size": 20,
    "totalElements": 3,
    "totalPages": 1,
    "hasNext": false,
    "hasPrevious": false,
    "shopsCount": 0,
    "productsCount": 3,
    "purchaseGroupsCount": 0
  }
}

```

**Product Info Fields**:

<table id="bkmrk-field-type-descripti-2"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>slug</td><td>string</td><td>URL-friendly product identifier</td></tr><tr><td>price</td><td>decimal</td><td>Regular price</td></tr><tr><td>discountPrice</td><td>decimal</td><td>Discounted price (null if no discount)</td></tr><tr><td>currency</td><td>string</td><td>Price currency (e.g., TZS)</td></tr><tr><td>inStock</td><td>boolean</td><td>Whether product is in stock</td></tr><tr><td>rating</td><td>double</td><td>Product rating (0-5)</td></tr><tr><td>shopName</td><td>string</td><td>Name of the shop selling this product</td></tr><tr><td>shopIsVerified</td><td>boolean</td><td>Whether the shop is verified</td></tr></tbody></table>

---

## 4. Search Purchase Groups

**Purpose**: Search for active (open, non-expired) purchase groups by name or group code

**Endpoint**: <span style="background-color: #61affe; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">GET</span> `{base_url}/api/v1/e-commerce/search/groups`

**Access Level**: 🌐 Public (No Authentication Required)

**Query Parameters**:

<table id="bkmrk-parameter-type-requi-3"><thead><tr><th>Parameter</th><th>Type</th><th>Required</th><th>Default</th><th>Description</th><th>Validation</th></tr></thead><tbody><tr><td>query</td><td>string</td><td>Yes</td><td>-</td><td>Group name or code</td><td>Min 2 characters</td></tr><tr><td>page</td><td>int</td><td>No</td><td>1</td><td>Page number</td><td>Min 1</td></tr><tr><td>size</td><td>int</td><td>No</td><td>20</td><td>Results per page</td><td>Max 50</td></tr></tbody></table>

**Request Examples**:

```bash
# Search by group name
GET /api/v1/e-commerce/search/groups?query=iPhone Deal&page=1&size=20

# Search by group code
GET /api/v1/e-commerce/search/groups?query=GP-ABC123&page=1&size=20

```

**Success Response JSON Sample**:

```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Purchase groups retrieved successfully",
  "action_time": "2025-12-30T10:30:45",
  "data": {
    "content": [
      {
        "type": "PURCHASE_GROUP",
        "id": "550e8400-e29b-41d4-a716-446655440000",
        "name": "iPhone 15 Pro - Dar es Salaam Deal",
        "imageUrl": "https://storage.example.com/products/iphone15.jpg",
        "purchaseGroup": {
          "groupCode": "GP-ABC123",
          "regularPrice": 2500000.00,
          "groupPrice": 2200000.00,
          "savingsPercentage": 12,
          "seatsRemaining": 2,
          "totalSeats": 5,
          "expiresAt": "2025-12-31T08:00:00",
          "shopName": "TechZone TZ"
        }
      },
      {
        "type": "PURCHASE_GROUP",
        "id": "660e8400-e29b-41d4-a716-446655440001",
        "name": "iPhone 15 Plus Group Buy",
        "imageUrl": "https://storage.example.com/products/iphone15plus.jpg",
        "purchaseGroup": {
          "groupCode": "GP-XYZ789",
          "regularPrice": 2200000.00,
          "groupPrice": 1950000.00,
          "savingsPercentage": 11,
          "seatsRemaining": 4,
          "totalSeats": 8,
          "expiresAt": "2025-12-30T18:00:00",
          "shopName": "Mobile World"
        }
      }
    ],
    "page": 0,
    "size": 20,
    "totalElements": 2,
    "totalPages": 1,
    "hasNext": false,
    "hasPrevious": false,
    "shopsCount": 0,
    "productsCount": 0,
    "purchaseGroupsCount": 2
  }
}

```

**Purchase Group Info Fields**:

<table id="bkmrk-field-type-descripti-3"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>groupCode</td><td>string</td><td>Unique group code (e.g., GP-ABC123)</td></tr><tr><td>regularPrice</td><td>decimal</td><td>Original product price</td></tr><tr><td>groupPrice</td><td>decimal</td><td>Discounted group price</td></tr><tr><td>savingsPercentage</td><td>int</td><td>Percentage saved</td></tr><tr><td>seatsRemaining</td><td>int</td><td>Available seats</td></tr><tr><td>totalSeats</td><td>int</td><td>Total seats in group</td></tr><tr><td>expiresAt</td><td>datetime</td><td>Group expiration time</td></tr><tr><td>shopName</td><td>string</td><td>Name of the shop</td></tr></tbody></table>

**Note**: Only returns groups that are:

- Status: `OPEN`
- Not expired (`expiresAt` &gt; now)
- Not deleted

---

## Endpoints Summary

<table id="bkmrk-%23-method-endpoint-pu"><thead><tr><th>\#</th><th>Method</th><th>Endpoint</th><th>Purpose</th></tr></thead><tbody><tr><td>1</td><td>GET</td><td>`/api/v1/e-commerce/search`</td><td>Search all types</td></tr><tr><td>2</td><td>GET</td><td>`/api/v1/e-commerce/search/shops`</td><td>Search shops only</td></tr><tr><td>3</td><td>GET</td><td>`/api/v1/e-commerce/search/products`</td><td>Search products only</td></tr><tr><td>4</td><td>GET</td><td>`/api/v1/e-commerce/search/groups`</td><td>Search purchase groups only</td></tr></tbody></table>

---

## Common Error Responses

<table id="bkmrk-status-message-cause"><thead><tr><th>Status</th><th>Message</th><th>Cause</th></tr></thead><tbody><tr><td>`400 BAD_REQUEST`</td><td>Search query must be at least 2 characters</td><td>Query too short</td></tr><tr><td>`400 BAD_REQUEST`</td><td>Invalid type. Must be: SHOP, PRODUCT, or PURCHASE\_GROUP</td><td>Invalid type parameter</td></tr><tr><td>`429 TOO_MANY_REQUESTS`</td><td>Rate limit exceeded</td><td>Too many requests</td></tr></tbody></table>

---

## Frontend Usage Examples

### JavaScript/TypeScript

```javascript
// Global search
const searchAll = async (query, page = 1, size = 20) => {
  const response = await fetch(
    `/api/v1/e-commerce/search?query=${encodeURIComponent(query)}&page=${page}&size=${size}`
  );
  return response.json();
};

// Search with type filter
const searchByType = async (query, type, page = 1) => {
  const response = await fetch(
    `/api/v1/e-commerce/search?query=${encodeURIComponent(query)}&type=${type}&page=${page}`
  );
  return response.json();
};

// Search shops only
const searchShops = async (query) => {
  const response = await fetch(
    `/api/v1/e-commerce/search/shops?query=${encodeURIComponent(query)}`
  );
  return response.json();
};

// Search products only
const searchProducts = async (query) => {
  const response = await fetch(
    `/api/v1/e-commerce/search/products?query=${encodeURIComponent(query)}`
  );
  return response.json();
};

// Search groups by name or code
const searchGroups = async (query) => {
  const response = await fetch(
    `/api/v1/e-commerce/search/groups?query=${encodeURIComponent(query)}`
  );
  return response.json();
};

```

### Rendering Results

```javascript
const renderResults = (results) => {
  results.content.forEach(item => {
    switch (item.type) {
      case 'SHOP':
        renderShopCard(item);
        break;
      case 'PRODUCT':
        renderProductCard(item);
        break;
      case 'PURCHASE_GROUP':
        renderGroupCard(item);
        break;
    }
  });
  
  // Show counts
  console.log(`Found: ${results.shopsCount} shops, ${results.productsCount} products, ${results.purchaseGroupsCount} groups`);
};

```

## 5. Get Product Details by ID

**Purpose**: Retrieve detailed product information by product ID for public viewing

**Endpoint**: <span style="background-color: #61affe; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">GET</span> `{base_url}/api/v1/e-commerce/search/id/{productId}`

**Access Level**: 🌐 Public (No Authentication Required)

**Path Parameters**:

<table id="bkmrk-parameter-type-requi-4"><thead><tr><th>Parameter</th><th>Type</th><th>Required</th><th>Description</th><th>Validation</th></tr></thead><tbody><tr><td>productId</td><td>UUID</td><td>Yes</td><td>Unique product identifier</td><td>Valid UUID format</td></tr></tbody></table>

**Request Example**:

```bash
GET /api/v1/e-commerce/search/id/550e8400-e29b-41d4-a716-446655440000

```

**Success Response JSON Sample**:

```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Product retrieved successfully",
  "action_time": "2025-12-30T10:30:45",
  "data": {
    "productId": "550e8400-e29b-41d4-a716-446655440000",
    "productName": "iPhone 15 Pro Max",
    "productSlug": "iphone-15-pro-max",
    "productDescription": "The latest iPhone with advanced features, A17 Pro chip, and titanium design.",
    "shortDescription": "Latest iPhone with A17 Pro chip",
    "productImages": [
      "https://storage.example.com/products/iphone15-1.jpg",
      "https://storage.example.com/products/iphone15-2.jpg",
      "https://storage.example.com/products/iphone15-3.jpg"
    ],
    "price": 2500000.00,
    "comparePrice": 2800000.00,
    "discountPercentage": 10.71,
    "isOnSale": true,
    "stockQuantity": 25,
    "lowStockThreshold": 5,
    "isInStock": true,
    "isLowStock": false,
    "trackInventory": true,
    "brand": "Apple",
    "sku": "IPHONE15PM-256-BLK",
    "condition": "NEW",
    "status": "ACTIVE",
    "tags": ["smartphone", "apple", "iphone", "5g"],
    "metaTitle": "iPhone 15 Pro Max - Buy Online",
    "metaDescription": "Buy the latest iPhone 15 Pro Max with free shipping",
    "shopId": "660e8400-e29b-41d4-a716-446655440001",
    "shopName": "TechZone TZ",
    "shopSlug": "techzone-tz",
    "categoryId": "770e8400-e29b-41d4-a716-446655440002",
    "categoryName": "Smartphones",
    "isFeatured": true,
    "isDigital": false,
    "requiresShipping": true,
    "createdAt": "2025-12-01T08:00:00",
    "updatedAt": "2025-12-30T10:00:00",
    "specifications": {
      "Display": "6.7-inch Super Retina XDR",
      "Chip": "A17 Pro",
      "Storage": "256GB",
      "Camera": "48MP Main + 12MP Ultra Wide + 12MP Telephoto",
      "Battery": "4422 mAh",
      "5G": "Yes"
    },
    "hasSpecifications": true,
    "specificationCount": 6,
    "colors": [
      {
        "name": "Natural Titanium",
        "hex": "#9A9A98",
        "images": ["https://storage.example.com/products/iphone15-titanium.jpg"],
        "priceAdjustment": 0.00,
        "finalPrice": 2500000.00,
        "hasExtraFee": false,
        "extraFeeReason": null
      },
      {
        "name": "Blue Titanium",
        "hex": "#394C5F",
        "images": ["https://storage.example.com/products/iphone15-blue.jpg"],
        "priceAdjustment": 50000.00,
        "finalPrice": 2550000.00,
        "hasExtraFee": true,
        "extraFeeReason": "Limited edition color"
      }
    ],
    "hasMultipleColors": true,
    "colorCount": 2,
    "priceRange": {
      "minPrice": 2500000.00,
      "maxPrice": 2550000.00,
      "priceStartsFrom": 2500000.00,
      "hasPriceVariations": true
    },
    "orderingLimits": {
      "minOrderQuantity": 1,
      "maxOrderQuantity": 5,
      "canOrderQuantity": 5,
      "maxAllowedQuantity": 5,
      "hasOrderingLimits": true
    },
    "groupBuying": {
      "isEnabled": true,
      "maxGroupSize": 5,
      "groupPrice": 2200000.00,
      "groupDiscount": 300000.00,
      "groupDiscountPercentage": 12.00,
      "timeLimitHours": 48,
      "canJoinGroup": true
    },
    "installmentOptions": {
      "isEnabled": true,
      "isAvailable": true,
      "downPaymentRequired": true,
      "minDownPaymentPercentage": 20.00,
      "plans": [
        {
          "planId": "3-months",
          "duration": 3,
          "interval": "MONTHLY",
          "interestRate": 0.00,
          "description": "3 months interest-free",
          "calculations": {
            "downPayment": 500000.00,
            "remainingAmount": 2000000.00,
            "totalInterest": 0.00,
            "paymentAmount": 666667.00,
            "totalAmount": 2500000.00
          },
          "paymentSchedule": [
            {
              "paymentNumber": 1,
              "amount": 666667.00,
              "dueDate": "2026-01-30T10:30:45",
              "description": "Payment 1 of 3"
            },
            {
              "paymentNumber": 2,
              "amount": 666667.00,
              "dueDate": "2026-02-28T10:30:45",
              "description": "Payment 2 of 3"
            },
            {
              "paymentNumber": 3,
              "amount": 666666.00,
              "dueDate": "2026-03-30T10:30:45",
              "description": "Payment 3 of 3"
            }
          ],
          "isPopular": true
        },
        {
          "planId": "6-months",
          "duration": 6,
          "interval": "MONTHLY",
          "interestRate": 5.00,
          "description": "6 months with 5% interest",
          "calculations": {
            "downPayment": 500000.00,
            "remainingAmount": 2000000.00,
            "totalInterest": 100000.00,
            "paymentAmount": 350000.00,
            "totalAmount": 2600000.00
          },
          "isPopular": false
        }
      ],
      "eligibilityStatus": "ELIGIBLE",
      "creditCheckRequired": false
    },
    "purchaseOptions": {
      "canBuyNow": true,
      "canJoinGroup": true,
      "canPayInstallment": true,
      "recommendedOption": "GROUP_BUYING",
      "bestDeal": {
        "option": "GROUP_BUYING",
        "savings": 300000.00,
        "finalPrice": 2200000.00
      }
    }
  }
}

```

**Response Fields**:

### Basic Information

<table id="bkmrk-field-type-descripti-4"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>productId</td><td>UUID</td><td>Unique product identifier</td></tr><tr><td>productName</td><td>string</td><td>Product name</td></tr><tr><td>productSlug</td><td>string</td><td>URL-friendly product identifier</td></tr><tr><td>productDescription</td><td>string</td><td>Full product description</td></tr><tr><td>shortDescription</td><td>string</td><td>Short product summary</td></tr><tr><td>productImages</td><td>array</td><td>List of product image URLs</td></tr></tbody></table>

### Pricing Information

<table id="bkmrk-field-type-descripti-5"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>price</td><td>decimal</td><td>Current selling price</td></tr><tr><td>comparePrice</td><td>decimal</td><td>Original/compare price</td></tr><tr><td>discountPercentage</td><td>decimal</td><td>Discount percentage</td></tr><tr><td>isOnSale</td><td>boolean</td><td>Whether product is on sale</td></tr></tbody></table>

### Inventory Information

<table id="bkmrk-field-type-descripti-6"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>stockQuantity</td><td>int</td><td>Available stock</td></tr><tr><td>lowStockThreshold</td><td>int</td><td>Low stock alert threshold</td></tr><tr><td>isInStock</td><td>boolean</td><td>Whether product is in stock</td></tr><tr><td>isLowStock</td><td>boolean</td><td>Whether stock is low</td></tr><tr><td>trackInventory</td><td>boolean</td><td>Whether inventory is tracked</td></tr></tbody></table>

### Product Details

<table id="bkmrk-field-type-descripti-7"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>brand</td><td>string</td><td>Product brand</td></tr><tr><td>sku</td><td>string</td><td>Stock keeping unit</td></tr><tr><td>condition</td><td>enum</td><td>NEW, USED, REFURBISHED</td></tr><tr><td>status</td><td>enum</td><td>ACTIVE, INACTIVE, DRAFT</td></tr></tbody></table>

### Shop &amp; Category

<table id="bkmrk-field-type-descripti-8"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>shopId</td><td>UUID</td><td>Shop identifier</td></tr><tr><td>shopName</td><td>string</td><td>Shop name</td></tr><tr><td>shopSlug</td><td>string</td><td>Shop URL slug</td></tr><tr><td>categoryId</td><td>UUID</td><td>Category identifier</td></tr><tr><td>categoryName</td><td>string</td><td>Category name</td></tr></tbody></table>

### Specifications

<table id="bkmrk-field-type-descripti-9"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>specifications</td><td>object</td><td>Key-value pairs of product specs</td></tr><tr><td>hasSpecifications</td><td>boolean</td><td>Whether product has specs</td></tr><tr><td>specificationCount</td><td>int</td><td>Number of specifications</td></tr></tbody></table>

### Colors

<table id="bkmrk-field-type-descripti-10"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>colors</td><td>array</td><td>Available color options</td></tr><tr><td>colors\[\].name</td><td>string</td><td>Color name</td></tr><tr><td>colors\[\].hex</td><td>string</td><td>Hex color code</td></tr><tr><td>colors\[\].images</td><td>array</td><td>Color-specific images</td></tr><tr><td>colors\[\].priceAdjustment</td><td>decimal</td><td>Price adjustment for color</td></tr><tr><td>colors\[\].finalPrice</td><td>decimal</td><td>Final price with adjustment</td></tr><tr><td>colors\[\].hasExtraFee</td><td>boolean</td><td>Whether color has extra fee</td></tr><tr><td>hasMultipleColors</td><td>boolean</td><td>Whether multiple colors exist</td></tr><tr><td>colorCount</td><td>int</td><td>Number of color options</td></tr></tbody></table>

### Price Range

<table id="bkmrk-field-type-descripti-11"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>priceRange.minPrice</td><td>decimal</td><td>Minimum price across variants</td></tr><tr><td>priceRange.maxPrice</td><td>decimal</td><td>Maximum price across variants</td></tr><tr><td>priceRange.priceStartsFrom</td><td>decimal</td><td>Starting price</td></tr><tr><td>priceRange.hasPriceVariations</td><td>boolean</td><td>Whether prices vary</td></tr></tbody></table>

### Ordering Limits

<table id="bkmrk-field-type-descripti-12"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>orderingLimits.minOrderQuantity</td><td>int</td><td>Minimum order quantity</td></tr><tr><td>orderingLimits.maxOrderQuantity</td><td>int</td><td>Maximum order quantity</td></tr><tr><td>orderingLimits.canOrderQuantity</td><td>int</td><td>Quantity user can order</td></tr><tr><td>orderingLimits.hasOrderingLimits</td><td>boolean</td><td>Whether limits apply</td></tr></tbody></table>

### Group Buying

<table id="bkmrk-field-type-descripti-13"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>groupBuying.isEnabled</td><td>boolean</td><td>Whether group buying is enabled</td></tr><tr><td>groupBuying.maxGroupSize</td><td>int</td><td>Maximum group participants</td></tr><tr><td>groupBuying.groupPrice</td><td>decimal</td><td>Group purchase price</td></tr><tr><td>groupBuying.groupDiscount</td><td>decimal</td><td>Savings amount</td></tr><tr><td>groupBuying.groupDiscountPercentage</td><td>decimal</td><td>Savings percentage</td></tr><tr><td>groupBuying.timeLimitHours</td><td>int</td><td>Hours to fill group</td></tr><tr><td>groupBuying.canJoinGroup</td><td>boolean</td><td>Whether user can join</td></tr></tbody></table>

### Installment Options

<table id="bkmrk-field-type-descripti-14"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>installmentOptions.isEnabled</td><td>boolean</td><td>Whether installments enabled</td></tr><tr><td>installmentOptions.isAvailable</td><td>boolean</td><td>Whether available for product</td></tr><tr><td>installmentOptions.downPaymentRequired</td><td>boolean</td><td>Whether down payment needed</td></tr><tr><td>installmentOptions.minDownPaymentPercentage</td><td>decimal</td><td>Minimum down payment %</td></tr><tr><td>installmentOptions.plans</td><td>array</td><td>Available payment plans</td></tr><tr><td>installmentOptions.eligibilityStatus</td><td>string</td><td>User eligibility status</td></tr></tbody></table>

### Purchase Options Summary

<table id="bkmrk-field-type-descripti-15"><thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>purchaseOptions.canBuyNow</td><td>boolean</td><td>Can purchase immediately</td></tr><tr><td>purchaseOptions.canJoinGroup</td><td>boolean</td><td>Can join group purchase</td></tr><tr><td>purchaseOptions.canPayInstallment</td><td>boolean</td><td>Can use installments</td></tr><tr><td>purchaseOptions.recommendedOption</td><td>string</td><td>Best option for user</td></tr><tr><td>purchaseOptions.bestDeal.option</td><td>string</td><td>Best deal type</td></tr><tr><td>purchaseOptions.bestDeal.savings</td><td>decimal</td><td>Amount saved</td></tr><tr><td>purchaseOptions.bestDeal.finalPrice</td><td>decimal</td><td>Final price with deal</td></tr></tbody></table>

**Error Response JSON Sample**:

```json
{
  "success": false,
  "httpStatus": "NOT_FOUND",
  "message": "Product not found",
  "action_time": "2025-12-30T10:30:45",
  "data": null
}

```

**Standard Error Types**:

<table id="bkmrk-status-message-cause-1"><thead><tr><th>Status</th><th>Message</th><th>Cause</th></tr></thead><tbody><tr><td>`404 NOT_FOUND`</td><td>Product not found</td><td>Invalid product ID</td></tr><tr><td>`400 BAD_REQUEST`</td><td>Invalid product ID format</td><td>Malformed UUID</td></tr></tbody></table>

---

## Endpoints Summary (Updated)

<table id="bkmrk-%23-method-endpoint-pu-1"><thead><tr><th>\#</th><th>Method</th><th>Endpoint</th><th>Purpose</th></tr></thead><tbody><tr><td>1</td><td>GET</td><td>`/api/v1/e-commerce/search`</td><td>Search all types</td></tr><tr><td>2</td><td>GET</td><td>`/api/v1/e-commerce/search/shops`</td><td>Search shops only</td></tr><tr><td>3</td><td>GET</td><td>`/api/v1/e-commerce/search/products`</td><td>Search products only</td></tr><tr><td>4</td><td>GET</td><td>`/api/v1/e-commerce/search/groups`</td><td>Search purchase groups only</td></tr><tr><td>5</td><td>GET</td><td>`/api/v1/e-commerce/search/id/{productId}`</td><td>Get product details by ID</td></tr></tbody></table>

---

## Frontend Usage Example

```javascript
// Get full product details after search result click
const getProductDetails = async (productId) => {
  const response = await fetch(
    `/api/v1/e-commerce/search/id/${productId}`
  );
  return response.json();
};

// Usage
const handleSearchResultClick = async (item) => {
  if (item.type === 'PRODUCT') {
    const productDetails = await getProductDetails(item.id);
    showProductModal(productDetails.data);
  }
};

```