自訂觸發器
從任何事件建立自訂的Shopify Flow觸發器,並設定觸發前與觸發後的數值,以及 JavaScript 轉換函式,以便在您指定的確切條件下觸發。
內建的觸發器涵蓋了大多數商店所關注的變更。自訂觸發器則涵蓋其餘情況:您選擇一個 Shopify 事件,撰寫幾行程式碼來判斷該事件是否重要以及應傳送哪些資料,如此一來,它便會成為您可在 Shopify Flow 中使用的觸發器。
它能解決三項內建觸發器無法解決的問題:
- 僅在您指定的條件下觸發。「僅當訂單金額超過 500 歐元時,才標記為『
vip』」這條邏輯只需一行程式碼即可實現,無需建立一個針對每筆訂單執行後再進行篩選的工作流程。 - 在過渡階段觸發,而非在某個狀態下。您的程式碼會看到變更前與變更後的值,因此「狀態從草稿變為活躍」或「庫存跌破 5」這種情況是可能的。若僅以當前值為條件,則無法表達這種情況。
- **僅傳送您真正需要的欄位。**將事件重新塑造成工作流程實際使用的值,而非逐一讀取這些值。
自訂觸發器甚至不需要「Shopify」事件:它也可以 依照排程執行,並自行判斷哪些內容已變更。
運作原理
- 選擇觸發條件:一次「Shopify」事件,或一個排程。
- 針對某個事件,請選擇該事件所監聽的事件 - - 即應用程式已接收的任何Shopify事件 - - 或從範本開始,系統會自動選取該事件並填入相應程式碼。
- **擷取真實的有效載荷。**在您的商店中進行該變更後,應用程式便會擷取確切的事件正文。
- 撰寫一個轉換函式 - - 若要觸發,請傳回一個物件;若要跳過,請傳回
null。 - 請將其與擷取的事件進行比對,以確切了解您的工作流程將會收到什麼內容。
- 把它打開。

撰寫轉換函數
您的觸發器是一個 JavaScript 模組,該模組會匯出一個名為 transform 的函式。此函式接受四個參數:
payload- 原始的Shopify事件主體,完全依照其傳入的原始樣貌呈現。topic- 觸發了哪個事件,例如PRODUCTS_UPDATE。shop- 您的 myshopify 網域。ctx-ctx.log(...)會將結果輸出至編輯器旁邊的日誌面板,ctx.shopify(...)會執行一個管理用 GraphQL 查詢,而ctx.fetch(...)則會呼叫網際網路上的一個 URL。
您回傳的內容將決定後續的發展:
- 傳回一個物件,觸發器隨即觸發,並攜帶該物件。
- **回傳 ``null
**,該事件即會被跳過。這就是篩選機制運作的方式 - - 無需額外學習任何獨立的篩選語言。 - 若將該檔案留空,則每當發生該類型的事件時,它就會觸發。
/**
* Only fire for high-value orders carrying the vip tag.
*/
export async function transform(payload, topic, shop, ctx) {
const total = parseFloat(payload.total_price || "0");
const tags = (payload.tags || "").split(",").map(t => t.trim());
if (total < 500) return null;
if (!tags.includes("vip")) return null;
ctx.log("firing for", payload.name, total);
return {
orderId: payload.admin_graphql_api_id,
orderNumber: payload.name,
total,
currency: payload.currency,
customerEmail: payload.email,
};
}變更前的數值
Shopify
在「產品更新」、「訂單更新」和「客戶更新」中,payload._changes 會列出此次更新所變更的所有受追蹤欄位,並分別提供 oldValue 和 newValue:
- 產品:
title、handle、description、status、vendor、productType、tags - 訂單:
financialStatus、fulfillmentStatus、tags、note、lineItems以及customAttributes.<name> - 客戶:
tags、note、state(ENABLED、DISABLED、INVITED、DECLINED)
當更新內容不在這些欄位範圍內(例如庫存變動)時,或是應用程式首次讀取該記錄時,清單會是空的。您在編輯器中擷取的範例事件會顯示該欄位,因此您可以在對其寫入資料之前,先確認其實際結構。
/**
* Fire only when a product BECOMES active - not on every later edit of an
* active product, which is what a condition on the current status would do.
*/
export async function transform(payload, topic, shop, ctx) {
const change = (payload._changes || []).find((c) => c.field === "status");
if (!change) return null;
if (change.oldValue !== "draft" || change.newValue !== "active") return null;
return {
productId: payload.admin_graphql_api_id,
title: payload.title,
oldStatus: change.oldValue,
newStatus: change.newValue,
};
}較具體的事件本身就具備新舊兩方面的價值,因此您無需在該處使用 _changes:價格變更包含 oldPrice、newPrice 及 percentChange;庫存變更包含 _oldAvailable、_newAvailable 及 _delta;元字段變更則包含 previousValue 以及 metafield.value。
從範本開始
在編輯器中的「Fires on」功能下,選擇「從範本開始」會選取事件並填入可運作的程式碼。編輯頂部的常數,進行測試,然後儲存。每個範本都會讀取變更前後的值:
- 某項產品、訂單或客戶的欄位值從一個值變更為另一個值 - - 狀態從「草稿」變為「有效」、訂單變為「已付款」、帳戶變為「已啟用」。
- 價格下跌了超過 N 百分比 - - 這才是真正的降價,並非每次價格調整都是如此。
- 庫存跌破閾值 - - 觸發一次,即在庫存跨越該線的當下觸發,而非在庫存已處低位時每次銷售都觸發。
- 產品元資料欄位數值超過閾值 - - 評分低於 3,利潤率高於 40。
- 「高價值客戶已付款訂單」 - - 系統會讀取該客戶在您店舖的累計消費金額,並僅在金額超過您設定的門檻時觸發此條件。

