商家 API:把 SKU、採購預報與深圳倉庫存接進你的系統
給在蝦皮、酷澎、momo 開店的公司行號。上架時產生的商品條碼用 API 傳進來,採購時預報快遞單號與內容物,貨到深圳倉就自動開工單、貼碼、逐件掃描上架,庫存與差異即時回傳。
公司行號
開放對象(需 8 碼統編)
200 筆
SKU 單次批量上限
WO-SK
入倉自動開貼碼工單
60 次/分
每把金鑰預設限流
1 服務概覽
第一期提供「商品與入庫」:從建立 SKU 到貨物在深圳倉上架成庫存。
目前可以做的事
- 批量建立或更新 SKU(以你的
seller_sku為準),每個顏色、尺碼各自一組商品條碼,並綁定蝦皮/酷澎/momo 的商品規格編號。 - 依 11 碼稅則號列自動比對輸入規定,標出需要商品檢驗、許可或不能進口的商品。
- 採購時預報快遞單號與每箱內容物;貨到深圳倉自動開 WO-SK 貼碼工單,倉庫逐件貼碼、掃描上架。
- 查詢分倉庫存與庫存流水,並用 webhook 即時收到到倉、入庫完成、數量差異與庫存變動。
入庫流程
- 建立 SKU:傳入商品條碼、規格、申報品名、稅則號列。
- 預報入庫:傳入快遞單號與每個 SKU 的預報數量。
- 深圳倉入倉:掃快遞單號後自動開 WO-SK 工單並列印。
- 貼碼上架:倉庫列印商品條碼、逐件貼上並掃描計數。
- 完成入庫:實收進入深圳倉庫存,破損另計,差異通知你的系統。
規劃中的功能
批量出貨訂單、依申報規定自動歸組的出貨批次,以及桃園倉前置庫存、分揀與平台面單。上線時會在本頁公布。
2 申請與金鑰
商家 API 只開放公司行號,每把金鑰都綁定一個已核准的商家帳號。
開通步驟
- 在會員中心新增「公司」申報人,填寫 8 碼統一編號。
- 聯繫好運器客服申請開通商家 API,審核通過後會核發金鑰。
- 金鑰只會顯示一次,請保存在伺服器端的環境變數,不要放進瀏覽器或 App。
- 需要 webhook 時,先到會員中心的 API 頁面設定並驗證網址,再訂閱商家事件。
Authorization: Bearer <金鑰>。一般會員登入 App 或網頁產生的權杖不能呼叫商家 API。Authorization: Bearer 12|xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx Accept: application/json
3 共通規範
所有端點共用以下規則。
Base URL
格式與語言
請求與回應都是 JSON。錯誤訊息預設為繁體中文,帶 Accept-Language: en 回英文;程式判斷請一律使用固定英文的 error.code。
回應結構
成功回 {"success": true, "data": …, "meta": …};失敗回 {"success": false, "error": {"code", "message", "errors", "context"}},errors 是逐欄明細。唯一例外是沒帶或帶了無效金鑰:HTTP 401,body 為 {"message": "Unauthenticated."}。
{
"success": false,
"error": {
"code": "VALIDATION_FAILED",
"message": "…",
"errors": [
{ "code": "TRACKING_IN_USE", "field": "packages.0.tracking_no", "message": "…" }
]
}
}
限流
每把金鑰每分鐘 60 次、每日 5,000 次(高用量方案每分鐘 300 次、每日 50,000 次),批量端點一次算一次。需要即時資料請訂閱 webhook,不要輪詢。
| 回應標頭 | 說明 |
|---|---|
X-RateLimit-Limit | 每分鐘上限 |
X-RateLimit-Remaining | 本分鐘剩餘次數 |
X-RateLimit-Limit-DayX-RateLimit-Remaining-Day | 每日上限與剩餘次數 |
Retry-After | 超過上限時,幾秒後可以重試 |
冪等(Idempotency-Key)
寫入請求可帶 Idempotency-Key 標頭(8–128 字元,英數與 _-:.)。網路逾時後用同一把 key、同一份內容重送,會拿到第一次的回應,標頭 Idempotent-Replayed: true;同一把 key 換了內容回 422。紀錄保留 24 小時。
批量回應
批量端點逐筆回報結果:全部成功回 200、部分失敗回 207、全部失敗回 422。每一筆都有 index、status(created/updated/failed)與失敗原因。
識別碼字元
seller_sku 只能用英數與 ._-(1–64 字元);商品條碼另可用 / 與 +(3–64 字元)。大小寫視為相同。
4 商品 SKU
SKU 以你的 seller_sku 為唯一鍵:同一個 seller_sku 重送就是更新,只會修改有帶的欄位。
批量建立或更新 SKU
POST/api/v1/merchant/skus/batch
一次最多 200 筆,每一筆獨立處理,一筆失敗不影響其他筆。
| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
seller_sku | string | 必填 | 你的 SKU 編號,例如蝦皮的商品選項貨號。 |
product_name | string | 新建必填 | 商品名稱。同名的 SKU 會歸在同一個商品底下。 |
barcode | string | 選填 | 商品條碼,倉庫會照這個碼印標籤並掃描上架。不同顏色、尺碼必須不同;沒有條碼可以不填,系統會產生系統條碼。超過 16 字元時標籤改印系統條碼。 |
gtin | string | 選填 | 國際條碼(8/12/13/14 碼,會驗證檢查碼)。蝦皮「商品無國際條碼」傳 00。 |
spec | object | 選填 | 規格,例如 {"color": "黑", "size": "M"},最多 5 項。 |
declared_name_zhdeclared_name_en | string | 選填 | 申報品名(中文/英文),請寫材質與用途,不要寫「樣品」「禮物」。 |
declared_unit_value | number | 選填 | 申報單價,是你向供應商的採購單價,不是平台售價。 |
declared_currency | string | 選填 | CNY、TWD 或 USD,填了單價就必填。 |
hs_code | string | 選填 | 11 碼貨品分類號列,系統依此比對輸入規定。 |
origin_country | string | 選填 | 原產地 ISO 代碼,預設 CN。 |
weight_g | integer | 選填 | 單件重量(公克)。 |
dimensions_cm | array | 選填 | 長、寬、高(公分),例如 [20, 15, 3]。 |
battery_type | string | 選填 | none、contained(內建)、packed(隨附)、standalone(單獨電池)。 |
channels | array | 選填 | 平台商品對應,最多 20 組:platform(shopee/coupang/momo/other)、shop_id、item_id、model_id、platform_sku。 |
請求範例 (POST JSON)
curl -X POST https://0523.tw/api/v1/merchant/skus/batch \ -H "Authorization: Bearer $LUCKY_MERCHANT_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: sku-sync-20260917-001" \ -d '{ "items": [ { "seller_sku": "TEE-RED-S", "barcode": "4710000000011", "product_name": "Cotton T-shirt", "spec": { "color": "red", "size": "S" }, "declared_name_en": "Cotton knitted T-shirt", "declared_unit_value": 18.5, "declared_currency": "CNY", "hs_code": "61091000006", "weight_g": 160, "channels": [ { "platform": "shopee", "shop_id": "123456", "item_id": "2233", "model_id": "445566" } ] } ] }'
回應 200
{
"success": true,
"data": [
{
"index": 0,
"seller_sku": "TEE-RED-S",
"status": "created",
"sku": {
"seller_sku": "TEE-RED-S",
"barcode": "4710000000011",
"system_barcode": null,
"pick_code": "131999",
"hs_code": "61091000006",
"regulation": { "status": "ok", "import_codes": [], "reasons": [] },
"channels": [ { "platform": "shopee", "shop_id": "123456", "item_id": "2233", "model_id": "445566", "platform_sku": null } ]
}
}
],
"meta": { "summary": { "created": 1, "updated": 0, "failed": 0 } }
}
輸入規定判定(regulation.status)
| ok | 沒有輸入規定代號。 |
| review_required | 規定只涵蓋部分商品(例如 C02「本項下部分商品屬應施檢驗」),要依商品實際用途確認。 |
| formal_required | 涉及輸入規定(檢驗、檢疫、許可等),不能用簡易申報。 |
| blocked | 大陸物品不准輸入(MW0,原產地為中國大陸),不能預報入庫。 |
| unclassified | 還沒提供稅則號列,無法判定。 |
查詢 SKU
GET/api/v1/merchant/skus
可用 seller_sku、barcode、regulation_status、updated_since(ISO 8601)篩選,page/per_page(最多 100)分頁。
單一 SKU
GET/api/v1/merchant/skus/{seller_sku}
更新平台商品對應
PUT/api/v1/merchant/skus/{seller_sku}/channels
以傳入的清單整批取代原本的對應;同一組平台規格不能同時綁兩個 SKU。
下載商品條碼標籤
GET/api/v1/merchant/skus/{seller_sku}/label.pdf?qty=10
回傳 40×30mm 標籤 PDF,qty 為張數(1–1000)。深圳倉貼碼時印的是同一種標籤;這支供你想在出貨前自行貼碼時使用。
5 入庫預報
採購出貨後,把快遞單號與內容物預報進來。一張入庫單可以有多個快遞單號,每個單號底下列 SKU 與數量。
建立入庫單
POST/api/v1/merchant/inbound-orders
整張驗證:任一錯誤都不會建立。同一個快遞單號同時只能屬於一張進行中的入庫單;同一個包裹內重複的 SKU 會合併數量。
| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
reference_no | string | 必填 | 你的採購單號或入庫單號,同一商家內唯一(英數與 ._-/)。 |
supplier_order_no | string | 選填 | 供應商訂單號,例如 1688 訂單編號。 |
warehouse_code | string | 選填 | 入庫倉,目前只收 SZ(深圳倉)。 |
notes | string | 選填 | 備註(最多 1,000 字)。 |
packages | array | 必填 | 快遞包裹清單,最多 50 個。 |
packages[].tracking_no | string | 必填 | 中國快遞單號。 |
packages[].carrier | string | 選填 | 快遞公司代碼,例如 yto、sf。 |
packages[].items | array | 必填 | 包裹內容物:seller_sku 與 qty,整張入庫單合計最多 500 行。 |
請求範例 (POST JSON)
curl -X POST https://0523.tw/api/v1/merchant/inbound-orders \ -H "Authorization: Bearer $LUCKY_MERCHANT_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: po-20260917-0001" \ -d '{ "reference_no": "PO-20260917-0001", "supplier_order_no": "1688-3312456789", "packages": [ { "tracking_no": "YT1234567890123", "carrier": "yto", "items": [ { "seller_sku": "TEE-RED-S", "qty": 40 }, { "seller_sku": "TEE-RED-M", "qty": 60 } ] } ] }'
reference_no 再建立一次會回 409 INBOUND_REFERENCE_EXISTS,error.context.inbound_order_id 帶原本的入庫單 id。要安全重試請帶 Idempotency-Key。入庫單狀態
| created | 已預報,包裹都還沒到倉。 |
| partially_arrived | 部分包裹已到深圳倉。 |
| arrived | 所有包裹都已到倉,貼碼上架中。 |
| completed | 全部上架完成,數量與預報相同。 |
| completed_with_discrepancy | 全部完成,但有短收、超收或破損。 |
| cancelled | 已取消。 |
包裹狀態
| awaiting_arrival | 等待到倉。 |
| labeling | 已入倉,工單已開,貼碼上架中。 |
| received | 上架完成。 |
| mismatch | 入倉時的包裹歸屬與你的預報對不上,客服核對中。 |
| cancelled | 已取消或作廢,快遞單號已釋出。 |
查詢入庫單
GET/api/v1/merchant/inbound-orders
GET/api/v1/merchant/inbound-orders/{id}
可用 status、reference_no、updated_since 篩選。每個包裹會回傳預報、實收、破損數量與差異。
取消入庫單
POST/api/v1/merchant/inbound-orders/{id}/cancel
只有包裹都還沒到倉時可以取消;取消後快遞單號可以重新預報。
6 庫存查詢
庫存按倉別分開:SZ 深圳倉、TY 桃園倉(桃園倉庫存隨出貨功能開放)。
查詢庫存
GET/api/v1/merchant/inventory?warehouse_code=SZ
可用 warehouse_code 與 seller_sku[](最多 200 個)篩選。
on_hand | 在庫數量。 |
reserved | 已被訂單保留的數量。 |
available | 可用數量=在庫-保留。 |
damaged | 破損數量,不可出貨。 |
回應 200
{
"success": true,
"data": [
{ "seller_sku": "TEE-RED-S", "barcode": "4710000000011", "warehouse_code": "SZ",
"on_hand": 38, "reserved": 0, "available": 38, "damaged": 2, "updated_at": "2026-09-17T15:20:11+08:00" }
],
"meta": { "total": 1, "page": 1, "per_page": 100, "last_page": 1 }
}
庫存流水
GET/api/v1/merchant/inventory/movements?after_id=0&limit=200
只增不改的異動紀錄,用 after_id 游標往後翻頁(每次最多 500 筆),記住回應的 meta.next_after_id 即可續抓對帳。
7 Webhook 事件
事件發生時主動推送到你的網址,不必輪詢。
設定方式
- 到會員中心 API 頁面設定 webhook 網址(僅接受 https),完成擁有權驗證:你的端點要回 HTTP 200,body 為
{"challenge": "收到的值"}。 - 呼叫
PUT /api/v1/merchant/webhook/events訂閱商家事件。之後在會員中心改網址不會清掉商家事件的訂閱。
curl -X PUT https://0523.tw/api/v1/merchant/webhook/events \ -H "Authorization: Bearer $LUCKY_MERCHANT_KEY" \ -H "Content-Type: application/json" \ -d '{"events": ["inbound.arrived", "inbound.received", "inbound.discrepancy", "inventory.changed"]}'
事件清單
sku.regulation_flagged | SKU 的輸入規定判定變成需要確認、需要正式報關或不准輸入。 |
inbound.arrived | 包裹到深圳倉,工單已開。 |
inbound.mismatch | 包裹入倉時的歸屬與預報對不上,客服核對中。 |
inbound.received | 包裹上架完成,附每個 SKU 的預報、實收、破損數量。 |
inbound.discrepancy | 上架數量與預報不同或有破損(會與 inbound.received 同時送出)。 |
inventory.changed | 庫存有異動,附異動 SKU 的最新庫存。 |
inbound.received
{
"event": "inbound.received",
"delivery_id": "5b0f1c2e-8f3a-4d7e-9c21-0a6b2d4e8f10",
"created_at": "2026-09-17T15:20:11+08:00",
"data": {
"inbound_order_id": 128,
"reference_no": "PO-20260917-0001",
"order_status": "completed_with_discrepancy",
"tracking_no": "YT1234567890123",
"work_order": "WO-SK-315",
"warehouse_code": "SZ",
"items": [
{ "seller_sku": "TEE-RED-S", "expected_qty": 40, "received_qty": 38, "damaged_qty": 2, "difference": 0 },
{ "seller_sku": "TEE-RED-M", "expected_qty": 60, "received_qty": 59, "damaged_qty": 0, "difference": -1 }
],
"received_at": "2026-09-17T15:20:10+08:00"
}
}
驗證簽章
每次推送都帶 X-Lucky-Event、X-Lucky-Delivery-Id 與 X-Lucky-Signature: t=時間戳,v1=簽章。以你的 webhook secret 對「時間戳 + . + 原始 body」做 HMAC-SHA256,比對 v1,並拒絕超過 5 分鐘的時間戳。同一事件重試時 delivery_id 不變,可用來去重。
回應非 2xx 會在 1 分鐘、5 分鐘、30 分鐘、2 小時、6 小時後重試;連續失敗 20 次會自動停用,需到會員中心重新設定。
// PHP $body = file_get_contents('php://input'); $header = $_SERVER['HTTP_X_LUCKY_SIGNATURE'] ?? ''; parse_str(str_replace(',', '&', $header), $sig); // ['t' => '1789…', 'v1' => 'ab12…'] $expected = hash_hmac('sha256', ($sig['t'] ?? '') . '.' . $body, getenv('LUCKY_WEBHOOK_SECRET')); if (! hash_equals($expected, $sig['v1'] ?? '') || abs(time() - (int) ($sig['t'] ?? 0)) > 300) { http_response_code(400); exit; } $event = json_decode($body, true); // de-duplicate by $event['delivery_id'] http_response_code(200);
8 錯誤碼
error.code 固定為英文,下列是常見錯誤。
| HTTP | error.code | 說明 |
|---|---|---|
| 401 | — | 沒有帶金鑰、金鑰無效或已過期(body 為 {"message": "Unauthenticated."})。 |
| 403 | MERCHANT_TOKEN_REQUIRED | 這把權杖沒有商家 API 權限(例如一般會員登入產生的權杖)。 |
| 403 | MERCHANT_NOT_APPROVEDMERCHANT_SUSPENDED | 帳號尚未核准為商家,或商家已暫停。 |
| 403 | APP_SUSPENDEDAPP_REVOKEDAPP_NOT_FOUND | 金鑰對應的 API 應用已停權或撤銷。 |
| 422 | VALIDATION_FAILEDPRODUCT_NAME_REQUIRED | 資料格式錯誤,請看 error.errors 的逐欄明細。 |
| 422 | BATCH_TOO_LARGE | 批量筆數超過上限。 |
| 207 / 409 | SKU_BARCODE_DUPLICATESKU_BARCODE_CONFLICT | 商品條碼已被另一個 SKU 使用,或與另一個 SKU 的系統條碼、揀貨碼相同。 |
| 207 / 422 | GTIN_INVALIDHS_CODE_NOT_FOUND | 國際條碼檢查碼錯誤,或稅則號列不存在。 |
| 404 / 422 | SKU_NOT_FOUNDSKU_IMPORT_BLOCKED | 找不到 SKU,或 SKU 屬不准輸入商品不能預報。 |
| 409 | INBOUND_REFERENCE_EXISTS | 入庫單參考編號已存在。 |
| 422 | TRACKING_IN_USETRACKING_FORECASTED_BY_OTHERTRACKING_ALREADY_WAREHOUSEDTRACKING_NOT_AVAILABLE | 快遞單號已在進行中的入庫單、已被其他會員預報或已入倉在其他會員名下。 |
| 409 | INBOUND_NOT_CANCELLABLE | 已有包裹到倉,入庫單不能取消。 |
| 400 / 409 / 422 | IDEMPOTENCY_KEY_INVALIDIDEMPOTENCY_IN_PROGRESSIDEMPOTENCY_KEY_REUSED | Idempotency-Key 格式錯誤、被用於不同內容,或上一個請求還在處理中。 |
| 409 | CONFLICT_RETRY | 同一筆資料正被另一個請求修改,請稍後重送。 |
| 429 | RATE_LIMIT_EXCEEDED | 超過限流,依 Retry-After 等待後重試。 |
9 程式範例
以下範例請在伺服器端執行,不要把金鑰放進前端程式。
use Illuminate\Support\Facades\Http; use Illuminate\Support\Str; $api = Http::withToken(config('services.lucky.merchant_key')) ->acceptJson() ->baseUrl('https://0523.tw/api/v1/merchant') ->timeout(20); // 1) Sync SKUs (up to 200 per request) $res = $api->withHeaders(['Idempotency-Key' => (string) Str::uuid()]) ->post('/skus/batch', ['items' => $items]); foreach ($res->json('data') as $row) { if ($row['status'] === 'failed') { logger()->warning('SKU failed', $row['errors']); } } // 2) Forecast an inbound order $order = $api->withHeaders(['Idempotency-Key' => 'po-' . $po->id]) ->post('/inbound-orders', [ 'reference_no' => $po->number, 'packages' => [[ 'tracking_no' => $po->tracking_no, 'items' => [['seller_sku' => 'TEE-RED-S', 'qty' => 40]], ]], ])->throw()->json('data'); // 3) Read Shenzhen stock $stock = $api->get('/inventory', ['warehouse_code' => 'SZ'])->json('data');
// Node.js 18+ (server side only) import { randomUUID } from 'node:crypto'; const base = 'https://0523.tw/api/v1/merchant'; const headers = { Authorization: `Bearer ${process.env.LUCKY_MERCHANT_KEY}`, 'Content-Type': 'application/json', Accept: 'application/json', }; const res = await fetch(`${base}/skus/batch`, { method: 'POST', headers: { ...headers, 'Idempotency-Key': randomUUID() }, body: JSON.stringify({ items }), }); const { data, meta } = await res.json(); console.log(meta.summary); // { created, updated, failed } const stock = await fetch(`${base}/inventory?warehouse_code=SZ`, { headers }).then(r => r.json());
import os, uuid, requests base = "https://0523.tw/api/v1/merchant" session = requests.Session() session.headers.update({ "Authorization": f"Bearer {os.environ['LUCKY_MERCHANT_KEY']}", "Accept": "application/json", }) r = session.post(f"{base}/inbound-orders", headers={"Idempotency-Key": "po-20260917-0001"}, json={ "reference_no": "PO-20260917-0001", "packages": [{"tracking_no": "YT1234567890123", "items": [{"seller_sku": "TEE-RED-S", "qty": 40}]}], }, timeout=20) if r.status_code == 409: existing_id = r.json()["error"]["context"]["inbound_order_id"] else: r.raise_for_status()
10 技術支援
串接過程有任何問題,請提供請求時間、端點與 error.code,方便我們協助排查。
LINE 官方帳號 @0523tw
技術信箱 howyunqisoftware@gmail.com
版本 v1 第一期(2026-09-17):商品 SKU、入庫預報、深圳倉貼碼上架、庫存查詢、webhook。