# 第三方系統 API 對接手冊

Star ERP 系統提供外部整合 API (Integration API) 供電商前台、POS 機或其他第三方系統串接。
所有的整合 API 均受到 Laravel Sanctum Token 與多租戶 (Multi-tenant) Middleware 保護。

## 基礎連線資訊

- **API Base URL**: `https://[租戶網域]/api/v1/integration` (依實際部署網址為準，單機開發為 `http://localhost/api/v1/integration`)
- **Headers 要求**:
  - `Accept: application/json`
  - `Content-Type: application/json`
  - `Authorization: Bearer {YOUR_API_TOKEN}` (由 ERP 系統管理員核發的 Sanctum Token)
- **速率限制**：每位使用者每分鐘最多 60 次請求。超過時會回傳 `429 Too Many Requests`。

---

## 1. 產品資料同步 (Upsert Product)

此 API 用於將第三方系統（如 POS）的產品資料單向同步至 ERP。採用 Upsert 邏輯：若 `external_pos_id` 存在則更新資料，不存在則新增產品。

- **Endpoint**: `/products/upsert`
- **Method**: `POST`

### Request Body (JSON)

| 欄位名稱 | 類型 | 必填 | 說明 |
| :--- | :--- | :---: | :--- |
| `external_pos_id` | String | **是** | 在 POS 系統中的唯一商品 ID (Primary Key) |
| `name` | String | **是** | 商品名稱 (最大 255 字元) |
| `price` | Decimal | 否 | 商品售價 (預設 0) |
| `barcode` | String | 否 | 商品條碼 (最大 100 字元) |
| `category` | String | 否 | 商品分類名稱。若系統中不存在，會自動建立 (最大 100 字元) |
| `unit` | String | 否 | 商品單位 (例如：個、杯、件)。若不存在會自動建立 (最大 100 字元) |
| `brand` | String | 否 | 商品品牌名稱 (最大 100 字元) |
| `specification` | String | 否 | 商品規格描述 (最大 255 字元) |
| `cost_price` | Decimal | 否 | 成本價 (預設 0) |
| `member_price` | Decimal | 否 | 會員價 (預設 0) |
| `wholesale_price` | Decimal | 否 | 批發價 (預設 0) |
| `is_active` | Boolean| 否 | 是否上架/啟用 (預設 true) |
| `updated_at` | String | 否 | POS 端的最後更新時間 (格式: YYYY-MM-DD HH:mm:ss) |

**請求範例：**
```json
{
  "external_pos_id": "POS-PROD-9001",
  "name": "特級冷壓初榨橄欖油 500ml",
  "price": 380.00,
  "barcode": "4711234567890",
  "category": "調味料",
  "unit": "瓶",
  "brand": "健康王",
  "specification": "500ml / 玻璃瓶裝",
  "cost_price": 250.00,
  "member_price": 350.00,
  "wholesale_price": 300.00,
  "is_active": true,
  "updated_at": "2024-03-15 14:30:00"
}
```

### Response

**Success (HTTP 200)**
```json
{
    "message": "Product synced successfully",
    "data": {
        "id": 15,
        "external_pos_id": "POS-ITEM-001"
    }
}
```

---

## 2. 門市庫存查詢 (Query Inventory)

此 API 用於讓外部系統（如 POS）依據特定的「倉庫代碼」，查詢該倉庫目前所有商品的庫存餘額。
**注意**：此 API 會回傳該倉庫內的所有商品數量，不論該商品是否已綁定外部 POS ID。

- **Endpoint**: `/inventory/{warehouse_code}`
- **Method**: `GET`

### URL 路徑參數

| 參數名稱 | 類型 | 必填 | 說明 |
| :--- | :--- | :---: | :--- |
| `warehouse_code` | String | **是** | 要查詢的倉庫代碼 (例如：`STORE-001`) |

### Response

**Success (HTTP 200)**
回傳該倉庫內所有的商品目前庫存總數。若商品未建置 `external_pos_id`，該欄位將顯示為 `null`。
```json
{
    "status": "success",
    "warehouse_code": "STORE-001",
    "data": [
        {
            "external_pos_id": "POS-ITEM-001",
            "product_code": "PROD-A001",
            "product_name": "特級冷壓初榨橄欖油 500ml",
            "quantity": 15
        },
        {
            "external_pos_id": null,
            "product_code": "MAT-001",
            "product_name": "未包裝干貝醬原料",
            "quantity": 2.5
        }
    ]
}
```

**Error: Warehouse Not Found (HTTP 404)**
當傳入系統中不存在的倉庫代碼時發生。
```json
{
    "status": "error",
    "message": "Warehouse with code 'STORE-999' not found."
}
```

---

## 3. 訂單資料寫入與扣庫 (Create Order)

