消费管理
访问控制
访问控制解决两个问题:这个用户能不能用我的产品? 以及 该扣多少?
Pay4SaaS 提供两个维度的访问控制:
- 基础准入(
allowed)—— 能/不能,适用全部四种模式 - 等级权限(
useTierAccess)—— 判断用户的订阅等级够不够用某个功能,仅订阅模式适用
首页 Hero 区(
components/landing/Hero.tsx)有完整的前端调用示例,包括权限判断、消费积分、赠送积分三种场景。
四种模式速览
在看 API 之前,先搞清楚你的产品用的是哪种模式——它决定了 allowed 和 useService 的行为:
| 模式 | allowed 的含义 | useService 的行为 |
|---|---|---|
| 积分模式 | 购买过积分(purchased_credits > 0) | 按 SERVICE_COSTS[serviceType] 扣积分 |
| 无限订阅 | 有活跃订阅(含试用期) | 只记录用量,不扣积分;isUnlimited = true |
| 配额订阅 | 有活跃订阅 | 按优先级扣减:配额 → 赠送积分 → 购买积分;配额每月重置 |
| 买断制 | 有买断记录 | 只记录用量,不扣积分;isUnlimited = true |
useAccess Hook
import { useAccess } from '@/hooks/useAccess'
function MyComponent() {
const { status, loading, error, refreshStatus, useService } = useAccess()
if (loading) return <div>加载中...</div>
if (!status.allowed) {
return <div>请先订阅或购买积分</div>
}
return <div>欢迎使用!</div>
}后端等价写法:
import { checkUserAccess } from '@/lib/payment/access'
const access = await checkUserAccess(userId)
if (!access.allowed) {
return Response.json({ error: 'No access' }, { status: 403 })
}status 对象
{
allowed: boolean, // 是否有权限
accessType: AccessType, // 权限类型(见下表)
details: {
hasSubscription: boolean,
subscriptionPlan: string, // basic / pro / max
subscriptionStatus: string,
subscriptionBillingCycle: string, // monthly / yearly
subscriptionEndDate: string,
subscriptionProvider: string,
hasLifetime: boolean,
lifetimePlan: string,
availableCredits: number,
quota: { // 仅配额订阅模式
monthlyLimit: number,
used: number,
remaining: number,
resetDate: string
},
isUnlimited: boolean,
hasUsedTrial: boolean,
}
}accessType 值
| 值 | 说明 |
|---|---|
subscription_unlimited | 无限订阅 |
subscription_quota | 配额订阅 |
lifetime | 买断 |
credits | 积分 |
none | 无权限 |
useService —— 消费一次服务
用户点击「生成」「使用」等按钮时,调用 useService 一步完成权限判断和扣费:
const { useService } = useAccess()
async function handleGenerate() {
const result = await useService(
'article_generation', // 服务类型(必须是 SERVICE_COSTS 中的 key)
'生成了一篇博客文章', // 描述(可选)
'article-123' // 关联 ID(可选)
)
if (result.success) {
console.log('剩余积分:', result.remainingCredits)
} else {
console.log('错误:', result.message)
}
}重要: 前端只传 serviceType,实际扣减金额由后端 config/payment.ts 中的 SERVICE_COSTS 决定,用户无法通过 DevTools 篡改。
后端等价写法:
import { fastConsumeService } from '@/lib/payment/access'
import { SERVICE_COSTS } from '@/config/payment'
const amount = SERVICE_COSTS['article_generation']
const result = await fastConsumeService(
userId,
amount, // 从 SERVICE_COSTS 查表获取
'article_generation', // 服务类型
'生成文章', // 描述(可选)
relatedId // 关联 ID(可选)
)
if (!result.success) {
return Response.json({ error: result.message }, { status: 403 })
}fastConsumeService 通过数据库 RPC 原子扣减积分/配额。扣减金额始终来自服务端 SERVICE_COSTS 配置。
SERVICE_COSTS —— 定价表
所有服务的消费金额统一配置:
// config/payment.ts
export const SERVICE_COSTS: Record<string, number> = {
'demo-consume': 25,
'article_generation': 10, // 你的自定义服务
}添加新服务只需加一行,无需其他改动。
useService 返回值
{
success: boolean,
accessType: AccessType, // 实际使用的权限类型
remainingCredits: number,
message: string,
error?: string
}等级权限 —— 按订阅等级解锁功能
等级权限解决的是另一个问题:不关心「用户有没有积分」,只问 「用户的订阅等级够不够用这个功能?」 它不扣积分、不记录用量,纯粹是一道门。
- 等级关系:
basic < pro < max(由SUBSCRIPTION_PLANS声明顺序决定) - 仅适用于订阅模式——买断和积分没有等级概念
- 没有订阅的用户永远通不过等级判断
useTierAccess —— 前端
import { useTierAccess } from '@/hooks/useTierAccess'
function ProFeatureButton() {
const canUse = useTierAccess('pro')
if (!canUse) {
return (
<button onClick={() => router.push('/pricing?plan=pro')}>
升级到 Pro 解锁
</button>
)
}
return <button onClick={handleDoFeature}>使用 Pro 功能</button>
}hasTierAccess —— 后端
后端必须再验证一次——前端 disabled 只是 UX,用户可以绕过。
import { hasTierAccess } from '@/lib/payment/access'
export async function POST(req) {
const userId = await getUser(req)
if (!await hasTierAccess(userId, 'pro')) {
return Response.json({ error: 'tier_required', required: 'pro' }, { status: 403 })
}
return doProFeature()
}allowed vs useTierAccess —— 什么时候用哪个
status.allowed | useTierAccess('pro') | |
|---|---|---|
| 问的问题 | 「有没有?」(积分、订阅、或买断) | 「等级够不够用这个功能?」 |
| 适用模式 | 全部四种 | 仅订阅模式 |
实践建议:
- 积分模式 →
status.allowed+useService - 订阅模式 →
status.allowed判断通用功能,useTierAccess判断高级功能 - 混合场景 → 两者配合:
allowed判断基础准入,useTierAccess判断高级功能
文档首页
回到完整实施目录。
价格方案
查看订阅、积分和终身买断方案。
博客
阅读更多 SaaS 支付和增长经验。