# Conversion Chat AI → Laravel Order — แผนงานทั้ง 2 ฝั่ง

> เอกสารนี้เป็นแผนงานปฏิบัติการสำหรับวัด funnel จาก LINE AI แนะนำสินค้า → เปิดหน้าเว็บ → ใส่ตะกร้า → ชำระเงิน  
> โดย **ไม่ย้ายระบบ order มาที่ Chat**

| | |
|---|---|
| สถานะ | **เฟส 1 ฝั่ง Laravel ลงมือแล้ว** (L1.1–L1.6) — รอ Chat ติด query + UAT T1–T7 |
| เจ้าของร่วม | Chat (chat-v2 / Admin) + Laravel (The Medicine web) |
| เอกสารที่เกี่ยวข้อง | [`pharmacy-chat-internal-api.md`](./pharmacy-chat-internal-api.md), [`pharmacy-conversion-attribution-handoff.md`](./pharmacy-conversion-attribution-handoff.md) |
| ทางเลือกเริ่มต้น | **ทางเลือก 1** — Attribution ใน Order (รายงานฝั่ง Laravel/Excel ก่อน) |
| อัปเดตล่าสุด | 2026-07-21 — stamp ที่คอลัมน์ `order_orders.ai_attribution` (แยกจาก `json`) |

---

## 1) สถานะปัจจุบัน (ของจริง)

### 1.1 จุดเชื่อม Chat ↔ Laravel ที่มีแล้ว

| จุดเชื่อม | ทิศทาง | Endpoint |
|---|---|---|
| Catalog sync | Laravel → Chat | `GET /api/internal/pharmacy/products` |
| Availability + `product_url` | Laravel → Chat | `POST /api/internal/pharmacy/availability` |
| LINE ↔ member | Laravel → Chat | `POST /api/internal/member/line-user-id/check` |
| Member by uid | Laravel → Chat | `POST /api/internal/member/uid/check` |

### 1.2 พฤติกรรมฝั่ง Chat ตอนแนะนำสินค้า

1. ค้นหาสินค้า → เรียก availability  
2. ได้ `product_url` จาก Laravel แล้วใส่ใน product card ของ LINE **ตามที่ส่งมาเป๊ะ**  
3. **ยังไม่มี** `utm_*` / `chat_sid` / `rec_id` บนลิงก์  
4. Chat รู้ได้: สนทนา, LINE/member uid, เวลา, ประเภทคำถาม, สินค้าที่แนะนำ  
5. Chat **ไม่รู้** add-to-cart / checkout / paid

### 1.3 สิ่งที่พบจากโค้ด Laravel (สำคัญต่อแผน)

| หัวข้อ | สถานะ |
|---|---|
| เพิ่ม query บน `product_url` | ได้ — routing `/{slug?}` ไม่พังจาก query |
| อ่าน `utm_*` / `chat_sid` ที่ PDP | ยังไม่ทำ |
| คอลัมน์ utm/channel บน `order_orders` | มีแล้ว — **`ai_attribution`** (แยกจาก `json`) |
| Guest cart / guest checkout | **ไม่มี** — ต้อง login member ก่อนสั่งซื้อ |
| PDP บังคับ login | มี — `FrontendController::product_list` redirect ไป login **โดยยังไม่ส่ง URL เดิม** → **เสี่ยงทิ้ง attribution** |
| นิยาม paid ที่เสนอ | `payment_status = COMPLETED` (ไม่ใช้ order `status = COMPLETED`) |

### 1.4 ข้อสรุปสถานะ

- วัด conversion จนชำระเงิน **ทำได้** ถ้า Laravel รับ attribution + เก็บ event  
- Chat อย่างเดียวทำได้แค่ขั้น **แนะนำสินค้า**  
- **Blocker หลักของ PoC:** login redirect ทิ้ง query → ต้องแก้คู่กับเฟส 1

---

## 2) เป้าหมายและ KPI

### 2.1 Funnel ที่วัด

| ขั้น | ความหมาย | เจ้าของข้อมูลหลัก |
|---|---|---|
| A | AI recommend product | Chat |
| B | Click เปิดหน้าสินค้า (PDP) | Laravel |
| C | Add to cart | Laravel |
| D | Checkout / Paid | Laravel |

