CodeGym /課程 /ChatGPT Apps /在同一個專案中整合 Product Feed、Merchant 與 ACP

在同一個專案中整合 Product Feed、Merchant 與 ACP

ChatGPT Apps
等級 14 , 課堂 4
開放

1. 各自都已能運作……

此時你已經大致理解,圍繞 ChatGPT 的 commerce 流程如何運轉。商家有產品 feed,實作了 ACP 端點(例如 /checkout_sessions 及相關介面),Instant Checkout 會完成付款,而後端接收 webhooks 並建立訂單。即便沒有你的 ChatGPT App,這一切也能運作:只要有 Product Feed + ACP backend 即可。

獨立來看,你已經會:

  • 依照 OpenAI 規格整理出 Product Feed;
  • 設計並實作 Agentic Checkout / Delegated Payment;
  • 撰寫帶有小工具與 MCP 工具(tool)的 ChatGPT App 來搜尋禮物。

分開看一切都很美好,但合在一起很容易變成「服務動物園」。小工具有自己的生命週期,MCP 伺服器走自己的節奏,ACP 後端又是另一套,而訂單與 webhook 的邏輯是第四套。一旦你嘗試調試真實購買或修奇怪的 bug,常會驚覺沒有人真正看到全貌。

本講座的目標,是把你從這種狀態中拉出來,提供一個完整但可落地的架構:Product Feed 與 ACP 後端到底如何相連,它們又如何對應到 ChatGPT App 與小工具,支付服務提供商在何處登場,並把一切包裝成團隊可理解的元件:服務、資料庫、API。

同時我們會不斷強調:哪些屬於嚴格標準(SPEC),而哪些只是我們為 GiftGenius 做出的架構選擇。

洞見:ChatGPT 是免費的 Google

ChatGPT 與使用者互動的方式某種程度上和 Google 類似:它免費為你帶來相關流量,因為它在別的地方賺錢——賺的是使用者本身。

從商業角度,這代表一件簡單的事:ChatGPT 會成為你商品的免費「行銷管道」,前提是你接上了 Product Feed 與 ACP 後端。當你的商品能有效滿足使用者的需求,模型就會推薦你的品項,而且你無需額外支付展示或點擊費用。

由此帶來兩個實務結論:

  1. 機會之窗目前暫時非常便宜。 現在 ACP 生態的競爭不算激烈,進入高價區隔不一定需要傳統的廣告預算。這是少見的情況:對高單價產品(機票、房產、精品、保險)具有高轉換率的流量,很可能幾乎免費。
  2. 值得從毛利最高的垂直類別開始。 若你能觸及高客單價的品類,理性上應該優先接入:
    • 飛機、遊艇、別墅的販售/租賃;
    • 住宅與高端房地產的販售/租賃;
    • 珠寶、名錶、保險產品與服務。

這不保證「快速致富」,但會創造一種不對稱:最先在高價區隔接上高品質 Product Feed 與穩健 ACP 後端的人,會從這個仍被低估、且實質上免費的管道中,獲得不成比例的高收益。

2. GiftGenius 參考架構:鳥瞰

先從高處看。回想前面模組的整體圖景:使用者在 ChatGPT 內發話,模型呼叫你的工具,而 commerce 層在另一個後端中運作。

我們先列出 GiftGenius 的主要模組。

第一,ChatGPT 的 UI 與 GPT 模型,與使用者對話,並在需要時接上 GiftGenius App(甚至可能完全不需要 App——只靠 Product Feed)。

第二,GiftGenius 小工具(Next.js + Apps SDK),用來顯示禮物卡片,以及在需要時顯示結帳進度。它活在 window.openai 的沙盒中,對真實支付資訊一無所知。

第三,MCP 層,為模型提供在目錄(Product Feed)中搜尋禮物的工具,並可能提供讀取訂單歷史的能力。

