Credits System

Credits System

The credits system is particularly helpful for building AI applications where you want to charge users based on usage (e.g., image generation, video generation, API calls). It provides a flexible pay-as-you-go model alongside your subscription plans. 🚀

Enabling Credits

To enable the credits system in your application:

  1. Open src/lib/credits/config.ts
  2. Set enableCredits to true
export const enableCredits = true; // Enable or disable credits
  1. The system will now be active and ready to configure

Adding New Credit Types

Credit types define what users can purchase and use (e.g., "image_generation", "video_generation").

Where to configure: src/lib/credits/config.ts

Example:

import { z } from "zod";

// 1. Define your credit types in the schema
export const creditTypeSchema = z.enum([
  "image_generation",
  "video_generation",
  // Add more types here
]);

// 2. Configure pricing for each type
export const creditsConfig: CreditsConfig = {
  image_generation: {
    name: "Image Generation Credits",
    currency: "USD",
    minimumAmount: 1,
    slabs: [
      { from: 0, to: 1000, pricePerUnit: 0.01 },
      { from: 1001, to: 5000, pricePerUnit: 0.008 }, // Volume discount
    ],
  },
  video_generation: {
    name: "Video Generation Credits",
    currency: "USD",
    minimumAmount: 1,
    priceCalculator: (amount, userPlan) => {
      // Custom pricing based on user's plan
      return amount * 0.05;
    },
  },
};

Pricing Models:

  • Use slabs for simple tiered pricing
  • Use priceCalculator for dynamic pricing based on user's plan

Auto Adding Credits on Signup

Reward new users with free credits when they register! 🎁

Where to configure: onRegisterCredits in src/lib/credits/config.ts