### 2.2 KPI

- Recommend → Click  
- Click → Add to cart  
- Add to cart → Paid  
- (ออปชัน) Recommend → Paid  

**ข้อตกลง:** conversion ของ LINE AI ≠ ยอดขายทั้งร้าน

---

## 3) โมเดลและสัญญา Attribution

### 3.1 Flow

```
[chat-v2] recommend
   │  บันทึก recommendation event ใน chat DB
   │  แนบ query บน product_url
   ▼
[Laravel web] PDP / Cart / Order
   │  อ่าน attribution จาก URL → cookie/session
   │  ไม่ให้ login redirect ทิ้ง param
   │  stamp ลง order meta + (เฟส 2) funnel events
   ▼
(ทางเลือกภายหลัง) webhook กลับ Chat หรือ BI join
```

- **Laravel** = แหล่งความจริงของ cart/order  
- **Chat** = แหล่งความจริงของ “แนะนำจาก AI”

### 3.2 พารามิเตอร์บนลิงก์ (**ล็อกแล้ว** — ใช้ชื่อชุดนี้ทั้งสองฝั่ง)

```text
{product_url}
  ?utm_source=line_ai
  &utm_medium=chat
  &utm_campaign=pharmacy_recommend
  &chat_sid={conversationId}
  &rec_id={recommendationId}
  &pid={laravelProductId}
```

| พารามิเตอร์ | ความหมาย | ใครสร้าง |
|---|---|---|
| `utm_source=line_ai` | ช่องทาง | Chat |
| `utm_medium=chat` | สื่อ | Chat |
| `utm_campaign=pharmacy_recommend` | แคมเปญ | Chat |
| `chat_sid` | = `v2_conversations.conversation_id` (ชื่อเดียวกันทั้งสองฝั่ง เพื่อไล่ตรวจสอบง่าย) | Chat |
| `rec_id` | รหัสครั้งที่แนะนำ — **1 ค่าต่อเทิร์นแนะนำ** (ไม่ต่อ card); รวม token ของเทิร์นนั้นด้วยใน event log | Chat |
| `pid` | `product_products.id` | Chat |

### 3.3 ข้อตกลงที่ล็อกแล้ว (เฟส 0)

| หัวข้อ | ค่าที่ล็อก |
|---|---|
| ชื่อพารามิเตอร์ | ใช้ชุดใน §3.2 **เหมือนกันทั้ง Chat และ Laravel** |
| อายุ cookie/session | **30 วัน** — ชื่อ cookie: `tm_ai_attr` |
| นิยาม paid | **`payment_status = COMPLETED`** |
| รักษา attribution ตอน login | **แบบ B** — `redirect={fullUrl}` ที่มี query ครบ แล้วหลัง login กลับ PDP |
| ที่เก็บบน order (PoC) | คอลัมน์ **`order_orders.ai_attribution`** (json) — แยกจาก `json` ที่ถูกทับตอนเปลี่ยนสถานะ |
| รายงานเฟส 1 | **ทางเลือก 1** — ใน Laravel / Excel |
| คนแชท ≠ คนสั่ง | ใช้ `chat_sid`/`rec_id` เป็นหลัก หลัง login ผูก `member_id`/`uid` เพิ่ม |
| Attribution เมื่อหลายสินค้าในตะกร้า | **ยังไม่ล็อกกติกา** — PoC เริ่มแบบ last-touch ได้ก่อน; ต้องไล่ผลกระทบก่อนเฟส 2 |
| ยกเลิก / refund | **แยกสถานะ** (ไม่ตัด conversion ทิ้งเงียบ ๆ) |
| Webhook ส่ง `amount` / `member_uid` กลับ Chat | **ได้** (เมื่อทำทางเลือก 2) |
| สิทธิ์ดูรายงาน / event | **Admin เท่านั้น** |
| ข้อความ PDPA | ปรับทีหลังได้ (ไม่บล็อกเฟส 1) |
| SEO/CDN กับ query | **ยังไม่ได้ทดสอบ** — ใส่ในงาน L1.7 |

### 3.4 บันทึกคำตอบ kickoff (2026-07-21)