第四,commerce / ACP 後端,負責:

  • 讀取 Product Feed 作為商品與 SKU 的單一真實來源;
  • 實作 Agentic Checkout 規格(/checkout_sessions、webhooks、狀態);
  • 依照 Delegated Payment 規格與支付服務提供商(例如 Stripe)溝通。

第五,目錄資料庫(若 feed 由資料庫產出)、訂單資料庫,與其他輔助結構(使用者、設定)。

最後是支付服務提供商,負責儲存與處理支付資料,並發送 webhooks 通知付款結果。

示意如下:

graph LR
  U[使用者於 ChatGPT] --> GPT[GPT 模型]
  GPT -->|渲染| W[GiftGenius Widget
Next.js + Apps SDK] GPT -->|MCP tools| MCP[MCP 伺服器
禮物搜尋] MCP --> PF["Product Feed
(資料庫/JSON)"] GPT -->|ACP HTTP| ACP[GiftGenius Commerce Backend
Agentic Checkout] ACP --> ORDERS[訂單資料庫] ACP --> PSP["支付服務提供商
(Stripe 等)"] PSP --> ACP ACP -->|webhooks/事件| GPT

這張圖把 GiftGenius 描繪為一個實作範例。Product Feed 的格式、/checkout_sessions 的合約,以及 Delegated Payment 的協定屬於 ACP 標準;服務的部署位置、資料庫綱要與程序切分則是你的架構決策。

3. Product Feed、ACP 與小工具在邏輯上的關聯

為了不在滿天飛的箭頭裡迷失,先記住一個簡單但關鍵的觀念:你只有一個商品的單一真實來源

在 GiftGenius 的世界裡,假設這是 PostgreSQL 裡的 products + skus 資料表。你會:

  1. 依 OpenAI 規格產出 Product Feed(直接或透過匯出)。
  2. 為 MCP 工具建立搜尋索引(例如 search_gifts)。
  3. 對 ACP 後端的請求進行驗證——檢查傳入的 sku_id 是否存在、價格與幣別是否正確。

如此一來,MCP 搜尋與 ACP 結帳看到的是同一批資料,而小工具只是呈現結果,這些結果要嘛來自 MCP 工具,要嘛間接來自 ACP(例如訂單資訊)。

你可以把這想成是同一個目錄的兩扇「窗口」:一扇提供搜尋與推薦,另一扇用於下單結帳。如果這兩扇窗口看到的是不同的資料庫,你就會與不同步的狀態長期為伍。

4. 數據建模:從 Product Feed 到訂單

先定義幾個會存在你 GiftGenius 儲存庫中的 TypeScript 型別(例如放在 src/domain/commerce.ts)。這些型別並非規格的逐字拷貝,但以對應應用程式的便利形式,反映其核心理念。

// src/domain/commerce.ts

export interface ProductSku {
  id: string;          // 穩定的 SKU ID(與 Product Feed 一致)
  title: string;       // 人類可讀的名稱
  priceCents: number;  // 以分/角為單位的價格
  currency: string;    // ISO 代碼,例如 "usd"
}

export type CheckoutStatus = "pending" | "succeeded" | "failed";

export interface CheckoutSession {
  id: string;
  skuId: string;
  totalCents: number;
  currency: string;
  status: CheckoutStatus;
}

這裡我們明確在 CheckoutSession 中帶上對 skuId 的參照與固定幣別/金額。這是我們的內部模型;實際的 Agentic Checkout 規格更豐富,但基本想法相同:會話(session)就是「買多少、買什麼、目前狀態為何」。

接著需要訂單型別:

export interface Order {
  id: string;
  userId: string;
  skuId: string;
  totalCents: number;
  currency: string;
  checkoutSessionId: string;
  status: "awaiting_payment" | "paid" | "canceled" | "refunded";
}

你會在此感受到前一模組通用實體的影響: intentcheckout_sessionorder。 在我們的教學專案中,我們稍微合併了 intent 與 order,以免增加實體數量,但仍保留對 checkoutSessionId 的關聯。

5. GiftGenius 小工具如何「窺視」commerce 世界

重點:小工具本身不會直接呼叫支付服務,甚至不需要了解 ACP 的細節;它的角色是呈現由後端計算並落定的狀態。

最簡單也最有用的場景:付款成功後,使用者回到對話問「顯示我在 GiftGenius 的最近訂單」。GPT 會呼叫像 get_user_orders 的 MCP 工具,該工具會連到你的後端,而小工具則顯示清單。

想像一個 Next.js 的 API 路由,回傳最近訂單(簡化版):

// app/api/orders/recent/route.ts

import { NextRequest, NextResponse } from "next/server";
import { getRecentOrdersForUser } from "@/lib/orders";

export async function GET(req: NextRequest) {
  const userId = req.headers.get("x-giftgenius-user-id")!;
  const orders = await getRecentOrdersForUser(userId);
  return NextResponse.json({ orders });
}

函式 getRecentOrdersForUser 已存在於你的 commerce 層,會操作資料庫並理解訂單結構。小工具則可透過 window.fetch 呼叫此路由(前面模組已示範),顯示購買卡片。

「MCP tool → 你的 API → 訂單資料庫 → 小工具」的組合,讓使用者產生 App 擁有「購買記憶」的感覺,然而小工具只是把後端狀態視覺化。

6. 以 Next.js 風格實作一個簡單的 ACP 端點

現在勾勒一下關鍵 ACP 端點之一——建立 checkout_session——的教學實作。規格中的合約相當豐富,但課程裡可先保留核心:傳入 skuId,我們依 feed/資料庫檢查,建立會話並回傳其 ID 與金額。

假設我們有一條路由 POST /api/checkout-sessions

// app/api/checkout-sessions/route.ts

import { NextRequest, NextResponse } from "next/server";
import { findSkuById, createCheckoutSession } from "@/lib/checkout";

export async function POST(req: NextRequest) {
  const body = await req.json();          // { skuId: string }
  const sku = await findSkuById(body.skuId);

  if (!sku) {
    return NextResponse.json(
      { error: "SKU not found" },
      { status: 400 },
    );
  }

  const session = await createCheckoutSession(sku);
  return NextResponse.json({ session });
}

這裡有幾個重點。

其一,正是在這裡 commerce 層會與 Product Feed/資料庫核對:findSkuById 必須查詢與 feed 生成相同的資料來源。我們不信任任何「空中掉下來」的資料——無論來自 GPT 還是小工具。

其二,我們只回傳 ChatGPT/ACP 客戶端需要的資訊:會話 ID、金額、幣別與狀態(預設 pendingnot_ready_for_payment,視你採用的術語而定)。真實 ACP 會有更多欄位,包括可用支付方式與 fulfillment 資訊,但教學範例聚焦於會話的初始建立。

其三,這條路由很適合做契約測試:若未來 Product Feed 結構變更,findSkuByIdcreateCheckoutSession 的測試應該在 ChatGPT 對使用者丟出奇怪錯誤之前就能偵測到。

7. ACP 會話與支付服務提供商的關聯

到目前為止我們還沒碰支付服務提供商。在真實的整合中,大致會發生以下流程(簡化版)。

首先,ChatGPT(透過 ACP)呼叫你的 POST /checkout_sessions。你的後端會在本地資料庫建立一筆會話。當使用者在 Instant Checkout UI 中確認付款後,平台會向 PSP 依 Delegated Payment 規格,為特定商家與金額請求委派的支付權杖(Shared Payment Token)。這個權杖會在 complete 請求(或依 Delegated Payment 規格的類似呼叫)中送達你這邊。

之後你使用該權杖在 PSP 端建立付款,而無需接觸真實支付資料。PSP 會發送 webhook 通知結果;你更新訂單與/或結帳會話狀態。

在教學程式碼中,我們可先模擬這個步驟。例如,函式 completeCheckoutSession 可以長這樣:

// src/lib/checkout.ts

export async function completeCheckoutSession(sessionId: string, spt: string) {
  // 現實中此處會以委派的權杖(SPT)呼叫 PSP API
  const paymentOk = await mockChargeWithToken(spt);

  return paymentOk
    ? { status: "succeeded" as const }
    : { status: "failed" as const };
}

呼叫 PSP 與使用 Shared Payment Token 是 Delegated Payment 標準的一部分,而 mockChargeWithToken 則是我們為教學而設的抽象層,用以模擬該規格。

8. GiftGenius 端到端流程:從需求到已付款的禮物

現在把一切串起來,做成一段步驟序列。這就是我們結合所有層級的「實戰」故事。為避免混淆兩個世界,我們分開討論。

方案 A:無 App,僅 Product Feed + ACP

在此場景,你有 Product Feed 與 ACP 後端,但沒有 ChatGPT App 與小工具。這是典型的 Instant Checkout 商家。

使用者在 ChatGPT 裡輸入:「幫我挑一個 50 美元以內的數位禮物」。GPT 使用你的 Product Feed 尋找合適的 SKU,並在它的原生 UI 以購物卡片的形式顯示。這裡沒有你的任何 React 代碼——卡片完全由 ChatGPT 繪製。

使用者在其中一張卡片上點擊「Buy」。這個點擊由ChatGPT 本身處理。平台會:

  1. 根據 Product Feed 組出 line_items
  2. 依 Agentic Checkout 規格呼叫你的 POST /checkout_sessions
  3. 向使用者顯示 Instant Checkout UI(支付方式、地址等)。
  4. 在確認後,向 PSP 取得 Shared Payment Token,並呼叫你的 .../complete
  5. 取得你回傳的最終 checkout_session 狀態,並在需要時等待你的訂單 webhook。

就你的程式碼而言,這裡只有ACP 端點與 Product Feed 在運作。完全不涉及 Apps SDK、window.openai 與小工具。這是完全有效且「純粹」的 ACP 商家場景。

方案 B:有 ChatGPT App 與 GiftGenius 小工具

現在在上面加上 ChatGPT App 與 GiftGenius 小工具。Product Feed 與 ACP 後端仍在:它們依舊負責搜尋與付款。差別在於,我們有了自己的 UI 與 App 內部的步驟邏輯。

想像對話:使用者在 ChatGPT 裡輸入:「幫我替媽媽挑一個 50 美元以內的禮物」。GPT 理解這是 commerce 請求,並建議使用 GiftGenius App。小工具詢問幾個問題:年齡、興趣、國家。接著 GPT 呼叫你的 MCP 工具 search_gifts 並帶入篩選條件;MCP 伺服器查詢目錄(資料庫或預先建立的索引),找到幾個合適的 SKU,並以結構化資料回傳。

GPT 把這些資料交給小工具,小工具用你自家的卡片(React 元件、轉盤等)將其呈現。這已是你的設計與 UX,而非 ChatGPT 的標準購物 UI。

當使用者在小工具中點擊「購買」,流程會與方案 A 不同。這個點擊由小工具處理:

  1. 小工具辨識使用者選擇了哪個 SKU。
  2. 透過自家 API(例如 POST /api/checkout-sessions)呼叫你的後端以建立 checkout_session(或取得已準備好的會話 ID)。
  3. 然後小工具呼叫 Apps SDK 的執行期方法,類似:
    // 請以 Apps SDK 文件為準,查看最新的方法簽章
    await window.openai.requestCheckout({
      checkoutSessionId: session.id, ...
    });
    

    這個呼叫是小工具主動發起。對 ChatGPT 來說,這意味著:「請為這個 checkout_session 開啟 Instant Checkout」。

接下來,ChatGPT 平台的行為與方案 A 類似,但都在幕後進行:

  • 向使用者顯示原生的 Instant Checkout UI;
  • 向 PSP 取得 Shared Payment Token;
  • 呼叫你的 ACP 端點以完成會話(.../complete);
  • 協助接收與處理你後端的 webhooks。

也就是說,在方案 B 中,小工具透過 Apps SDK 觸發結帳;而 ACP 的呼叫(建立/完成 checkout_session)不是在此之前(你在後端自行建立會話),就是在 requestCheckout 之後,但始終在伺服器端進行。

同時,小工具可以並行透過你的 API(/api/orders/...)與 MCP 工具,顯示「結帳流程」、狀態與訂單預覽。

若以圖示表達方案 B,大致如下:

sequenceDiagram
  participant User as 使用者
  participant GPT as ChatGPT / GPT
  participant W as GiftGenius 小工具
  participant MCP as MCP 伺服器
  participant ACP as Commerce Backend
  participant PSP as 支付服務提供商

  User->>GPT: "幫我挑一個 50 美元以內的禮物"
  GPT->>MCP: search_gifts(...)
  MCP-->>GPT: SKU 清單
  GPT->>W: 用於渲染卡片的資料
  User->>W: 點擊「購買」
  W->>ACP: POST /api/checkout-sessions (skuId)
  ACP-->>W: checkout_session(id、金額、幣別)
  W->>GPT: window.openai.requestCheckout({ checkoutSessionId })
  GPT->>User: Instant Checkout UI
  User->>GPT: 確認付款
  GPT->>PSP: 請求 Shared Payment Token
  PSP-->>GPT: SPT
  GPT->>ACP: complete(sessionId, SPT)
  ACP->>PSP: charge(SPT)
  PSP-->>ACP: 付款結果
  ACP->>GPT: 訂單狀態
  GPT->>User: 成功/失敗付款訊息

與方案 A 的關鍵差異:

  • 在 A 中,卡片與「Buy」按鈕由ChatGPT 本身繪製,且由它直接呼叫 ACP。
  • 在 B 中,卡片與「購買」按鈕由你的小工具繪製,並由它呼叫 window.openai.requestCheckout(...)。之後 ChatGPT 會在幕後與你的 ACP 後端與 PSP 溝通。

Insight

ChatGPT 在其 SDK 中提到,應用很快就會支援變現。事實確實如此。小工具已能使用多個尚未公開的 API,而其中最有趣的,就是 requestCheckout()

呼叫看起來如下:

window.openai.requestCheckout({
  id: "checkout_session_123",

  payment_provider: {
    merchant_id: "stripe",
    supported_payment_methods: ["card"]
  },
   ...
}

它會顯示一個對話框,讓使用者完成付款。因此,請以「變現已啟用」的方式來設計你的應用:等你完成時,它就會是如此。

9. 課程用微型實作:單體式 backend

在架構模組中我們已討論過:要不要一開始就拆成 MCP 伺服器、commerce 後端與獨立的支付整合服務?對教學目的而言,多半「近似單體」就夠了:一個儲存庫、一個部署,但把邏輯乾淨地分層。

GiftGenius 的教學版本可以這樣:一個 Next.js 應用,裡面:

  • 小工具放在 app/widget/page.tsx
  • ACP 端點在 app/api/checkout-sessions 與相鄰路由中;
  • MCP 工具在 app/api/mcp/route.ts 或獨立資料夾中;
  • 訂單相關實作放在 src/lib/orders.tssrc/lib/checkout.ts 等模組。

物理上它是同一台伺服器(特別在 dev/staging 階段),但在邏輯上你已用三種角色來思考:UI(小工具)、MCP(提供 GPT 的工具/資源)與 ACP(commerce 後端)。

之後在生產環境模組中,你會看到如何把這個「單體」拆成多個服務與環境,並在它們前方放上一個 MCP Gateway。但在第 14 模組的層級上,這樣一個「有正確分層的單體」已經能提供非常可信的架構。

10. 實作作業:以 ACP 為核心設計你的架構

為了讓上述內容不只停留在理論,最好現在就套用到你的領域。這一講可以做兩個小練習。

其一,選擇你的情境:SaaS 訂閱、訂位、外送、線上課程——任何包含商品/服務、價格與合理結帳流程的案例。回想階段模型:discovery → decision → checkout → post‑payment。

其二,依據 GiftGenius 架構,自由描述你會如何落地:如何建立 Product Feed(SKU 與價格存在哪、誰更新)、在哪裡實作 ACP 合約(獨立服務或現有後端的一部分)、如何接上支付服務提供商,以及你的小工具(如果有)如何透過 MCP 與 Apps SDK 與上述元件互動。

最好直接說清楚,你的專案會只用方案 A(無 App 的 Instant Checkout)、只用方案 B(App + 小工具),或兩者並行。即便只是文字草稿,也能大幅降低真正整合時的意外風險。

11. 整合 Product Feed、ACP 與小工具時的常見錯誤

錯誤 №1:兩個不同的目錄——一個用於搜尋,另一個用於結帳。
常見做法是先為 GPT 架一個「快做版」搜尋 feed(例如一個小型 JSON),之後再獨立打造一個 commerce 資料庫來做訂單。如果兩者沒有用共同的 ID 與共同的更新邏輯綁在一起,GPT 可能會把已無法購買或舊價格的商品推薦給使用者。正確做法是使用單一真實來源,從中同時生成 Product Feed 與 ACP 端點所用的內部表。

錯誤 №2:信任來自 GPT 或小工具的資料。
checkout_session 帶著 skuId 與價格進來時,很容易就想直接相信:「GPT 應該不會亂說吧」。但模型很可能「發揮創意」或搞混 SKU,而使用者也可能嘗試竄改請求。如果不把輸入與 Product Feed/資料庫對照,你就有賣錯商品或賣錯價格的風險。任何 ACP 端點都應從依據主資料來源驗證開始。

錯誤 №3:混淆小工具與 commerce 後端的角色。
有時開發者習慣在前端直接調支付 SDK、在 Stripe 建立會話,像一般網站一樣。在 ChatGPT Apps 的脈絡下,這會破壞安全模型並違反 ACP:支付流程應經由 ChatGPT 與你的 commerce 後端進行,而小工具只負責呈現狀態並發送事件(例如 requestCheckout)。若小工具知道太多支付細節,你會同時得到複雜度與更高風險。

錯誤 №4:過度簡化 ACP 合約。
在教學範例裡,我們刻意只保留 skuId、金額與狀態,以免細節淹沒主題。問題是當這種「demo 合約」悄悄流入生產,你會發現缺欄位:地址、稅、運送方式、折扣碼等等,接著就開始東拼西補。最好一開始就用能涵蓋真實場景的內部模型,即便部分欄位初期用不到。

錯誤 №5:缺乏訂單與使用者之間的連結。
在 demo 裡只用 orderIdskuId 很容易,卻忽略使用者可能一週後回來問:「顯示我的購買」。如果一開始不把 userId(或其他穩定識別碼)放進訂單與結帳會話,之後就得做遷移與繁瑣的橋接。圍繞 ChatGPT 的 commerce 架構幾乎都預期 GPT 能把當前對話與使用者的訂單歷史關聯起來——請預先考量。

錯誤 №6:低估 webhooks 與冪等性的必要性。
在這一講我們只談到 webhooks,詳細會放到後續模組。很容易想成:「webhook 來一次、更新訂單、結束」。實務上支付系統常會重試事件,網路也可能丟失回應。如果不把訂單與結帳會話設計為冪等結構(以 checkoutSessionIdpaymentId 為鍵),就可能遇到重複扣款、訂單重複與 PSP 與你資料庫之間的難以察覺落差。

錯誤 №7:忽視 Product Feed 中的限制與政策。
為了盡快做出 demo feed,很容易忽略年齡限制、國家可售性、禁售類別等「小事」。之後你會發現,GPT 會愉快地向使用者推薦在其地區或年齡不可販售的商品。與政策與限制相關的欄位應該一開始就設計並填入,即便你目前只販售無害的數位禮物。

1
問卷/小測驗
付款:ACP 與 Instant Checkout,等級 14,課堂 4
未開放
付款:ACP 與 Instant Checkout
商務:Product Feed、ACP 與 Instant Checkout
留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION