Add separate Runtime mode selection for ordinary sub and swarm flows, including Swarm-specific create payload shaping and Azure VM env wiring. Document the ordinary sub artifact callback gap, swarm runtime findings, PayPal billing boundaries, deployment migration requirements, and desktop/API progress. Constraint: Keep ordinary sub and HeiCode-Swarm Runtime deployments separate Confidence: high Scope-risk: moderate Tests: go test ./...
14 KiB
Heicode Manager PayPal 支付接入与计费关系说明
更新时间:2026-05-28
1. 结论
Heicode Manager 目前已经有余额充值、套餐订阅、订单、回调、补单、模型调用扣费和订阅扣费的基础能力。当前要接 PayPal,优先不要重做计费体系,而是在现有支付网关抽象中新增 paypal 支付提供方。
建议分两期:
| 阶段 | 范围 | 说明 |
|---|---|---|
| 第一期 | PayPal Checkout 一次性支付 | 覆盖余额充值和 Heicode 内部订阅套餐购买。支付成功后沿用现有 top_ups、subscription_orders、user_subscriptions 逻辑。 |
| 第二期 | PayPal 原生自动续订 | 只有业务明确需要自动续费时再做。需要新增 PayPal product / plan / subscription 映射、续费 webhook、取消和过期同步。 |
当前更稳妥的方案是第一期:用户通过 PayPal 支付一笔钱,Manager 在回调确认成功后给用户增加余额或开通一个 Heicode 内部订阅套餐。
2. 现有计费对象
| 对象 | 当前代码/表 | 作用 |
|---|---|---|
| 钱包余额 | users.quota |
用户模型调用的可用余额。 |
| 充值订单 | top_ups |
记录一次余额充值订单,成功后增加用户 quota。 |
| 订阅套餐 | subscription_plans |
管理员配置套餐价格、周期、总额度、重置周期、升级分组。 |
| 订阅订单 | subscription_orders |
用户购买套餐时生成订单,支付成功后创建用户订阅。 |
| 用户订阅 | user_subscriptions |
用户已购买的套餐实例,模型调用可优先从订阅额度扣。 |
| 订阅预扣记录 | subscription_pre_consume_records |
模型调用时对订阅额度做预扣、结算、失败退款。 |
| 模型调用日志 | logs、task 相关表 |
记录模型、用量、扣费、请求状态。 |
3. 充值后模型费用怎么走
余额充值和模型扣费是两个独立阶段。
3.1 充值阶段
用户发起充值:
用户选择充值金额
-> Manager 创建支付订单
-> PayPal 创建 checkout order
-> 用户跳转 PayPal 支付
-> PayPal 回调 Manager
-> Manager 验证回调和订单状态
-> top_ups 标记 success
-> users.quota 增加对应额度
-> 记录充值日志
现有非 PayPal 代码里,充值金额换算逻辑大致是:
支付金额 = 用户选择的充值数量
* Price
* TopupGroupRatio
* AmountDiscount
如果系统展示类型是 tokens,则会先按 QuotaPerUnit 换算成金额单位。当前生产公开状态中 quota_display_type=USD,quota_per_unit=500000,price=7.3,说明用户侧余额展示和内部 quota 之间已有换算关系。
PayPal 接入后也应复用同一套换算规则,不应单独发明 PayPal 专属余额单位。
3.2 模型调用扣费阶段
模型调用时不是从 PayPal 扣费,而是从 Heicode / NewAPI 的余额或订阅额度扣。
流程:
客户端/网页发起模型调用
-> Manager / NewAPI relay 计算模型费用
-> 根据用户计费偏好选择资金来源
-> 预扣额度
-> 请求上游模型
-> 根据真实 usage 结算差额
-> 成功记录日志,失败则退款预扣
当前资金来源有两类:
| 资金来源 | 代码标识 | 说明 |
|---|---|---|
| 钱包余额 | wallet |
从 users.quota 扣。 |
| 订阅额度 | subscription |
从 user_subscriptions.amount_used 扣。 |
用户计费偏好:
| 偏好 | 说明 |
|---|---|
subscription_first |
默认。优先订阅额度,不足或无订阅时回退钱包。 |
wallet_first |
优先钱包余额,不足时回退订阅。 |
subscription_only |
只用订阅额度。 |
wallet_only |
只用钱包余额。 |
因此 PayPal 只负责把钱变成 Heicode 的余额或订阅权益;后续模型费用仍由现有 NewAPI/Manager 计费链路处理。
4. NewAPI / CodeGW 的订阅模式
产品文档里 CodeGW / NewAPI 的定位是模型网关和计费服务。普通用户不进入 NewAPI 后台,只在 Heicode Manager / 客户端看模型、余额、额度、调用日志。
当前代码里的“订阅”不是 PayPal 原生订阅,而是 Heicode 内部套餐:
subscription_plans
-> 用户购买
-> subscription_orders
-> 支付成功
-> user_subscriptions
-> 模型调用扣订阅额度
套餐可配置:
| 字段 | 说明 |
|---|---|
price_amount |
套餐展示价格。 |
duration_unit / duration_value |
套餐有效期,如月、年、天。 |
total_amount |
套餐总额度,0 可表示不限量。 |
quota_reset_period |
额度重置周期,如 daily / weekly / monthly / never。 |
upgrade_group |
购买后升级用户分组。 |
max_purchase_per_user |
单用户购买上限。 |
PayPal 接入时有两种做法:
| 做法 | 推荐度 | 说明 |
|---|---|---|
| PayPal 一次性支付购买内部套餐 | 高 | 最小改动,支付成功后创建 user_subscriptions。适合当前系统。 |
| PayPal 原生订阅自动续费 | 中 | 需要维护 PayPal subscription 状态和续费 webhook,复杂度更高。 |
建议第一期先实现“一次性支付购买内部套餐”。这样不改变 NewAPI 订阅扣费逻辑,也不要求 PayPal 成为系统账本。
5. Agent 运行费用是否在项目设计文档里
有,但目前文档里表达的是“预算和用量上限”,不是已经完整落地的独立收费账本。
相关设计口径:
| 文档口径 | 含义 |
|---|---|
budget.max_tokens |
本次 Agent / Agnet 部署允许消耗的 token 上限。 |
budget.max_cost_usd |
本次 Agent / Agnet 部署允许消耗的美元成本上限。 |
budget.max_duration_sec |
本次 Agent / Agnet 部署允许运行的时间上限。 |
billing_context.provider = newapi |
表示模型调用费用应映射到 NewAPI 用户、Token、Group 或 quota。 |
agent_runtime |
表示子 Agent 角色、模型 profile、实例数,不能和 NewAPI 扣费对象混在一起。 |
产品文档明确:子 Agnet 的运行模型属于 Agnet 平台部署配置,不等同于 CodeGW 后台模型供应商配置。NewAPI 负责模型网关、余额、用量、日志和扣费;Agnet 平台负责真实执行和运行态。
所以当前要分成两类费用:
| 费用类型 | 当前是否有闭环 | 说明 |
|---|---|---|
| 模型调用费用 | 有 | 通过 Manager/NewAPI 的 quota、subscription、usage 日志扣费。 |
| Agent 运行预算 | 有字段和展示/回调基础 | deployment payload 和事件里有 budget,但真实成本依赖 Agent Manager / Runtime 回传。 |
| Agent 基础设施费用 | 未形成用户账本 | CPU、内存、Pod、运行时资源成本目前不是 Manager 本地自动扣费项。 |
如果后续要把 Agent 运行也收费,需要 Agent Manager 回传真实用量:
{
"deployment_id": "dep_xxx",
"agent_instance_id": "agi_backend_001",
"usage": {
"model_tokens": 32000,
"model_cost_usd": 4.21,
"runtime_seconds": 930,
"cpu_core_seconds": 1200,
"memory_mb_seconds": 2048000
},
"billing_source": "newapi",
"correlation_id": "corr_xxx"
}
没有 Runtime 真实回传前,Manager 不能把页面上的预算估算当成真实扣费依据。
6. PayPal 接入建议接口
PayPal 官方当前推荐一次性结账使用 Orders v2 API:创建订单后,用户批准,再 capture 订单。订阅自动续费使用 PayPal Subscriptions API 和订阅 webhook。
6.1 钱包充值
新增接口:
POST /api/user/paypal/pay
请求:
{
"amount": 20,
"success_url": "https://code.xinghanlab.com/console/topup?pay=success",
"cancel_url": "https://code.xinghanlab.com/console/topup?pay=cancel"
}
响应:
{
"message": "success",
"data": {
"approval_url": "https://www.paypal.com/checkoutnow?token=ORDER_ID",
"order_id": "PAYPAL_ORDER_ID",
"trade_no": "PPUSR1NO..."
}
}
回调:
POST /api/paypal/webhook
处理规则:
- 验证 PayPal webhook 签名。
- 根据 PayPal order / capture id 找到本地
top_ups.trade_no或 provider reference。 - 确认支付状态为完成。
- 幂等加锁,避免重复加余额。
- 将
top_ups.status改为 success。 - 增加
users.quota。 - 记录充值日志。
6.2 内部订阅套餐购买
新增接口:
POST /api/subscription/paypal/pay
请求:
{
"plan_id": 3,
"success_url": "https://code.xinghanlab.com/console/topup?pay=success",
"cancel_url": "https://code.xinghanlab.com/console/topup?pay=cancel"
}
响应:
{
"message": "success",
"data": {
"approval_url": "https://www.paypal.com/checkoutnow?token=ORDER_ID",
"order_id": "PAYPAL_ORDER_ID",
"trade_no": "SUBPPUSR1NO..."
}
}
支付成功后复用现有逻辑:
CompleteSubscriptionOrder(trade_no, provider_payload, "paypal", "")
最终效果:
subscription_orders.status = success
-> 创建 user_subscriptions
-> 可按 billing_preference 从订阅额度扣模型费用
7. 需要新增或调整的数据字段
最小实现可以复用现有字段:
| 表 | 字段 | 用途 |
|---|---|---|
top_ups.payment_provider |
paypal |
标识支付提供方。 |
top_ups.payment_method |
paypal |
标识支付方式。 |
top_ups.trade_no |
本地订单号 | 本地幂等主键。 |
subscription_orders.payment_provider |
paypal |
标识订阅订单来自 PayPal。 |
subscription_orders.provider_payload |
PayPal 回调摘要 | 保存脱敏后的回调信息。 |
建议新增字段或配置:
| 类型 | 名称 | 说明 |
|---|---|---|
| Option | PaypalClientId |
PayPal REST app client id。 |
| Option | PaypalClientSecret |
PayPal REST app secret,保存时不回显。 |
| Option | PaypalWebhookId |
PayPal webhook 验签需要。 |
| Option | PaypalEnvironment |
sandbox / live。 |
| Option | PaypalCurrency |
默认 USD。 |
| Option | PaypalMinTopUp |
PayPal 最小充值金额。 |
| Model const | PaymentMethodPaypal |
值为 paypal。 |
| Model const | PaymentProviderPaypal |
值为 paypal。 |
如果第二期做 PayPal 原生自动续订,再新增:
| 字段 | 说明 |
|---|---|
subscription_plans.paypal_plan_id |
PayPal 端 plan id。 |
user_subscriptions.provider_subscription_id |
PayPal subscription id。 |
user_subscriptions.auto_renew |
是否自动续费。 |
subscription_orders.provider_order_id |
PayPal order/capture/subscription 关联 ID。 |
8. 前端页面变化
钱包充值页:
- 充值方式增加 PayPal。
- 点击 PayPal 后调用
/api/user/paypal/pay。 - 成功后打开
approval_url。 - 回到
success_url后刷新余额和充值记录。
套餐订阅页:
- 购买弹窗增加 PayPal。
- 点击后调用
/api/subscription/paypal/pay。 - 支付成功后刷新我的订阅。
管理员设置页:
- 支付设置增加 PayPal 配置块。
- Secret 类型字段保存后不回显原文。
- 展示 webhook 地址:
https://code.xinghanlab.com/api/paypal/webhook。
9. 安全和幂等要求
| 要求 | 说明 |
|---|---|
| webhook 必须验签 | 不能只信任前端 return URL。 |
| 本地订单必须先创建 | 不能收到 PayPal 回调才创建订单。 |
| 回调必须幂等 | 同一 trade_no 或 PayPal capture id 重复通知不能重复加余额或重复开通订阅。 |
| 支付金额必须二次校验 | PayPal 回调金额、币种必须等于本地订单金额、币种。 |
| 支付提供方必须匹配 | 防止用其他支付网关回调完成 PayPal 订单。 |
| 密钥不进日志 | client_secret、webhook 签名、access token 不得写日志。 |
| 退款先不自动扣回 | 第一期可只记录 PayPal refund/dispute 事件,由管理员人工处理;后续再做自动扣减。 |
10. 当前项目还缺什么
| 项 | 当前状态 | PayPal 接入需要做 |
|---|---|---|
| 钱包充值闭环 | 已有,非 PayPal | 新增 PayPal provider/controller/service。 |
| 内部订阅闭环 | 已有,非 PayPal | 新增 PayPal 购买入口并复用 CompleteSubscriptionOrder。 |
| 模型费用扣费 | 已有 | 不需要因 PayPal 重写。 |
| NewAPI/CodeGW 用户侧余额和日志 | 已有基础 | PayPal 只影响充值入口,不影响模型计费规则。 |
| Agent 运行预算 | 有字段和回调基础 | 真实收费需要 Agent Manager 回传真实 usage。 |
| Agent 基础设施收费 | 未闭环 | 需要单独设计资源计价规则和 Runtime 用量回传。 |
| PayPal 管理配置 | 未实现 | 新增系统设置项和前端配置。 |
| PayPal webhook | 未实现 | 新增 /api/paypal/webhook。 |
11. 建议实施顺序
- 新增 PayPal 配置项和支付 provider 常量。
- 实现 PayPal OAuth access token 获取和 Orders v2 create/capture 查询封装。
- 实现余额充值 PayPal 下单接口。
- 实现 PayPal webhook 验签和充值订单完成。
- 实现内部订阅套餐 PayPal 下单接口。
- webhook 成功后复用
CompleteSubscriptionOrder。 - 前端充值页和套餐购买弹窗增加 PayPal。
- 增加单测:金额校验、重复 webhook、provider mismatch、订单状态异常。
- Sandbox 冒烟:创建订单、支付、回调、余额增加、订阅开通、模型调用扣费。
- 生产上线前配置 live client、secret、webhook id、currency、回调域名。
12. 对外口径
可以这样向上级说明:
Heicode Manager 当前已有余额和订阅计费闭环。PayPal 接入不改变模型调用扣费规则,只作为新的收款渠道接入。
用户通过 PayPal 支付后,Manager 将支付结果转换为 Heicode 钱包余额或内部订阅套餐。模型调用仍由 NewAPI/CodeGW 计费链路按余额或订阅额度扣费。
项目设计文档中存在 Agent 运行预算字段,例如 token、美元成本和运行时长上限,但这目前是部署预算和审计约束,不等同于已完成的 Agent 基础设施收费账本。若要对 Agent 运行单独收费,需要 Agent Manager 回传真实模型用量、运行时长、CPU/内存等数据后再纳入扣费。
13. 参考资料
- Heicode 产品资料包:
docs/product-package/ - NewAPI / CodeGW 边界:
docs/heicode-runtime-auth-newapi-secret-design.md - Agnet 请求契约:
docs/integration/agnet-platform-request-contract.md - PayPal Orders v2:
https://developer.paypal.com/docs/api/orders/v2/ - PayPal Subscriptions:
https://developer.paypal.com/docs/subscriptions/reference/ - PayPal Webhook 事件:
https://developer.paypal.com/api/rest/webhooks/event-names