积分消费
积分系统用于 积分模式 和 配额订阅模式。如果你的产品只使用无限订阅或买断制,可以跳过这一页。
积分如何工作
Pay4SaaS 支持购买积分、月度配额和赠送活动。后端负责决定这些积分如何合并、如何扣减,所以前端通常只需要展示可用余额,并调用模板提供的消费接口。
当用户执行付费动作时,Pay4SaaS 会优先使用最适合当前场景的可用积分,并把购买积分尽量留到最后。如果用户没有可用积分,付费动作会被拒绝。
useCredits Hook
import { useCredits } from '@/hooks/useCredits'
function CreditsDisplay() {
const { balance, loading, refreshBalance, useCredit } = useCredits()
if (loading) return <div>加载中...</div>
return (
<div>
<p>可用积分: {balance.availableCredits}</p>
<p>已使用: {balance.usedCredits}</p>
</div>
)
}余额字段
大多数页面展示 availableCredits 就够了。如果需要展示用量,可以加上 usedCredits。除非你的产品明确需要拆分展示,否则不要围绕内部积分来源设计公开 UI。
消费积分
const { useCredit } = useCredits()
async function handleUse() {
const result = await useCredit(
'image_generation', // 服务类型
'生成了一张图片' // 描述,可选
)
if (result.success) {
console.log('剩余:', result.remainingCredits)
} else {
console.log(result.message)
}
}useCredit 和 useService 走的是同一套后端消费流程。积分展示页面可以用 useCredit,具体付费功能页面更适合用 useService,因为它会同时刷新访问状态。
扣减数量由服务端
config/payment.ts中的SERVICE_COSTS决定。不要从前端传递可信价格或积分成本。
刷新余额
积分购买成功、领取赠送积分成功,或者页面切换后需要最新余额时,调用 refreshBalance():
const { refreshBalance } = useCredits()
await refreshBalance()如果只是为了让已成功的本地动作马上反馈到界面,也可以做乐观更新:
const { addCredits } = useCredits()
addCredits(50)乐观更新只用于显示。真正的访问判断和扣减仍然以服务端为准。
服务端积分操作
自定义 API route 或 server action 时,使用支付模块提供的 helper,不要直接写数据库表:
import {
grantPurchasedCreditsToUser,
addBonusCredits,
refreshUserCreditBalance,
} from '@/lib/payment/credits'
import { getBonusExpiryDate } from '@/config/payment'
await grantPurchasedCreditsToUser(userId, 50)
const grant = await addBonusCredits(userId, 10, getBonusExpiryDate(30))
if (!grant.success) {
console.log(grant.error)
}
const balance = await refreshUserCreditBalance(userId)这些 helper 会保持积分发放、过期和余额刷新逻辑与模板其他地方一致。
赠送积分
赠送积分适合用于注册奖励、活动赠送、订阅附赠和客服补偿。
Demo 里的赠送按钮只用于交付预览。生产环境中,赠送积分应该来自明确的业务规则,例如一次性领取活动、注册奖励、支付事件、管理后台或定时任务。
领取活动
在 config/payment.ts 中配置用户可领取的赠送活动:
export const BONUS_CAMPAIGNS = {
hero_welcome_bonus: {
amount: 10,
expiresInDays: 7,
oncePerUser: true,
enabled: true,
},
}前端只发送活动 key。赠送数量、过期时间和防重复领取规则都由后端决定:
const { claimBonus } = useAccess()
const res = await claimBonus('hero_welcome_bonus')手动赠送
后端主动赠送时,在 API route 或 server action 中调用 addBonusCredits():
import { addBonusCredits } from '@/lib/payment/credits'
import { getBonusExpiryDate } from '@/config/payment'
await addBonusCredits(userId, 20, getBonusExpiryDate(30))
await addBonusCredits(userId, 50, getBonusExpiryDate(7), 'campaign')扣减安全
积分扣减在后端原子完成。并发请求不会重复消费同一份余额,每次成功扣减也会记录到用量历史里。
文档首页
回到完整实施目录。
价格方案
查看订阅、积分和终身买断方案。
博客
阅读更多 SaaS 支付和增长经验。