使用 ctx.shopify 擷取額外資料
Webhook 的內容僅包含 Shopify 傳送的欄位。若您需要其他資料 - - 例如客戶的訂單數量、變體的庫存或元欄位 - - 請直接在轉換程式中查詢 Admin API:
export async function transform(payload, topic, shop, ctx) {
const data = await ctx.shopify(`
query($id: ID!) {
customer(id: $id) { numberOfOrders tags }
}
`, { id: payload.customer.admin_graphql_api_id });
// Only fire for repeat customers
if (data.customer.numberOfOrders < 5) return null;
return {
orderId: payload.admin_graphql_api_id,
orderCount: data.customer.numberOfOrders,
};
}它會傳回查詢的 data,若查詢出現錯誤則會拋出例外,如此一來,錯誤便會出現在您的測試中,而非默默地不返回任何結果。
三個值得了解的限制:
- **每次執行最多 10 次呼叫。**資料豐富化通常只需少量資料;若超過這個數量,通常就需要建立迴圈。只要可行,請盡量透過單一查詢取得所需資料。
- **它僅能讀取,無法寫入。**它會使用您已授予的權限,且該應用程式僅會請求讀取權限。除非在「權限」頁面中授予「訂單資料存取」權限,否則查詢訂單資料的請求將會失敗。若要變更商店中的內容,請在觸發器後續的「Shopify Flow」動作中進行操作。
- **您的商店憑證絕不會傳遞至您的程式碼中。**該查詢是由應用程式代表您執行的,因此沙盒環境中並不存在會外洩的存取憑證。
ctx.fetch(url, options) 其運作方式類似瀏覽器的 fetch,適用於公開網際網路上的任何內容:供應商的庫存資料、匯率,或是您自己的 API。內部及私有網路位址將被拒絕。若您的觸發程式碼已儲存至 GitHub,請勿在其中放入 API 金鑰。
依排程執行的觸發器
Shopify Flow 當發生某些事情時,它才會做出反應。若沒有發生任何事情,它便無法做出反應,且無法偵測到您商店外的任何狀況。因此,請在「觸發條件」下的「**執行時機」中選擇「按排程執行」**。此類觸發器背後並無「Shopify」事件:您的程式碼會以固定間隔執行(從每 30 秒一次到每天一次不等),並自行判斷何謂「變更」。
這與 transform 函式相同,只是 payload 不同,且回傳值也不同:
payload.state- 無論您的程式碼在先前執行時,state傳回的值為何。首次執行時為null。payload.now以及payload.lastRunAt- - 時間戳記。- 回傳
{ state, events }:需儲存的新狀態(最多 32 KB)以及待觸發的事件清單(每次執行最多 100 個)。每個事件會觸發Custom Trigger一次;若您設定了事件的resourceId,該值將成為Shopify Flow中的記錄 ID。若無需回報任何內容,請回傳null。
**請讓首次執行時僅進行記錄,切勿觸發任何動作。**在首次執行時,您的程式碼沒有任何可比較對象,因此一切都會顯得嶄新。將所見內容儲存下來,並返回零個事件;如此一來,即使啟用觸發器,也不會讓您的工作流程被事件淹沒。所有範本皆採用此做法。

