OneWarehouse × OMS 串接 API
版本:v1.2(2026-07-20)— 契約更新:帳號僅置於 HTTP header,body 不再攜帶 account_no 狀態:7 支 endpoint 已全數實測通過(含冪等重送),正式開放 UAT
1. 連線資訊
| 項目 | 值 |
|---|---|
| Base URL | https://oms.licodes.net/example/onewarehouse/v1 |
| Method | 一律 POST |
| Content-Type | application/json |
帳號(HTTP header account-no) | oms_example_wh01 |
| shared_secret | 由 LifeCOM 窗口另行提供 |
| warehouse_tpw_id | OW_UAT_TPW(UAT 暫定值,正式對接時更換) |
1.1 UAT tenant 對映與現成測試資料
以 header 帳號 oms_example_wh01 + body warehouse_tpw_id=OW_UAT_TPW 呼叫時,單據會由伺服器自動對映落到:
| 項目 | 值 | 說明 |
|---|---|---|
| 倉別 | 翔丰倉(warehouse_no=2) | 進出庫單據落此倉。payload 不需要也不能指定倉別,由 tenant 對映決定 |
| 賣場 | Upload 型測試賣場(markets_no=13) | 出庫訂單掛此賣場,不會觸發任何真實電商平台同步 |
現成測試資料(可直接用,不必從零建):
| 資料 | 值 | 用途 |
|---|---|---|
| 測試 SKU | UAT-SKU-001(效期 2027-12-31) | 已建檔且有庫存 100 pcs,可直接測 stock/query、outbound/create |
也可以自建:用 product/sync 建自己的 SKU(建議前綴 UAT- 便於識別清理)→ inbound/create 進庫存 → 再測出庫。
⚠️ 這是 UAT 測試環境:資料會定期清理,請勿放正式資料;出庫單會走到「待轉倉」狀態為止,不會真實出貨。
2. 鑑權(每個請求都要)
2.1 HTTP Header(3 個,缺一即 401)
| Header | 說明 |
|---|---|
account-no | 帳號。必須用連字號 account-no,不可用底線 account_no |
timestamp | Unix 秒(10 位)。與伺服器時間差必須在 ±300 秒內 |
sign | 簽章,計算方式見下 |
2.2 簽章計算
sign = HMAC-SHA256( <raw request body 字串> + <timestamp> , shared_secret )
- body 與 timestamp 直接字串串接,中間無分隔符
- 輸出 hex 小寫
- body 必須與實際送出的 raw bytes 完全一致(注意 JSON 序列化差異)
範例(bash):
SECRET="<由 LifeCOM 窗口取得的 shared_secret>"
BODY='{"sku_ids":["SKU-001"]}'
TS=$(date +%s)
SIGN=$(printf '%s%s' "$BODY" "$TS" | openssl dgst -sha256 -hmac "$SECRET" -hex | sed 's/^.*= //')
curl -X POST "https://<host>/onewarehouse/v1/stock/query" \
-H "Content-Type: application/json" \
-H "account-no: oms_example_wh01" \
-H "timestamp: $TS" \
-H "sign: $SIGN" \
-d "$BODY"
範例(PHP):
$sign = hash_hmac('sha256', $rawBody . $timestamp, $secret);
2.3 tenant 識別(body 內)
除鑑權 header 外,inbound/create、inbound/return、outbound/create 的 body 必須帶:
| 欄位 | 值 |
|---|---|
warehouse_tpw_id | OW_UAT_TPW |
伺服器以 warehouse_tpw_id + 鑑權 header 中驗簽通過的帳號 解析對映到內部倉別/賣場;未對映 → PARAM_INVALID。
帳號只需要放在 HTTP header(
account-no)一個地方,body 不需要(也無法)攜帶帳號——伺服器一律以驗簽通過的 header 帳號為準。
3. 回應格式與狀態碼
統一回應封套:
{
"code": "LST-TPW-2000",
"trace_id": "uuid(每請求唯一,回報問題時請附上)",
"message": "success",
"success": true,
"data": { }
}
| HTTP | code | 意義 |
|---|---|---|
| 200 | LST-TPW-2000 | 成功 |
| 400 | LST-TPW-2001 | 重複單(冪等:同單號重送)。非錯誤,代表原單已受理 |
| 400 | PARAM_INVALID | 參數缺漏/格式錯/tenant 未對映/庫存不足,詳見 message |
| 401 | LST-TPW-4001 | 鑑權失敗:缺 header / 簽章不符 / timestamp 超窗 / 帳號未註冊 |
| 503 | LST-TPW-5030 | 整合功能未開通 |
4. API 一覽
| # | Endpoint | 功能 | 冪等 |
|---|---|---|---|
| F-1 | /product/sync | 商品主檔同步(upsert) | 同 sku_id 重呼=update |
| F-2 | /inbound/create | 進貨入庫單建立 | 同 inbound_short_id 重送 → 2001 |
| F-3 | /inbound/return | 退貨入庫單建立 | 同單重送 → 2001 |
| F-4 | /inbound/cancel | 進貨單批次取消(≤100) | 每筆回獨立狀態 |
| F-5 | /outbound/create | 出庫(訂單)建立 | 同 outbound_short_id 重送=回原單 |
| F-6 | /outbound/cancel | 出庫單批次取消(≤100) | 每筆回獨立狀態 |
| F-7 | /stock/query | 庫存批次查詢(建議 ≤1000) | 唯讀 |
5. 各 API 詳細
F-1 POST /product/sync — 商品主檔同步
必填:sku_id、sku_name、storage_type(1=常溫 2=空調 3=冷凍 4=冷藏)
{
"sku_id": "SKU-001",
"sku_name": "測試商品001",
"length": 10, "width": 8, "height": 5, "weight": 200,
"storage_type": 1
}
sku_images(選填,URL 陣列):僅做格式驗證(array、最多 5 張),OMS 不儲存圖片內容——傳了也不會落地,商品圖請勿依賴此欄位。超過 5 張回 PARAM_INVALID- 同
sku_id重複呼叫為更新(無副作用) - 成功 data:
{"success":true,"products_id":<內部id>} - ⚠️ 商品必須先經此 API 建立,後續進出庫的 sku_id 才查得到
F-2 POST /inbound/create — 進貨入庫
必填:inbound_short_id、warehouse_tpw_id、details[](每筆:sku_id + expires[] 非空)
{
"inbound_short_id": "IN-20260717-001",
"warehouse_tpw_id": "OW_UAT_TPW",
"estimated_arrival_time": 1784448000000,
"details": [
{
"sku_id": "SKU-001",
"expires": [
{ "expire_date": "2027-12-31", "qty": 100 }
]
}
]
}
- 數量放在
expires[].qty(每個效期一筆,qty 必須 >0);detail 層qty可不帶,若帶則必須等於該 detail 所有 expires qty 加總,不符回 PARAM_INVALID estimated_arrival_time:毫秒 timestamp,可不帶- 成功 data:
{"success":true,"stock_in_no":"si_...","stock_in_sn":"<你的inbound_short_id>","warehouse_no":"..."} - 冪等:同
inbound_short_id重送 → 400LST-TPW-2001
F-3 POST /inbound/return — 退貨入庫
必填:inbound_short_id、outbound_short_id(原出庫單號)、warehouse_tpw_id、details[](結構同 F-2)
{
"inbound_short_id": "RET-20260717-001",
"outbound_short_id": "OUT-20260717-001",
"warehouse_tpw_id": "OW_UAT_TPW",
"details": [
{
"sku_id": "SKU-001",
"expires": [ { "expire_date": "2027-12-31", "qty": 5 } ]
}
]
}
- 以
outbound_short_id關聯原出庫單;查無對應退貨單時仍受理(靜默略過關聯) - 冪等:同單重送 → 400
LST-TPW-2001
F-4 POST /inbound/cancel — 進貨批次取消
{ "inbound_short_ids": ["IN-20260717-001", "IN-20260717-002"] }
- 上限 100 筆
- data.results 每筆獨立回狀態,例:
[{"stock_in_sn":"IN-...","status":"not_found"}](查無單回 not_found,不視為整批失敗)
F-5 POST /outbound/create — 出庫(訂單)建立
必填:outbound_short_id、warehouse_tpw_id、consignee_country(目前僅支援 "TW")、logistics_type、express_type、details[](每筆 sku_id + qty>0)
{
"outbound_short_id": "OUT-20260717-001",
"warehouse_tpw_id": "OW_UAT_TPW",
"consignee_name": "王小明",
"consignee_phone": "0912345678",
"consignee_address": "台北市信義區信義路五段7號",
"consignee_country": "TW",
"consignee_zip": "110",
"logistics_type": "home",
"express_type": "tcat",
"details": [ { "sku_id": "SKU-001", "qty": 2 } ]
}
- 物流對照(
logistics_type::express_type),目前支援:
| logistics_type | express_type | 說明 |
|---|---|---|
| home | tcat | 宅配-黑貓 |
| home | hct | 宅配-新竹物流 |
| home | post | 宅配-郵局 |
| csvs | pcsc | 超商-7-11 |
| csvs | family | 超商-全家 |
| csvs | hilife | 超商-萊爾富 |
- 查無對照 → PARAM_INVALID
unknown ship_method mapping - 庫存檢查:qty 超過可售量 → PARAM_INVALID
sku X available=N < requested=M consignee_country非 TW → PARAM_INVALID- 成功 data:
{"success":true,"orders_id":"ow_<outbound_short_id>_xxxx","idempotent":false} - 冪等:同
outbound_short_id重送 → 200 且idempotent:true(回原單,不重建)
F-6 POST /outbound/cancel — 出庫批次取消
{ "outbound_short_ids": ["OUT-20260717-001"] }
- 上限 100 筆;data.results 每筆獨立狀態(查無回 not_found)
- 已進倉/揀貨後不可取消:該筆回
status:"cannot_cancel_after_picking"+code:"LST-TPW-6001"(HTTP 仍 200,批次其他筆不受影響)
F-7 POST /stock/query — 庫存批次查詢
{ "sku_ids": ["SKU-001", "SKU-002"] }
- 建議單次 ≤1000;空陣列 → PARAM_INVALID
- 回應 data:
{
"stock_details": [
{
"sku_id": "SKU-001",
"status": "ok",
"stock_type": "good",
"batch_id": "",
"expire_date": "2027-12-31",
"qty": 100,
"qty_lock": 0,
"qty_available": 100
},
{ "sku_id": "SKU-002", "status": "not_found", "qty": 0, "qty_lock": 0, "qty_available": 0 }
]
}
stock_type:good=良品 / bad=不良品;qty=在庫量、qty_lock=已配鎖定量、qty_available=可售量- 已建檔但目前無庫存的 sku:回
status:"ok"且 qty/qty_available=0(與 not_found 區分:not_found=商品未建檔) - 查無 sku 每筆回
status:"not_found"(200,不報錯)
6. 建議測試順序(資料相依)
1. product/sync 建商品(沒建商品,後面全部 sku_id not found)
2. inbound/create 進貨建庫存
3. stock/query 確認庫存入帳
4. outbound/create 出庫(需有可售量)
5. inbound/return 退貨(引用步驟4的 outbound_short_id)
6. inbound/cancel / outbound/cancel 取消流程
7. 冪等驗證 同單號重送,F-2/F-3 應回 2001、F-5 應回 idempotent:true
7. 常見錯誤速查
| 現象 | 原因 | 處理 |
|---|---|---|
| 一律 401 | header 用了底線 account_no | 改連字號 account-no |
| 401 | timestamp 超出 ±300s | 校時,用當下 Unix 秒 |
| 401 | sign 不符 | 確認 body 原始字串+timestamp 直接串接、hex 小寫、secret 正確 |
| PARAM_INVALID unknown tenant | body 的 warehouse_tpw_id 錯或 header 帳號未註冊 | body 用 OW_UAT_TPW,header 用 oms_example_wh01 |
| PARAM_INVALID sku_id not found | 商品未同步 | 先呼 product/sync |
| PARAM_INVALID available < requested | 可售量不足 | 先 inbound/create 建庫存 |
| 400 LST-TPW-2001 | 同單號重送(冪等) | 非錯誤;原單已受理,換新單號即可 |
| 503 LST-TPW-5030 | 功能未開通 | 聯繫 OMS 端確認環境設定 |
回報問題時請附:呼叫的 endpoint、完整 request(header+body)、回應內容、trace_id。
8. 注意事項
- UAT 期間
warehouse_tpw_id=OW_UAT_TPW為暫定值,正式對接時由雙方確認真實值後更換,屆時本文件同步改版 outbound_short_id/inbound_short_id請保證全域唯一(它們直接作為 OMS 內部單號與冪等鍵)- 出庫單建立成功後由 OMS 既有排程推送 WMS,實際出貨狀態回拋(webhook callback)屬後續階段,UAT 範圍先以 API 受理成功為準