# Flow Lay Thong Tin San Pham Shopee (get_pc)

## Tong quan

He thong lay thong tin san pham Shopee hoat dong dua tren su phoi hop giua 3 thanh phan cua browser extension: **Background**, **Content Script** va **Popup/Options**. Endpoint chinh la Shopee API `/api/v4/pdp/get_pc`.

**Diem dac biet cua kien truc nay:**

- Su dung **headers that** tu request cua Shopee frontend (chong anti-bot)
- Fetch trong **main world** cua trang Shopee (co cookie/session tu dong)
- Cache nhieu tang de giam so lan goi API
- Tu dong bat va luu stock data tu hanh vi nguoi dung

### So do tong quan

```
                        webext-bridge
  +-----------+                                    +------------+
  |  Popup /  |  --- sendMessage (EVENT_*) --->    | Background |
  |  Options  |  <-- response (Promise) --------   |  (SW/BG)   |
  +-----------+                                    +-----+------+
                                                         |
  +-----------+                                          |
  |  Content  |  --- sendMessage (EVENT_*) --->          |
  |  Script   |  <-- response (Promise) --------        |
  +-----+-----+                                         |
        |            sendMessage (tabId) ---------------+
        +-----------------------------------------------+
        |
        |  window.postMessage
        v
  +------------+
  | Page Script|  (injected, main world)
  | fetch.js   |  thuc hien fetch that su
  +------------+
```

---

## 1. Cac thanh phan tham gia

### 1.1 Endpoint `get_pc`

**File**: `src/app/api/shopee/client/product/get.js:16-24`

```javascript
const getProductApiUrl = (productId, shopId) => {
  return queryString.stringifyUrl({
    url: `${SHOPEE_DOMAIN_HOME}/api/v4/pdp/get_pc`,
    query: {
      shop_id: shopId,
      item_id: productId,
    },
  });
};
```

URL pattern duoc dang ky trong constant:

**File**: `src/app/constant/shopee.js:11`

```
PRODUCT_GET_PC: 'https://shopee.vn/api/v4/pdp/get_pc*'
```

### 1.2 Ba phuong thuc lay san pham

**File**: `src/app/api/shopee/client/product/get.js:103-107`

| Phuong thuc | Ham | Su dung khi |
|-------------|-----|-------------|
| `request` | `requestProduct()` | Co headers bat duoc tu network (uu tien nhat) |
| `popup` | `runtimeProductId()` | Goi tu popup/options, chua co headers |
| `content` | `fetchProductId()` | Goi tu content script, fetch truc tiep |

### 1.3 In-memory Store (Background)

**File**: `src/background/store/index.js`

| Store | Kieu | Muc dich |
|-------|------|----------|
| `RequestProductPayload` | `GetListPayload` | Luu headers bat duoc tu `/api/v4/pdp/get_pc` |
| `RequestSearchPayload` | `GetListPayload` | Luu headers tu `/api/v4/search/search_items` |
| `RequestShopeeClient` | `GetListPayload` | Luu headers tu tat ca `/api*` |
| `ShopeeProductStock` | `Map` | Luu stock cua tung model variant |
| `FetchListenerBucketShopee` | `FetchListenerBucket` | Thu thap fetch data de gui len server |

### 1.4 Cache

| Tang | Luu tru | Ton tai |
|------|---------|---------|
| `CacheShopeeProduct` | In-memory | Mat khi reload extension |
| `CacheShopeeStock` | In-memory | Mat khi reload extension |
| `KVShopeeProductStock` | IndexedDB (KV) | Ton tai qua reload |

---

## 2. Flow 1: Background bat headers tu network

Khi nguoi dung duyet trang san pham Shopee, browser tu dong goi API `get_pc`. Background chan request nay de lay headers.

### So do