此 API 用於讓第三方系統（如 POS 結帳後）將「已成交」的訂單推送到 ERP。
**重要提醒**：寫入訂單的同時，ERP 會自動且**強制**扣除對應倉庫的庫存（允許扣至負數，以利後續盤點或補單）。

- **Endpoint**: `/orders`
- **Method**: `POST`

### Request Body (JSON)

| 欄位名稱 | 型態 | 必填 | 說明 |
| :--- | :--- | :---: | :--- |
| `external_order_id` | String | **是** | 第三方系統中的唯一訂單編號，不可重複 (Unique) |
| `warehouse_code` | String | **是** | 指定扣除庫存的倉庫代碼 (例如：`STORE-001`)。若找不到對應倉庫將直接拒絕請求 |
| `payment_method` | String | 否 | 付款方式，僅接受：`cash`, `credit_card`, `line_pay`, `ecpay`, `transfer`, `other`。預設為 `cash` |
| `sold_at` | String(Date) | 否 | 交易發生時間，預設為當下時間 (格式: YYYY-MM-DD HH:mm:ss) |
| `items` | Array | **是** | 訂單明細陣列，至少需包含一筆商品 |

#### `items` 陣列欄位說明：

| 欄位名稱 | 型態 | 必填 | 說明 |
| :--- | :--- | :---: | :--- |
| `pos_product_id` | String | **是** | 對應產品的 `external_pos_id`。**注意：商品必須先透過產品同步 API 建立至 ERP 中。** |
| `qty` | Number | **是** | 銷售數量 (必須 > 0) |
| `price` | Number | **是** | 銷售單價 |

**請求範例：**
```json
{
    "external_order_id": "ORD-20231026-0001",
    "warehouse_code": "STORE-001",
    "payment_method": "credit_card",
    "sold_at": "2023-10-26 14:30:00",
    "items": [
        {
            "pos_product_id": "POS-ITEM-001",
            "qty": 2,
            "price": 450
        },
        {
            "pos_product_id": "POS-ITEM-005",
            "qty": 1,
            "price": 120
        }
    ]
}
```

### Response

**Success (HTTP 201)**
```json
{
    "message": "Order synced and stock deducted successfully",
    "order_id": 42
}
```

**Error: Product Not Found (HTTP 400)**
若 `items` 內傳入了未曾同步過的 `pos_product_id`，會導致寫入失敗。
```json
{
    "message": "Sync failed: Product not found for POS ID: POS-ITEM-999. Please sync product first."
}
```

#### 錯誤回應 (HTTP 422 Unprocessable Entity - 驗證失敗)

當傳入資料格式有誤、商品編號不存在，或是該訂單正在處理中被系統鎖定時返回。

- **`message`** (字串): 錯誤摘要。
- **`errors`** (物件): 具體的錯誤明細列表，能一次性回報多個商品不存在或其它欄位錯誤。

```json
{
  "message": "Validation failed",
  "errors": {
    "items": [
      "The following products are not found: POS-999, POS-888. Please sync products first."
    ],
    "external_order_id": [
      "The order ORD-01 is currently being processed by another transaction. Please try again later."
    ]
  }
}
```

#### 錯誤回應 (HTTP 500 Internal Server Error - 伺服器異常)

當系統發生預期外的錯誤（如資料庫連線失敗）時返回。

```json
{
  "message": "Sync failed: An unexpected error occurred."
}
```

---

## 幂等性說明 (Idempotency)

訂單 API 支援幂等性處理：若傳入已存在的 `external_order_id`，系統不會報錯，而是回傳該訂單的資訊：

```json
{
    "message": "Order already exists",
    "order_id": 42
}
```

這讓第三方系統在網路問題導致重送時，不會產生重複訂單或重複扣庫。

---

## 常見問題與除錯 (FAQ)

1. **收到 `401 Unauthorized` 錯誤？**
   - 請檢查請求標頭 (Header) 是否有正確攜帶 `Authorization: Bearer {Token}`。
   - 確認該 Token 尚未過期或被撤銷。

2. **收到 `422 Unprocessable Entity` 錯誤？**
   - 代表傳送的 JSON 欄位不符合格式要求，例如必填欄位遺漏、數量為負數、或 `payment_method` 不在允許的值中。Laravel 會在回應的 `errors` 物件中詳細說明哪個欄位驗證失敗。

3. **收到 `429 Too Many Requests` 錯誤？**
   - 代表已超過速率限制（每分鐘 60 次），請稍後再試。

4. **庫存扣除邏輯**
   - POS 訂單寫入視為「已發生之事實」，系統會無條件扣除庫存。若該產品在指定倉庫原先庫存為 0，訂單寫入後庫存將變為負數，提醒門市人員需進行調撥補貨。