// Fires when a value at a JSON URL changes, with the value before and after.
const URL = "https://api.example.com/stock/1042"
export async function transform(payload, topic, shop, ctx) {
const res = await ctx.fetch(URL, { headers: { accept: "application/json" } })
if (!res.ok) throw new Error("The URL answered with HTTP " + res.status)
const value = String((await res.json()).stock)
// The first run only remembers the value.
const previous = payload.state ? payload.state.value : undefined
if (previous === undefined || previous === value) return { state: { value } }
return {
state: { value },
events: [{ oldValue: previous, newValue: value }],
}
}「排程」卡片中「從範本開始」下的排程觸發器範本:
- 產品、訂單或客戶已 N 天未更新 - - 過期的目錄評論、停滯的訂單、客戶回流。每個記錄在每個「靜默期」內觸發一次,且在啟用此功能時已逾期的項目不會一次性全部觸發。
- Shopify 以外的值已變更 - - 供應商庫存資料、匯率、價格表,以及數值的差額與百分比。
- RSS 或 Atom 訂閱源中的新項目 - - 供應商新聞、商品清單訂閱源、狀態頁面。
- N 小時內無訂單 - - 這是您店鋪的「心跳」。當訂單停止時觸發一次,訂單恢復時再觸發一次。
**「測試」**會將您的程式碼執行一次,過程中不會觸發任何操作,也不會儲存狀態,因此您可以隨心所欲地執行它。
在 Shopify Flow 中使用它
每個自訂觸發器在「Shopify Flow」中都會以相同的觸發器名稱「Custom Trigger」出現。將其新增至工作流程後,再新增一個條件:「觸發器識別碼等於您的觸發器識別碼」。
該名稱會顯示在觸發器的頁面中,且絕不會改變,即使您重新命名該觸發器也是如此 - - 因此您的工作流程仍能正常運作。
您的轉換所返回的每個鍵都會成為觸發器的欄位,若您希望自行解析,整個物件亦可作為 JSON 格式取得。
將程式碼存放於 GitHub
您可以連結一個 GitHub 儲存庫,讓您的觸發程式碼納入版本控制。如此一來,變更內容將透過拉取請求供審查,您可以查看誰修改了哪些內容,並能將不再運作的轉換操作還原。
請在「開發者」頁面的「連線」區段中進行設定。您可以選擇讓應用程式存取哪些儲存庫,並可隨時在 GitHub 上撤銷該存取權限。
一旦選定儲存庫,所有現有的觸發器便會立即寫入該儲存庫,且在雙向同步時均能保持同步:
| 您的職責 | 會發生什麼事 |
|---|---|
| 在應用程式中建立、編輯或複製觸發器 | 該檔案已提交至您的儲存庫 |
| 在應用程式中刪除觸發器 | 該檔案已從您的儲存庫中移除 |
| 將變更推送到已連線的分支 | 該觸發器的程式碼已在應用程式中更新 |
每個觸發器都是一個以該觸發器處理名命名的檔案,其中僅包含您在編輯器中看到的模組 - - 沒有任何額外的封裝。這意味著您可以像處理其他 JavaScript 檔案一樣,在自己的編輯器中開啟它、執行它,並對其進行程式碼檢查。
還原至較早的版本
您無需了解 Git 即可撤銷變更。一旦儲存庫連線完成,編輯器便會顯示一個「版本」下拉選單,列出該檔案的所有先前版本,並附上日期與作者。選擇其中一個版本,系統便會將其以未儲存的變更形式載入編輯器中,讓您能先閱讀內容 - - 只有儲存後,該版本才會被還原。
在本地編輯時進行測試
如果您是在自己的編輯器中編輯該檔案,無需先將其儲存至應用程式,即可直接針對擷取的樣本事件執行該程式。請使用「開發者」頁面中具備「**執行」**權限等級的 API 金鑰:
curl -X POST https://shopify.workflow-trigger-extensions.app/api/v1/triggers/custom/high-value-vip-order/test \
-H "Authorization: Bearer ftk_your_key_here" \
-H "Content-Type: application/json" \
-d "$(jq -Rn --rawfile c flow-triggers/high-value-vip-order.js '{code:$c}')"您會獲得與應用程式「測試」按鈕所顯示相同的結果:包括是否會觸發、輸出物件、您的 ctx.log 指令,以及執行時間。
此端點既不會觸發任何動作,也不會進行任何儲存,且不佔用任何方案配額 - - 因此可在檔案監控器每次儲存時安全地執行。(應用程式內的「執行測試」按鈕會觸發您的 Shopify Flow 工作流程,讓您能全程觀察其執行過程;此操作同樣免費。)
您也可以使用讀取金鑰單獨取得範例載荷,將其儲存至本機,並完全離線執行該檔案:
curl https://shopify.workflow-trigger-extensions.app/api/v1/triggers/custom/high-value-vip-order/test \
-H "Authorization: Bearer ftk_your_key_here"自訂觸發器會佔用我的方案配額嗎?
是的,而且它會統計所有經過檢查的事件 - - 而不僅僅是實際觸發的那些。如果您的觸發器監聽「產品更新」事件,而您的商店每月有 60,000 次產品更新,那麼即使您的程式碼僅在其中 100 次觸發,這仍會被計為 60,000 次事件。
我們會接收、去重並將這些事件排入佇列,接著在隔離的沙盒中執行您的程式碼 - - 所有這些步驟都在您的程式碼決定是否觸發之前完成。該計數即反映了這項工作。
換句話說:其費用與同一事件的內建觸發器相同。您無需為篩選功能支付額外費用,且測試始終免費。預排程觸發器每次執行僅計費一次。
檢測是免費的嗎?
是的。無論是應用程式中的「執行測試」按鈕,還是「/test」API 端點,均不計入配額,儘管「執行測試」功能確實會觸發您的 Shopify Flow 工作流程,以便您觀察其執行過程。只有即時執行才會消耗您的配額,因此您可以隨心所欲地對轉換進行反覆測試。
為什麼 payload._changes 是空的?
要麼是更新內容不在追蹤欄位範圍內 - - 例如產品的庫存或變體變更 - - 要麼是該應用程式目前尚未為該記錄建立基準。基準是在應用程式首次偵測到記錄時儲存的,因此對於從未見過的記錄,其首次更新將不會包含舊值;此後的每次更新則會包含舊值。
我的程式碼能否修改儲存庫中的資料?
不,ctx.shopify 會以您授予的讀取權限執行,且該應用程式絕不會要求寫入權限。這是刻意設計的:若觸發器修改了其監聽的記錄,便會重新觸發自身,而這正是 Shopify 會因此停用工作流程的原因。請在緊接該觸發器之後的 Shopify Flow 動作中修改資料。
我之後可以更換把手嗎?
不,這是刻意設計的。您的 Shopify Flow 工作流程會根據處理名稱進行篩選,因此若變更處理名稱,該工作流程將會在無任何提示的情況下停止運作。您可以自由重新命名觸發器 - - 處理名稱則保持不變。
這個名稱同時也是您 GitHub 儲存庫中的檔案名稱,因此它也不會被移動。
如果我的程式碼有錯誤,會怎麼樣?
該事件將被跳過,並在觸發器上記錄錯誤,以便您了解問題出在哪裡。失敗的轉換絕不會阻塞其他任何操作 - - 您的其他觸發器(無論是自訂或內建的)都會照常運作,不受影響。
我的程式碼會在哪裡執行?
在一個與應用程式其餘部分隔離的沙盒環境中,設有短暫的時間限制,且無法存取您商店的憑證。它僅能看到您擷取的事件有效載荷,以及您透過 ctx.shopify 或 ctx.fetch 擷取的內容。
如果我同時在 GitHub 和應用程式中編輯同一個檔案,會怎麼樣?
最後儲存的一方即為勝出。在應用程式中儲存會將內容提交至檔案,而推送至已連線的分支則會覆寫應用程式中儲存的程式碼。若您主要在儲存庫中工作,請將應用程式的編輯器視為唯讀模式,以免造成意外。
我儲存庫中的檔案能否建立新的觸發器?
不。觸發器還需要知道它正在監聽哪個 Shopify 事件,而該檔案僅包含程式碼 - - 若憑猜測來判斷事件,可能會將觸發器連接到錯誤的對象上。請先在應用程式中建立觸發器,然後再自由編輯其檔案。
它能否針對應用程式尚未接收到的事件觸發?
不。自訂觸發器會監聽應用程式已為您的商店訂閱的事件,而這些事件取決於您所授予的權限。只要授予某個資源的權限,該資源的事件也會對自訂觸發器開放。至於其他情況,請透過排程來執行。
下一步
- 開發者 API、MCP、GitHub 及觸發器模擬 - 透過您自己的程式碼或 AI 助理來管理及測試觸發器。
- 使用 AI 產生觸發程式碼 - 用淺顯易懂的語言描述觸發條件,並讓 AI 撰寫程式碼。
- 方案與使用方式 - 什麼算作「事件」,以及這項津貼如何運作。
- 觸發器的運作原理 - 內建觸發器及其觸發方式。
