1. Всё уже работает по отдельности…
К этому моменту вы уже представляете, как крутится commerce‑флоу вокруг ChatGPT. У мерчанта есть продуктовый фид, реализованы ACP‑эндпоинты (/checkout_sessions и компания), Instant Checkout проводит оплату, а backend получает webhooks и создаёт заказы. Всё это может работать даже без вашего ChatGPT App: достаточно Product Feed + ACP-backend.
Отдельно вы уже умеете:
- собирать Product Feed по спецификации OpenAI;
- проектировать и реализовывать Agentic Checkout / Delegated Payment;
- писать ChatGPT App с виджетом и MCP‑tool’ами для поиска подарков.
По отдельности всё выглядит прекрасно, а вместе легко превращается в «зоопарк сервисов». Виджет живёт своей жизнью, MCP‑сервер — своей, ACP‑backend ещё одной, а логика заказов и вебхуков — четвёртой. При первой же попытке отлаживать реальную покупку или чинить странный баг вы внезапно понимаете, что никто толком не видит общую картину.
Цель этой лекции — вытащить вас из этого состояния и дать цельную, но при этом реализуемую архитектуру: как именно связан Product Feed с ACP‑backend’ом, как оба они соотносятся с ChatGPT App и виджетом, где именно в картинке появляется платёжный провайдер, и как всё это оборачивается в понятные для команды компоненты: сервисы, БД, API.
При этом мы постоянно будем подчёркивать: что является жёстким стандартом (SPEC), а что — всего лишь нашим архитектурным выбором для GiftGenius.
Insight: ChatGPT это бесплатный Google
ChatGPT работает с пользователями примерно так же, как Google: он бесплатно приводит к вам релевантный трафик, потому что зарабатывает на другом — на самих пользователях.
С точки зрения бизнеса это означает простую вещь: ChatGPT становится бесплатным «рекламным каналом» для ваших товаров, при условии, что вы подключили Product Feed и ACP-backend. Модель будет предлагать ваши позиции, если они хорошо закрывают запрос пользователя, и вам не нужно отдельно платить за показы или клики.
Из этого следуют два практических вывода:
- Окно возможностей ВРЕМЕННО очень дешёвое. Сейчас конкуренция в ACP-экосистеме невысокая, и выход в верхние ценовые сегменты можно получить без привычных рекламных бюджетов. Это редкая ситуация, когда трафик с высокой конверсией по дорогим продуктам (авиа, недвижимость, премиальные товары, страховки) может ничего не стоить.
- Имеет смысл начинать с самых маржинальных вертикалей. Если у вас есть доступ к категориям с высокой стоимостью чека, их рационально подключать первыми:
- продажа / аренда самолётов, яхт, вилл;
- продажа / аренда домов и премиальной недвижимости;
- ювелирные изделия, дорогие часы, страховые продукты и услуги.
Это не гарантирует «быстрых миллионов», но создаёт асимметрию: те, кто первыми заведут качественный Product Feed и надёжный ACP-backend в дорогих сегментах, получат непропорционально высокий выигрыш от канала, пока он остаётся недооценённым и фактически бесплатным.
2. Reference‑архитектура GiftGenius: крупными мазками
Начнём с вида сверху. Вспомним общую картинку из предыдущих модулей: пользователь пишет в ChatGPT, модель вызывает ваши инструменты, а commerce‑слой живёт в отдельном backend’е.
Сформулируем основные блоки GiftGenius.
Во‑первых, ChatGPT UI и модель GPT, которые ведут диалог с пользователем и при необходимости подключают GiftGenius‑App (или вообще работают без App — только по Product Feed).
Во‑вторых, виджет GiftGenius (Next.js + Apps SDK), который показывает карточки подарков и, по мере необходимости, прогресс checkout. Он живёт в песочнице window.openai и не знает ничего про реальные платёжные реквизиты.
В‑третьих, MCP‑слой, который даёт модели инструменты для поиска подарков по каталогу (Product Feed) и, возможно, для чтения истории заказов.
В‑четвёртых, commerce / ACP‑backend, который:
- читает Product Feed как источник правды о товарах и SKU;
- реализует Agentic Checkout Spec (/checkout_sessions, webhooks, статусы);
- разговаривает с платёжным провайдером (например, Stripe) по Delegated Payment Spec.
В‑пятых, базы данных каталога (если фид формируется из БД), заказов и вспомогательных структур (пользователи, настройки).
И, наконец, платёжный провайдер, который хранит и обрабатывает платёжные данные, а также шлёт 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 пусть это будет таблица products + skus в PostgreSQL. Из неё вы:
- Формируете Product Feed по спецификации OpenAI (напрямую или через выгрузку).
- Строите поисковый индекс для MCP‑tool’ов (например, search_gifts).
- Проводите валидацию запросов ACP‑backend’а — проверяете, что приходящий sku_id вообще существует и имеет корректную цену и валюту.
Таким образом, MCP‑поиск и ACP‑checkout смотрят на одни и те же данные, а виджет лишь показывает результаты, которые приходят либо из MCP‑tool’ов, либо опосредованно из 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 Spec богаче, но базовая идея та же: сессия — это «сколько, за что и в каком статусе».
Дальше нужен тип заказа:
export interface Order {
id: string;
userId: string;
skuId: string;
totalCents: number;
currency: string;
checkoutSessionId: string;
status: "awaiting_payment" | "paid" | "canceled" | "refunded";
}
Здесь чувствуется влияние общих сущностей из предыдущего модуля: intent, checkout_session, order. В нашем учебном проекте мы слегка схлопываем intent и order, чтобы не множить сущности, но сохраняем связь с checkoutSessionId.
5. Как виджет GiftGenius «подглядывает» в commerce‑мир
Важный момент: виджет сам по себе не ходит в платёжку и даже не обязан знать детали ACP; его роль — показывать пользователю состояние, которое вычислено и зафиксировано на backend’ах.
Самый простой полезный сценарий: после успешной покупки пользователь может вернуться в чат и спросить «Покажи мои последние заказы в GiftGenius». GPT вызовет MCP‑tool вроде get_user_orders, который сходит на ваш backend, а виджет покажет список.
Представим 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 есть «память» о покупках, хотя виджет просто отображает состояние backend’а.
6. Простая реализация ACP‑эндпоинта в стиле Next.js
Теперь наметим, как может выглядеть учебная реализация одного из ключевых ACP‑эндпоинтов — создания checkout_session. По спецификации там довольно богатый контракт, но для курса можно оставить только суть: приходит skuId, мы проверяем его по фиду/БД, создаём сессию и возвращаем её 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 обязан смотреть в тот же источник, из которого формируется фид. Мы не доверяем ничему, что пришло «из воздуха» — ни от GPT, ни от виджета.
Во‑вторых, мы возвращаем только то, что нужно ChatGPT/ACP‑клиенту: ID сессии, сумму, валюту и статус (по умолчанию pending или not_ready_for_payment, в зависимости от выбранной терминологии). В реальном ACP там больше полей, включая информацию о доступных методах оплаты и fulfillment, но учебный пример концентрируется на первичном создании сессии.
В‑третьих, такой маршрут удобно покрывать контрактными тестами: если завтра структура Product Feed поменяется, тесты на findSkuById и createCheckoutSession должны это поймать раньше, чем ChatGPT начнёт выдавать пользователям странные ошибки.
7. Связь ACP‑сессий и платёжного провайдера
Пока мы никак не трогали платёжного провайдера. В реальной интеграции происходит примерно следующее (упрощённый сценарий).
Сначала ChatGPT (через ACP) вызывает ваш POST /checkout_sessions. Ваш backend создаёт локальную сессию в своей БД. Когда пользователь подтверждает оплату в UI Instant Checkout, платформа запрашивает у PSP делегированный платёжный токен (Shared Payment Token) для конкретного мерчанта и суммы. Этот токен прилетает к вам в complete‑запросе (или аналогичном вызове по Delegated Payment Spec).
После этого вы создаёте платёж у PSP, используя токен, без доступа к реальным платёжным данным. PSP шлёт webhook о результате; вы обновляете статус заказа и/или checkout‑сессии.
В нашем учебном коде мы можем ограничиться имитацией этого шага. Например, функция completeCheckoutSession может выглядеть так:
// src/lib/checkout.ts
export async function completeCheckoutSession(sessionId: string, spt: string) {
// Здесь в реальности вызывается PSP API с делегированным токеном (SPT)
const paymentOk = await mockChargeWithToken(spt);
return paymentOk
? { status: "succeeded" as const }
: { status: "failed" as const };
}
Вызов PSP и использование Shared Payment Token — это часть стандарта Delegated Payment, а функция mockChargeWithToken — наш учебный архитектурный слой, имитирующий эту специфику.
8. Сквозной флоу GiftGenius: от запроса до оплаченного подарка
Теперь соберём всё вместе в виде последовательности шагов. Это та самая «боевая» история GiftGenius, ради которой мы комбинируем все слои. Важно не смешивать два разных мира, поэтому рассмотрим их по отдельности.
Схема A: без App, только Product Feed + ACP
В этом сценарии у вас есть Product Feed и ACP‑backend, но нет ChatGPT App и виджета. Это классический Instant Checkout‑мерчант.
Пользователь пишет в ChatGPT что‑то вроде: «Подбери цифровой подарок до $50». GPT использует ваш Product Feed, чтобы найти подходящие SKU, и показывает их в своём нативном UI в виде шопинг‑карточек. Здесь никакого вашего React‑кода ещё нет — карточки полностью рисует ChatGPT.
Пользователь кликает по кнопке «Buy» на одной из таких карточек. Этот клик обрабатывается самим ChatGPT. Платформа:
- Формирует line_items на основе Product Feed.
- Вызывает ваш POST /checkout_sessions по Agentic Checkout Spec.
- Показывает пользователю UI Instant Checkout (способ оплаты, адрес и т.д.).
- После подтверждения получает Shared Payment Token от PSP и вызывает ваш .../complete.
- Получает от вас финальное состояние checkout_session и, при необходимости, ждёт вебхука о заказе.
С точки зрения вашего кода здесь работают только ACP‑эндпоинты и Product Feed. Никакого Apps SDK, window.openai и виджета вообще не существует. И это абсолютно валидный, «чистый» сценарий ACP‑мерчанта.
Схема B: с ChatGPT App и виджетом GiftGenius
Теперь добавим сверху ChatGPT App и виджет GiftGenius. Product Feed и ACP‑backend никуда не деваются: они по‑прежнему обеспечивают поиск и оплату. Разница в том, что у нас появляется собственный UI и логика шагов внутри App.
Представим диалог: пользователь пишет в ChatGPT: «Подбери подарок для мамы до 50$». GPT понимает, что это commerce‑запрос, и предлагает использовать GiftGenius‑App. Виджет задаёт пару уточняющих вопросов: возраст, интересы, страна. После этого GPT вызывает ваш MCP‑tool search_gifts с фильтрами, а MCP‑сервер обращается к каталогу (БД или подготовленному индексу), находит несколько подходящих SKU и возвращает их в структурированном виде.
GPT передаёт эти данные в виджет, и виджет показывает свои фирменные карточки подарков (React‑компоненты, карусели и так далее). Это уже ваш дизайн и ваш UX, а не стандартный шопинг‑UI ChatGPT.
Когда пользователь кликает в виджете по кнопке «Купить», происходит другое, чем в схеме A. Этот клик обрабатывается виджетом:
- Виджет понимает, какой SKU выбрал пользователь.
- По своему API (например, POST /api/checkout-sessions) обращается к вашему backend’у, чтобы создать checkout_session (или получить ID уже подготовленной сессии).
- Затем виджет вызывает рантайм‑метод Apps SDK наподобие:
// Актуальную сигнатуру метода смотрите в доке Apps SDK await window.openai.requestCheckout({ checkoutSessionId: session.id, ... });Этот вызов — инициатива виджета. Для ChatGPT это сигнал: «Пора открыть Instant Checkout для вот этой checkout_session».
Дальше платформа ChatGPT уже работает очень похоже на схему A, но за кулисами:
- показывает пользователю нативный Instant Checkout UI;
- получает Shared Payment Token у PSP;
- вызывает ваш ACP‑эндпоинт завершения сессии (.../complete);
- участвует в получении и обработке вебхуков от вашего backend’а.
То есть в схеме B виджет запускает checkout через Apps SDK, а вызовы по ACP (создание/завершение checkout_session) происходят либо до этого (когда вы сами создаёте сессию в backend’е), либо уже после requestCheckout, но всегда на серверной стороне.
Виджет при этом может параллельно показывать шаги «Оформление покупки», статусы и превью заказа, опираясь на ваш API (/api/orders/...) и MCP‑tool’ы.
Если изобразить схему B диаграммой, получится что‑то вроде:
sequenceDiagram
participant User as Пользователь
participant GPT as ChatGPT / GPT
participant W as GiftGenius Widget
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: UI Instant Checkout
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‑backend’ом и PSP.
Insight
ChatGPT в своем SDK написали, что скоро в приложениях появится монетизация. Так оно и есть. Виджетам уже доступно несколько еще не анонсированных методов. И самый интересный из них - это requestCheckout().
Выглядит его вызов так:
window.openai.requestCheckout({
id: "checkout_session_123",
payment_provider: {
merchant_id: "stripe",
supported_payment_methods: ["card"]
},
...
}
Он отображает диалоговое окно, которое позволяет пользователю завершить оплату. Так что проектируйте свое приложение так, как будто монетизация уже включена: когда вы закончите работу, так оно и будет.
9. Мини‑реализация для курса: монолитный backend
В модулях про архитектуру уже поднимался вопрос: делать ли всё одним сервисом или сразу делить на MCP‑сервер, commerce‑backend и отдельный сервис для платёжной интеграции. Для образовательных целей чаще всего достаточно «почти монолита»: один репозиторий, один деплой, но логика аккуратно разнесена по слоям.
Учебный вариант GiftGenius может выглядеть так: Next.js‑приложение, в котором:
- виджет живёт в app/widget/page.tsx;
- ACP‑эндпоинты — в app/api/checkout-sessions и соседних маршрутах;
- инструменты MCP — в app/api/mcp/route.ts или отдельной папке;
- работа с заказами — в src/lib/orders.ts, src/lib/checkout.ts и близких модулях.
Физически это один сервер (особенно на этапе dev/staging), но логически вы мыслите уже в терминах трёх ролей: UI (виджет), MCP (инструменты/ресурсы для GPT) и ACP (commerce‑backend).
Позже, в модулях про продакшен, вы увидите, как этот «монолит» разносится по нескольким сервисам и окружениям, а перед ними появляется MCP Gateway. Но на уровне модуля 14 такой «монолит с правильными слоями» уже даёт очень правдоподобную архитектуру.
10. Практическое задание: ваша архитектура вокруг ACP
Чтобы всё вышеописанное не осталось теорией, имеет смысл уже сейчас приложить это к своему домену. В рамках лекции можно сделать два мини‑упражнения.
Во‑первых, выберите собственный сценарий: SaaS‑подписка, бронирование, доставка еды, онлайн‑курсы — любой кейс, где есть продукт/услуга, цена и разумный checkout. Вспомните фазовую модель: discovery → decision → checkout → post‑payment.
Во‑вторых, опираясь на архитектуру GiftGenius, опишите в свободной форме: как вы будете строить Product Feed (где живут SKU и цены, кто их обновляет), где реализуете ACP‑контракт (отдельный сервис или часть существующего backend’а), как подключите платёжного провайдера и как ваш виджет (если он есть) будет взаимодействовать с этим всем через MCP и Apps SDK.
Полезно прямо проговорить, будет ли ваш проект использовать только схему A (Instant Checkout без App), или только схему B (App + виджет), или оба сценария сразу. Даже такой текстовый набросок архитектуры сильно снижает риск неожиданностей на этапе реальной интеграции.
11. Типичные ошибки при интеграции Product Feed, ACP и виджета
Ошибка №1: два разных каталога — один для поиска, другой для checkout.
Иногда команда сначала поднимает быстрый «поисковый» фид для GPT (например, небольшой JSON), а потом отдельно пилит commerce‑БД для заказов. Если их не связать общими ID и общей логикой обновления, GPT может предлагать пользователю товары, которые уже нельзя купить, или по старой цене. Правильный подход — один источник правды, из которого формируются и Product Feed, и внутренние таблицы для ACP‑эндпоинтов.
Ошибка №2: доверие данным, пришедшим от GPT или виджета.
Когда в checkout_session прилетает skuId и цена, очень хочется просто поверить этим значениям: «ну GPT же не будет врать». Но модель легко может «скреативить» или перепутать SKU, а пользователь — попытаться взломать запрос. Если не сверять входящие данные с Product Feed/БД, вы рискуете продавать не то и не за те деньги. Любой ACP‑эндпоинт должен начинать с валидации по первичному хранилищу каталога.
Ошибка №3: смешивание ролей виджета и commerce‑backend’а.
Иногда разработчики по привычке из фронтенда сразу дергают платёжный SDK, создают сессии в Stripe и вообще живут как на обычном сайте. В контексте ChatGPT Apps это ломает модель безопасности и противоречит ACP: платёжный флоу должен проходить через ChatGPT и ваш commerce‑backend, а виджет — только отображать состояние и отправлять события (вроде requestCheckout). Если виджет знает слишком много о платёжном контуре, вы получаете и сложность, и повышенные риски.
Ошибка №4: чрезмерное упрощение ACP‑контракта.
В учебном примере мы сознательно оставляем только skuId, сумму и статус, чтобы не утонуть в деталях. Проблема начинается, когда такой «демо‑контракт» незаметно перетекает в продакшен. Вы внезапно обнаруживаете, что не хватает полей для адреса, налогов, методов доставки, промокодов, и начинаете «прикручивать» их хаотично. Лучше сразу проектировать внутренние модели с запасом под реальные сценарии, даже если часть полей первое время будет неиспользуемой.
Ошибка №5: отсутствие связки между заказами и пользователями.
В демо легко ограничиться orderId и skuId, не думая о том, как пользователь вернётся через неделю и спросит: «Покажи мои покупки». Если с самого начала не заложить userId (или другой устойчивый идентификатор) в заказ и checkout‑сессию, позже придётся устраивать миграции и сложные мостики. Commerce‑архитектура вокруг ChatGPT почти всегда предполагает, что GPT сможет связывать текущий диалог с историей заказов пользователя — это стоит учесть заранее.
Ошибка №6: недооценка важности webhooks и идемпотентности.
В лекции мы про webhooks только говорили, а глубоко их разбирать будете в следующих модулях. Легко подумать: «ну webhook придёт один раз, обновим заказ — и всё». На практике платёжки любят ретраить события, а сеть — терять ответы. Если не проектировать заказы и checkout‑сессии как идемпотентные структуры (по checkoutSessionId или paymentId), можно получить двойные списания, дубли заказов и неочевидные расхождения между PSP и вашей БД.
Ошибка №7: игнорирование ограничений и политики в Product Feed.
В погоне за быстрым демо‑фидом легко забыть про возрастные ограничения, доступность по странам, запрещённые категории и прочие «мелочи». Потом оказывается, что GPT радостно предлагает пользователю товар, который ему нельзя продать в его регионе или возрасте. Поля, связанные с политикой и ограничениями, нужно проектировать и заполнять с самого начала, даже если вы пока торгуете только безобидными цифровыми подарками.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