| # | คำถาม | คำตอบ |
|---|---|---|
| 1 | ชื่อพารามิเตอร์ | ใช้ชื่อเดียวกันทั้งสองฝั่ง (§3.2) |
| 2 | อายุ cookie | 30 วัน |
| 3 | นิยาม paid = `payment_status=COMPLETED` | ใช่ |
| 4 | ทางรายงาน | คงทางเลือก 1 |
| 5 | มี UAT ทดสอบจาก LINE จริง | มี |
| 6 | วิธีแก้ login ไม่ทิ้ง attribution | **B** (`redirect=fullUrl`) |
| 7 | ที่เก็บบน order | **ล็อกแล้ว** = คอลัมน์ `order_orders.ai_attribution` |
| 8 | แหล่ง `chat_sid` | `v2_conversations.conversation_id` ให้เหมือนกัน |
| 9 | ระดับ `rec_id` | **ต่อเทิร์น**; รวม token ของเทิร์นใน event |
| 10 | จุดต่อ query ที่ product card / flex | ใช่ — ถ้าผิดค่อยแก้ |
| 11 | schema `recommendation_events` | ใช้ชุดขั้นต่ำตามแผนไปก่อน |
| 12 | ตัวอย่าง `product_url` | `http://127.0.0.1:8000/acne-aid-foaming-cleanser-100ml-exea` |
| 13 | SEO/CDN | ยังไม่ได้ทดสอบ |
| 14 | ชื่อ cookie `tm_ai_attr` | ได้ |
| 15 | กติกาหลายสินค้า (last-touch) | ยังไม่แน่ใจผลกระทบ — เปิดค้าง |
| 16 | ยกเลิก/refund | แยกสถานะ |
| 17 | ส่ง amount/member_uid กลับ Chat | ได้ |
| 18 | ใครเห็น LINE id / amount / event | Admin เท่านั้น |
| 19 | ข้อความ PDPA | ไม่สำคัญ ปรับทีหลังได้ |

### ทางเลือกสถาปัตย์

| ทางเลือก | สรุป | เมื่อใช้ |
|---|---|---|
| **1 — Attribution ใน Order (เริ่มที่นี่)** | Chat ติด query + log recommend; Laravel stamp order meta; รายงาน Laravel/Excel | พิสูจน์ว่าวัดได้เร็ว |
| 2 — Webhook กลับ Chat | Laravel ยิง `add_to_cart` / `paid` กลับ Chat Admin | อยากเห็น funnel ในแดชบอร์ดแชท |
| 3 — BI รวม | Export ทั้งสองฝั่ง แล้ว join `chat_sid`/`rec_id` | มี Looker/Metabase อยู่แล้ว |

---

## 4) แผนงานรายเฟส — ทั้ง 2 ฝั่ง

### เฟส 0 — Discovery / Kickoff (3–5 วัน)

**เป้าหมาย:** ล็อกสัญญา + รู้ว่า login ไม่ทิ้ง attribution ได้อย่างไร  
**สถานะ:** สัญญาหลักล็อกแล้ว (§3.3–3.4) — เหลือยืนยันผลกระทบหลายสินค้า + SEO/CDN ระหว่างเฟส 1

#### งานร่วม

- [x] ประชุม kickoff ส่งเอกสารนี้ + checklist  
- [x] ยืนยันชื่อพารามิเตอร์ (`chat_sid`, `rec_id`, …) — ใช้ชื่อเดียวกันทั้งสองฝั่ง  
- [x] ยืนยันอายุ cookie = 30 วัน (`tm_ai_attr`)  
- [x] ยืนยันนิยาม paid = `payment_status = COMPLETED`  
- [x] เลือกทางเลือกรายงาน = ทางเลือก 1  
- [x] ยืนยัน UAT ที่ทดสอบจาก LINE จริงได้ — **มี**  
- [ ] ตกลงกติกาหลายสินค้าในตะกร้า (ยังไม่แน่ใจผลกระทบ — PoC ใช้ last-touch ชั่วคราวได้)

#### งานฝั่ง Laravel