```
Nguoi dung mo trang san pham Shopee
         |
         v
Browser gui request: GET /api/v4/pdp/get_pc?item_id=...&shop_id=...
         |
         v
+--------------------------------------------+
| Background: webRequest.onBeforeSendHeaders |
| File: src/background/bridge/listener/      |
|       index.js:15-19                        |
|                                            |
| Pattern: SHOPEE_URLS.PRODUCT_GET_PC        |
|   = 'https://shopee.vn/api/v4/pdp/get_pc*' |
+--------------------------------------------+
         |
         v
+--------------------------------------------+
| onFetchProductId()                         |
| File: src/background/bridge/listener/      |
|       shopee/product.js:13-34              |
|                                            |
| 1. parseRequest() -> trich xuat            |
|    query, headers, url                     |
| 2. Kiem tra co item_id, shop_id,           |
|    va header 'af-ac-enc-dat'               |
| 3. Luu vao RequestProductPayload           |
|    { uuid, headers, url, query, time }     |
+--------------------------------------------+
         |
         v
+--------------------------------------------+
| RequestProductPayload (in-memory store)    |
| File: src/background/store/index.js:98     |
|                                            |
| Giu danh sach headers da bat              |
| San sang de tai su dung cho cac            |
| request tiep theo                          |
+--------------------------------------------+
```

### Chi tiet code

**File**: `src/background/bridge/listener/shopee/product.js:13-34`

- `parseRequest(payload)` trich xuat `query`, `headers`, `url`, `isIgnore` tu raw request
- Kiem tra header `af-ac-enc-dat` (header anti-bot cua Shopee) phai ton tai
- Them header `af-ac-ignore: 'yes'` de danh dau request da xu ly
- Gan UUID va timestamp cho moi entry

---

## 3. Flow 2: Content script bat response tu fetch

Dong thoi voi Flow 1, content script cung bat **response** cua cac fetch request tren trang Shopee.

### So do

```
+--------------------------------------------------+
| Content Script (isolated world)                  |
| File: src/content/service/event.js:53            |
|                                                  |
| PostMessageFetchListener.startHandler(           |
|   bridgeShopeeListenFetch                        |
| )                                                |
+--------------------------------------------------+
         ^
         | window.postMessage (ATSP_FETCH_LISTENER)
         |
+--------------------------------------------------+
| Page Script (main world)                         |
| File: src/content/shopee/script/fetch.js:29-49   |
|                                                  |
| listenerFetchData():                             |
| 1. Luu giu window.fetch goc                     |
| 2. Ghi de window.fetch moi                      |
| 3. Moi lan Shopee goi fetch:                    |
|    - Thuc hien fetch goc (khong can thiep)       |
|    - Neu URL chua '/api/': clone response        |
|    - Parse JSON va gui qua PostMessageFetchListener
+--------------------------------------------------+
         |
         v
+--------------------------------------------------+
| Background: onBridgeShopeeListenFetch()          |
| File: src/background/bridge/global.js:178-208    |
|                                                  |
| 1. filterFetchUrlPush(url):                      |
|    - Loc URL (bo qua URL trong ignore list)      |
|    - Luu vao FetchListenerBucketShopee           |
|                                                  |
| 2. Neu URL chua 'select_variation_pc'            |
|    hoac 'get_pc_vsku':                           |
|    - handleShopeeSelectVariation()               |
|    - Trich xuat stock tu response                |
|    - Luu vao ShopeeProductStock Map              |
|    - Luu vao CacheShopeeStock va KV              |
+--------------------------------------------------+
```

### PostMessage API

**File**: `src/app/bridge/content/postMessage.js`

Giao tiep giua content script (isolated world) va page script (main world) su dung lop `PostMessageApi`:

| Instance | Channel | Timeout | Muc dich |
|----------|---------|---------|----------|
| `PostMessageFetchUrl` | `ATSP_FETCH_URL_*` | 5000ms | Thuc hien fetch theo yeu cau |
| `PostMessageFetchListener` | `ATSP_FETCH_LISTENER_*` | 3000ms | Chuyen tiep fetch data da bat |

Co che:
1. Moi message duoc gan UUID de khop request/response
2. Gui qua `window.postMessage(payload, '*')`
3. Ben nhan kiem tra `channelRequest` va `messageId`
4. Response gui lai qua `window.postMessage` voi `channelReply`
5. Tu dong timeout va remove listener

---

## 4. Flow 3: Popup/Options goi lay san pham

Day la flow chinh khi nguoi dung su dung tinh nang Spy, Clone, hoac bat ky chuc nang nao can thong tin san pham.

