Files
sys-analysis-design/ba-output/e-commerce/03-specification/API_US002-003_v1.0.md
Leonard-ThindPad-P50 2c7bcde741 improve BA skill
2026-09-09 06:34:57 +07:00

12 KiB

API — Contract — US-002, US-003 (SCR-04 Giỏ hàng)

Version 1.0
Date 2026-09-08
Author BA (qua skill ba-3-specification)
Status 🟡 Draft
Nguồn contract 🔶 Hỗn hợp — method/path/auth: ✅ BE cung cấp (e-commerce/docs/sections/04-api-design.md §4.1.5 "Cart & Order Service"); request/response body schema: ⚠️ BA đề xuất — chờ BE xác nhận (SAD liệt kê method/path/mô tả ngắn nhưng không có ví dụ JSON cho ba endpoint dưới đây, khác với POST /v1/checkout là endpoint duy nhất SAD có ví dụ đầy đủ)
Approved by BE Lead: —
Source SRS_US002-003_v1.0.md · e-commerce/docs/sections/04-api-design.md §4.1.1, §4.1.5, §4.1.13, §4.2
Scope US-002, US-003

🔴 Đọc dòng Nguồn contract trước. Method/path là ràng buộc thật (đã có trong SAD đã duyệt). Cấu trúc request/response bên dưới là giả định của BA, dev phải đối chiếu với tài liệu BE thật (hoặc OpenAPI spec nếu có) trước khi code.


1. Ba điểm phải chốt trước tiên

# Vấn đề Quyết định Lý do
1 Số lớn (cartItemId, productVariantId, sellerId) truyền dạng gì string Khớp ví dụ POST /v1/checkout của SAD ("orderId": "order_001" dạng string) — áp dụng nhất quán cho mọi id trong hệ thống
2 Thời gian — SCR-04 không hiển thị trường thời gian nào N/A Không có trường *_at nào cần hiển thị ở US-002/US-003
3 Phân trang Không áp dụng — GET /v1/cart trả toàn bộ giỏ hàng, không phân trang Giỏ hàng không có khái niệm "trang", số dòng thực tế nhỏ (không giống danh sách sản phẩm)

2. Quy ước chung

Base URL https://api.<domain>/v1 (theo SAD §4.1.1)
Xác thực Bearer JWT (Customer) hoặc header X-Guest-Session-Id (Guest) — theo SAD §4.1.1, §4.1.5
Định dạng phản hồi thành công { "data": {...} } (theo SAD §4.1.1)
Định dạng phản hồi lỗi { "error": { "code", "message", "details" }, "traceId" } (theo SAD §4.1.13 — khác với format mặc định của template BA, ưu tiên đúng theo SAD)
Mã HTTP dùng 200 · 204 · 400 · 401 · 403 · 404 · 409 · 500 · 503
Ngôn ngữ thông điệp FE tự dịch theo mã lỗi (code) + bảng text §4.2 của SRS, không dựa vào message trả về từ BE để hiển thị cho người dùng cuối (theo UICONV §9 — BA sở hữu text hiển thị, W5)

🔴 Lỗi nghiệp vụ trả mã HTTP 4xx (không phải 200 kèm cờ lỗi) — đúng theo thiết kế đã có ở SAD §4.1.13.


3. Endpoint

3.1 GET /v1/cart — Lấy giỏ hàng hiện tại (đa seller)

Mục đích Tải dữ liệu hiển thị SCR-04 (US-002)
Màn hình SCR-04
Quyền ROLE-01 Guest (X-Guest-Session-Id) hoặc ROLE-02 Customer (Bearer JWT) — không cần scope riêng, chỉ cần định danh hợp lệ

Query parameters: không có (không phân trang).

Response 200 (⚠️ đề xuất của BA — SAD không có ví dụ JSON cho endpoint này)

{
  "data": {
    "cartId": "cart_123",
    "status": "active",
    "sellers": [
      {
        "sellerId": "seller_11",
        "sellerName": "Shop A",
        "subtotalVnd": 325000,
        "items": [
          {
            "cartItemId": "citem_001",
            "productVariantId": "variant_789",
            "productName": "Áo thun basic",
            "variantLabel": "Size M, Đỏ",
            "unitPriceVnd": 100000,
            "quantity": 2,
            "imageUrl": "https://cdn.example.com/p/789.jpg"
          }
        ]
      }
    ],
    "totalVnd": 585000,
    "totalItemCount": 5
  }
}

Từng trường