- [x] ยืนยันจุดสร้าง `product_url` (`PharmacyAvailabilityService` / `SlugController`)  
- [x] ยืนยันพฤติกรรม `FrontendController::product_list` เมื่อยังไม่ login  
- [x] เลือกวิธีรักษา attribution ตอน redirect login = **B)** `redirect={fullUrl}` ที่มี query ครบ แล้วหลัง login กลับ PDP  
- [x] ที่เก็บบน order = คอลัมน์ `order_orders.ai_attribution` (แยกจาก `json`)
- [x] ส่งตัวอย่าง `product_url`: `http://127.0.0.1:8000/acne-aid-foaming-cleanser-100ml-exea`

#### งานฝั่ง Chat

- [x] ยืนยันแหล่ง `chat_sid` = `v2_conversations.conversation_id`  
- [x] schema `recommendation_events` — ใช้ชุดขั้นต่ำตามแผนไปก่อน; **`rec_id` ต่อเทิร์น** + เก็บ token ของเทิร์น  
- [x] จุดต่อ query ใน `extractProductCards` / ส่ง LINE flex — ทำตามนี้ ถ้าผิดค่อยแก้  
- [x] เตรียม env/UAT ที่ยิงลิงก์ไปโดเมนเว็บได้ (มี UAT LINE)

#### Definition of Done — เฟส 0

- [x] Checklist คำถามหลักตอบครบ (§3.4)  
- [x] มี design แก้ login redirect ที่ทีมยอมรับ (= แบบ B)  
- [x] ชื่อ param + paid + cookie TTL ล็อกแล้ว  
- [x] Go/No-Go = **Go** สำหรับเฟส 1 (ดู §8)  
- [ ] กติกาหลายสินค้า + SEO/CDN — เปิดค้าง ไม่บล็อกเริ่ม PoC
---

### เฟส 1 — PoC Attribution (1–2 สัปดาห์) ⭐ งานหลัก

**เป้าหมาย:** มีอย่างน้อย **1 ออเดอร์ทดสอบ** ที่ผูก `chat_sid` ได้ครบเส้นทาง LINE → login → PDP → cart → paid

#### 4.1 งานฝั่ง Chat (chat-v2)

| # | งาน | รายละเอียด | DoD |
|---|---|---|---|
| C1.1 | ติด query บน `productUrl` | เมื่อสร้าง product card จาก `lookup_medicine` ต่อ query ตามสัญญา (§3.2) โดยไม่ทำลาย URL เดิม | ลิงก์ใน LINE มี `utm_*`, `chat_sid`, `rec_id`, `pid` |
| C1.2 | สร้าง `rec_id` | **1 `rec_id` ต่อเทิร์นแนะนำ** (ไม่ต่อ card); ใน `recommendation_events` เก็บ token ของเทิร์นนั้นด้วย | ทุกลิงก์ในเทิร์นเดียวกันใช้ `rec_id` เดียวกัน; event มี token |
| C1.3 | บันทึก recommendation event | เก็บอย่างน้อย: `rec_id`, `conversation_id` (`chat_sid`), `external_user_id`, `product_id`/`external_id`, `url_sent`, `issue_type`, **`token` (ของเทิร์น)**, `created_at` — schema ใช้ชุดนี้ไปก่อน | query DB แล้วย้อนดูได้ว่า recommend อะไร เมื่อไหร่ + token |
| C1.4 | ไม่เปลี่ยนความหมาย card | กรอง `visible` + `inStock` เหมือนเดิม | regression: การ์ดยังโชว์เฉพาะสินค้าที่มองเห็นและมีสต็อก |
| C1.5 | เอกสารสั้นใน repo | อัปเดตส่วน attribution ใน docs นี้หลังลงมือ | ทีม Laravel อ่าน param จริงตรงกับโค้ด |

**จุดโค้ดอ้างอิง (เริ่มดู):**

- `apps/chat-v2/src/chat/product-cards.util.ts` — สร้าง card + URL  
- `apps/chat-v2/src/external/pharmacy.client.ts` — ได้ `productUrl` จาก availability  
- `v2_conversations.conversation_id` — ใช้เป็น `chat_sid`

#### 4.2 งานฝั่ง Laravel (เว็บร้านยา)

