Building a Credits/Tokens System in Laravel SaaS: A Practical Guide

Overview: Why Credits Matter in Modern SaaS

Modern

applications increasingly adopt a credit or token-based pricing structure, often alongside traditional monthly subscriptions. This approach allows users to consume resources based on a finite pool of credits, which makes particular sense for applications integrating
AI
features, where underlying
AI
service costs often correlate with usage. Implementing this in
Laravel
allows you to offer flexible billing and manage resource consumption efficiently, giving you granular control over user access.

Prerequisites: Your Development Foundation

To follow this guide, you need a solid understanding of

development. This includes familiarity with routes, controllers, Eloquent models, and database migrations. Basic knowledge of
SaaS
billing concepts and experience with external payment processors like
Stripe
, along with
Laravel Cashier
, will greatly benefit your learning curve. We are building on these foundations to add a local credit management layer.

Key Libraries & Tools

  • Laravel
    : The PHP framework that powers our entire application. It provides the structure and tools for rapid web development.
  • Stripe
    : Our payment gateway for handling recurring subscriptions. It manages the financial transactions.
  • Laravel Cashier
    : Laravel's elegant wrapper for
    Stripe
    , simplifying subscription management within your application.
  • PHP Enums: Used to define and manage our subscription plans with associated credit limits and pricing details, offering type-safe plan definitions.
  • Artisan Commands
    : Laravel's powerful command-line interface. We utilize it for scheduled tasks, like resetting monthly credits.
  • Middleware: Intercepts HTTP requests, allowing us to enforce checks like ensuring a user has sufficient credits before an action.

Code Walkthrough: Implementing Credit Logic

We integrate credit management directly into the application's core. Your users table gains credits_remaining, credits_limit, and credits_reset_at columns. A credit_transactions table logs every credit deduction or addition.

Deducting Credits

After a resource-consuming action, like generating a blog post, a dedicated CreditService handles the deduction. This service encapsulates the logic, ensuring consistency.

Building a Credits/Tokens System in Laravel SaaS: A Practical Guide
Laravel SaaS with Credits/Tokens Pricing: Demo Project
// app/Services/CreditService.php
class CreditService
{
    public function deduct(User $user, int $amount)
    {
        $user->decrement('credits_remaining', $amount);
        $user->creditTransactions()->create([
            'type' => 'deduct',
            'amount' => $amount,
            'description' => 'Blog post generation'
        ]);
    }
}

Enforcing Credit Limits with Middleware

To prevent actions when a user has insufficient credits, we implement a middleware. This EnsureSufficientCredits middleware checks the user's credits_remaining before allowing access to a route. It redirects with an error if the user cannot perform the action.

// app/Http/Middleware/EnsureSufficientCredits.php
public function handle(Request $request, Closure $next)
{
    if (!auth()->user()->hasCredits(1)) { // Custom method on User model
        return redirect()->back()->with('error', 'Insufficient credits. Please top up or upgrade your plan.');
    }
    return $next($request);
}

Subscription Plan Enums

We define subscription plans using a PHP Enum, making it easy to associate credit limits and

price_id values with each plan.

// app/Enums/SubscriptionPlan.php
enum SubscriptionPlan: string
{
    case FREE = 'free';
    case PRO = 'pro';
    case ENTERPRISE = 'enterprise';

    public function creditLimit(): int
    {
        return match ($this) {
            self::FREE => 100,
            self::PRO => 1000,
            self::ENTERPRISE => 5000,
        };
    }
    // ... other methods like price(), priceId()
}

Resetting Credits

When a user subscribes or changes their plan via

, a
Stripe webhook
(specifically handleInvoicePaymentSucceeded) triggers a credit reset. The CreditService::reset method updates the user's credits_remaining, credits_limit, and credits_reset_at based on their new plan's creditLimit from the enum. For monthly resets, a scheduled
Artisan Commands
(reset:monthly-credits) iterates through users, checking credits_reset_at to determine if a reset is due.

Syntax Notes

We leverage modern PHP features like Enums for robust and readable plan definitions. Implementing dedicated Service classes for credit management encapsulates business logic, keeping controllers lean. Middleware provides a clean, declarative way to enforce pre-action checks, centralizing authorization rules.

Practical Examples

Imagine building a tool similar to

or
Midjourney
where generating content or images consumes a set number of tokens. A credit system offers a clear pricing model: users purchase credits, and each generation depletes their balance. Other
SaaS
platforms offering limited actions, such as monthly reports or data analyses, also benefit from this model.

Tips & Gotchas

Keep your initial credit implementation simple. Locally managing credits in the database, as demonstrated, offers full control and avoids external dependencies that can complicate future

upgrades. While
Stripe
offers metered billing, querying usage data directly from
Stripe
can be less efficient than a local solution. Be cautious with third-party wallet packages; weigh their benefits against the added dependency burden. Always log every credit transaction to maintain an auditable history for users and debugging.

Building a Credits/Tokens System in Laravel SaaS: A Practical Guide

Fancy watching it?

Watch the full video and context

5 min read