Trường Kiểu Có thể null Nguồn Ghi chú
cartId string ❌ cart.id —
status enum ❌ cart.status active/converted/abandoned — SCR-04 chỉ hiển thị khi active
sellers[].sellerId string ❌ cart_item.seller_id —
sellers[].sellerName string ❌ Module Seller Management (tham chiếu) 🔴 BA đề xuất BE trả kèm tên đã join sẵn — cần BE xác nhận, xem §5 mục 1
sellers[].subtotalVnd number ❌ Tính từ items[] 🔴 BA đề xuất BE tính sẵn (không để FE tự cộng) — tránh sai lệch làm tròn giữa FE/BE, xem §5 mục 2
items[].unitPriceVnd number ❌ cart_item.unit_price_snapshot hoặc giá real-time 🔴 Phụ thuộc OQ-029 (chưa chốt ở SRS)
items[].quantity number ❌ cart_item.quantity Số nguyên
totalVnd number ❌ Σ sellers[].subtotalVnd —
totalItemCount number ❌ Σ items[].quantity toàn giỏ Dùng cho C01

Response lỗi

HTTP code Khi nào Mã lỗi SRS
401 ERR_AUTH_INVALID_TOKEN Customer token hết hạn/không hợp lệ Điều hướng đăng nhập (AC-US002-06), không có mã E-CART riêng
500 / 503 ERR_INTERNAL / ERR_SERVICE_UNAVAILABLE Lỗi hệ thống/timeout E-CART-0004

3.2 PATCH /v1/cart/items/{cartItemId} — Cập nhật số lượng

Mục đích Cập nhật quantity của một CartItem (US-003, field F01)
Màn hình SCR-04
Quyền Chủ sở hữu CartItem (ownership theo customerId/sellerId trong JWT, hoặc session_id cho Guest — cơ chế Guest chưa xác nhận, OQ-032)

Request body (⚠️ đề xuất)

{ "quantity": 3 }
Trường Kiểu Bắt buộc Ràng buộc Field SRS
quantity number ✅ Số nguyên ≥ 1; ngưỡng trên 🔴 chưa chốt (OQ-030) F01

🔴 Ràng buộc ở API phải khớp bảng field trong SRS — giữ nguyên "≥ 1", không thêm ngưỡng trên tự ý cho tới khi OQ-030 được trả lời.

Response 200 (⚠️ đề xuất — cần BE xác nhận trả về CartItem đã cập nhật hay toàn bộ Cart mới, xem §5 mục 3)

{
  "data": {
    "cartItemId": "citem_001",
    "quantity": 3,
    "sellerSubtotalVnd": 375000,
    "cartTotalVnd": 635000
  }
}

Response lỗi

HTTP code Khi nào Mã lỗi SRS
400 ERR_VALIDATION quantity không phải số nguyên ≥ 1 E-CART-0002
403 ERR_FORBIDDEN_OWNERSHIP cartItemId không thuộc giỏ hàng của caller E-CART-0005
404 ERR_NOT_FOUND cartItemId không tồn tại (đã bị xoá) E-CART-0003
409 ERR_CONFLICT cartItemId đã bị xoá bởi request khác trong lúc xử lý E-CART-0003
422 ERR_BUSINESS_RULE 🔴 Dự kiến — nếu OQ-030 chốt có kiểm tra tồn kho tại đây (chưa có mã — chỉ tạo khi OQ-030 xác nhận thuộc scope)
500 / 503 ERR_INTERNAL / ERR_SERVICE_UNAVAILABLE Lỗi hệ thống/timeout E-CART-0006

3.3 DELETE /v1/cart/items/{cartItemId} — Xoá sản phẩm khỏi giỏ

Mục đích Xoá một CartItem (US-003, nút C07, sau khi xác nhận modal)
Màn hình SCR-04
Quyền Chủ sở hữu CartItem — cùng quy tắc §3.2

Response 200/204 (⚠️ đề xuất — 204 không có body; BA đề xuất 200 kèm sellerSubtotalVnd/ cartTotalVnd mới để FE khỏi tự tính lại, cần BE xác nhận, xem §5 mục 4)

{
  "data": {
    "deletedCartItemId": "citem_001",
    "sellerSubtotalVnd": 260000,
    "cartTotalVnd": 260000,
    "sellerRemoved": false
  }
}

Response lỗi

HTTP code Khi nào Mã lỗi SRS
403 ERR_FORBIDDEN_OWNERSHIP Không thuộc quyền sở hữu E-CART-0005
404 ERR_NOT_FOUND Đã bị xoá trước đó E-CART-0003
409 ERR_CONFLICT Xung đột đồng thời E-CART-0003
500 / 503 ERR_INTERNAL / ERR_SERVICE_UNAVAILABLE Lỗi hệ thống/timeout E-CART-0006