| # | งาน | รายละเอียด | DoD |
|---|---|---|---|
| L1.1 | **แก้ login ไม่ทิ้ง attribution** | ใช้ **แบบ B**: ก่อน redirect ไป login ส่ง `redirect={fullUrl}` ที่มี query attribution ครบ แล้วหลัง login กลับ PDP พร้อม param | เทส T5* ผ่านบนเบราว์เซอร์ปกติ + LINE in-app |
| L1.2 | อ่าน param ที่ PDP | ที่ `product_list` / `FrontendController::slug` อ่าน `chat_sid`, `rec_id`, `utm_*`, `pid` | log หรือ debug เห็นค่าหลังเข้า PDP |
| L1.3 | เก็บ cookie/session | ชื่อ cookie: **`tm_ai_attr`** (json) อายุ **30 วัน**; first-party บนโดเมน checkout | ข้ามหน้าแล้วยังมีค่า |
| L1.4 | Merge หลัง login | หลัง login จาก `redirect` กลับ PDP — ค่ายังอยู่ใน URL/cookie และผูก `member_id` ได้ | T5*/T6* ผ่าน |
| L1.5 | Stamp ลง order | เขียนลงคอลัมน์ **`ai_attribution`** เช่น `{ "chat_sid", "rec_id", "utm_source", "pid", "captured_at" }` — ไม่ใช้ `json` | ออเดอร์ทดสอบมี attribution ครบหลังเปลี่ยนสถานะด้วย |
| L1.6 | (ออปชัน PoC) log ตอนเข้า PDP | ตารางหรือ log `product_view_ai` สำหรับนับ Click | นับ Recommend→Click ได้แม้ยังไม่ซื้อ |
| L1.7 | ไม่กระทบ SEO/CDN | query ไม่ทำให้ cache แยกหน้าสินค้าพัง — **ยังไม่ได้ทดสอบใน kickoff** | ทีมยืนยันระหว่าง/หลัง PoC |

**จุดโค้ดอ้างอิง (จาก discovery):**

- `FrontendController::product_list` — login gate + อ่าน query  
- `Livewire Product::addToCart` → `OrderController::process_update_item`  
- `process_checkout` / `process_update_payment_status`  
- `DetectLineInAppBrowser` — มีอยู่แล้ว แต่อย่าสับสนกับ attribution

#### 4.3 Acceptance เฟส 1

| เทส | ฝั่ง | คาดหวัง |
|---|---|---|
| T1 | ทั้งคู่ | ลิงก์มี query ใช้งานได้ (เข้า PDP หรือ redirect login แล้วยังไม่เสีย attribution) |
| T2 | Laravel | อ่าน `chat_sid` / `rec_id` ได้ |
| T3 | Laravel | ข้ามหน้าแล้ว cookie ยังอยู่ |
| T4 | Laravel | ออเดอร์ทดสอบมี attribution ในคอลัมน์ `ai_attribution` |
| T5* | Laravel | คลิกตอนยังไม่ login → หลัง login attribution ยังอยู่ → กลับ PDP ได้ |
| T6* | ทั้งคู่ | login แล้วสั่งจน `payment_status=COMPLETED` → order ผูก `chat_sid` ได้ และ Chat มี recommend event ของ `rec_id` นั้น |
| T7 | LINE UAT | ทำ T5*+T6* บน LINE in-app browser อย่างน้อย 1 รอบ |

#### Definition of Done — เฟส 1

- [ ] T1–T4, T5*, T6* ผ่านใน UAT  
- [ ] T7 ผ่านบน LINE จริง (หรือบันทึก blockers ถ้า LINE ตัด cookie)  
- [ ] มีอย่างน้อย 1 ออเดอร์ทดสอบที่ join กับ `recommendation_events` ด้วย `chat_sid`/`rec_id` ได้  
- [ ] **ยังไม่** อัปเดตข้อความ PDPA เรื่อง conversion จนชำระเงิน จนกว่าข้อนี้ผ่าน

---

### เฟส 2 — Funnel ครบขั้น (1–2 สัปดาห์หลัง PoC)

**เป้าหมาย:** นับได้ครบ A→B→C→D และมีกติกาธุรกิจชัด

#### งานฝั่ง Laravel