### So do tong the

```
+--------------------------------------------------+
| Vue Component (Popup/Options)                    |
| VD: Shopee/Spy/Product/Index.vue                 |
|                                                  |
| this.ShopeeGetProduct({ productId, shopId })     |
+--------------------------------------------------+
         |
         v
+--------------------------------------------------+
| Vuex Action: ShopeeGetProduct                    |
| File: src/app/vue/store/modules/shopee/product.js|
|                                                  |
| Goi: getShopeeProductId(                         |
|   productId, shopId, 'popup', isFetchRealData    |
| )                                                |
+--------------------------------------------------+
         |
         v
+--------------------------------------------------+
| getShopeeProductId()                             |
| File: src/app/api/shopee/client/product/         |
|       get.js:271-370                             |
|                                                  |
| Buoc 1: Check cache (CacheShopeeProduct)         |
|   - Neu co va !isFetchRealData -> return cache   |
|   - Neu co va isFetchRealData -> bo sung stock   |
|                                                  |
| Buoc 2: Thu 3 phuong thuc (theo thu tu):         |
|   1. request -> requestProduct()                 |
|   2. content -> fetchProductId() (neu method=    |
|      content)                                    |
|   3. popup -> runtimeProductId() (neu method=    |
|      popup)                                      |
|                                                  |
| Buoc 3: Xu ly response                           |
|   - adapterDetailProduct() -> chuan hoa du lieu  |
|   - refactorProduct() -> chuyen doi schema       |
|     (goi 1 lan duy nhat, dung chung cho ca 2     |
|      nhanh isFetchRealData true/false)            |
|   - Neu isFetchRealData:                         |
|     + pullProductExtraInfo() -> lay SKU, dimension|
|     + fetchStockProduct() -> bo sung stock        |
|                                                  |
| Buoc 4: Cache va return                          |
|   - CacheShopeeProduct.set()                     |
|   - Return productData                           |
+--------------------------------------------------+
```

### 4.1 Phuong thuc `request` — Su dung headers da bat

**File**: `src/app/api/shopee/client/product/get.js:35-79`

```
1. getRequestProductId(productId, shopId)
   - Lay headers tu RequestProductPayload (in-memory store)
   - Match theo item_id va shop_id trong query
   |
   v
2. fetchJson(url, headers)
   - Fetch truc tiep voi headers that cua Shopee
   - Concurrency: 1 (tuan tu tung request)
   |
   v
3. checkCrawlerDetected(response)
   - Kiem tra bi Shopee phat hien la bot khong
   - Neu bi phat hien -> bo qua, thu header tiep theo
```

### 4.2 Phuong thuc `popup` — Fetch qua content script bridge

**File**: `src/app/api/shopee/client/product/get.js:81-90`

Day la flow phuc tap nhat, di qua nhieu tang:

```
runtimeProductId(productId, shopId)
         |
         v
readyFetchUrlShopeeClient(url)               [bridge/shopee/api.js:11-22]
  1. makeTabShopeeClient()                    [bridge/shopee/tab.js]
     - Tim tab Shopee da mo (shopee.vn)
     - Neu chua co -> tao tab moi, doi load
     - Loc bo tab bi block (/verify) hoac captcha
  2. bridgeTabFetchShopeeUrl(tab.id, payload) [bridge/content/tab.js]
         |
         v
EVENT_TAB_FETCH_URL (webext-bridge)
  Background --> Content Script (theo tabId)
         |
         v
Content Script nhan message                   [content/service/event.js:10-18]
  PostMessageFetchUrl.sendMessage(data)
         |
         v  (window.postMessage: ATSP_FETCH_URL_REQUEST)
Page Script (main world)                      [content/shopee/script/fetch.js:3-27]
  fetchUrlService(payload):
  1. Tao payload voi headers:
     - accept: 'application/json'
     - af-ac-enc-dat: null
     - content-type: 'application/json'
  2. fetch(url, payloadFetch)
     -> Thuc hien trong context Shopee
     -> Tu dong co cookie/session
  3. Parse response.json()
         |
         v  (window.postMessage: ATSP_FETCH_URL_REPLY)
Content Script nhan response
         |
         v  (webext-bridge response)
Background / Popup nhan response JSON
```

