Billing 模块(统一计费)
Billing 是对免费次数 + 积分扣除的高层统一封装。内部整合了 3 个后端接口,开发者无需分别调用。
计费优先级:免费时段(30 秒内重复操作免费) → 免费次数 → 积分扣除
API 列表
| 方法 | 返回值 | 说明 |
|---|---|---|
sdk.billing.getUsage() | Promise<UsageInfo> | 获取使用额度(网络请求) |
sdk.billing.getCachedUsage() | UsageInfo | null | 获取缓存额度(首次调用前返回 null) |
sdk.billing.check() | BillingCheckResult | 同步校验是否可操作(不扣费) |
sdk.billing.consume(options?) | Promise<BillingConsumeResult> | 消耗一次 |
sdk.billing.refreshCredits() | Promise<void> | 手动刷新积分余额 |
sdk.billing.onChange(callback) | () => void | 监听额度变化 |
详细用法
获取使用额度
typescript
const usage = await sdk.billing.getUsage()
// {
// isEnabled: true, 当前模块是否启用计费
// freeRemaining: 5, 剩余免费次数
// freeTotal: 10, 免费总次数
// creditsPerUse: 6, 每次操作所需积分(免费用完后)
// creditsBalance: 78633, 当前积分余额
// inFreePeriod: false, 是否在免费时段内
// freePeriodRemaining: 0, 免费时段剩余秒数
// }内部并行请求:
GET /ai/v5/artimind/free_trial/count?module=generator_{appKey}→ 免费次数credits.getBalance()→ 积分余额
返回值中的 isEnabled 直接来自后端 free_trial/count 响应里的 is_enabled 字段,可用于判断当前生成器模块是否已启用计费。
校验是否可操作
typescript
const check = sdk.billing.check()
// { canProceed: true, reason: 'free_period' } 免费时段内
// { canProceed: true, reason: 'free_count' } 有免费次数
// { canProceed: true, reason: 'credits' } 将扣积分
// { canProceed: false, reason: 'insufficient' } 积分不足消耗一次
typescript
const result = await sdk.billing.consume({
externalId: 'download_123', // 可选,业务幂等 ID,未传则 SDK 自动生成 UUID
})
// { freeRemaining: 4, isCredit: false, isBlacklisted: false } 扣了免费次数
// { freeRemaining: 0, isCredit: true, isBlacklisted: false } 扣了积分
// { freeRemaining: 4, isCredit: false, isBlacklisted: true } 命中黑名单用户
// isCredit=true 时 SDK 自动调用 credits.getBalance() 刷新余额返回字段说明:
freeRemaining: 当前剩余免费次数isCredit: 本次是否实际扣了积分isBlacklisted: 是否为黑名单用户;当后端返回状态码20102时为true,此时 SDK 不再抛错,业务层可据此决定提示或拦截后续流程
监听额度变化
typescript
const unsubscribe = sdk.billing.onChange((usage) => {
renderBillingUI(usage)
})免费时段机制
consume() 成功后 30 秒内视为免费时段:
- 期间再次调用
consume()不会发起后端请求,直接返回成功 - 适用于"导出后短时间内免费重复导出"的体验
- SDK 内部通过时间戳
_freePeriodEnd管理,无需开发者维护定时器
typescript
function renderBilling(usage) {
if (usage.inFreePeriod) {
btn.textContent = `Free Re-export ${usage.freePeriodRemaining}s`
} else if (usage.freeRemaining > 0) {
btn.textContent = `Download (Free ${usage.freeRemaining}/${usage.freeTotal})`
} else {
btn.textContent = `Download (${usage.creditsPerUse} credits)`
}
}免费时段倒计时实现示例
javascript
let timer = null
sdk.billing.onChange((usage) => {
clearInterval(timer)
if (usage.inFreePeriod) {
timer = setInterval(() => {
const cached = sdk.billing.getCachedUsage()
if (cached) updateUI(cached)
if (!cached?.inFreePeriod) clearInterval(timer)
}, 1000)
}
})后端接口对照
| SDK 方法 | 后端接口 | 说明 |
|---|---|---|
getUsage() | GET /ai/v5/artimind/free_trial/count?module=generator_{appKey} | 查询免费次数 |
getUsage() | GET /ai/v1/credit/getBalance | 并行查积分(复用 credits) |
consume() | POST /ai/v5/artimind/free_trial | 后端自动判断扣免费/扣积分 |
module 参数格式为 generator_{appKey},后端以此区分不同生成器应用。