What to specify:

  • Credit type
  • Amount of credits to give
  • Expiry duration (optional - credits won't expire if not set)

This is perfect for letting users try your AI features before purchasing.

Adding Credits on Plan Change

Automatically grant credits when users upgrade or change their subscription plan.

Where to configure: onPlanChangeCredits in src/lib/credits/config.ts

How it works:

  • Map plan codenames to credit grants
  • Each plan can give different amounts of different credit types
  • Set expiry periods per credit type
  • Credits are added automatically when the plan change is detected

Great for incentivizing upgrades or rewarding loyal customers! 💎

Expiring Credits

Credits can automatically expire after a set duration (e.g., 30 days from grant).

Configuration: Set expiryAfter (in days) when defining credits in:

  • onRegisterCredits (signup bonuses)
  • onPlanChangeCredits (plan upgrade bonuses)

Cron Job Setup (if not using Inngest):

  1. Add to your .env:

    • CRON_USERNAME - Secure username for authentication
    • CRON_PASSWORD - Secure password for authentication
  2. Setup on cron-job.org:

    • URL: https://yourdomain.com/api/cron/expire-credits
    • Add HTTP Basic Authentication with your credentials
    • Schedule: Daily at midnight (recommended)

The cron job will automatically expire credits based on your configuration. ⏰

Usage in Frontend

Two powerful hooks make it easy to work with credits:

useCredits Hook

Fetch and display user's current credit balances.

Returns:

  • credits - Object with all credit types and amounts
  • isLoading - Loading state
  • error - Any errors
  • mutate - Function to refresh credits

Example:

import useCredits from "@/lib/credits/useCredits";

function CreditBalance() {
  const { credits, isLoading } = useCredits();

  if (isLoading) return <div>Loading...</div>;

  return (
    <div>
      <p>Image Credits: {credits?.image_generation || 0}</p>
      <p>Video Credits: {credits?.video_generation || 0}</p>
    </div>
  );
}

useBuyCredits Hook

Handle credit purchasing with automatic price calculation.

Parameters:

  • Credit type
  • Amount to purchase

Returns:

  • price - Calculated price (considers user's plan for personalized pricing)
  • currency - Currency for transaction
  • isLoading - Loading state
  • error - Any errors
  • getBuyCreditsUrl(provider) - Generate payment URL

Example:

import useBuyCredits from "@/lib/credits/useBuyCredits";
import { PlanProvider } from "@/lib/plans/getSubscribeUrl";
import { Button } from "@/components/ui/button";

function BuyCreditButton() {
  const { price, isLoading, getBuyCreditsUrl } = useBuyCredits(
    "image_generation",
    100 // Buy 100 credits
  );

  const handlePurchase = () => {
    const url = getBuyCreditsUrl(PlanProvider.STRIPE);
    window.location.href = url;
  };

  return (
    <Button onClick={handlePurchase} disabled={isLoading}>
      {isLoading ? "Loading..." : `Buy 100 Credits - $${price?.toFixed(2)}`}
    </Button>
  );
}

Benefits:

  • Automatic price calculation based on user's plan
  • Support for multiple payment providers
  • Real-time pricing updates

Backend Functions

On the server side, you have access to powerful functions for managing credits programmatically.

deductCredits Function

Deducts credits from a user's account with automatic balance checking.

Parameters:

  • userId - User ID
  • creditType - Type of credit to deduct
  • amount - Amount to deduct
  • metadata - Optional metadata for tracking

Throws: Error if user has insufficient credits

Example:

import { deductCredits } from "@/lib/credits/credits";

// In your API route or server action
async function generateImage(userId: string) {
  try {
    // Deduct credits before processing
    await deductCredits(userId, "image_generation", 1, {
      action: "generate_image",
      prompt: "A beautiful sunset",
    });
    
    // Process the image generation
    const image = await aiService.generateImage();
    
    return { success: true, image };
  } catch (error) {
    // Handle insufficient credits
    return { success: false, error: "Insufficient credits" };
  }
}

addCredits Function

Adds credits to a user's account with duplicate prevention.

Parameters:

  • userId - User ID
  • creditType - Type of credit to add
  • amount - Amount to add
  • paymentId - Payment ID (prevents duplicate credits)
  • metadata - Optional metadata
  • expirationDate - Optional expiration date

Example:

import { addCredits } from "@/lib/credits/credits";

// Grant bonus credits to a user
async function grantBonusCredits(userId: string) {
  const result = await addCredits(
    userId,
    "image_generation",
    50,
    `bonus-${userId}-${Date.now()}`, // Unique payment ID
    { reason: "promotional_bonus" },
    new Date(Date.now() + 30 * 24 * 60 * 60 * 1000) // Expires in 30 days
  );
  
  return result;
}

Use these functions for:

  • 🎨 Deducting credits when users consume AI services
  • 🎁 Granting bonus or promotional credits
  • 🔄 Refunding credits after failed operations
  • 📊 Implementing custom credit workflows

Payment Providers

⚠️ Important: Credits are only available for these payment providers:

  • Stripe - Full support
  • PayPal - Full support
  • DodoPayments - Full support

DodoPayments Setup

For DodoPayments credit purchases:

  1. Create a product in DodoPayments dashboard
  2. Set pricing model to "Pay as you go"
  3. Add to .env:
    DODO_CREDITS_PRODUCT_ID="pdt_xxxxxxxxxxxxx"

Super Admin

Administrators have full control over user credits from the admin panel.

Features:

  • ➕ Add credits to any user
  • ➖ Remove/deduct credits from users
  • 📊 View credit transaction history
  • 🔍 Monitor credit usage patterns

Access: Navigate to user detail page in the super admin section to manage credits.

This is useful for:

  • Customer support (issuing refunds or bonuses)
  • Fixing issues
  • Running promotions
  • Testing features

Quick Recap 🎯

With the credits system enabled, you can:

  • Create multiple credit types for different features
  • Auto-grant credits on signup and plan changes
  • Set credit expiry periods
  • Offer personalized pricing based on subscription tiers
  • Use frontend hooks (useCredits, useBuyCredits) for UI
  • Use backend functions (deductCredits, addCredits) for server-side logic
  • Manage credits via admin panel
  • Accept payments through Stripe, PayPal, or DodoPayments

Perfect for AI SaaS where you want usage-based pricing! 💡