| # | งาน | รายละเอียด |
|---|---|---|
| L2.1 | Event `product_view_ai` | บันทึกเมื่อเข้า PDP พร้อม attribution จาก LINE AI |
| L2.2 | Event `add_to_cart` | ที่ `addToCart` / `process_update_item` — ผูก attribution ที่ยังมีใน cookie |
| L2.3 | Event `checkout_start` | เมื่อเข้าสถานะชำระเงิน (`PAYMENT` / เทียบเท่า) |
| L2.4 | Event `paid` | เมื่อ `payment_status = COMPLETED` (โอนธนาคารต้องรอ confirm ไม่นับตอนอัปโหลดสลิป) |
| L2.5 | กติกาหลายชิ้น | **ยังไม่ล็อก** — ไล่ผลกระทบก่อน; PoC ใช้ last-touch ทั้งออเดอร์ชั่วคราวได้ หรือ per `order_orders_items` |
| L2.6 | ยกเลิก/refund | **แยกสถานะ** (ไม่ตัด conversion ทิ้งเงียบ) |
| L2.7 | รายงานขั้นต่ำใน Laravel | จำนวน/อัตราต่อขั้น + top products — **สิทธิ์ Admin เท่านั้น** |

#### งานฝั่ง Chat

| # | งาน | รายละเอียด |
|---|---|---|
| C2.1 | (ถ้าเลือกทางเลือก 2) รับ webhook | Endpoint internal รับ `add_to_cart` / `paid` |
| C2.2 | (ถ้าเลือกทางเลือก 2) เก็บ conversion events | join กับ `recommendation_events` |
| C2.3 | (ถ้าเลือกทางเลือก 2) แสดงใน Admin | หน้าสรุป funnel เบื้องต้น |
| C2.4 | (ทางเลือก 3) Export | ให้ BI ดึง recommend events |

#### Payload webhook ร่าง (ทางเลือก 2 เท่านั้น)

```http
POST {CHAT_INTERNAL}/internal/pharmacy/conversion-events
Authorization: Bearer {shared-key}

{
  "event": "add_to_cart" | "checkout_start" | "paid",
  "occurred_at": "2026-07-21T10:00:00+07:00",
  "chat_sid": "...",
  "rec_id": "...",
  "product_id": 123,
  "order_id": "SO-1001",
  "amount": 350.00,
  "member_uid": "..."
}
```

**PDPA webhook:** ส่งได้แค่ order id, amount, member_uid, chat_sid, rec_id — **ห้าม** ที่อยู่ / สลิป / บัตร  
**สิทธิ์:** event / รายงาน (รวม amount, LINE id) = **Admin เท่านั้น**  
**Kickoff:** ส่ง `amount` / `member_uid` กลับ Chat **ได้** เมื่อทำทางเลือก 2

#### Definition of Done — เฟส 2

- [ ] นับ Recommend / Click / Cart / Paid ได้ใน UAT  
- [ ] กติกาหลายชิ้น + paid + refund เอกสารไว้แล้ว  
- [ ] ถ้าทำ webhook: Chat รับและเก็บ event ได้

---

### เฟส 3 — รายงาน + PDPA + Hardening

#### งานร่วม / Product

| # | งาน |
|---|---|
| P3.1 | Dashboard: #recommend, #click, #cart, #paid, conversion ต่อขั้น, top products |
| P3.2 | อัปเดตข้อความเก็บข้อมูลให้ตรงของจริง (ดู §6) — **ไม่บล็อกเฟส 1 ปรับทีหลังได้** |
| P3.3 | Retention: เก็บ event กี่วัน / ลบหรือ hash PII |
| P3.4 | สิทธิ์เข้าถึงรายงาน — **Admin เท่านั้น** (LINE id / amount / event) |
| P3.5 | Runbook ทดสอบ regression บน LINE หลัง deploy |

#### งานฝั่ง Chat

- [ ] (ถ้ามี Admin dashboard) สิทธิ์เมนู + i18n  
- [ ] เอกสาร ops: cache / วิธีไล่ `rec_id` เมื่อลูกค้าร้องเรียน

#### งานฝั่ง Laravel

- [ ] Index/query รายงานไม่หนัก production  
- [ ] Monitor cookie/consent ถ้ามี banner คุกกี้

#### Definition of Done — เฟส 3