### 4.3 Phuong thuc `content` — Fetch truc tiep

**File**: `src/app/api/shopee/client/product/get.js:92-101`

Su dung khi code dang chay trong context cua content script (VD: loader tren trang san pham).

```
fetchProductId(productId, shopId)
         |
         v
excuteFetchUrl(url)                           [api/shopee/fetch.js]
  - Inject script vao page de fetch
  - Tuong tu popup nhung khong can qua bridge
```

---

## 5. Flow xu ly Stock (ton kho)

Stock cua tung variant san pham duoc thu thap tu 3 nguon:

### 5.1 Tu hanh vi nguoi dung (tu dong)

Khi nguoi dung click chon variant tren trang Shopee, Shopee goi API `select_variation_pc`. Content script bat response nay va gui ve background.

```
Nguoi dung click variant tren trang Shopee
         |
         v
Shopee frontend goi: POST /api/v4/pdp/cart_panel/select_variation_pc
         |
         v
Content script bat response (listenerFetchData)
         |
         v
bridgeShopeeListenFetch() -> Background
         |
         v
onBridgeShopeeListenFetch()                   [background/bridge/global.js:178-208]
  URL chua 'select_variation_pc' hoac 'get_pc_vsku'
         |
         v
handleShopeeSelectVariation(body, jsonData)    [background/bridge/global.js:89-171]
  1. Trich xuat productId, shopId, modelId tu body
  2. Lay max_quantity tu response -> modelStock
  3. Doc listStock hien tai tu:
     - ShopeeProductStock Map (in-memory)
     - CacheShopeeStock (in-memory cache)
     - KVShopeeProductStock (IndexedDB)
  4. Push {modelId, modelStock, stock} vao listStock
  5. Dedupe theo modelId
  6. Luu vao ca 3 tang:
     - ShopeeProductStock.set()
     - CacheShopeeStock.set()
     - KVShopeeProductStock.set()
```

### 5.2 Tu AutoShopee API (bo sung)

**File**: `src/app/api/shopee/client/product/get.js:207-261`

```
pullProductExtraInfo(productData)
         |
         v
getShopeeProductExtraInfo(shopId, productId)   [api/autoshopee/public.js]
  - Goi AutoShopee backend API
  - Tra ve: listModelSku, listModelDimension
         |
         v
Enrich product.models:
  - model.stock = matchStock.stock
  - model.sku = matchStock.modelSku
  - product.sku = matchStock.productSku
  - product.dimension = { weight, width, height, length }
```

### 5.3 Tu API Shopee truc tiep

**File**: `src/app/api/shopee/client/product/get.js:109-126`

```
fetchShopeeProductStock(productId, shopId, tierIndex, tierValue)
         |
         v
readyPostUrlShopeeClient(
  'https://shopee.vn/api/v4/pdp/cart_panel/select_variation_pc',
  payload
)
  - Gui POST request qua content script bridge
  - Payload: { item_id, shop_id, quantity, selected_tiers }
```

---

## 6. Xu ly response san pham

Sau khi lay duoc raw data tu Shopee API, response di qua 4 buoc xu ly:

```
Raw API Response (data tu get_pc)
         |
         v
adapterDetailProduct(productDetail)            [adapter/shopee/adapter.js]
  - Chuan hoa response format
  - Xu ly truong hop response co nhieu format khac nhau
         |
         v
refactorProduct(productRender)                 [adapter/shopee/product.js]
  - Chuyen doi tu Shopee schema sang internal schema
  - Map cac field: item_id, shop_id, name, images, models, ...
  - Goi 1 lan duy nhat, ket qua (productData) dung chung
    cho ca nhanh isFetchRealData va khong isFetchRealData
         |
         +--- isFetchRealData = false ---> Cache & return productData
         |
         +--- isFetchRealData = true ---+
                                        |
                                        v
pullProductExtraInfo(productData)              [api/shopee/client/product/get.js:207-261]
  - Lay them SKU, dimension tu AutoShopee API
  - Enrich product.models voi real stock
         |
         v
fetchStockProduct(productData)                 [api/shopee/client/product/get.js:161-202]
  - Bo sung stock tu cache (ShopeeProductStock, KV)
  - Match theo modelId hoac tier_index
  - Set: model.model_stock, model.real_stock, model.stock
  - Danh dau: model.fetched_stock = true
```

