Loading...
Loading...
> Token-based billing system for AI features.
The AI Credits system lets you monetize AI features with a token-based model. Users get a monthly credit allowance based on their subscription tier. Each AI operation (form generation, chat, code generation) costs a set number of credits. The system tracks usage, handles refills, and blocks operations when credits run out - all automatically.
DESC: The schema includes CreditBalance and CreditTransaction models. Push the schema to your database.
1$npx prisma db push
DESC: Adjust credit costs per AI feature in src/lib/credits/pricing.ts
1// src/lib/credits/pricing.ts2export const CREDIT_COSTS = {3 FORM_GENERATION: 10,4 CHAT_MESSAGE: 1,5 CODE_GENERATION: 20,6 IMAGE_GENERATION: 50,7} as const;
DESC: Set monthly credit allowances per subscription level
1// src/lib/credits/pricing.ts2export const TIER_ALLOWANCES = {3 free: 100,4 starter: 1000,5 pro: 10000,6 enterprise: Infinity,7} as const;
Always verify the user has enough credits before calling the AI
1import { hasCredits, CREDIT_COSTS } from "@/lib/credits";23const creditCost = CREDIT_COSTS.FORM_GENERATION;45const hasEnoughCredits = await hasCredits(userId, creditCost);6if (!hasEnoughCredits) {7 return NextResponse.json(8 { error: "Insufficient credits", code: "INSUFFICIENT CREDITS" },9 { status: 402 }10 );11}
Only deduct credits after the AI operation succeeds
1import { deductCredits, CREDIT_COSTS } from "@/lib/credits";23// After successful AI operation...4await deductCredits(userId, CREDIT_COSTS.FORM_GENERATION, {5 description: "Form generation",6 endpoint: "/api/ai/generate-form",7 metadata: { prompt: prompt.slice(0, 100) },8});
Fetch the user's current credit balance
1import { getBalance, getOrCreateBalance } from "@/lib/credits";23// Simple balance check4const balance = await getBalance(userId);56// Or get full balance object (creates if doesn't exist)7const balanceRecord = await getOrCreateBalance(userId);8// balanceRecord.balance, balanceRecord.monthlyAllowance
Add credits from purchases, referrals, or bonuses
1import { addCredits } from "@/lib/credits";2import { CreditTransactionType } from "@/generated/prisma/client";34// After successful purchase5await addCredits(6 userId,7 500, // amount8 CreditTransactionType.PURCHASE,9 "Purchased 500 credit pack"10);1112// Bonus credits13await addCredits(14 userId,15 50,16 CreditTransactionType.BONUS,17 "Referral bonus"18);
| Option | Type | Default | Description |
|---|---|---|---|
| FORM GENERATION | number | 10 | Credits per form generation |
| CHAT MESSAGE | number | 1 | Credits per chat message |
| CODE GENERATION | number | 20 | Credits per code generation |
| IMAGE GENERATION | number | 50 | Credits per image generation |
402 Insufficient Credits Error
User has run out of credits. Direct them to the /usage page to see their balance, or upgrade their plan for more credits.
Balance not updating after purchase
Ensure you're calling addCredits() with CreditTransactionType.PURCHASE after successful payment webhook.
Credits not refilling monthly
The refillCreditsIfEligible() function checks lastRefill date. Call it on user login or via a cron job.
The credit system uses two Prisma models: CreditBalance (one per user) and CreditTransaction (history of all credit changes).
model CreditBalance {
id String @id @default(cuid())
userId String @unique
balance Int @default(0)
monthlyAllowance Int @default(100)
lastRefill DateTime @default(now())
user User @relation(...)
transactions CreditTransaction[]
}
model CreditTransaction {
id String @id @default(cuid())
balanceId String
amount Int // Positive = add, Negative = deduct
type CreditTransactionType
description String?
endpoint String?
metadata Json?
createdAt DateTime @default(now())
}
enum CreditTransactionType {
SUBSCRIPTION_REFILL
PURCHASE
USAGE
REFUND
BONUS
}GET /api/credits/balanceReturns current balance, monthly allowance, and tier for the authenticated user.
GET /api/credits/historyReturns transaction history with optional type and date filters.
Built-in Dashboard
Here's the complete pattern for integrating credits into an AI route:
// src/app/api/ai/your-feature/route.ts
import { NextResponse } from "next/server";
import { auth } from "@/lib/auth";
import { hasCredits, deductCredits, CREDIT_COSTS } from "@/lib/credits";
export async function POST(request: Request) {
const session = await auth();
const userId = session?.user?.id;
// 1. Check credits (if authenticated)
const creditCost = CREDIT_COSTS.CODE_GENERATION;
if (userId) {
const hasEnough = await hasCredits(userId, creditCost);
if (!hasEnough) {
return NextResponse.json(
{ error: "Insufficient credits", code: "INSUFFICIENT CREDITS" },
{ status: 402 }
);
}
}
// 2. Do the AI operation
const result = await callYourAIService();
// 3. Deduct credits on success
if (userId) {
await deductCredits(userId, creditCost, {
description: "Code generation",
endpoint: "/api/ai/your-feature",
});
}
return NextResponse.json(result);
}Pre-Built Components
Current credit balance with color-coded progress bar. Auto-fetches from API.
Progress bar for resource usage. Warnings at 75%/90% thresholds.
Pricing cards for upgrade flows. Popular badge and current plan indicator.
14-day bar chart of daily consumption. Hover tooltips show exact values.
Complete transaction history with type icons, amounts, and timestamps.