配置产品
本文写给负责人:在管理控制台(/console)建产品、配收款、上架额度包、签发密钥并交给开发者。顺序就是控制台「总览」里「接入进度」的顺序。自己部署 Billing 的服务本身,见部署与运维。
发布 0.4.0 / API v1。日常管理都在控制台完成,没有操作 CLI;同样的 /v1/admin/* 接口也接受负责人的浏览器会话和其 MCP 连接(见下文「连接 AI 工具」)。每位负责人对全部产品有全部权限,没有成员、角色或产品范围。
控制台的左侧菜单只放顶栏所选产品的页面;全部产品、新建产品在产品切换器旁的「管理」菜单里,你本人的登录方式和已连接的 AI 工具在右上角账号菜单的「我的账号」里。界面有简体中文和英文,默认跟随浏览器,顶栏可以切换,也可以在地址里加 ?lang=en 或 ?lang=zh-CN。
创建产品
一个产品是一本独立的账:自己的终端用户、额度、订单、密钥和收款配置。不同产品即使用同一个用户 ID,账户和余额也互不相干。新部署一个产品都没有,控制台落在「产品」页的空状态;在「管理」菜单点「+ 新建产品」创建。
- 环境在创建时选定,之后不能改:
test(默认)或live。不要把测试产品改成正式产品,正式环境另建一个(见下文「正式环境」)。 - 计量也在创建时填:计量单位(如
voice_seconds、tokens)和每单位扣多少额度,之后可以修改(见下文「计量」)。 - 产品名在同一环境里唯一。创建请求超时就用同一输入重试,返回已建的那个;换了输入的同名请求返回
product_name_taken。
产品可以在「产品」页(「管理」菜单的「全部产品」)归档或删除。归档后不再接受新订单,历史照常可查、可退款。删除保留账本历史,删掉 Stripe 配置并移除 Billing 为它在 Stripe 注册的回调地址,最后一个产品也可以删;以下情况不能删:还有未撤销的产品密钥、还有进行中的付款、退款、争议或未结束的订阅、正式产品最近 120 天内有过付款(返回 409 product_has_recent_payments,先归档,满 120 天再删)。页面会写明原因,删除前要输入产品名称确认。
管理请求用 X-Billing-Product(MCP 工具用 product 参数)指明产品;只有一个未归档产品时可以省略,有多个时省略返回 409 product_required,一个都没有返回 409 no_product。
收款:Stripe 配置
在「收款配置」页填 API 密钥、收款方式和可选的回跳站点,点「保存」并在确认框里核对后,一步生效:Billing 先用密钥向 Stripe 读出账户和已开通的收款方式,再注册本产品的回调地址(付款、退款、争议与订阅事件)并保存签名密钥。账户由密钥读出并绑定,之后不可更换;换商户或 Sandbox 请新建产品。不用在 Stripe 后台建 Webhook 或 Price:Stripe 后台只剩两件事,拿 API 密钥,以及为账户开通要用的收款方式(支付宝、微信支付)。
- 勾选的收款方式在 Stripe 账户里没开通时,以
payment_method_not_enabled_<method>拒绝并说明去哪开通。选币种时一并考虑:例如香港账户的微信支付只接受hkd/cny。 - 注册回调需要服务地址是公网 HTTPS,否则以
webhook_registration_needs_public_origin拒绝(本机wrangler dev也是);这时在请求里带上stripe listen给出的webhook_secret。 - 已有生效配置时,API 密钥可留空沿用。密钥不会回显;MCP 连接只能读取 Stripe 配置,不能保存。
- API 验证通过只说明密钥可用,回调是否接通以一笔实际测试付款为准。
付完回到哪里
return_origin(回跳站点)可选,决定付款完成或取消后付款人回到哪里。不配时回到 Billing 自己的地址;配了就回到你的产品。它必须是裸 origin(如 https://your-product.example),带路径、查询、结尾斜杠等以 invalid_return_origin 拒绝;正式产品必须用 HTTPS,测试产品另外允许 http://127.0.0.1:<port> 供本地开发。配好后,产品每次建单可以传 return_path 指定这个站点下的页面;没配站点时传 return_path 返回 return_origin_not_configured。
托管充值页的域名
topup_origin(充值页域名)可选,只在产品仍用托管充值页时有意义;托管充值页主要供控制台「测试体验」使用,不建议做正式购买入口。规则同 return_origin,不合规返回 invalid_topup_origin,不能是 Billing 自己的地址,一个域名只归一个产品(topup_origin_in_use)。先给 Worker 加这个自定义域名(部署配置 routes 里加 { "pattern": "pay.your-product.example", "custom_domain": true }),再在这里填。该域名只响应充值页及其资源,其他路径一律 403 invalid_host。
修改与重试
编辑基于最初读到的版本:别人先保存了,本次返回 stripe_config_stale,重新读取核对后再保存。响应丢失时用原请求(含原版本号)重试,不会注册第二个端点。有在途订单(creating、awaiting_payment)或待处理退款时,改账户、回调或回跳站点返回 stripe_config_payments_active,只有 API 密钥、收款方式和充值页域名的变更能生效;有未结束的订阅时,回调端点与签名密钥不能变(409 stripe_config_subscriptions_active)。表单提交的就是 POST /v1/admin/stripe,字段见 OpenAPI。
上架额度包与档位
额度包
在「计费设置」页点「+ 上架额度包」:固定金额(如 78 港元得 100,000 额度),或自定义金额(用户在范围内自选,按每 1 元发放多少额度换算)。金额按主单位填写,确认后直接在售。要改价、改额度或改币种,点在售额度包旁的「改版」:表单按当前条款填好,只需填新的版本号;同一 SKU 以新版本发布即替换在售条款,新 SKU 追加一档。「下架」只阻止新订单,已有订单、重试和退款照原快照处理。同一产品在售的额度包必须同币种,没有上架任何额度包的产品不售卖。
也可以调用 POST /v1/admin/offers({version, catalog})与 POST /v1/admin/offers/{sku}/retire。收银台按订单金额内联定价(名称「产品名 · sku」),不读取 Stripe Price。
收银台品牌是额度包的一部分:经 POST /v1/admin/offers 发布时在 catalog 里带可选的 checkout(控制台表单暂不含),决定这一档收银台的商家展示名、Logo、颜色和语言;不填就沿用 Stripe 账户的品牌。订单保存建单时的品牌快照。
{
"version": "credits-branded-v1",
"catalog": {
"sku": "credits-100s",
"amount": "1000",
"currency": "usd",
"credits": "100000",
"checkout": {
"display_name": "Example App",
"logo_url": "https://assets.example.com/logo.png",
"button_color": "#2563eb",
"locale": "zh"
}
}
}
档位与订阅
在「计费设置」页的「档位」点「+ 发布档位」,或调用 POST /v1/admin/plans({sku, name, price, currency, credits, entitlements})与 POST /v1/admin/plans/{sku}/retire。
price是每月价格,0即免费档:每个终端用户每个自然月(UTC)第一次被读写时获得一次它的额度,月底作废、不结转;每个产品至多一个在售免费档。- 付费档经 Stripe 订阅按月扣款(只收银行卡),每期付款后发放当期额度,期末作废。
entitlements是你产品自己定义的 JSON 对象(如{"rpm":120},最多 4 KiB),Billing 原样保存、原样返回,由产品执行。- 同一 SKU 再发布即替换定义,从下一期起生效,已发放的额度不变;有人订阅着的付费档不能改价格、每期额度或币种(409
plan_has_subscribers),调价请发布新 SKU、下架旧的。点档位旁的「修改」会把当前定义填进表单。
订阅还需要在 Stripe 后台设置两处:
- 客户管理页:Settings → Billing → Customer portal 保存一次设置,否则
POST .../billing-portal返回 409billing_portal_not_configured。建议开启更新付款方式、查看发票、到期取消,关闭「切换方案」(换档只经 Billing)。 - 扣款失败的最终处理:Billing → Revenue recovery 里必须选「取消订阅」(用户回到免费档)。选「标记为未付款」或「暂停」时,订阅会一直挡住用户重新开通。
验收续费时,在 Stripe 测试环境建一个测试时钟,用 POST /v1/test/users/{user}/subscription-checkouts {plan_sku, purchase_ref, test_clock} 为一个新的 demo_ 用户开通,推进时钟核对续费发放、上期作废、升降级与取消。
计量
计量决定产品上报的单位和每单位扣多少额度:一条用量扣 units × credits_per_unit,精确相乘、不取整。credits_per_unit 只能是正整数;需要不足 1 的费率时,换更细的单位(按毫秒而不是秒),或让一个额度值更多钱(额度包少给额度)。每个产品当前只配一个计量项。
在「计费设置」页点「修改计量」,或调用 POST /v1/admin/meter {metric, credits_per_unit}。每次修改生成新版本(m2、m3……),只用于之后上报的用量;已有额度、余额和流水不变,额度包和档位不会换算,额度的含义变了就重新发布它们。上报时带 meter_version 的产品,修改后会收到 409 meter_changed 并按新单位重报;不带的产品,修改前要先让它停止新工作、报完已有用量。
签发密钥并交给开发者
在「开发接入」页填一个名称签发密钥,它只显示这一次。把它和服务地址写进产品后端私有的 product.json({"base_url": "…", "api_key": "…"},权限 0600),不要粘贴进对话。密钥不过期,只能撤销,撤销立即生效;轮换就是先签发新的、切换调用方、再撤销旧的。每把密钥显示最近使用时间(精确到小时),「还没有成功调用过」的可以放心撤销。不要让两个产品共用一把密钥。
同一页的「接入资料」可复制服务地址、产品标识、环境和指南链接,说明全文默认收起。复制只操作剪贴板,不创建用户或赠额,也不包含密钥。开发者在自己的任务中补充收费需求与私有配置路径。
需要接入验证数据时,在「测试体验」点「准备测试用户并赠额」:准备 demo_integration 和 demo_empty,向前者一次性赠额:采用首个在售额度包的额度数,没有在售额度包时为 100,000。已有账户不重置,重复准备不补足已消费额度;检查会分别说明用户不存在、账户未启用、额度不足、非零余额或读取失败。检查就绪后,接入说明才带上这两个用户 ID;正式产品没有此操作。开发者按接入指南接入。
之后在「总览」看「接入进度」,每一步都按 Billing 实际看到的事打勾:产品密钥第一次调用成功即「后端已连通」,产品第一次上报用量即「第一笔用量」,产品第一次用密钥建单、开通订阅或打开充值页即「购买已接通」(只上架了免费档时这一步不需要)。你在控制台「测试体验」里做的用量和购买都不算:它们用的是你的登录,不是产品密钥。
不配 Stripe 也能先接用量:在「测试体验」可以选测试用户、领一次合成测试额度、记录测试用量,对 demo_empty 记一笔就能看到欠额与 allowed: false。测试额度每个测试用户只发一次,不是收入。测试用户必须以 demo_ 开头,每个产品最多 100 个。
正式环境
正式收费用一个新建的 live 产品,不改测试产品。服务地址、负责人和部署共用,账务按产品隔离。
- live 产品的 Stripe 密钥对应 live 账户,首次保存时验证并绑定账户,之后不会自动切换商户。每个产品单独建一把受限密钥(Restricted Key,权限相同),不要多个产品共用:一把泄露只影响一个产品,可以各自轮换。受限密钥须有读取账户以及 Checkout、PaymentIntent、Charge、Refund、Dispute 所需的权限。
- 多个产品可以共用同一个 Stripe 账户:各自注册回调端点、各建自己的 Stripe Customer,只处理自己的订单。卡账单上的商户名、收据、客户管理页和扣款重试策略按账户生效,不按产品区分;收入进同一余额,按产品对账用 Stripe 对象元数据
billing_product_id筛选。 - 额度包与档位在 live 产品里重新上架,不会从测试产品复制。live 产品没有测试用户、测试额度或测试用量,真实用户从产品后端进来。
- 上线前,实际付款、退款、回调、每小时补漏和银行到账要另行验收。
日常:订单、退款与需要处理的事
「总览」的「需要处理」列出要人工处理的事:有争议的付款、在 Stripe 后台被退款的订单、超过一天还没完成的退款、用欧盟或英国发卡的银行卡付的订单(暂不向这些地区的个人消费者销售:额度照常发放,配置了告警邮箱时另发一封邮件(不保证送达);发卡国只是线索。在订单详情里用原有的「退款」,或点「保留,不退款」移出列表,保留不改变额度及其冻结状态)、账本备份或恢复演练的问题、Stripe 配置异常,没有就不显示。「订单与退款」可按终端用户和状态筛选,按创建时间倒序每页 25 条,通过「上一页 / 下一页」切换;筛选条件与当前页保存在地址中,关闭详情保留原位置,手动刷新更新当前页,修改筛选回到第一页。「终端用户」页默认列出当前产品已有的账务账户(含已关闭账户),每页 25 条,按用户 ID 排序;支持区分大小写的 ID 前缀搜索或完整 ID 匹配。点击用户查看余额、档位和额度流水,返回时保留搜索与分页位置。可用余额包含欠额,可能为负数;列表不代表产品的完整注册用户名单。
退款一步完成:在订单详情点「退款」,选「退还剩余全部」或「部分退款」(填金额;收回额度留空按金额比例,向上取整),确认后 Billing 当即向 Stripe 发起退款(POST /v1/admin/orders/{order}/refund),并收回相应额度;退到不剩可退金额的那一笔总是收回剩余全部额度。已经用掉一部分的购买也能退剩下的;被冻结(争议、Stripe 后台的退款未处理、另一笔退款处理中)时不能退。一张订单可以有多笔退款,每笔由引用识别(整单为 full,部分退款由控制台生成),同一笔再点一次返回它的当前状态,只有失败后才能用同一引用重新发起。在 Stripe 后台直接退了一部分(或争议部分败诉)的订单会冻结额度,在订单详情点「处理部分退款」,确认 Stripe 上的退款总额与收回多少额度,其余解锁(POST /v1/admin/orders/{order}/external-refund)。退款结束后额度的冻结要等一次对 Stripe 的重读才解除,通常在退款请求里就完成,否则由每小时补漏完成。结果为 pending 时由回调、每小时补漏或订单详情的「向 Stripe 核对」(POST /v1/admin/orders/{order}/recheck)查回,不要为保险再点一次。产品密钥不能退款。
连接 AI 工具(MCP)
负责人可以把 Claude Code、Codex 等 AI 工具连到 Billing,让它以你的身份查看订单、余额、流水、异常和审计记录,并做你有权做的操作(退款、向 Stripe 核对、额度包上下架、测试用户、产品管理)。不需要签发或复制任何凭据:
claude mcp add --transport http billing https://billing.example.com/mcp
codex mcp add billing --url https://billing.example.com/mcp
工具会打开浏览器进入授权页(/connect):未登录时先登录,页面显示工具自报的名称和回调地址,只批准你刚刚发起的连接。
- 权限:以批准它的负责人身份行事。产品密钥和 Stripe 配置只在控制台修改(Stripe 配置可读)。退款、归档和删除产品标为破坏性操作,工具会先请你确认;上下架额度包会改变在售价格,也应先征得同意。重试不会重复执行:每个写操作由它指向的对象识别(订单、额度包版本、产品名)。
- 期限与撤销:访问令牌 1 小时、自动续期;连接最长 90 天。在「我的账号」的「已连接的 AI 工具」随时撤销,立即失效。你被移出负责人名单、修改密码或增减登录方式后,连接自动失效。
- 只能经
/mcp使用:令牌直接调用/v1/admin/*会被拒绝。 - 数据:工具读到的内容会进入它的会话记录;它以你的名义做的操作照常写入审计。产品名、SKU、原因等由人写的文字原样返回,工具应把它们当数据,不当指令。
你的登录方式
在「我的账号」的「我的登录方式」里管理你自己的登录:添加邮箱或手机号(向新地址发验证码)、绑定 Google 或微信、移除(至少保留一种)、设置或修改密码。完成后你的其他会话和已连接的 AI 工具都会退出,当前浏览器换新会话。你不能移除自己账号上最后一个负责人名单内的邮箱(409 owner_email_required),也不能替别人改登录方式。谁是负责人由部署配置决定,见部署与运维。