---

## 7. Tai sao fetch phai qua Main World?

Content script chay trong **isolated world** — khong chia se JavaScript context voi trang Shopee. Khi content script goi `fetch()`, request **khong co**:

1. **Cookie Shopee** — can cho authentication
2. **Same-origin context** — bi CORS chan
3. **Headers anti-bot** — `af-ac-enc-dat`, session tokens

Giai phap: inject script vao **main world** cua trang Shopee (`src/content/shopee/script/fetch.js`). Script nay chay cung context voi Shopee frontend, nen:

- `fetch()` tu dong dinh kem cookie
- Khong bi CORS (same-origin)
- Co the dat headers dac biet cua Shopee

Giao tiep giua isolated world va main world su dung `window.postMessage` thong qua lop `PostMessageApi`.

---

## 8. Thu tu uu tien khi lay san pham

`getShopeeProductId()` luon thu theo thu tu sau, dung lai khi thanh cong:

```
1. Cache (CacheShopeeProduct)
   |-- Co    -> return (hoac bo sung stock neu isFetchRealData)
   |-- Khong -> tiep tuc
   v
2. Request method (requestProduct)
   - Su dung headers bat tu webRequest.onBeforeSendHeaders
   - Uu diem: headers that 100%, it bi detect
   - Nhuoc diem: chi co khi nguoi dung da mo trang san pham truoc do
   |-- Co    -> xu ly response
   |-- Khong -> tiep tuc
   v
3. Content method (fetchProductId) — chi khi method='content'
   - Fetch truc tiep tu content script qua PostMessage
   - Uu diem: nhanh, khong can tim tab
   - Nhuoc diem: chi kha dung khi dang o trong content script
   |-- Co    -> xu ly response
   |-- Khong -> tiep tuc
   v
4. Popup method (runtimeProductId) — chi khi method='popup'
   - Tim/tao tab Shopee, gui fetch qua bridge
   - Uu diem: hoat dong tu bat ky dau (popup, options)
   - Nhuoc diem: cham hon, can tab Shopee
   |-- Co    -> xu ly response
   |-- Khong -> return null (that bai)
```

---

## 9. Danh sach file lien quan

### Core API

| File | Vai tro |
|------|---------|
| `src/app/api/shopee/client/product/get.js` | Ham chinh `getShopeeProductId()`, 3 phuong thuc fetch, xu ly stock |
| `src/app/api/shopee/fetch.js` | `fetchJson()`, `excuteFetchUrl()` — thuc thi HTTP request |
| `src/app/api/shopee/getter.js` | `getRequestProductId()` — lay headers tu store; `checkCrawlerDetected()` |
| `src/app/api/autoshopee/public.js` | `getShopeeProductExtraInfo()` — lay SKU, dimension tu AutoShopee API |

### Adapter

| File | Vai tro |
|------|---------|
| `src/app/adapter/shopee/adapter.js` | `adapterDetailProduct()` — chuan hoa raw response |
| `src/app/adapter/shopee/product.js` | `refactorProduct()` — chuyen doi sang internal schema |

### Bridge (Messaging)

| File | Vai tro |
|------|---------|
| `src/app/bridge/config/event.js` | Dinh nghia tat ca event name |
| `src/app/bridge/content/background.js` | Content Script --> Background (webext-bridge) |
| `src/app/bridge/content/postMessage.js` | Content Script <--> Page Script (window.postMessage) |
| `src/app/bridge/content/tab.js` | Background --> Content Script (chrome runtime) |
| `src/app/bridge/shopee/api.js` | `readyFetchUrlShopeeClient()` — tim tab va gui fetch |
| `src/app/bridge/shopee/tab.js` | Quan ly tab Shopee (tim, tao, loc block/captcha) |

### Background

