FIM One 在管理员控制的功能标志后面提供完整的 Stripe 计费管道。不需要支付功能的私有部署可以将其关闭,永远不会看到相关 UI。SaaS 运营商只需切换一个开关,即可获得托管结账、客户门户、由 webhook 驱动的订阅生命周期管理和配额强制执行。Documentation Index
Fetch the complete documentation index at: https://docs.fim.ai/llms.txt
Use this file to discover all available pages before exploring further.
计费默认禁用。全新安装和现有自托管部署都以
system_settings.billing_enabled = FALSE 开始。在管理员明确激活之前,不会显示任何计费 UI。您将获得
- 免费版和专业版 配有月度令牌配额(默认值:免费版 1M / 专业版 5M;激活后可调整)
- Stripe 托管结账 — 用户无需您的代码接触卡数据即可升级
- 客户门户 — 用户可更新支付方式、下载发票、取消订阅——全部在 Stripe 的界面上完成
- Webhook 驱动的生命周期 — 订阅自动配置和续期;已取消的订阅在周期结束时降级为免费版
- 配额强制执行 — 按周期跟踪令牌使用情况;中途截断并显示结构化升级提示
- 管理页面 用于计划 CRUD 和订阅监控
前置条件
- Stripe账户已启用实时模式。新加坡注册公司必须完成KYC(营业执照、董事身份证、银行账户)。审批通常需要1-3天。
- Stripe实时API密钥,类型为受限制(推荐使用,相比标准
sk_live_***更易撤销、权限范围更小)。 - Webhook端点可公开访问,地址为
<your-domain>/api/webhooks/stripe。 - 银行账户用于支付。建议多币种结算(例如USD支付到USD账户),以避免非USD默认Stripe账户每笔交易产生1.5-2%的汇兑损失。
设置
1. Stripe 仪表板
创建Pro产品
- Catalog → Products → + Add product
- Name:
Pro, description:5M tokens / month, priority support - Pricing: Recurring, monthly, $20.00 USD (根据您的定价策略调整)
- Save → 复制生成的
price_***ID(激活后,您将使用此值更新本地billing_plans表)
创建受限API密钥
- Developers → API keys → + Create restricted key
- Name:
fim-one production - Permissions (minimum):
- Customers: Write
- Subscriptions: Write
- Checkout Sessions: Write
- Customer portal: Write
- Prices: Read
- Products: Read
- Save → copy
rk_live_***
注册webhook端点
- Developers → Webhooks → + Add endpoint
- URL:
https://<your-domain>/api/webhooks/stripe - 要接收的事件:
checkout.session.completedcustomer.subscription.createdcustomer.subscription.updatedcustomer.subscription.deletedinvoice.payment_succeededinvoice.payment_failed
- 保存后,点击”Reveal signing secret”→ 复制
whsec_***
配置多币种结算(推荐)
如果您的Stripe账户默认币种与您收费的价格币种不同(常见情况:SGD账户收取USD费用):- Settings → Bank accounts and currencies → Add a settlement currency
- 选择价格币种(例如USD)
- 关联匹配的银行账户(例如Aspire USD虚拟账户)
- 保存——Stripe将USD费用直接路由到USD支付,无需外汇转换
2. 后端 .env
在生产环境的 .env 中设置这三个密钥:
.env 后重启后端以使密钥生效:
3. 在管理员中激活
- 以管理员身份登录
- 管理员 → 系统设置 → 计费
- 切换启用 Stripe 计费为 ON
- 后端验证
STRIPE_SECRET_KEY和STRIPE_WEBHOOK_SECRET都存在——如果任一缺失,返回 400 并且切换保持 OFF - 仅在首次激活时,后端运行幂等设置:
- 种子化免费版和专业版套餐(如果已存在则跳过)
- 设置
system_settings.default_plan_id为免费版套餐 id - 为任何没有套餐的用户回填
users.plan_id = free.id - 同步
default_token_quota→ 免费版套餐monthly_token_quota,使现有的管理员控制的全局配额得以保留
- 后续的切换关闭/打开是纯标志翻转,无数据副作用
4. 使用您的实时价格更新Pro套餐
激活后,更新预设的Pro套餐以指向您的实时Stripe价格:- Admin → Billing → Plans → Pro → Edit
- 将您的
price_1***(来自第1步)粘贴到Stripe Price ID - 保存
5. 烟雾测试
- 以普通用户身份打开
/settings?tab=billing - 点击升级到 Pro
- Stripe Checkout 打开;使用低额真实卡片完成支付(之后退款)
- Webhook 应该触发——在 Stripe Dashboard → Webhooks → 最近事件中验证显示 2xx 响应
- 订阅行出现在
subscriptions表中;users.plan_id切换为pro - UI 现在显示 Pro 计划 + “管理订阅”按钮
禁用账单
在管理员 → 系统设置 → 账单中,将启用 Stripe 账单开关切换为关闭。 禁用后:- 所有
/api/billing/*端点返回 503 - Webhook 端点返回 503(Stripe 将重试,然后在仪表板中显示为失败——这是正常的,如果账单永久关闭,你可以改为在 Stripe 仪表板中禁用 Webhook)
Plan & Billing用户界面标签页消失- 管理员 → 账单导航组被隐藏
- 配额链跳过计划层级,直接回退到
default_token_quota
subscriptions、billing_plans 和 users.plan_id 行保持不变。重新启用将从相同状态恢复,无需迁移。
计算参考——配额和令牌数学
这是权威参考,用于决定用户被允许消费的内容、计数器何时重置以及解析链如何组合的每条数值规则。在更改定价、调整配额、构建使用情况仪表板或规划v2/v3工作之前,请阅读本文档。尚未发布的未来规则记录在其保留位置,以便贡献者知道新逻辑在何处插入。词汇表
| 变量 | 存储 | 语义 | 范围 |
|---|---|---|---|
users.token_quota | 按用户(覆盖) | 三态覆盖;见下文语义 | NULL、0 或正整数 |
users.tokens_used_this_period | 按用户(计数器) | 自上次重置以来的累计令牌 | 非负整数 |
users.quota_reset_at | 按用户(锚点) | 镜像付费用户的 Subscription.current_period_end | 时间戳 |
users.plan_id | 按用户(FK) | 活跃计划 | FK billing_plans.id |
billing_plans.monthly_token_quota | 按计划 | 该计划用户的硬上限 | 非负整数 |
system_settings.default_token_quota | 单例 | 当无计划适用时的防御性回退 | 非负整数 |
system_settings.default_plan_id | 单例 | 新用户/未分配用户的免费计划指针 | FK 或 NULL |
system_settings.billing_enabled | 单例 | 主开关——控制链的第 2 步 | 布尔值 |
什么算作一个令牌
令牌消耗在LLM调用层进行计算,来自LiteLLM在每次完成时的usage对象。
- 计算:每次模型调用的提示令牌 + 完成令牌
- 计算:多步骤/工具使用智能体流中的每个往返(每次模型调用都是单独的扣费)
- 计算:嵌入请求(知识库摄取、检索评分)
- 不计算:输入已暂存但从未发送到模型的内容(例如用户丢弃的已上传文件)
- 不计算:在到达提供商之前失败的请求(身份验证错误、速率限制预检查)
- 缓存输入:在v1中按全价计算(不显示提供商缓存折扣)。v2可能会单独计入缓存提示令牌。
三态覆盖语义
users.token_quota 是按用户的管理覆盖。它在一列中承载三种含义:
| 值 | 含义 | 用例 |
|---|---|---|
NULL | 未设置——遵循套餐/默认值 | 所有普通用户的默认状态 |
0 | 无限制 | 管理员/内部账户;“VIP礼赠” |
N > 0 | 硬限制为 N | 在不取消付费订阅的情况下阻止滥用者;预付企业配额 |
配额解析链——v1(当前版本)
对于任何已认证的请求,上限按自上而下的方式计算——首次匹配获胜:users.plan_id。它作为纵深防御存在,以确保配置错误的计划永远不会静默地为用户解除上限。
周期重置
- 对于付费用户,
quota_reset_at镜像Subscription.current_period_end。invoice.payment_succeededwebhook 处理程序在每次成功续期时将tokens_used_this_period设置为 0,并将quota_reset_at推进到新周期结束。 - 对于免费用户(无 Stripe 订阅),每小时 cron 任务在以计划分配日期为锚点的日历月边界处将
tokens_used_this_period重置为 0。 - 周期中途的计划变更不会重置计数器——仅续期会重置。这可防止配额循环利用漏洞(“订阅 → 使用专业版配额 → 取消 → 重新订阅”)。
中流强制执行
- 聊天调用入口的预检查:最便宜的路径,阻止用户无法承担的请求。
- 流式传输期间,运行中的令牌计数在每个分块上重新评估。超过上限会关闭流,返回结构化终止帧,而不是网络错误。
- 前端解释终止帧并显示
<QuotaExceededDialog>,其中包含指向/settings?tab=billing的深层链接。 - 非流式响应返回 HTTP
402,响应体为{ code: "QUOTA_EXCEEDED", reset_at, upgrade_url }。
账单禁用回退
当system_settings.billing_enabled = FALSE 时:
- 跳过链的第 2 步——链折叠为
override → default → unlimited。 /api/billing/*和/api/webhooks/stripe返回503。Plan & Billing用户标签页和 Admin → Billing 导航组被隐藏。- 所有账单数据(订阅、套餐、
users.plan_id)被保留——重新启用时从相同状态恢复,无需迁移。
保留:配额链 v2 — 团队座位
尚未发布。此处记录以便 v2 工作有已知的落地点。
Subscription.quantity携带座位数(Stripe 原生)。- 用户的有效计划通过团队成员身份解析,然后回退到其个人计划:
- 配额是按座位(每个团队成员获得完整的
monthly_token_quota),而不是共享池。共享池会导致先到先得的耗尽,对客户不利。 - 覆盖语义保持不变——团队管理员仍然可以通过
users.token_quota = N对单个成员进行硬限制,该限制在链中位于团队计划之上。
Reserved: quota chain v3 — native Org allocation (no Stripe)
Not yet shipped. Reserved for on-prem / enterprise deployments that allocate quota internally without paying Stripe per user.
- New table
org_quota_allocations(user_id, monthly_token_quota, org_id)distributes a parent budget across members. - Allocations are per user, not a shared pool — every member has a clear individual SLA.
- Updated chain:
max(),而非sum()。付费Pro用户永远不会获得少于他们已支付的配额,即使他们的组织管理员设置了较低的分配。Stripe支付的配额是神圣的。
预留:按使用量付费的信用额度余额(v3 独立维度)
尚未发布。与上述链独立的轴——信用额度是一次性充值,而非订阅层级。
- 新表
user_credits(user_id, balance_cents, currency)——通过 Stripe Checkoutmode='payment'充值。 - 消费顺序:订阅配额优先,然后是信用额度余额(仅在订阅耗尽后才开始扣除信用额度)。
- 信用额度余额不可退款(预付款行业标准)。
- UI 同时展示两个进度条:
订阅配额:已使用 4.2M / 5M+信用额度:剩余 $7.40。
默认值
发货时的默认值——除特别说明外,所有值都可在安装后调整。| 变量 | 默认值 | 可通过以下方式调整 |
|---|---|---|
billing_plans.monthly_token_quota (Free) | 1,000,000 | 管理员 → 计费 → 套餐 → Free → 编辑 |
billing_plans.monthly_token_quota (Pro) | 5,000,000 | 管理员 → 计费 → 套餐 → Pro → 编辑 |
system_settings.default_token_quota | 1,000,000(激活时与 Free 同步) | 管理员 → 系统设置 → 配额 |
system_settings.billing_enabled | FALSE | 管理员 → 系统设置 → 计费 |
| Pro 列表价格 | $20.00 USD / month | Stripe Dashboard(价格对象) |
| Stripe webhook 事件订阅数 | 6 | Stripe Dashboard → Webhooks |
| Stripe 价格缓存 TTL | 5 minutes | stripe_client.py 中硬编码 |
| 订阅生命周期 cron | 每小时 | web/main.py 中的 APScheduler |
| 免费层重置 cron | 每小时(日历月边界) | web/main.py 中的 APScheduler |
定价模型
V1 是一个统一订阅。免费 + 专业版,按月计费,仅支持美元。 V1 范围外(有意推迟至路线图):- 团队计划(Stripe 座位 /
subscription.quantity) - 年度计费
- 多币种展示
- 优惠券 / 促销代码
- 税务处理(Stripe Tax 集成——需要单独的合规审查)
- 基于使用量的计量 / 超额费用
- 按次付费信用余额(一次性充值)
故障排除
切换开关无法激活——400错误
切换开关无法激活——400错误
激活端点需要同时设置
STRIPE_SECRET_KEY 和 STRIPE_WEBHOOK_SECRET。确认它们存在于 .env 中,并且编辑后后端已重启。Webhook返回503
Webhook返回503
要么是账单被禁用(切换开关为OFF),要么是请求签名验证失败(
STRIPE_WEBHOOK_SECRET 不匹配)。检查Stripe仪表板→Webhooks→最近事件以查看实际错误正文。用户已订阅但仍显示免费计划
用户已订阅但仍显示免费计划
checkout.session.completed webhook未到达你的后端。验证Stripe仪表板中的端点URL与 <your-domain>/api/webhooks/stripe 完全匹配,包括末尾路径。检查Webhook最近交付记录中的失败。Pro用户看到测试模式价格ID
Pro用户看到测试模式价格ID
种子迁移写入了测试模式价格ID。激活生产账单后,通过管理员→账单→套餐→Pro→编辑,或通过直接SQL UPDATE,将Pro计划更新为使用你的实时
price_1***。收据使用Stripe默认品牌
收据使用Stripe默认品牌
在Stripe仪表板→设置→品牌中配置你的业务品牌。添加你的徽标、业务名称(例如”FIM Labs Pte. Ltd.”)和地址。Stripe会将这些应用于所有自动生成的收据和发票。
支付中的汇兑损失
支付中的汇兑损失
如果你的Stripe账户默认货币与收费货币不同,Stripe会在每次支付时进行转换(1.5-2%的差价)。在设置→银行账户和货币下添加匹配的结算货币,附加相同货币的银行账户,Stripe将路由相同货币的付款而无需转换。