ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

在 Flue 中接入 Stripe Channel:Webhook 安全入口、Agent 派发与凭证校验实战指南

在 Flue 中接入 Stripe Channel:Webhook 安全入口、Agent 派发与凭证校验实战指南 在 Flue 中接入 Stripe ChannelWebhook 安全入口、Agent 派发与凭证校验实战指南【免费下载链接】flueThe sandbox agent framework.项目地址: https://gitcode.com/GitHub_Trending/flue1/flue本篇技术指南以 Flue 仓库中的官方 Blueprintblueprints/channel--stripe.md为核心骨架结合flue/stripe包源码与examples/stripe-channel完整示例系统讲解如何在 Flue 项目中接入 Stripe包括创建经官方 SDK 签名验证的 Webhook 入口、将已核验事件派发给 Billing Agent、通过initialData绑定可信客户身份、以及 Snapshot 与 Thin 两种事件模式的选型。读完本文你将掌握一套可直接复制运行、可本地验证、并兼容 Cloudflare Workersworkerd与 Node 双目标的 Stripe 集成方案。一、Blueprint 定位Stripe Channel 是什么channel--stripe.md是 Flue 的 channel 类 Blueprint它的角色是AI 编码代理的操作手册当你在一个 Flue 项目里运行flue add channel url或flue update channel url时CLI 会以此为起点指导代理完成 Stripe 集成参见 channel.md 中的通用约定。其核心要求是入站实现经过验签的 Stripe Webhook 入口使用官方 SDK 校验原始请求字节与Stripe-Signature头出站使用 Stripe 官方 SDK 做 API 调用且 SDK 客户端必须由项目自己持有project-owned不能把密钥交给框架或模型工具只定义应用真正需要的窄范围narrow模型工具绝不让模型随意选择 Stripe 账户、客户 id、API 路径或请求选项。仓库中的flue/stripe包packages/stripe/package.json版本 2.0.6正是这个 Blueprint 的落地实现它对外只暴露一个固定路由POST /webhook并且在调用应用代码之前先通过项目自有的 Stripe 客户端完成官方验签与事件解析见 packages/stripe/README.md。二、动手前勘察项目与安装依赖Blueprint 要求先读清现场再动工顺序如下阅读AGENTS.md及相关本地说明探测项目包管理器与 Flue 目标Node / Cloudflare按序选择第一个已存在的源码根root/.flue/→root/src/→root/检查现有agents/、channels/、app.ts应用路由地图、环境类型与密钥约定以及目标事件源期望的 payload 风格。随后用项目自己的包管理器安装两个依赖# 示例pnpm 工作区 pnpm add flue/stripe stripe^22.2.1 pnpm add -D types/node需要注意三点types/node是必需的 peer 依赖Stripe 的官方类型声明会引用 Node 类型即便运行时选择的是 Worker 实现也是如此。当包管理器不会自动安装 peer 依赖时请显式将其作为开发依赖安装。flue/stripe的 peerDependencies 声明为types/node 18、stripe ^22.3.2见 packages/stripe/package.json并依赖hono4.12.32不要安装通用 Stripe 工具集合generic Stripe tool collection工具应该由应用按需用defineTool定义默认使用 Snapshot 事件仅当 Stripe 事件目标明确配置为发送 thin 通知时才设置eventPayload: thin。三、创建 Channelchannels/stripe.ts 完整实现在选定的源码根下创建source-dir/channels/stripe.ts。以下是 Blueprint 提供的核心模板其中导入的 agent、派发的消息与客户策略需要按应用实际调整但项目自有的客户端与固定路由必须保留// flue-blueprint: channel/stripe1 import Stripe from stripe; import { createStripeChannel } from flue/stripe; import { defineTool, dispatch } from flue/runtime; import { Billing } from ../agents/billing.ts; export const client new Stripe(process.env.STRIPE_SECRET_KEY!, { httpClient: Stripe.createFetchHttpClient(), }); export const channel createStripeChannel({ client, webhookSecret: process.env.STRIPE_WEBHOOK_SECRET!, // Path: /channels/stripe/webhook async webhook({ event }) { switch (event.type) { case checkout.session.completed: case checkout.session.async_payment_succeeded: { const session event.data.object; const customerId typeof session.customer string ? session.customer : session.customer?.id; if (!customerId) return; await dispatch(Billing, { id: customerId, // Recorded once when this event creates the instance; ignored after. initialData: { customerId }, message: { kind: signal, type: stripe.${event.type}, body: Checkout session ${session.id} reported payment status ${session.payment_status}., attributes: { eventId: event.id, customerId, sessionId: session.id, paymentStatus: session.payment_status, ...(session.amount_total null ? {} : { amountTotal: String(session.amount_total) }), ...(session.currency null ? {} : { currency: session.currency }), }, }, }); return; } default: return; } }, }); export function retrieveCustomer(customerId: string) { return defineTool({ name: retrieve_stripe_customer, description: Retrieve the Stripe customer bound to this billing agent., async run() { const customer await client.customers.retrieve(customerId); return { output: deleted in customer ? { id: customer.id, deleted: true } : { id: customer.id, name: customer.name, email: customer.email }, }; }, }); }3.1 逐段拆解客户端、Channel 选项与事件处理项目自有客户端。client使用 Fetch 兼容的Stripe.createFetchHttpClient()这使得同一份代码在 Node原生 crypto与 Cloudflare WorkersWeb Crypto下都能完成官方异步验签。flue/stripe的isStripeClient校验见 packages/stripe/src/index.ts要求客户端同时具备webhooks.constructEventAsync与parseEventNotificationAsync两个方法这恰好对应 Snapshot 与 Thin 两条验签路径。Channel 选项校验。createStripeChannel()会在构造时立即校验参数packages/stripe/src/index.tsclient必须是合法的 Stripe 客户端webhookSecret必须是非空字符串eventPayload只能是snapshot默认或thin必须提供webhook处理函数。另外两个可选参数packages/stripe/src/index.ts参数类型默认值说明bodyLimitnumber1 MiB1024 * 1024请求体最大字节数超出返回 413signatureToleranceSecondsnumberStripe 默认 300 秒签名时间戳容忍窗口事件分组派发。将共享行为的多个事件类型合并到同一case先抽取customerId兼容customer为字符串或对象两种形态缺失则直接returnWebhook 仍会返回空 200。随后用dispatch(Billing, ...)向 Billing agent 派发kind: signal信号。从dispatch的实现看packages/runtime/src/runtime/flue-app.ts它属于异步投递语义在当前运行时受理并入队后即返回submissionId不等待模型处理完成在 Cloudflare 目标下投递是 durable 的、可能至少一次at-least-once因此业务副作用必须设计为幂等。3.2 属性字典哪些数据该进attributesBlueprint 明确划分了三类数据的去向initialData实例的创建数据creation data。仅在该事件首次创建 agent 实例时记录一次之后被忽略Channel 会在每次派发时原样带上它。它承载绑定工具所需的客户身份agent 用useInitialData()读取而不是去解析实例 idattributes每条消息的即时事实per-message facts如eventId、sessionId、paymentStatus、amountTotal、currency。注意amountTotal与currency在null时会被条件展开省略保持属性字典干净不进模型视野原始 payload、webhook 响应 URL、交互 token、凭证等短生命周期能力一律不得放入模型可见或 durable 的输入中。四、挂载 Channelapp.ts 路由Channel 只在app.ts显式挂载处提供 HTTP 路由。挂载方式如下// app.ts import { Hono } from hono; import { channel } from ./channels/stripe.ts; const app new Hono(); app.route(/channels/stripe, channel.route()); export default app;channel.route()是纯路由工厂pure router factory返回一个相对挂载路径提供服务的 Hono 子应用实现见 packages/stripe/src/index.ts内部通过createChannelRouter(routes)构建。Blueprint 中所有// Path:注释都假设采用约定的/channels/stripe挂载点——更改挂载路径会整体平移所有提供方 URL。4.1 多账户 / Connect 场景的实例 id 策略模板默认假设单个 Stripe 账户并把customerId直接作为 agent 实例 id。对于Connect 或组织级事件目标应派生一个稳定的、应用特有的 id并纳入已核验的event.account或event.context同时在可信代码中绑定对应的请求上下文。仓库示例examples/stripe-channel/src/channels/stripe.ts展示了这一进阶做法它把客户引用建模为StripeCustomerRef实例 id 形如stripe-customer:${encodeURIComponent(JSON.stringify(ref))}并保留accountId/context供出站查询时作为 request options 使用见 examples/stripe-channel/src/channels/stripe.ts。4.2 工具授权边界如果应用不需要客户检索可以替换或省略示例工具。关键原则是未经应用显式授权绝不让模型选择任意的 Stripe 账户、凭证、客户 id、API 路径或请求选项。工具通过闭包绑定可信客户 id模型只能请求这个客户的信息而不是任意路径。五、绑定 AgentinitialData 与工具在 agent 组件内绑定可信客户 iduse agent; import { useInitialData, useModel, useTool } from flue/runtime; import * as v from valibot; import { retrieveCustomer } from ../channels/stripe.ts; const initialDataSchema v.object({ customerId: v.string(), }); export function Billing() { useModel(anthropic/claude-haiku-4-5); const data useInitialDatav.InferOutputtypeof initialDataSchema(); if (!data) throw new Error(This agent is created by the Stripe channel dispatch.); useTool(retrieveCustomer(data.customerId)); return Review the completed Checkout event and summarize any billing follow-up that is needed.; } Billing.initialData initialDataSchema;要点说明Billing.initialData静态校验实例创建时会用该 schema 校验派发来的initialDatauseInitialData()则在每次渲染时返回解析后的值实现见 packages/runtime/src/hooks/use-initial-data.ts。data为空说明实例并非由 Stripe 派发创建直接抛错即可use agent指令它是模块的第一条语句负责将 agent 注册进应用——Channel 回调里的dispatch(...)无需在app.ts挂载。只有当 agent 需要直接通过 HTTP 访问时才在app.ts中追加app.route(/agents/name, createAgentRouter(Billing))来自flue/runtime/routing。示例项目即同时挂载了/agents/assistant与/channels/stripe两条路由见 examples/stripe-channel/src/app.tsChannel ↔ Agent 的循环导入是安全的因为导入的绑定只在延迟回调webhook 回调与 agent 函数体内读取均在模块求值完成之后。不要在构造channel时就读取 agent 绑定。六、Thin 事件通知模式当 Stripe 事件目标配置为 thin payload 时必须显式声明模式export const channel createStripeChannel({ client, webhookSecret: process.env.STRIPE_WEBHOOK_SECRET!, eventPayload: thin, // Path: /channels/stripe/webhook async webhook({ event }) { switch (event.type) { default: return; } }, });在 thin 模式下回调收到的是 Stripe 原生类型Stripe.V2.Core.EventNotification类型定义见 packages/stripe/src/index.ts。当应用需要最新数据时可通过项目自有客户端调用event.fetchEvent()或event.fetchRelatedObject()按需拉取这些 API 调用、凭证与授权策略必须留在flue/stripe之外。Blueprint 特别强调两个纪律不要把 Snapshot 事件与 Thin 通知归一化成同一 schema——两者提供方语义与 SDK API 均不同未来事件类型的处理Stripe 生成的事件联合类型描述的是已安装 SDK 的版本Flue 仍会转发比这些声明更新的已核验事件类型。在项目升级 Stripe 之前用switch (event.type as string)来观测未来类型并在应用代码里校验其资源字段——而不是削弱所有已知事件的原生类型收窄narrowing。从 handler 实现可以印证Snapshot 与 Thin 走两条完全独立的验签路径constructEventAsyncvsparseEventNotificationAsync且验签后还会核对object字段eventvsv2.core.event以阻止模式错配见 packages/stripe/src/webhook.ts。七、凭证与验证两条独立密钥链7.1 两个凭证各司其职环境变量用途说明STRIPE_WEBHOOK_SECRET验签校验Stripe-Signature头的精确请求字节与时间戳来自事件目标配置STRIPE_SECRET_KEY出站认证认证所有出站 Stripe API 调用初始化项目自有的 SDK 客户端它们是互相独立的凭证。遵循项目既有的密钥约定示例项目在 examples/stripe-channel/README.md 中以sk_.../whsec_...形式给出占位绝不凭空发明值。7.2 Webhook 地址与订阅范围在 Stripe 控制台配置事件目标时URL 为挂载路径加路由后缀。按约定挂载app.route(/channels/stripe, ...)时https://example.com/channels/stripe/webhook更改挂载路径则 URL 相应变化。只订阅应用实际处理的事件类型。7.3 底层验签流水线源码级flue/stripe的 webhook handler 在调用应用回调前执行了完整的防御链packages/stripe/src/webhook.tscontent-length非数字 →400Content-Type不是application/json→415content-length超过bodyLimit→413缺少stripe-signature头 →400流式读取原始字节分段累加超限即弃并返回413→ 得到未消费的精确 body调用官方 SDK 异步验签constructEventAsync/parseEventNotificationAsync失败一律400模式守卫object字段核对后才调用你的webhook({ c, event })。回调结果会被序列化返回undefined→ 空200协议允许的空成功确认返回Response则原样透传其余 JSON 值以Response.json输出packages/stripe/src/webhook.ts。7.4 Cloudflareworkerd目标官方 Stripe SDK 暴露了基于 Fetch 与 Web Crypto 的workerd实现。对 Cloudflare 项目沿用既有凭证约定即可Flue 必需的nodejs_compat配置支持process.env类型化 Worker bindings 仍是可选方案。验收标准完成后的项目必须在 workerd 下、在该配置中成功执行一次 webhook 验签与一次 fake-transport 客户端请求并跑通实际的 Cloudflare 构建。八、测试与验收清单Blueprint 要求对集成做如下本地验证绝不联系真实 Stripe运行项目类型检查typecheck与vite build针对配置的目标生成本地原始 Snapshot 与 thin payload用Stripe-SignatureHMAC 签名覆盖用例有效字节与篡改字节、缺失与过期签名、payload 模式不匹配、畸形与超限 body、/channels/stripe/webhook路由、空200默认响应在 Node 与 workerd 下各用 fake Fetch 执行一次官方 SDK 请求。仓库示例项目已内置这两套测试Node 与 workerd 均执行真实 Stripe Fetch 客户端、对接本地原始响应而不联系 Stripeworkerd 套件运行在 Flue 必需的nodejs_compat配置下见 examples/stripe-channel/README.md。九、幂等性、顺序与边界场景重复与乱序Stripe 可能重复投递且不保证顺序。当重复入账duplicate admission有影响时用event.id作为事件级幂等键存入应用自有的 durable 存储。但要意识到不同的 Stripe Event 对象仍可能描述同一资源变更因此业务操作本身也必须幂等不适用于 Issuing 实时授权本普通 webhook Blueprint 不用于同步的实时 Issuing 授权决策——那种场景有更严格的响应语义示例项目 README 亦明确指出见 examples/stripe-channel/README.md仍是应用职责事件目标注册、签名密钥轮换、OAuth、API 密钥存储、顺序处理、回放恢复与业务持久化都不属于 Channel 的职责范围。flue/stripe本身是无状态的不做去重与重排见 packages/stripe/src/index.ts升级既有集成若在更新现有集成需将当前实现与本 Blueprint 完整比对应用所有相关变更并保留自定义内容然后在主标记文件中添加或更新flue-blueprint标记——当标记缺失时这次比对是强制要求。十、升级指南Version 1 — 2026-06-14初始版本。进一步探索完整可运行示例见 examples/stripe-channel其中还包含stripe-client.ts客户端工厂与带账户上下文的请求选项与agents/assistant.ts带initialData校验的 agentflue/stripe的源码与类型定义在 packages/stripe/src通用 Channel 约定参见 blueprints/channel.mddispatch的投递语义可查阅 packages/runtime/src/runtime/flue-app.ts。【免费下载链接】flueThe sandbox agent framework.项目地址: https://gitcode.com/GitHub_Trending/flue1/flue创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表