logoPay4SaaS
消费管理

访问控制

访问控制只回答一个实际问题,这个用户现在能不能使用这个付费功能?

Pay4SaaS 提供两类工具:

  • 通用访问判断,使用 useAccess()checkUserAccess()
  • 订阅等级判断,使用 useTierAccess()hasTierAccess()

不同定价模式的内部规则不同,但多数产品只需要按下面的方式接入。

定价模式

模式通用访问含义消费行为
积分模式用户有可用积分SERVICE_COSTS 扣积分
无限订阅用户有有效订阅访问权不扣积分
配额订阅用户有订阅配额或可用积分SERVICE_COSTS 扣配额或积分
买断制用户有买断权益不扣积分

前端判断

需要展示或隐藏付费 UI 时,使用 useAccess()

import { useAccess } from '@/hooks/useAccess'

function PaidPanel() {
  const { status, loading } = useAccess()

  if (loading) return <div>加载中...</div>

  if (!status.allowed) {
    return <a href="/pricing">升级后继续</a>
  }

  return <div>你的付费功能</div>
}

status.allowed 是通用的能不能访问结果。如果需要展示具体数值,按当前定价模式读取对应字段,例如 status.details.availableCreditsstatus.details.hasSubscriptionstatus.details.hasLifetime

前端判断只负责用户体验。真正的付费功能必须在服务端再次保护。

服务端判断

当 API route 只需要判断用户能不能访问某个付费功能时,使用 checkUserAccess()

import { checkUserAccess } from '@/lib/payment/access'
import { getAuthUserId } from '@/lib/auth'

export async function POST() {
  const userId = await getAuthUserId()
  if (!userId) {
    return Response.json({ error: 'Unauthorized' }, { status: 401 })
  }

  const access = await checkUserAccess(userId)
  if (!access.allowed) {
    return Response.json({ error: 'No access' }, { status: 403 })
  }

  return Response.json({ ok: true })
}

消费一次付费服务

对于生成、分析、导出、API 调用这类按次使用的功能,前端调用 useService(),服务端调用 fastConsumeService()。这条路径会在一个流程里完成权限判断和用量记录。

import { useAccess } from '@/hooks/useAccess'

function GenerateButton() {
  const { useService } = useAccess()

  async function handleGenerate() {
    const result = await useService('article_generation')

    if (!result.success) {
      alert(result.message)
      return
    }

    // 权限确认后执行你的功能。
  }

  return <button onClick={handleGenerate}>生成</button>
}

客户端只发送 serviceType。真实扣减数量来自 config/payment.ts 里的 SERVICE_COSTS,用户无法从浏览器篡改扣减金额。

// config/payment.ts
export const SERVICE_COSTS: Record<string, number> = {
  'demo-consume': 25,
  'article_generation': 10,
}

服务端消费

import { fastConsumeService } from '@/lib/payment/access'
import { SERVICE_COSTS } from '@/config/payment'

const serviceType = 'article_generation'
const amount = SERVICE_COSTS[serviceType]

const result = await fastConsumeService(userId, amount, serviceType)

if (!result.success) {
  return Response.json({ error: result.message }, { status: 403 })
}

订阅等级门槛

当某个功能要求最低订阅等级时,使用 tier access,例如 Pro 才能导出、Max 才能使用高级分析。

import { useTierAccess } from '@/hooks/useTierAccess'

function ProFeatureButton() {
  const canUsePro = useTierAccess('pro')

  if (!canUsePro) {
    return <a href="/pricing?plan=pro">升级到 Pro</a>
  }

  return <button>使用 Pro 功能</button>
}

服务端等价写法:

import { hasTierAccess } from '@/lib/payment/access'

if (!await hasTierAccess(userId, 'pro')) {
  return Response.json({ error: 'tier_required', required: 'pro' }, { status: 403 })
}

通用访问用 status.allowed。需要判断最低订阅等级时,用 useTierAccess()hasTierAccess()

文档首页

回到完整实施目录。

价格方案

查看订阅、积分和终身买断方案。

博客

阅读更多 SaaS 支付和增长经验。

On this page