Building a Credits/Tokens System in Laravel SaaS: A Practical Guide
Overview: Why Credits Matter in Modern SaaS
Modern
Prerequisites: Your Development Foundation
To follow this guide, you need a solid understanding of
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 forStripe, 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.

// 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 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 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
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

Fancy watching it?
Watch the full video and context