4. Bảng đối chiếu mã lỗi

Mã lỗi SRS Endpoint HTTP code của API ☐ Khớp
E-CART-0002 PATCH /v1/cart/items/{id} 400 ERR_VALIDATION ☐
E-CART-0003 PATCH, DELETE /v1/cart/items/{id} 404/409 ERR_NOT_FOUND / ERR_CONFLICT ☐
E-CART-0004 GET /v1/cart 500/503 ERR_INTERNAL / ERR_SERVICE_UNAVAILABLE ☐
E-CART-0005 PATCH, DELETE /v1/cart/items/{id} 403 ERR_FORBIDDEN_OWNERSHIP ☐
E-CART-0006 PATCH, DELETE /v1/cart/items/{id} 500/503 ERR_INTERNAL / ERR_SERVICE_UNAVAILABLE ☐

Cột ☐ do BE đánh dấu khi xác nhận khớp implementation thật.

5. Điểm cần BE xác nhận

# Điểm cần chốt Đề xuất của BA BE trả lời Ngày
1 GET /v1/cart trả sellerName đã join sẵn, hay chỉ sellerId để FE tự gọi API khác lấy tên? Trả sẵn sellerName — tránh FE gọi thêm N request cho N seller
2 sellers[].subtotalVnd/totalVnd do BE tính sẵn, hay FE tự cộng từ items[]? BE tính sẵn — tránh sai lệch làm tròn giữa FE/BE
3 PATCH /v1/cart/items/{id} trả về CartItem đã cập nhật + tổng nhóm/tổng giỏ, hay toàn bộ Cart mới? Trả CartItem + tổng nhóm/tổng giỏ liên quan (nhẹ hơn trả toàn bộ Cart)
4 DELETE /v1/cart/items/{id} trả 200 kèm body hay 204 rỗng? 200 kèm tổng nhóm/tổng giỏ mới + cờ sellerRemoved (để FE biết có cần ẩn cả nhóm không)
5 id (cartItemId, productVariantId, sellerId) dạng string hay number? string
6 Với Guest, ownership của cartItemId có được đối chiếu theo session_id giống cơ chế 403 ERR_FORBIDDEN_OWNERSHIP của JWT không? (OQ-032, đã ghi ở SRS) Áp dụng cùng cơ chế
7 PATCH /v1/cart/items/{id} có kiểm tra tồn kho (trả 422 ERR_BUSINESS_RULE) không? Phụ thuộc OQ-030 ở SRS Không kiểm tra ở endpoint này (giữ đúng khoanh vùng BACKLOG) — chờ PO xác nhận

6. Hành vi khi API lỗi (giao diện phải làm gì)

Tình huống Giao diện làm gì AC
401 hết phiên (Customer) Chuyển về đăng nhập, giữ đường dẫn quay lại /cart AC-US002-06
403 (ownership) Chuyển trang "Không có quyền", không hiển thị dữ liệu AC-US003-12, 13
404/409 (item đã bị xoá) Banner tại dòng, sau đó ẩn dòng, cập nhật lại tổng AC-US003-11
5xx / timeout khi tải (GET) Banner C12 thay chỗ danh sách, nút "Thử lại" AC-US002-03
5xx / timeout khi sửa/xoá (PATCH/DELETE) Banner tại dòng, giữ nguyên dữ liệu trước đó, mở lại nút AC-US003-09, 10
Đang gửi PATCH/DELETE Khoá F01/C07 của đúng dòng đang xử lý (không khoá toàn trang) để tránh gửi trùng —

🔴 Khoá đúng nút/dòng đang xử lý, không khoá toàn màn hình — SCR-04 thường có nhiều dòng CartItem cùng lúc; khoá toàn trang khi chỉ một dòng đang cập nhật sẽ chặn nhầm thao tác trên các dòng khác.

7. Open Questions

ID Câu hỏi Hỏi ai Từ ngày Chặn gì
OQ-032 Cơ chế ownership check của Guest cho PATCH/DELETE /v1/cart/items/{id} (xem SRS §8) Tech Lead 2026-09-08 §5 mục 6, E-CART-0005 cho Guest
OQ-030 PATCH /v1/cart/items/{id} có kiểm tra tồn kho (422 ERR_BUSINESS_RULE) không (xem SRS §8) PO, Tech Lead 2026-09-08 §3.2, §5 mục 7