WooCommerce REST API 轉接器
相容於 v3
最後更新:2026年9月5日
一套可直接替換的 STOAR REST API,對應 WooCommerce v3 的線路格式——相同的 URL 路徑(/wp-json/wc/v3/...)、相同的扁平查詢參數語法、相同的 JSON 回應欄位名稱、相同的驗證選項。現有的 WooCommerce 用戶端函式庫(例如 automattic/woocommerce PHP/JS、klarna/woocommerce-rest-api Node)無需修改即可與 STOAR 溝通。
本轉接器是 STOAR 可擴充的 API 轉接器框架的第二個實作(第一個是 Magento)。它示範了一種本質上截然不同的供應商風格——扁平查詢參數而非巢狀 searchCriteria[…]、Basic Auth 而非只有 Bearer、狀態詞彙為 processing/completed 而非 paid/delivered——如何在同一個資料層上與 Magento 並存。
目錄 #
快速上手 #
# 1. Get a Sanctum token with woocommerce:admin ability — issued via /manager/api-tokens
# or via the Magento token endpoint (POST /rest/V1/integration/admin/token), then
# grant the woocommerce:admin ability inside /manager.
TOKEN="2|L5pXj63e881lkgtHPK7Hjozyq7Pb3trHCy0iaDRf76405f72"
# 2. List the last 5 orders (Bearer auth — STOAR extension; works alongside Basic)
curl -s "https://app.stoar.ai/wp-json/wc/v3/orders?per_page=5&orderby=date&order=desc" \
-H "Authorization: Bearer $TOKEN" \
| jq '.[] | {id, status, total, currency}'
# 3. Use HTTP Basic just like a real WooCommerce store
curl -s "https://app.stoar.ai/wp-json/wc/v3/products?per_page=3" \
-u "any-key:$TOKEN"
# 4. Or stuff credentials into the query string (still over HTTPS only)
curl -s "https://app.stoar.ai/wp-json/wc/v3/products?consumer_key=any&consumer_secret=$TOKEN"
驗證 #
WooCommerce 支援三種驗證風格;本轉接器全部接受,並在內部轉換成同一套 Sanctum personal-access-token 檢查。Token 由 STOAR 的 /manager/api-tokens 頁面核發(或以程式方式透過 AdminUser::createToken('label', ['woocommerce:admin']) 產生)。
| 方式 | 格式 | 使用情境 |
|---|---|---|
| HTTP Basic | Authorization: Basic base64(consumer_key:consumer_secret) |
多數 WC 用戶端函式庫的預設方式——僅限 HTTPS |
| 查詢參數 | ?consumer_key=…&consumer_secret=… |
快速用 curl 測試——僅限 HTTPS |
| Bearer (STOAR 擴充) | Authorization: Bearer <token> |
原生 Sanctum——可與 Magento 轉接器互換使用 |
對應到 Sanctum
轉接器會忽略 consumer_key(可以傳入任意字串——"any-key" 就可以)。consumer_secret 必須是一個有效的 Sanctum personal-access token,且其 abilities 陣列包含 woocommerce:admin。
這個對應純粹是一層解讀;從 STOAR 的角度來看,每個 WC 請求都只是以 Bearer 驗證的 Sanctum 請求。同一列 personal-access-token 記錄可同時支援:
- 使用
/rest/V1/orders的 Magento 用戶端(需要magento:admin能力) - 使用
/wp-json/wc/v3/orders的 WooCommerce 用戶端(需要woocommerce:admin能力)
您可以核發一個同時具備兩種能力的 token,或分開核發——由您決定。
顧客 token
顧客範圍的端點尚未提供。woocommerce:customer 能力保留供日後使用。
端點 #
所有路徑都相對於 /wp-json/wc/v3(原封不動沿用 WordPress REST 命名空間)。
GET /wp-json/wc/v3/orders
列出訂單。
授權: Bearer/Basic/查詢參數,需具備 woocommerce:admin 能力。
查詢參數: 見查詢參數。
回應(200): 訂單的扁平 JSON 陣列。分頁資訊放在標頭中,不在內文裡:
HTTP/1.1 200 OK
X-WP-Total: 150
X-WP-TotalPages: 8
Content-Type: application/json
[
{ /* order */ },
{ /* order */ }
]
為什麼用標頭而不是外層結構? 這正是 WooCommerce 的實際慣例——與 Magento 的
{items, search_criteria, total_count}包裝不同。WC 用戶端函式庫(Automattic 官方的 PHP/JS 用戶端)會讀取X-WP-Total來驅動分頁游標。
GET /wp-json/wc/v3/orders/{id}
以數字 id 取得單筆訂單。
回應(200): 見回應結構。
錯誤:
{
"code": "woocommerce_rest_shop_order_invalid_id",
"message": "Invalid shop_order ID.",
"data": { "status": 404, "id": 99999 }
}
GET /wp-json/wc/v3/orders/{id}/notes
列出該訂單的狀態歷程記錄,重新整理成 WooCommerce 備註的形式。
回應(200):
[
{
"id": 42,
"author": "system",
"date_created": "2026-04-10T12:00:00+00:00",
"date_created_gmt": "2026-04-10T12:00:00+00:00",
"note": "Payment received",
"customer_note": false,
"_links": { "self": [...], "collection": [...], "up": [...] }
}
]
customer_note 恆為 false,因為 Stoar 在資料層級並不區分顧客可見的備註與僅限後台的備註。
POST /wp-json/wc/v3/orders/{id}/notes
新增一則備註(對應為 Stoar 中一筆新的 OrderStatusLog 記錄)。
請求:
{ "note": "Customer phoned to confirm delivery slot" }
customer_note(bool)會被接受,但目前沒有實際作用。
回應(201): 新備註,結構與 GET .../notes 的項目相同。
錯誤:
400rest_invalid_param——缺少note404——未知的訂單 id
GET /wp-json/wc/v3/orders/{id}/refunds
列出某訂單的退款。Stoar 沒有獨立的退款資料表——轉接器會回傳下列兩者之一:
- 當
refunded_amount === 0時回傳[],或 - 回傳
[{...}],內含一筆合成的退款記錄,其id === parent_id === order.id
[
{
"id": 123,
"parent_id": 123,
"date_created": "2026-04-12T08:00:00+00:00",
"amount": "30.00",
"reason": "",
"refunded_by": 0,
"refunded_payment": true,
"meta_data": [],
"line_items": [],
"api_refund": true
}
]
POST /wp-json/wc/v3/orders/{id}/refunds
發出退款。會委派給 Stoar 既有的 Order::processRefund(),它再透過 StripeService::processRefund() 呼叫 Stripe。全額退款時 Stoar 會把訂單標記為 refunded,部分退款則累加 refunded_amount。
請求:
{ "amount": 30, "reason": "Customer changed mind" }
兩個欄位都是選填。若省略 amount,Stoar 會退還全部剩餘可退金額。
回應(201): 上述結構的退款記錄。
錯誤:
404——未知的訂單 id422woocommerce_rest_invalid_state——訂單不可退款(沒有 Stripe payment intent,或狀態不是 paid/processing/shipped/delivered)
GET /wp-json/wc/v3/products
列出商品。
查詢參數: 見查詢參數。
回應(200): 扁平陣列,附 X-WP-Total/X-WP-TotalPages 標頭。
GET /wp-json/wc/v3/products/{id}
以數字 id 取得單筆商品(不像 Magento 使用 SKU)。
回應(200): 見回應結構。
錯誤: 404 woocommerce_rest_product_invalid_id。
查詢參數 #
WooCommerce 使用扁平的查詢字串參數——遠比 Magento 巢狀的 searchCriteria[…] 文法單純。轉換成同一套與供應商無關的 OrderQuery/ProductQuery 值物件,是在轉接器的 Search/OrderQueryParser 與 Search/ProductQueryParser 類別中完成的。
共通參數(訂單 + 商品)
| 參數 | 預設值 | 用途 |
|---|---|---|
per_page |
10 |
每頁筆數(上限 100) |
page |
1 |
頁碼,從 1 起算 |
orderby |
date |
排序欄位——見下方各資源清單 |
order |
desc |
asc 或 desc |
include |
— | 以逗號分隔的 id 清單(?include=1,2,3) |
exclude |
— | 要排除的 id 清單,以逗號分隔 |
search |
— | 子字串比對——見下方各資源說明 |
僅限訂單
| 參數 | 範例 | 效果 |
|---|---|---|
status |
processing,completed |
CSV——轉換為 Stoar 狀態值;群組內以 OR 結合 |
customer |
42 |
依 customer_id 篩選 |
after |
2024-01-01T00:00:00 |
created_at >= … |
before |
2024-12-31T23:59:59 |
created_at <= … |
search |
jane |
對 customer_email 做 LIKE |
orderby 可用值 |
date(預設)、id、include、title |
date→created_at、title→customer_email |
僅限商品
| 參數 | 範例 | 效果 |
|---|---|---|
status |
publish、draft |
轉換為 Stoar 的 active/inactive |
type |
simple、variable |
variable 轉換為 Stoar 的 configurable |
featured |
true / false |
依 is_featured 篩選 |
category |
5 |
單一分類 id |
sku |
WID-1 |
精確比對 |
slug |
widget-pro |
精確比對 |
min_price |
10 |
price >= … |
max_price |
100 |
price <= … |
search |
widget |
對商品 name 做 LIKE |
orderby 可用值 |
date、id、include、title、price、slug |
title→name、slug→slug |
未知的參數會被靜默忽略(與 WC 行為一致)。無法轉換成合理值的篩選值(例如對商品下 ?status=on-hold)會產生空群組,而不是回傳 400。
欄位對應(WooCommerce ↔ STOAR) #
訂單
| WooCommerce 欄位 | STOAR 來源 | 備註 |
|---|---|---|
id |
id |
數字 |
number |
id(轉為字串) |
WC 慣例 |
order_key |
lookup_token |
Stoar 每筆訂單的魔法連結 token |
status |
status(已轉換) |
見狀態轉換 |
currency |
currency(轉為大寫) |
eur → EUR |
total |
total_amount |
兩位小數字串 |
cart_tax、total_tax |
tax_amount |
兩位小數字串 |
shipping_total |
shipping_amount |
兩位小數字串 |
discount_total |
discount_amount |
兩位小數字串 |
customer_id |
customer_id ?? 0 |
訪客為 0 |
customer_note |
customer_notes |
|
billing.* |
billing_info JSON 欄位 |
扁平的 WC 結構 |
shipping.* |
shipping_info JSON 欄位 |
扁平的 WC 結構 |
payment_method |
第一筆未封存的 OrderPayment.gateway |
退回使用 Order.payment_method |
payment_method_title |
由金流商鍵值推導 | stripe →「Credit / Debit Card」等 |
transaction_id |
stripe_payment_intent_id |
|
date_created、date_modified |
created_at、updated_at |
ISO8601 |
date_paid |
第一筆成功的 OrderPayment.created_at |
若無付款記錄則為 null |
date_completed |
當 status === delivered 時為 updated_at |
其餘為 null |
line_items[] |
Order.items(含規格與商品) |
見下方 |
coupon_lines[] |
由 coupon_code + discount_amount 推導 |
無折扣碼時為空 |
refunds[] |
當 refunded_amount > 0 時為一筆合成項目 |
見退款端點 |
meta_data[] |
一律包含 _stoar_status 與 _stoar_lookup_token |
擴充資料 |
訂單 line_item
| WC 欄位 | Stoar 來源 |
|---|---|
id |
OrderItem.id |
name |
OrderItem.name |
product_id |
OrderItem.product_id ?? 0 |
variation_id |
OrderItem.variant_id ?? 0 |
quantity |
OrderItem.quantity |
subtotal、total |
price * quantity(兩位小數字串) |
subtotal_tax、total_tax |
OrderItem.tax_amount |
sku |
優先取規格 SKU,退回商品 SKU |
price |
OrderItem.price |
商品
| WC 欄位 | STOAR 來源 | 備註 |
|---|---|---|
id |
id |
|
name、slug、sku、description、short_description |
直接沿用 | |
permalink |
url('/product/' . slug) |
|
type |
type(已轉換) |
configurable ↔ variable |
status |
status(已轉換) |
active ↔ publish |
featured |
is_featured |
bool |
regular_price |
price(原始值) |
兩位小數字串 |
sale_price |
special_price(為 null 時是 "") |
兩位小數字串 |
price |
min(regular, sale) | 兩位小數字串 |
on_sale |
special_price !== null && special_price < price |
|
purchasable |
stock > 0 && status === 'active' |
|
manage_stock |
恆為 true |
|
stock_quantity |
stock;configurable 時為各規格庫存加總 |
|
stock_status |
instock/outofstock |
|
weight |
weight(字串) |
|
tax_class |
tax_class_id(字串) |
|
categories[] |
來自 category 關聯的單一元素 |
Stoar 只有單一主分類 |
images[] |
image_path + gallery_paths 陣列 |
第一筆為主圖 |
variations[] |
規格 id(僅限 variable 商品) |
|
meta_data[] |
一律包含 _stoar_id、_stoar_low_stock_threshold、_stoar_image_prompt |
|
attributes[]、default_attributes[] |
[] |
Stoar 沒有全域屬性系統 |
tags[]、related_ids[]、upsell_ids[]、cross_sell_ids[] |
[] |
Stoar 未建模 |
dimensions |
空值 | 未追蹤 |
average_rating、rating_count |
"0.00"、0 |
此層級未彙總 |
狀態轉換 #
WooCommerce 與 Stoar 使用不同的狀態詞彙。轉接器會雙向轉換:篩選時(?status=processing)把 WC 值對應到 Stoar;序列化回應時再把 Stoar 的 status 對應回 WC。
訂單狀態
| WooCommerce | Stoar | 備註 |
|---|---|---|
pending |
pending |
未付款,等待付款 |
processing |
paid (輸出時) /篩選時接受 paid |
「款項已請款,出貨處理中」 |
processing |
processing、shipped (輸出時) |
Stoar 的 processing 與 shipped 都輸出為 WC 的 processing |
completed |
delivered |
|
cancelled |
cancelled |
|
refunded |
refunded |
|
on-hold |
pending (僅限篩選) |
Stoar 沒有明確的 on-hold 狀態 |
failed |
cancelled (僅限篩選) |
最接近的對應;Stoar 沒有明確的 failed 狀態 |
商品狀態
| WooCommerce | Stoar |
|---|---|
publish |
active |
draft、pending、private |
inactive |
商品類型
| WooCommerce | Stoar |
|---|---|
simple |
simple |
variable |
configurable |
grouped、external |
(不支援——Stoar 沒有) |
對應到 null 的篩選值(例如對商品下 ?status=trash)會被靜默忽略——不會回報錯誤。
回應結構 #
訂單——附註解範例
{
"id": 456,
"parent_id": 0,
"number": "456",
"order_key": "abc123…",
"created_via": "checkout",
"version": "8.5.0",
"status": "processing",
"currency": "EUR",
"date_created": "2026-04-10T09:00:00+00:00",
"date_modified": "2026-04-10T09:30:00+00:00",
"discount_total": "0.00",
"discount_tax": "0.00",
"shipping_total": "5.00",
"shipping_tax": "0.00",
"cart_tax": "10.00",
"total": "115.00",
"total_tax": "10.00",
"prices_include_tax": false,
"customer_id": 45,
"customer_note": "",
"billing": { /* WC address shape (flat) */ },
"shipping": { /* WC address shape (flat) */ },
"payment_method": "stripe",
"payment_method_title": "Credit / Debit Card",
"transaction_id": "pi_test_…",
"date_paid": "2026-04-10T09:05:00+00:00",
"date_completed": null,
"cart_hash": "",
"meta_data": [
{ "id": 0, "key": "_stoar_status", "value": "paid" },
{ "id": 0, "key": "_stoar_lookup_token", "value": "abc123…" }
],
"line_items": [
{
"id": 456,
"name": "Widget",
"product_id": 789,
"variation_id": 12,
"quantity": 2,
"tax_class": "",
"subtotal": "100.00",
"subtotal_tax": "10.00",
"total": "100.00",
"total_tax": "10.00",
"taxes": [],
"meta_data": [],
"sku": "WID-1-A",
"price": 50
}
],
"tax_lines": [],
"shipping_lines": [
{
"id": 0,
"method_title": "Standard",
"method_id": "flat_rate",
"instance_id": "",
"total": "5.00",
"total_tax": "0.00",
"taxes": [],
"meta_data": []
}
],
"fee_lines": [],
"coupon_lines": [],
"refunds": [],
"_links": {
"self": [{ "href": "https://app.stoar.ai/wp-json/wc/v3/orders/456" }],
"collection": [{ "href": "https://app.stoar.ai/wp-json/wc/v3/orders" }]
}
}
商品——節錄範例
{
"id": 789,
"name": "Widget",
"slug": "widget",
"permalink": "https://app.stoar.ai/product/widget",
"type": "simple",
"status": "publish",
"featured": true,
"catalog_visibility": "visible",
"description": "<p>A great widget</p>",
"short_description": "Great widget",
"sku": "WID-1",
"price": "79.99",
"regular_price": "99.99",
"sale_price": "79.99",
"on_sale": true,
"purchasable": true,
"manage_stock": true,
"stock_quantity": 42,
"stock_status": "instock",
"weight": "1.5",
"categories": [{ "id": 5, "name": "Widgets", "slug": "widgets" }],
"images": [
{ "id": 0, "src": "products/widget.webp", "name": "primary", "position": 0, "alt": "" },
{ "id": 0, "src": "products/g1.webp", "name": "gallery-1", "position": 1, "alt": "" }
],
"attributes": [],
"variations": [],
"meta_data": [
{ "id": 0, "key": "_stoar_id", "value": "789" },
{ "id": 0, "key": "_stoar_low_stock_threshold", "value": "3" }
],
"_links": {
"self": [{ "href": "https://app.stoar.ai/wp-json/wc/v3/products/789" }],
"collection": [{ "href": "https://app.stoar.ai/wp-json/wc/v3/products" }]
}
}
對於可組態/variable 商品,variations 會填入規格 id,stock_quantity 則是各規格庫存的加總。
實際範例 #
正式環境對 GET /wp-json/wc/v3/orders/10126 的即時回應(USD $936.98,兩個簡單商品明細,以 PayID 付款)。個資已匿名化。
請求
GET /wp-json/wc/v3/orders/10126
Authorization: Bearer 2|L5pXj63e881lkgtHPK7Hjozyq7Pb3trHCy0iaDRf76405f72
回應
{
"id": 10126,
"parent_id": 0,
"number": "10126",
"order_key": "REDACTED-FOR-DOCS",
"created_via": "checkout",
"version": "8.5.0",
"status": "processing",
"currency": "USD",
"date_created": "2025-06-03T04:56:43+00:00",
"date_modified": "2025-06-03T04:56:43+00:00",
"discount_total": "0.00",
"discount_tax": "0.00",
"shipping_total": "0.00",
"shipping_tax": "0.00",
"cart_tax": "0.00",
"total": "936.98",
"total_tax": "0.00",
"prices_include_tax": false,
"customer_id": 5794,
"customer_note": "",
"billing": {
"first_name": "Jane",
"last_name": "Doe",
"company": "",
"address_1": "1 Example Street",
"address_2": "",
"city": "Phoenix",
"state": "AZ",
"postcode": "85001",
"country": "US",
"email": "",
"phone": "+1-555-0100"
},
"shipping": { /* same shape as billing */ },
"payment_method": "payid",
"payment_method_title": "PayID",
"transaction_id": "",
"date_paid": null,
"date_completed": null,
"cart_hash": "",
"meta_data": [
{ "id": 0, "key": "_stoar_status", "value": "paid" },
{ "id": 0, "key": "_stoar_lookup_token", "value": "REDACTED-FOR-DOCS" }
],
"line_items": [
{
"id": 30219,
"name": "Reloop Terminal Mix 8",
"product_id": 112238,
"variation_id": 95589,
"quantity": 3,
"tax_class": "",
"subtotal": "897.00",
"subtotal_tax": "0.00",
"total": "897.00",
"total_tax": "0.00",
"taxes": [],
"meta_data": [],
"sku": "RELOOP_TERMINALMIX8_025-DEF",
"price": 299
},
{
"id": 30220,
"name": "Premium Skateboard Socks",
"product_id": 51706,
"variation_id": 33857,
"quantity": 2,
"tax_class": "",
"subtotal": "39.98",
"subtotal_tax": "0.00",
"total": "39.98",
"total_tax": "0.00",
"taxes": [],
"meta_data": [],
"sku": "SK8-SOCK-027-DEF",
"price": 19.99
}
],
"tax_lines": [],
"shipping_lines": [
{
"id": 0,
"method_title": "Free Shipping",
"method_id": "flat_rate",
"instance_id": "",
"total": "0.00",
"total_tax": "0.00",
"taxes": [],
"meta_data": []
}
],
"fee_lines": [],
"coupon_lines": [],
"refunds": [],
"_links": {
"self": [{ "href": "https://app.stoar.ai/wp-json/wc/v3/orders/10126" }],
"collection": [{ "href": "https://app.stoar.ai/wp-json/wc/v3/orders" }]
}
}
整合者應注意的細節
這些與 Magento 的注意事項重疊——底層的 Stoar 資料是同一份,只是以不同的外層結構呈現:
| 欄位 | 實際觀察 | 原因 |
|---|---|---|
transaction_id、date_paid |
已付款訂單為 ""/null |
取自 Stoar 的 OrderPayment 稽核記錄;早於該記錄的舊訂單會顯示為空 |
payment_method |
"payid" |
這是 STOAR 的金流商鍵值(stripe/bank_transfer/cash_on_delivery/payid/invoice)——並非 WC 標準的方式名稱 |
payment_method_title |
由金流商鍵值推導 | 寫死的對應表——如需擴充請修改 OrderResource::paymentMethodTitle() |
version |
恆為 "8.5.0" |
寫死——並非實際執行中的 WC 版本 |
created_via |
恆為 "checkout" |
Stoar 目前還沒有以 API 建立訂單的流程 |
customer_ip_address、customer_user_agent |
恆為 "" |
未儲存在 Order 上 |
cart_hash |
恆為 "" |
未儲存 |
tax_lines |
恆為 [] |
Stoar 只在訂單層級追蹤稅額,不分稅務轄區 |
attributes、default_attributes(商品) |
恆為 [] |
Stoar 沒有全域屬性分類體系 |
meta_data._stoar_lookup_token |
每筆訂單的魔法連結 token | 與 Magento 相同的安全警告——只要持有它就能在未驗證的情況下檢視訂單。切勿將其暴露在用戶端程式碼或記錄中。 |
錯誤結構 #
WooCommerce 的錯誤格式為:
{
"code": "machine_readable_code",
"message": "Human-readable message",
"data": { "status": 404, "id": 99999 }
}
……而非 Magento 的 {message, parameters, trace}。
| HTTP | 觸發原因 | code |
|---|---|---|
| 400 | 查詢參數錯誤/缺少必要內文 | rest_invalid_param |
| 401 | 缺少或無效的 token | woocommerce_rest_cannot_view |
| 403 | token 能力不符(例如只有 magento:admin 的 token,或顧客 token) |
woocommerce_rest_authorization_required |
| 404 | 未知的資源 id | woocommerce_rest_shop_order_invalid_id/woocommerce_rest_product_invalid_id |
| 422 | 前置條件不符(對不可退款的訂單退款等) | woocommerce_rest_invalid_state |
| 429 | 超出流量限制 | woocommerce_rest_too_many_requests |
| 500 | 非預期的伺服器錯誤 | woocommerce_rest_internal_error |
data.status 永遠與 HTTP 狀態碼一致。有意義時會加上資源專屬的鍵(data.id)。
流量限制 #
保護 Magento 的同一組以 Sanctum token 為鍵的節流機制(api-rest,預設每分鐘 60 次請求)同樣保護 WC 路由。兩者都可在 /manager/api-settings 全域設定。觸發時,回應會使用 WC 的錯誤外層結構:
{
"code": "woocommerce_rest_too_many_requests",
"message": "Too many requests. Please retry after a short pause.",
"data": { "status": 429 }
}
設定介面的完整操作說明請見 Magento 文件的「流量限制」一節。
延伸閱讀 #
app/Api/Core/OrderRepository.php、app/Api/Core/ProductRepository.php——每個轉接器都倚賴的抽象邊界app/Api/Adapters/WooCommerce/Support/StatusTranslator.php——雙向詞彙對應- WooCommerce REST API 參考文件——本轉接器所對應的上游規格