logoPay4SaaS
Consumption

Access Control

Access control answers one practical question: can this user use this paid feature right now?

Pay4SaaS gives you two tools:

  • General access with useAccess() and checkUserAccess()
  • Plan-level feature gates with useTierAccess() and hasTierAccess()

The internal rules differ by pricing model, but most products only need the examples below.

Pricing Modes

ModeGeneral access meansConsumption behavior
CreditsThe user has usable creditsDeducts credits by SERVICE_COSTS
Unlimited subscriptionThe user has valid subscription accessNo credit deduction
Quota subscriptionThe user has subscription quota or usable creditsDeducts quota or credits by SERVICE_COSTS
LifetimeThe user has a lifetime purchaseNo credit deduction

Frontend Check

Use useAccess() when you need to show or hide paid UI.

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

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

  if (loading) return <div>Loading...</div>

  if (!status.allowed) {
    return <a href="/pricing">Upgrade to continue</a>
  }

  return <div>Your paid feature</div>
}

status.allowed is a general yes/no result. If you need display values, use the fields that match your pricing model, such as status.details.availableCredits, status.details.hasSubscription, or status.details.hasLifetime.

Frontend checks are for UX only. Always protect paid work on the server too.

Server Check

Use checkUserAccess() when an API route only needs to know whether the user can access a paid feature.

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 })
}

Consuming a Paid Service

For usage-based features such as generation, analysis, export, or API calls, call useService() from the frontend or fastConsumeService() on the server. This checks access and records usage in one flow.

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
    }

    // Run your feature after access is confirmed.
  }

  return <button onClick={handleGenerate}>Generate</button>
}

The client only sends a serviceType. The actual cost comes from SERVICE_COSTS in config/payment.ts, so users cannot change the deduction amount from the browser.

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

Backend Consumption

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 })
}

Plan-Level Gates

Use tier access when a feature requires a specific subscription plan, such as Pro-only exports or Max-only analytics.

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

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

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

  return <button>Use Pro feature</button>
}

Server-side equivalent:

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

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

Use status.allowed for general access. Use useTierAccess() or hasTierAccess() when the feature requires a minimum subscription tier.

Docs home

Return to the full implementation guide.

Pricing

Review subscriptions, credits, and lifetime options.

Blog

Read more notes on SaaS payments and growth.

On this page