Files
star-erp/resources/markdown/manual/api-integration.md

346 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 第三方系統 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. 商品資料讀取 (Product Retrieval)
此 API 用於讓外部系統(如 POS依據關鍵字、分類或最後更新時間從 ERP 中批量抓取商品資料。支援分頁與增量同步。
- **Endpoint**: `/products`
- **Method**: `GET`
### Query Parameters
| 參數名稱 | 類型 | 必填 | 說明 |
| :--- | :--- | :---: | :--- |
| `product_id` | Integer| 否 | 依 ERP 商品 ID (`products.id`) 精準篩選 |
| `external_pos_id` | String | 否 | 依外部 POS 端的唯一識別碼 (`external_pos_id`) 精準篩選 |
| `barcode` | String | 否 | 依商品條碼 (Barcode) 精準篩選 |
| `code` | String | 否 | 依商品代碼 (Code) 精準篩選 |
| `category` | String | 否 | 分類名稱精準過濾 |
| `updated_after` | String | 否 | 增量同步機制。僅回傳該時間點之後有異動的商品 (格式: `YYYY-MM-DD HH:mm:ss`) |
| `per_page` | Integer| 否 | 每頁筆數 (預設 50, 最大 100) |
| `page` | Integer| 否 | 分頁頁碼 (預設 1) |
### Response
**Success (HTTP 200)**
僅開放公開價格 `price`,隱藏敏感成本與會員價。
```json
{
"status": "success",
"data": [
{
"id": 12,
"code": "PROD-001",
"barcode": "4710001",
"name": "可口可樂 600ml",
"external_pos_id": "POS-P-001",
"category_name": "飲品",
"brand": "可口可樂",
"specification": "600ml",
"unit_name": "瓶",
"price": 25.0,
"is_active": true,
"updated_at": "2026-03-19 09:30:00"
}
],
"meta": {
"current_page": 1,
"last_page": 5,
"per_page": 50,
"total": 240
}
}
```
---
## 2. 商品資料同步 (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 字元) |
| `category` | String | **是** | 商品分類名稱。若系統中不存在則自動建立 (最大 100 字元) |
| `unit` | String | **是** | 商品單位 (例如:個、杯、件)。若不存在則自動建立 (最大 100 字元) |
| `code` | String | 否 | 商品代碼。若未提供將由 ERP 自動產生 (最大 100 字元) |
| `price` | Decimal | 否 | 商品售價 (預設 0) |
| `barcode` | String | 否 | 商品條碼 (最大 100 字元)。若未提供將由 ERP 自動產生 |
| `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",
"category": "調味料",
"unit": "瓶",
"price": 380.00,
"barcode": "4711234567890",
"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"
}
```
> [!TIP]
> **自動編碼與分類機制**
> - **必填項**`category` 與 `unit` 為必填。若 ERP 中無對應名稱,將會依據傳入值自動建立。
> - **自動編碼**:若未提供 `code` (商品代碼),將由 ERP 自動產生 8 位隨機代碼。
> - **自動條碼**:若未提供 `barcode` (條碼),將由 ERP 自動產生 13 位隨機數字條碼。
> - 建議在同步後儲存回傳的 `id`、`code` 與 `barcode`,以利後續精確對接。
### Response
回傳 ERP 端的完整商品主檔資訊,供外部系統回存 ID 或代碼。
### 回傳範例 (Success)
- **Status Code**: `200 OK`
```json
{
"message": "Product synced successfully",
"data": {
"id": 1,
"external_pos_id": "POS-P-999",
"code": "A1B2C3D4",
"barcode": "4710009990001"
}
}
```
---
## 3. 門市庫存查詢 (Query Inventory)
此 API 用於讓外部系統(如 POS依據特定的「倉庫代碼」查詢該倉庫目前所有商品的庫存餘額。
**注意**:此 API 會回傳該倉庫內的所有商品數量,不論該商品是否已綁定外部 POS ID。
- **Endpoint**: `/inventory/{warehouse_code}`
- **Method**: `GET`
### URL 路徑參數
| 參數名稱 | 類型 | 必填 | 說明 |
| :--- | :--- | :---: | :--- |
| `warehouse_code` | String | **是** | 要查詢的倉庫代碼 (例如:`STORE-001`,測試可使用預設建立之 `api-test-01`) |
### Query Parameters (選填)
| 參數名稱 | 類型 | 說明 |
| :--- | :--- | :--- |
| `product_id` | String | 依 ERP 商品 ID (`products.id`) 篩選。 |
| `external_pos_id` | String | 依外部 POS 端的唯一識別碼 (`external_pos_id`) 篩選。 |
| `barcode` | String | 依商品條碼 (Barcode) 篩選商品。 |
| `code` | String | 依商品代碼 (Code) 篩選商品。 |
若不帶任何參數,將回傳該倉庫下所有商品的庫存餘額。
### Response
**Success (HTTP 200)**
回傳該倉庫內所有的商品目前庫存總數及詳細資訊。若商品未建置 `external_pos_id`,該欄位將顯示為 `null`
```json
{
"status": "success",
"warehouse_code": "api-test-01",
"data": [
{
"product_id": 1,
"external_pos_id": "PROD-001",
"product_code": "PROD-A001",
"product_name": "特級冷壓初榨橄欖油 500ml",
"barcode": "4710123456789",
"category_name": "調味料",
"unit_name": "瓶",
"price": 450.00,
"brand": "奧利塔",
"specification": "500ml/瓶",
"batch_number": "PROD-A001-TW-20231026-01",
"expiry_date": "2025-10-26",
"quantity": 15
},
{
"external_pos_id": null,
"product_code": "MAT-001",
"product_name": "未包裝干貝醬原料",
"barcode": null,
"category_name": "原料",
"unit_name": "kg",
"price": 0.00,
"brand": null,
"specification": null,
"batch_number": "MAT-001-TW-20231020-01",
"expiry_date": "2024-04-20",
"quantity": 2.5
}
]
}
```
**Error: Warehouse Not Found (HTTP 404)**
當傳入系統中不存在的倉庫代碼時發生。
```json
{
"status": "error",
"message": "Warehouse with code 'STORE-999' not found."
}
```
---
## 4. 訂單資料寫入與扣庫 (Create Order)
此 API 用於讓第三方系統(如 POS 結帳後)將「已成交」的訂單推送到 ERP。
**重要提醒**寫入訂單時ERP 會無條件扣除庫存。若指定的「批號」庫存不足,系統會自動轉向 `NO-BATCH` 庫存項目扣除;若最終仍不足,則會在 `NO-BATCH` 產生負數庫存。
- **Endpoint**: `/orders`
- **Method**: `POST`
### Request Body (JSON)
| 欄位名稱 | 型態 | 必填 | 說明 |
| :--- | :--- | :---: | :--- |
| `external_order_id` | String | **是** | 第三方系統中的唯一訂單編號,不可重複 (Unique) |
| `name` | String | **是** | 訂單名稱或客戶名稱 (最多 255 字元) |
| `warehouse_code` | String | **是** | 指定扣除庫存的倉庫代碼 (例如:`api-test-01` 測試倉)。若找不到對應倉庫將直接拒絕請求 |
| `payment_method` | String | 否 | 付款方式,僅接受:`cash`, `credit_card`, `line_pay`, `ecpay`, `transfer`, `other`。預設為 `cash` |
| `total_amount` | Number | **是** | 整筆訂單的總交易金額 (例如500) |
| `total_qty` | Number | **是** | 整筆訂單的商品總數量 (例如5) |
| `sold_at` | String(Date) | 否 | 交易發生時間,預設為當下時間 (格式: YYYY-MM-DD HH:mm:ss) |
| `items` | Array | **是** | 訂單明細陣列,至少需包含一筆商品 |
#### `items` 陣列欄位說明:
| 欄位名稱 | 型態 | 必填 | 說明 |
| :--- | :--- | :---: | :--- |
| `product_id` | Integer | **是** | **ERP 內部的商品 ID (`id`)**。請優先使用商品同步 API 取得此 ID |
| `batch_number` | String | 否 | **商品批號**。若提供,將優先扣除該批號庫存;若該批號無剩餘或找不到,將自動 fallback 至 `NO-BATCH` 扣除 |
| `qty` | Number | **是** | 銷售數量 (必須 > 0) |
| `price` | Number | **是** | 銷售單價 |
**注意**:請確保傳入正確的 `product_id` 以便 ERP 準確識別商品與扣除庫存。
**請求範例:**
```json
{
"external_order_id": "ORD-20231024-001",
"name": "陳小明-干貝醬訂購",
"warehouse_code": "STORE-01",
"payment_method": "credit_card",
"total_amount": 1050,
"total_qty": 3,
"sold_at": "2023-10-24 15:30:00",
"items": [
{
"product_id": 15,
"batch_number": "BATCH-2024-A1",
"qty": 2,
"price": 500
}
]
}
```
### Response
**Success (HTTP 201)**
```json
{
"message": "Order synced and stock deducted successfully",
"order_id": 42
}
```
#### 錯誤回應 (HTTP 422 Unprocessable Entity - 驗證失敗)
當傳入資料格式有誤、商品 ID 於系統中不存在(如 `items` 內傳入了無法辨識的商品 ID或是倉庫代碼無效時返回。
- **`message`** (字串): 錯誤摘要。
- **`errors`** (物件): 具體的錯誤明細列表,能一次性回報多個商品不存在或其它欄位錯誤。
```json
{
"message": "Validation failed",
"errors": {
"items": [
"The following product IDs are not found: 15, 22. Please ensure these products exist in the system."
],
"warehouse_code": [
"Warehouse with code STORE-999 not found."
]
}
}
```
#### 錯誤回應 (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訂單寫入後庫存將變為負數提醒門市人員需進行調撥補貨。