| File | Vai tro |
|------|---------|
| `src/background/bridge/listener/index.js` | Dang ky webRequest listeners |
| `src/background/bridge/listener/shopee/product.js` | `onFetchProductId()` — bat headers tu network |
| `src/background/bridge/global.js` | `onBridgeShopeeListenFetch()`, `onBridgeShopeeGetProduct()`, `handleShopeeSelectVariation()` |
| `src/background/store/index.js` | In-memory store: `RequestProductPayload`, `ShopeeProductStock` |

### Content Script

| File | Vai tro |
|------|---------|
| `src/content/shopee/script/fetch.js` | Page script (main world): `fetchUrlService()`, `listenerFetchData()` |
| `src/content/service/event.js` | `listenerFetch()` — dang ky handler cho `EVENT_TAB_FETCH_URL` va `PostMessageFetchListener` |

### Cache & Storage

| File | Vai tro |
|------|---------|
| `src/app/plugins/store/cache.js` | `CacheShopeeProduct`, `CacheShopeeStock` — in-memory cache |
| `src/app/plugins/kv.js` | `KVShopeeProductStock` — IndexedDB persistent storage |

### Vuex Store

| File | Vai tro |
|------|---------|
| `src/app/vue/store/modules/shopee/product.js` | Vuex action `ShopeeGetProduct` — entry point tu Vue component |

### Constants

| File | Vai tro |
|------|---------|
| `src/app/constant/shopee.js` | `SHOPEE_URLS.PRODUCT_GET_PC`, `SHOPEE_HEADER_KEYS` |

---

## 10. Van de da biet va trang thai

### Da fix

| # | Van de | File | Trang thai |
|---|--------|------|------------|
| 1 | `getShopeeProductId()` return `getCache` (luon null) thay vi `productData` khi cache miss va `isFetchRealData = false` | `api/shopee/client/product/get.js:358` | **Da fix** |
| 2 | `refactorProduct()` duoc goi 2 lan tren cung data khi `isFetchRealData = true` — lang phi xu ly | `api/shopee/client/product/get.js:353,363` | **Da fix** |

### Chua fix

| # | Muc do | Van de | File | Mo ta |
|---|--------|--------|------|-------|
| 3 | **High** | `excuteFetchUrl()` inject script bang string interpolation | `api/shopee/fetch.js:102-135` | `fetchUrl` duoc noi truc tiep vao chuoi JavaScript. Neu URL chua ky tu dac biet (VD: single quote) → break script hoac XSS. Ngoai ra dung `delay()` co dinh thay vi doi event — khong dang tin cay. **De xuat**: bo `excuteFetchUrl`, thong nhat dung `PostMessageFetchUrl` |
| 4 | **Medium** | `RequestProductPayload` khong bao gio clear | `background/store/index.js:98` | `GetListPayload` co `clearList()` nhung khong ai goi. Headers cu luu mai → memory leak, va `requestProduct()` phai duyet list dai. **De xuat**: gioi han size (VD: giu 20 entry moi nhat) hoac clear sau khi dung |
| 5 | **Medium** | `requestProduct()` luon chay round-trip vo ich | `api/shopee/client/product/get.js:299-309` | `requestProduct()` luon goi dau tien bat ke `method`. Neu khong co headers → gui bridge message, nhan list rong, return null. Mat 1 round-trip. **De xuat**: check `active` truoc khi fetch |
| 6 | **Medium** | Logic doc 3 tang cache trung lap | `background/bridge/global.js:124-147` va `api/shopee/client/product/get.js:134-155` | Ca `handleShopeeSelectVariation()` va `getShopeeProductStock()` deu co cung logic: Map → Cache → KV. **De xuat**: trich thanh 1 ham chung |
| 7 | **Low** | `fetchStockProduct()` va `pullProductExtraInfo()` mutate truc tiep object | `api/shopee/client/product/get.js:161-261` | Sua truc tiep `productData.models` — side effect, kho debug neu 1 ham fail giua chung |
| 8 | **Low** | `loggerShopee.error()` dung cho ca success log | `api/shopee/client/product/get.js:60,278,302,314,326` | Tat ca log (ke ca thanh cong) deu dung `.error()`. **De xuat**: dung `.info()` cho success |