- [ ] รายงานใช้งานได้ในสภาพแวดล้อมที่ตกลง  
- [ ] ข้อความ PDPA อัปเดตตาม scope จริง  
- [ ] Handoff เอกสารปิดงาน

---

## 5) Backlog รวมแบบเช็คลิสต์ (คัดลอกไป tracker ได้)

### Chat

- [x] **C0** Kickoff + ล็อกสัญญา param  
- [ ] **C1.1** Append attribution query บน product card URL  
- [ ] **C1.2** Generate `rec_id` ต่อเทิร์น + เก็บ token  
- [ ] **C1.3** Persist `recommendation_events`  
- [ ] **C1.4** Regression product cards  
- [ ] **C1.5** อัปเดต docs ให้ตรงโค้ด  
- [ ] **C2.x** Webhook receiver / Admin funnel (ถ้าเลือกทางเลือก 2)  
- [ ] **C3.x** รายงาน + PDPA ฝั่งแชท (ถ้ามี)

### Laravel

- [x] **L0** Kickoff + เลือก login redirect แบบ B  
- [x] **L1.1** แก้ login ด้วย `redirect=fullUrl` ไม่ทิ้ง `chat_sid`/`rec_id` ⭐ blocker  
- [x] **L1.2** อ่าน param ที่ PDP (middleware `captureAiAttribution`)  
- [x] **L1.3** Cookie `tm_ai_attr` 30 วัน  
- [x] **L1.4** Merge หลัง login (จาก redirect กลับ PDP + cookie)  
- [x] **L1.5** Stamp `order_orders.ai_attribution` (ตอนสร้างออเดอร์ + checkout; แยกจาก `json`)  
- [x] **L1.6** log `product_view_ai` เมื่อเข้า PDP มี attribution  
- [ ] **L1.7** ตรวจ SEO/CDN กับ query  
- [ ] **L2.1–L2.7** Funnel events + รายงาน (Admin only; refund แยกสถานะ)  
- [ ] **L2.x** Webhook ออก (ถ้าเลือกทางเลือก 2 — ส่ง amount/member_uid ได้)  
- [ ] **L3.x** Hardening + retention

### ร่วม / QA

- [ ] UAT บนเบราว์เซอร์ปกติ  
- [ ] UAT บน LINE in-app browser (มีสภาพแวดล้อมแล้ว)  
- [ ] ออเดอร์ทดสอบ 1 รายการที่ join ได้  
- [ ] ล็อกกติกาหลายสินค้าในตะกร้าก่อนเฟส 2  
- [x] นิยาม paid / refund เอกสารร่วม (paid=`COMPLETED`, refund=แยกสถานะ)
---

## 6) ข้อความ PDPA ที่สอดคล้องแผน

| ช่วง | พูดได้ |
|---|---|
| **ตอนนี้** | สนทนา, user id ช่องทางแชท, เวลา, ประเภทคำถาม, สถานะตอบ/ส่งต่อ |
| **หลังเฟส 1 ผ่าน** | + การแนะนำสินค้าและรหัสอ้างอิงเพื่อวิเคราะห์การใช้บริการ |
| **หลังเฟส 2** | + การวิเคราะห์เส้นทางจากคำแนะนำในแชทไปสู่การสั่งซื้อบนเว็บ (เท่าที่เว็บรองรับ) |

**Kickoff:** ข้อความ PDPA **ปรับทีหลังได้** — ไม่บล็อกเฟส 1  
อย่าประกาศ conversion จนชำระเงินใน PDPA จนกว่า PoC ผ่าน **login redirect + stamp order + T7**
---

## 7) ความเสี่ยงและวิธีลด

| ความเสี่ยง | ผล | วิธีลด |
|---|---|---|
| PDP redirect login ทิ้ง query | Click/Paid วัดไม่ได้ | **L1.1 ทำก่อนหรือคู่กับ PoC** |
| LINE in-app ตัด cookie | attribution หาย | ทดสอบ T7 ตั้งแต่เฟส 1; สำรองเก็บใน `redirect` URL |
| คนแชท ≠ คนสั่งบนเว็บ | attribution หลวม | ใช้ `chat_sid`/`rec_id` เป็นหลัก |
| หลายสินค้าในตะกร้า | ตัวเลขมั่ว | PoC ใช้ last-touch ชั่วคราวได้; **ล็อกกติกาก่อนเฟส 2** (ยังไม่แน่ใจผลกระทบ) |
| นับ paid ผิด (สลิปยัง PROCESSING) | conversion เพี้ยน | ใช้ `payment_status=COMPLETED` เท่านั้น |
| Guest สมมติในแผนเก่า | เทสผิดของจริง | ไม่มี guest checkout — ใช้ flow login บังคับ |
| SEO/CDN กับ query | cache/SEO พัง | L1.7 — **ยังไม่ได้ทดสอบ**; ตรวจระหว่าง PoC |

