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 后端。

单独来看,你已经会:

  • 按 OpenAI 规范构建 Product Feed;
  • 设计并实现 Agentic Checkout / Delegated Payment;
  • 编写带小部件的 ChatGPT App,以及用于礼物搜索的 MCP 工具。

单看一切都很美,但合在一起很容易变成“服务动物园”。小部件有自己的节奏,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),用于展示礼物卡片,以及在需要时展示 checkout 进度。它运行在 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 checkout 看的是同一份数据;小部件只是展示从 MCP 工具返回的结果,或间接来自 ACP(例如订单信息)。

可以把这理解为同一目录上的两扇“窗”:一扇用于搜索和推荐,另一扇用于下单。如果它们看向不同的数据库,你将拥有快乐的“不同步人生”。

4. 建模数据:从 Product Feed 到订单

先从一些简单的 TypeScript 类型开始,它们会放在你的 GiftGenius 仓库中(例如 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 规范更丰富,但基本思路一致:会话就是“买什么、多少钱、当前状态如何”。

接着需要订单类型:

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 工具 → 你的 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 还有更多字段,包括可用支付方式与履约信息,但教学示例聚焦于初始会话的创建。

其三,这样的路由很适合做契约测试:如果明天 Product Feed 的结构变化了,findSkuByIdcreateCheckoutSession 的测试应该比 ChatGPT 向用户抛出奇怪错误更早发现它。

7. ACP 会话与支付服务提供商的衔接

到目前为止我们还没触及支付服务提供商。在真实集成中,大致会发生如下(简化版)。

首先,ChatGPT(通过 ACP)调用你的 POST /checkout_sessions。你的后端在自己的数据库中创建本地会话。当用户在 Instant Checkout UI 中确认支付后,平台会向 PSP 为某个商户和金额请求委托支付令牌(Shared Payment Token)。这个令牌会在 complete 请求(或 Delegated Payment 规范中的等价调用)中带给你。

之后,你使用该令牌在 PSP 端创建支付,无需接触真实支付数据。PSP 发送 webhook 告知结果;你据此更新订单和/或 checkout 会话的状态。

在我们的教学代码中,这一步可以做成模拟。例如 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 组件、走马灯等)。这已经是你的设计与体验,而不是 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 启动 checkout,而 ACP 的调用(创建/完成 checkout_session)要么在此之前(你在后端先行创建会话),要么在 requestCheckout 之后,但始终发生在服务端。

与此同时,小部件可以基于你的 API(/api/orders/...)和 MCP 工具,展示“下单步骤”、状态与订单预览。

若把方案 B 画成时序图,大致如下:

sequenceDiagram
  participant User as 用户
  participant GPT as ChatGPT / GPT
  participant W as GiftGenius Widget
  participant MCP as MCP 服务器
  participant ACP as Commerce 后端
  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 通信。

洞见

ChatGPT 在其 SDK 中提到,应用很快会支持变现。事实确实如此。小部件已经可以使用若干尚未正式发布的方法,其中最有意思的就是 requestCheckout()

它的调用大致如下:

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

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

它会弹出一个对话框,让用户完成支付。所以请按“变现已开启”的假设来设计你的应用:等你做完的时候,很可能就真的如此了。

9. 课程用的迷你实现:单体后端

在架构模块里我们讨论过:要不要把一切做成一个服务,还是一开始就拆成 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 订阅、预订、外卖、在线课程——任何包含商品/服务、价格和合理 checkout 的用例。回忆阶段模型:discovery → decision → checkout → post‑payment。

其次,参考 GiftGenius 的架构,随笔描述:你会如何构建 Product Feed(SKU 与价格存放在哪里、由谁更新)、在哪儿实现 ACP 契约(独立服务还是现有后端的一部分)、如何接入支付服务提供商,以及你的小部件(如果有)如何通过 MCP 和 Apps SDK 与这一切交互。

最好明确说明你的项目会只用方案 A(没有 App 的 Instant Checkout)、只用方案 B(App + 小部件),还是两种方案同时支持。哪怕只是文字版的架构草图,也能在真实集成阶段显著降低意外风险。

11. 集成 Product Feed、ACP 与小部件时的常见错误

错误 1:为搜索与为 checkout 准备了两个不同的目录。
有时团队会先做一个给 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、金额与状态,以免细节过载。问题在于,当这样的“演示契约”不知不觉流入生产时,你会突然发现缺少地址、税费、配送方式、优惠码等字段,只能临时“打补丁”。更好的做法是从一开始就为真实场景预留字段,即便短期内暂时不用。

错误 5:订单与用户没有建立关联。
在演示中很容易只保留 orderIdskuId,而不考虑用户下周回来问“给我看下我的购买”。如果一开始不把 userId(或其他稳定标识)写入订单与 checkout 会话,之后就得做迁移与各种桥接。围绕 ChatGPT 的 commerce 架构几乎总是假设 GPT 能把当前对话与用户的订单历史关联起来——最好提前考虑。

错误 6:低估 webhooks 与幂等性的关键性。
本讲只提到 webhooks,深入会在后续模块。很容易以为“webhook 只来一次,更新订单就完了”。现实中支付系统喜欢重试事件,网络也会丢响应。如果不把订单与 checkout 会话设计成按 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