积分账户(Credit Account)
积分账户是 Realm 内积分的隔离单元:每个账户持有独立余额、覆盖一组 Client App,只有被覆盖的应用才能消费。积分按来源分五种类型入账,可用余额由逐笔发放的账本派生——你看到的余额永远就是你能花的余额。
积分账户是 Realm 内积分的隔离单元:每个账户持有独立余额、覆盖一组 Client App,只有被覆盖的应用才能消费。积分按来源分五种类型(充值、会员、注册、免费周期、主动发放)入账,可用余额由逐笔发放的账本派生——你看到的余额永远就是你能花的余额。
它解决的问题是:一个 Realm 下可能同时跑多条业务线,比如一个 AI 对话应用和一个图片生成应用,它们的积分要分开结算、分开消费,不能串。
给谁看
创建和管理积分账户的 Realm Admin,以及在建消费对接前想搞清楚"应用的积分到底放在哪"的开发者。消费侧的完整行为见积分消耗。
账户模型
| 字段 | 说明 |
|---|---|
bucket_key | 标识,匹配 ^[a-z0-9-]{1,64}$,Realm 内唯一 |
name | 展示名 |
description | 可选描述 |
display_order | 展示顺序 |
enabled | 是否启用。禁用后对新用户不可见、不可购;已持有该账户的用户仍能消费剩余积分 |
| 覆盖集 | 该账户覆盖的 Client App 列表。不能为空;只有被覆盖的应用才能消费这个池 |
Herald 没有"默认账户"的概念:每一个账户都是管理员显式建出来的,每一笔发放都由显式规则路由。一个 Client App 可以同时被多个账户覆盖——此时消费会跨这些账户扣减(见积分消耗)。
几个会直接报错而不是静默处理的约束:
- 覆盖集不能为空。创建和更新都校验,空集返回 400。
- 从一个积分账户移除已挂载的 Entitlement Mapping 会被拒绝,返回
bucket_orphan_mapping。要移动映射,先把它挂到目标积分账户。 - 改覆盖集只影响未来的路由,不回收已经发出去的余额。
五种积分类型
账户里的积分按来源分别记账。余额、后台概览、流水都沿这五个 key 展开:
| key | 含义 |
|---|---|
topup | 充值积分(用户主动购买) |
subscription | 会员积分(订阅套餐赠送) |
registration | 注册积分(注册时一次性赠送,永久有效) |
free_periodic | 免费周期积分(按周期自动发放) |
granted | 主动发放积分(管理员或 SDK 发放) |
余额:钱包与积分账本
每个(用户, 账户)对对应一个钱包。钱包只保留终身累计数(按来源累计发放 / 累计消费)和一个状态(active / frozen / closed——冻结或关闭的钱包会拒绝消费),它并不是余额本身。
可用余额由积分账本(credit ledger)派生:每笔发放写一条账本行,带 granted / used / revoked / remaining 四个金额、积分类型、可选的 expires_at(过期时间)与 effective_at(未来生效时间),以及状态(active / revoked / expired / fully_used)。可用余额 = 所有"生效中、未过期、状态 active"的行的 remaining 之和——这个判定谓词与消费时选取账本行的谓词完全一致,所以你看到的余额就是你能花的余额。
带 expires_at 的行过期后由后台任务扫出:行的状态翻成 expired,不再计入余额,并写一条回收记录。一笔发放是否过期,由发放规则的有效期策略决定。
管理积分账户
管理台:/{realmId}/manage/billing/credit-buckets 是账户目录,旁边有余额矩阵概览页。
API(全部需要 Realm Admin 的 points.manage 权限):
| 方法与路径 | 说明 |
|---|---|
GET /api/realms/{realmId}/billing/credit-buckets | 账户列表,带覆盖应用数和规则引用数 |
POST /api/realms/{realmId}/billing/credit-buckets | 创建,返回 201 |
GET /api/realms/{realmId}/billing/credit-buckets/overview | 余额矩阵:每个账户 × 每种积分类型,外加跨账户合计 |
GET /api/realms/{realmId}/billing/credit-buckets/{bucketId} | 详情,含覆盖集 |
PUT /api/realms/{realmId}/billing/credit-buckets/{bucketId} | 更新(覆盖集整体替换,不是合并) |
DELETE /api/realms/{realmId}/billing/credit-buckets/{bucketId} | 删除,返回 204 |
两个常遇到的报错:
- 400
bucket_key_duplicate:key 在这个 Realm 内已存在。 - 409
bucket_in_use:账户还有活跃订阅、有余额的持有者,或存在历史引用(任意钱包 / 流水 / 账本行)。响应会分列activeSubscriptions、holdersWithBalance、historyReferences。有历史的账户应当禁用而不是删除——删除只留给从未使用过的账户。
积分怎么进入账户
- 支付入账:Entitlement Mapping 的积分发放规则在购买和续费时向映射绑定的账户发放。见付费墙配置和计费架构。
- 注册与免费周期积分:由 Realm 的发放规则路由(
GET/PUT /api/points/{realmId}/registration-rules)。零到多条规则,每条独立指定一个目标账户;一次事件可同时扇出到多个账户。没有启用规则就不发——不存在隐式的兜底账户。 - 主动发放:管理台走
POST /api/points/{realmId}/grant(granted类型);你的后端可通过 Client API Key 走POST /api/ext/points/{realmId}/grant(同为granted类型,SDK 来源)。