---

## 8) Go / No-Go ก่อนเริ่มเฟส 1

| คำถาม | สถานะ |
|---|---|
| ติด query บน `product_url` ได้โดยไม่พัง? | **ได้** |
| เก็บลง order ได้? | **ได้** — คอลัมน์ `order_orders.ai_attribution` |
| แก้ login ไม่ทิ้ง attribution ได้? | **Go ด้วยแบบ B** (`redirect=fullUrl`) — ต้องทำ L1.1 ในเฟส 1 |
| ทดสอบบน LINE ใน UAT ได้? | **มี** |
| นิยาม paid ชัด? | **ล็อกแล้ว** = `payment_status=COMPLETED` |
| ทางรายงาน? | **ทางเลือก 1** |
| ชื่อ param / cookie / `chat_sid` / `rec_id`? | **ล็อกแล้ว** (§3.2–3.4) |

**สรุป Go:** **เริ่มเฟส 1 ได้** — สัญญาหลักล็อกแล้ว; Laravel ทำ **L1.1 (แบบ B)** คู่กับ Chat ติด query + `recommendation_events`

**เปิดค้างไม่บล็อก PoC:** กติกาหลายสินค้าในตะกร้า, SEO/CDN, ข้อความ PDPA

---

## 9) สรุปหนึ่งหน้าสำหรับประชุม

1. แผนวัด LINE AI → เว็บ → ออเดอร์ **ถูกต้องและทำได้** โดยไม่ย้าย order  
2. ของที่มีแล้ว: availability/`product_url`, member LINE/uid API, order จน paid  
3. ของที่ยังไม่มี: attribution บนลิงก์, cookie, stamp order, funnel log  
4. งานแรกฝั่ง Laravel: **L1.1 แบบ B** — redirect login ด้วย `fullUrl` ไม่ทิ้ง `chat_sid`/`rec_id` + cookie `tm_ai_attr` 30 วัน  
5. งานแรกฝั่ง Chat: ติด query ชื่อเดียวกัน + `rec_id` ต่อเทิร์น (รวม token) + เก็บ recommendation event  
6. ตัวอย่าง URL: `http://127.0.0.1:8000/acne-aid-foaming-cleanser-100ml-exea`  
7. เริ่มทางเลือก 1 → PoC ผ่าน → ค่อย webhook/BI; PDPA ปรับทีหลังได้; รายงาน = Admin เท่านั้น

---

## 10) ประวัติเอกสาร

| วันที่ | หมายเหตุ |
|---|---|
| 2026-07-21 | สร้างแผนงานทั้ง 2 ฝั่งจากสถานะโค้ด Chat + discovery Laravel (login gate, ไม่มี guest cart, stamp ผ่าน `order_orders.json`) |
| 2026-07-21 | ล็อกคำตอบ kickoff (§3.4): ชื่อ param เดียวกัน, cookie 30 วัน/`tm_ai_attr`, paid=`COMPLETED`, login=B, `chat_sid`=`conversation_id`, `rec_id` ต่อเทิร์น+token, UAT LINE มี, refund แยกสถานะ, สิทธิ์ Admin, PDPA ทีหลัง; เปิดค้างหลายสินค้า+SEO |
| 2026-07-21 | Laravel เฟส 1: `AiAttributionService`, middleware `captureAiAttribution`, login `redirect=fullUrl`, stamp `json.ai_attribution`, log `product_view_ai`, tests `AiAttributionTest` |
| 2026-07-21 | ย้าย stamp ไปคอลัมน์ `order_orders.ai_attribution` แยกจาก `json` (กันถูกเคลียร์ตอน `update_order_status`) |
