Day 25 已經能找商品,今天讓 Tool 修改真正的 WooCommerce 購物車。
這次從空購物車開始,加入兩件單價 1,290 元的機械鍵盤,再改成一件,最後移除。整條流程的重點是:每次都使用伺服器回傳的購物車,不自行猜測數量與總額。
?webmcp_lab=26。本機網址為 https://wordpress.local/?webmcp_lab=26。| Tool | 用途 | 主要輸入 |
|---|---|---|
| search_products | 搜尋公開商品 | keyword、maxPrice、limit |
| get_product | 讀取商品詳情 | productId |
| get_cart | 讀取目前購物車 | {} |
| add_to_cart | 再加入指定件數 | productId、quantity |
| update_cart_item | 將一筆商品改成指定件數 | key、quantity |
| remove_from_cart | 移除整筆購物車商品 | key |
商品搜尋沿用 Day 25。換站操作時,使用該站搜尋結果中的 ID。
本頁操作會改變目前工作階段的購物車。使用獨立測試工作階段,才能從空車核對數量。重新整理頁面或按 Inspector Reset,都不會清空 WooCommerce 購物車。
Cookie 讓伺服器辨認目前的購物車工作階段;Nonce 則用於 Store API 的請求驗證。本篇保留 Cookie,並用 Nonce header 傳送 Store API nonce。
Day 23 的 WordPress REST API 使用 wp_rest 與 X-WP-Nonce。本篇的 Store API 使用:
wp_create_nonce( 'wc_store_api' )
Day 26 的 PHP 將 URL 與 nonce 提供給自己的 JavaScript:
wp_localize_script(
'wp-webmcp-cart',
'wpWebMCPCart',
array(
'cartUrl' => esc_url_raw( rest_url( 'wc/store/v1/cart' ) ),
'productsUrl' => esc_url_raw( rest_url( 'wc/store/v1/products' ) ),
'nonce' => wp_create_nonce( 'wc_store_api' ),
)
);
不要直接把 wc/store/v1/cart 接在網站網址後方;使用 rest_url() 才能配合目前的永久連結設定。本機使用 index.php?rest_route=...,範例也支援 /wp-json/...。
JavaScript 的請求核心如下:
let nonce = wpWebMCPCart.nonce;
const response = await fetch(url, {
method: body ? 'POST' : 'GET',
credentials: 'same-origin',
cache: 'no-store',
headers: {
Accept: 'application/json',
...(nonce ? { Nonce: nonce } : {}),
...(body ? { 'Content-Type': 'application/json' } : {})
},
...(body ? { body: JSON.stringify(body) } : {})
});
const nextNonce = response.headers.get('Nonce');
if (nextNonce) nonce = nextNonce;
這是請求核心節錄;完整程式另外處理輸入驗證、同源 URL、錯誤與畫面更新。Nonce 保留在 JavaScript 內,Tool 結果與頁面紀錄只顯示 nonceSent: true,不輸出它的值。
每次寫入前,範例先 GET 購物車,取得目前狀態及回應提供的 nonce,再送 POST。寫入完成後,直接使用 POST 回傳的購物車,不需要為了更新畫面再 GET 一次。
在 Inspector 的 Tool 選 get_cart,Input Arguments 填 {},按 Execute Tool。
空購物車的主要欄位如下:
{
"status": "success",
"cart": {
"lineCount": 0,
"totalQuantity": 0,
"items": [],
"totals": {
"total": { "amount": 0, "currency": "TWD" }
}
}
}
這裡節錄主要欄位。完整回傳也包含商品金額、折扣、運費、稅額與是否需要配送。
lineCount 是商品項目筆數;totalQuantity 是所有項目的件數。相同鍵盤兩件通常是一筆商品、兩件,兩個數字用途不同。
圖片 1|get_cart 讀取目前工作階段的購物車,確認加入前的商品數量與總額。
先用 search_products 搜尋:
{"keyword":"鍵盤","maxPrice":2000,"limit":5}
本機符合條件的是 ID 101、1,290 TWD 的機械鍵盤。接著:
add_to_cart。{"productId":101,"quantity":2}
加入使用商品 ID。送到 Store API 時,欄位名稱改為 id:
POST /wc/store/v1/cart/add-item
{"id":101,"quantity":2}
範例在 POST 前先讀取商品,確認為簡單商品,再同步目前購物車。此次加入回傳 HTTP 201,JavaScript 用 response.ok 判斷成功,沒有將成功寫死成只接受 200。
圖片 2|add_to_cart 將商品 ID 與加入件數送到 Store API,成功回傳 HTTP 201。
成功後,Tool 與左側「目前購物車」共同使用回傳資料,顯示:
add_to_cart 的 quantity 是額外加入件數。再呼叫一次相同輸入,會再加兩件,不能把它當成設定總數。
圖片 3|加入兩件機械鍵盤後,購物車有 1 筆商品、2 件,總額為 2,580 TWD。
金額使用 API 的 currency_minor_unit 換算。本機回傳 total_price: "258000"、小數位 2,因此是 2,580 TWD。畫面總額直接取自 totals.total_price,不以「單價 × 數量」取代伺服器計算。
本機商品為虛擬商品,needsShipping: false。實體商品還要查看配送狀態;shipping: null 表示尚未計算運費,不應解讀為免運。
商品 ID 用來找商品,購物車的 key 用來指定某一筆已加入的項目。
先從最新的 get_cart 或剛才操作回傳中取得該筆 key。以下的「目前項目的 key」要換成實際值。
update_cart_item。{"key":"目前項目的 key","quantity":1}
Store API 對應 POST /cart/update-item。這次回傳 HTTP 200,數量變成 1 件,總額 1,290 TWD。
範例將數量限制在 1~20;移除商品使用獨立的 remove_from_cart。
圖片 4|update_cart_item 使用購物車項目 key,把數量改為 1 件,總額同步更新為 1,290 TWD。
Tool 改成 remove_from_cart,填入同一筆項目的 key,再按 Execute Tool:
{"key":"目前項目的 key"}
Store API 對應 POST /cart/remove-item,會移除整筆項目。本次回傳 HTTP 200,購物車回到 0 件、0 TWD。
圖片 5|remove_from_cart 移除指定項目後,購物車回到 0 件、0 TWD。
「把第二個商品改成兩件」則需要先看目前的購物車清單,取出第二筆 key。第二個是清單位置,不是商品 ID 2,也不是字串 key "2"。
get_cart 標記 readOnlyHint: true;加入、更新與移除標記為 false。所有 Schema 都設 additionalProperties: false,執行時也檢查整數、數量範圍和 key。
加入工具的描述明確說明三件事:
Use productId from search_products/get_product, never a list position.
Quantity is additional units.
Does not checkout or place an order.
更新工具則寫明 quantity 是新的總數。這些描述幫助 Agent 選擇工具;實際商品狀態、庫存與操作結果仍以 Store API 為準。
範例的回傳狀態如下:
| status | 情境 | 下一步 |
|---|---|---|
| success | 成功取得最新購物車 | 使用 cart 回答並更新畫面 |
| invalid_input | ID、key 或數量格式不合 | 修正參數 |
| needs_input | 商品需要規格或非簡單商品 | 到商品頁確認規格與加入方式 |
| busy | 同頁另一筆購物車操作尚未完成 | 等待前一筆結果 |
| error | API 回傳失敗 | 依 httpStatus、code 與 message 處理 |
| unknown_outcome | 已送出寫入,但未取得完整結果 | 先 get_cart 核對,不直接重送 |
| internal_error | 讀取、網路或資料格式問題 | 檢查後重新讀取 |
本機重複移除已不存在的 key,API 回傳 409 與目前購物車。程式會同步錯誤回應中的 cart,保留錯誤狀態;不把它改寫成移除成功。
本篇的 add_to_cart 只接受簡單商品,不接受 variation 參數。檢查到需要規格的商品時,回傳 needs_input,不會選第一個尺寸或顏色。
後續擴充規格商品時,要取得 WooCommerce 實際的 variation ID 或 attribute/value,再按 Store API 的格式送出。Day 25 的屬性名稱與選項展示,不能直接當成完整的規格選擇器。
Store API 也提供 Cart-Token 作為購物車識別方式,使用它時不需另外提供 Nonce。本篇固定使用 Cookie 工作階段加 Nonce;不將 Cart-Token 提供給 Agent 或放進操作紀錄。
頁面按鈕與 Tool 共用相同的操作函式。本次透過頁面按鈕核對購物車畫面,並用獨立 Cookie 工作階段直接呼叫 Store API,測試下列結果:
| 操作 | HTTP | 結果 |
|---|---|---|
| 讀取空購物車 | 200 | 0 件、0 TWD |
| 不帶 Nonce 加入商品 | 401 | woocommerce_rest_missing_nonce |
| 加入 ID 101、2 件 | 201 | 1 筆商品、2 件、2,580 TWD |
| 同工作階段再次讀取 | 200 | 保留 2 件與 2,580 TWD |
| 將該筆改成 1 件 | 200 | 1 件、1,290 TWD |
| 移除該筆商品 | 200 | 0 件、0 TWD |
| 再次移除相同 key | 409 | woocommerce_rest_cart_invalid_key |
程式測試共 15 項通過,包含 Day 23~26 的回歸測試。Day 26 覆蓋數量語意、nonce 更新、兩種 REST URL、參數驗證、規格商品阻擋、409 同步、並行操作與寫入中斷後不重送。
在 Inspector 的 User Prompt 輸入下列題目,按 Send,觀察工具呼叫。獨立案例先按 Reset;接續題目則保留前文,並核對目前購物車。
第 4 題是邊界測試:本機只有一筆符合預算,Agent 應說明沒有第二筆,不能改加第一筆。第 5 題則依前一次展示的商品清單,對應第二筆的實際 ID。
成功加入後若再呼叫一次 get_cart,可視為重新核對工作階段狀態,不直接判定為錯誤或多餘。
購物車 Tool 的關鍵是讓「使用者說的數量」、「送出的操作」與「伺服器回傳的狀態」一致。
本次完成的流程是:空車 → 加入兩件 → 改成一件 → 移除。加入使用 productId,修改與移除使用 key;總額取自 API 回傳。這樣 Agent 才能清楚回答目前購物車,而不是只說一句「已加入」。