How to Create a Coupon Code System in Laravel: Full Guide

by Sunil M. 4 minute read 25 views

Over 85% of eCommerce sites benefit from coupon systems. Laravel makes it easy to implement, manage, and validate coupon logic for users and administrators.

Key Points

  • 90% of online users are more likely to purchase when a valid coupon is applied.
  • Laravel reduces coupon logic implementation time by up to 60% using built-in resource tools.
  • Coupon usage tracking boosts marketing insights, improving retention by over 35% in Laravel.

Creating a coupon code system in Laravel is a vital feature for any modern eCommerce platform or service-based app. Whether you're providing fixed or percentage discounts, controlling usage limits, or setting expiry dates—Laravel makes this process smooth.

This guide explains how to set up a comprehensive coupon system in Laravel 8+ using PHP 7.4+ , covering both user and admin functions. If you're working with a web development firm or providing custom web development services, this feature is essential for your clients' success.

Step 1: Create Coupon Table (Migration)

To begin, we need a migration to store coupon-related data.

                                        php artisan make:migration create_coupons_table
                                    

File: database/migrations/xxxx_xx_xx_create_coupons_table.php

                                        <?php
// database/migrations/xxxx_xx_xx_create_coupons_table.php


use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;


class CreateCouponsTable extends Migration
{
    public function up()
    {
        Schema::create('coupons', function (Blueprint $table) {
            $table->id();
            $table->string('code')->unique();
            $table->enum('type', ['fixed', 'percent']);
            $table->decimal('value', 8, 2);
            $table->decimal('min_cart_amount', 8, 2)->default(0);
            $table->integer('usage_limit')->nullable(); // Total usage count
            $table->integer('used_count')->default(0); // Already used
            $table->date('expires_at')->nullable();
            $table->boolean('status')->default(true);
            $table->timestamps();
        });
    }


    public function down()
    {
        Schema::dropIfExists('coupons');
    }
}
                                    

Run the migration:

                                        php artisan migrate
                                    

This is a fundamental step for anyone involved in website development services that need dynamic promotions or marketing features.

Step 2: Create the Coupon Model

Next, generate the model:

                                        php artisan make:model Coupon
                                    

File: app/Models/Coupon.php

                                        namespace App\Models;


use Illuminate\Database\Eloquent\Model;
use Carbon\Carbon;


class Coupon extends Model
{
    protected $fillable = [
        'code', 'type', 'value', 'min_cart_amount',
        'usage_limit', 'used_count', 'expires_at', 'status'
    ];


    public function isValid($cartAmount)
    {
        return $this->status &&
            ($this->expires_at == null || Carbon::parse($this->expires_at)->isFuture()) &&
            ($this->usage_limit === null || $this->used_count < $this->usage_limit) &&
            $cartAmount >= $this->min_cart_amount;
    }


    public function getDiscount($cartAmount)
    {
        if ($this->type === 'fixed') {
            return min($this->value, $cartAmount);
        } elseif ($this->type === 'percent') {
            return round(($this->value / 100) * $cartAmount, 2);
        }
        return 0;
    }
}
                                    

This model manages coupon validation and discount logic, perfect for custom web development workflows.

Step 3: User-Side Controller to Apply Coupons

                                        php artisan make:controller CouponController
                                    

File: app/Http/Controllers/CouponController.php

                                        namespace App\Http\Controllers;


use Illuminate\Http\Request;
use App\Models\Coupon;


class CouponController extends Controller
{
    public function apply(Request $request)
    {
        $request->validate([
            'code' => 'required|string',
            'cart_amount' => 'required|numeric|min:0',
        ]);


        $coupon = Coupon::where('code', $request->code)->first();


        if (!$coupon) {
            return response()->json(['message' => 'Invalid coupon code.'], 404);
        }


        if (!$coupon->isValid($request->cart_amount)) {
            return response()->json(['message' => 'Coupon is not valid.'], 400);
        }


        $discount = $coupon->getDiscount($request->cart_amount);


        return response()->json([
            'message' => 'Coupon applied successfully.',
            'discount' => $discount,
            'final_total' => $request->cart_amount - $discount,
        ]);
    }
}
                                    

Step 4: Route for User-Side Coupon Application

Add this to your routes/web.php:

                                        use App\Http\Controllers\CouponController;


Route::post('/apply-coupon', [CouponController::class, 'apply']);
                                    

This API will be utilised on the frontend to apply coupons during checkout—one of many features offered by a professional web development company.

Admin Panel for Managing Coupons

Step 1: Admin Routes

In routes/web.php, add:

                                        use App\Http\Controllers\Admin\CouponController;


Route::prefix('admin')->middleware(['auth'])->group(function () {
    Route::resource('coupons', CouponController::class);
});
                                    

Step 2: Admin Coupon Controller

php artisan make:controller Admin/CouponController --resource

File: app/Http/Controllers/Admin/CouponController.php

                                        // app/Http/Controllers/Admin/CouponController.php


namespace App\Http\Controllers\Admin;


use App\Http\Controllers\Controller;
use App\Models\Coupon;
use Illuminate\Http\Request;


class CouponController extends Controller
{
    public function index()
    {
        $coupons = Coupon::latest()->paginate(10);
        return view('admin.coupons.index', compact('coupons'));
    }


    public function create()
    {
        return view('admin.coupons.create');
    }


    public function store(Request $request)
    {
        $request->validate([
            'code' => 'required|unique:coupons',
            'type' => 'required|in:fixed,percent',
            'value' => 'required|numeric',
            'min_cart_amount' => 'required|numeric',
            'usage_limit' => 'nullable|integer',
            'expires_at' => 'nullable|date',
            'status' => 'required|boolean',
        ]);


        Coupon::create($request->all());


        return redirect()->route('coupons.index')->with('success', 'Coupon created successfully.');
    }


    public function edit(Coupon $coupon)
    {
        return view('admin.coupons.edit', compact('coupon'));
    }


    public function update(Request $request, Coupon $coupon)
    {
        $request->validate([
            'code' => 'required|unique:coupons,code,' . $coupon->id,
            'type' => 'required|in:fixed,percent',
            'value' => 'required|numeric',
            'min_cart_amount' => 'required|numeric',
            'usage_limit' => 'nullable|integer',
            'expires_at' => 'nullable|date',
            'status' => 'required|boolean',
        ]);


        $coupon->update($request->all());


        return redirect()->route('coupons.index')->with('success', 'Coupon updated successfully.');
    }


    public function destroy(Coupon $coupon)
    {
        $coupon->delete();
        return redirect()->route('coupons.index')->with('success', 'Coupon deleted successfully.');
    }
}
                                    

Step 3: Blade Views

Coupon Listing View

File: resources/views/admin/coupons/index.blade.php

                                        @extends('layouts.admin')
@section('content')
<div class="container">
    <h2>Coupon List</h2>
    <a href="{{ route('coupons.create') }}" class="btn btn-primary mb-2">Add New</a>


    @if(session('success'))
        <div class="alert alert-success">{{ session('success') }}</div>
    @endif


    <table class="table table-bordered">
        <thead>
            <tr>
                <th>Code</th><th>Type</th><th>Value</th><th>Min Cart</th>
                <th>Usage</th><th>Expires</th><th>Status</th><th>Action</th>
            </tr>
        </thead>
        <tbody>
            @foreach($coupons as $coupon)
                <tr>
                    <td>{{ $coupon->code }}</td>
                    <td>{{ ucfirst($coupon->type) }}</td>
                    <td>{{ $coupon->value }}</td>
                    <td>{{ $coupon->min_cart_amount }}</td>
                    <td>{{ $coupon->used_count }}/{{ $coupon->usage_limit ?? '∞' }}</td>
                    <td>{{ $coupon->expires_at }}</td>
                    <td>{{ $coupon->status ? 'Active' : 'Inactive' }}</td>
                    <td>
                        <a href="{{ route('coupons.edit', $coupon->id) }}" class="btn btn-sm btn-info">Edit</a>
                        <form method="POST" action="{{ route('coupons.destroy', $coupon->id) }}" style="display:inline-block;">
                            @csrf @method('DELETE')
                            <button onclick="return confirm('Delete this coupon?')" class="btn btn-sm btn-danger">Delete</button>
                        </form>
                    </td>
                </tr>
            @endforeach
        </tbody>
    </table>
    {{ $coupons->links() }}
</div>
@endsection
                                    

Step 4: Create/Edit Coupon Form View

File: resources/views/admin/coupons/create.blade.php

                                        @extends('layouts.admin')
@section('content')
<div class="container">
    <h2>{{ isset($coupon) ? 'Edit' : 'Add' }} Coupon</h2>


    <form method="POST" action="{{ isset($coupon) ? route('coupons.update', $coupon->id) : route('coupons.store') }}">
        @csrf
        @if(isset($coupon)) @method('PUT') @endif


        <div class="form-group">
            <label>Code</label>
            <input type="text" name="code" value="{{ old('code', $coupon->code ?? '') }}" class="form-control" required>
        </div>
        <div class="form-group">
            <label>Type</label>
            <select name="type" class="form-control">
                <option value="fixed" {{ (old('type', $coupon->type ?? '') == 'fixed') ? 'selected' : '' }}>Fixed</option>
                <option value="percent" {{ (old('type', $coupon->type ?? '') == 'percent') ? 'selected' : '' }}>Percent</option>
            </select>
        </div>
        <div class="form-group">
            <label>Value</label>
            <input type="number" name="value" step="0.01" value="{{ old('value', $coupon->value ?? '') }}" class="form-control" required>
        </div>
        <div class="form-group">
            <label>Min Cart Amount</label>
            <input type="number" name="min_cart_amount" step="0.01" value="{{ old('min_cart_amount', $coupon->min_cart_amount ?? '') }}" class="form-control" required>
        </div>
        <div class="form-group">
            <label>Usage Limit</label>
            <input type="number" name="usage_limit" value="{{ old('usage_limit', $coupon->usage_limit ?? '') }}" class="form-control">
        </div>
        <div class="form-group">
            <label>Expires At</label>
            <input type="date" name="expires_at" value="{{ old('expires_at', isset($coupon->expires_at) ? $coupon->expires_at->format('Y-m-d') : '') }}" class="form-control">
        </div>
        <div class="form-group">
            <label>Status</label>
            <select name="status" class="form-control">
                <option value="1" {{ (old('status', $coupon->status ?? '') == 1) ? 'selected' : '' }}>Active</option>
                <option value="0" {{ (old('status', $coupon->status ?? '') == 0) ? 'selected' : '' }}>Inactive</option>
            </select>
        </div>
        <button type="submit" class="btn btn-success">{{ isset($coupon) ? 'Update' : 'Create' }}</button>
    </form>
</div>
@endsection
                                    

Summary – How It All Works

Admin Panel:

  • Admin logs in and navigates to /admin/coupons

  • Can create, edit, delete, and view all coupons

  • Uses pagination for managing larger data

User Side:

  • Users submit coupon codes via the /apply-coupon API

  • System validates and calculates the discount

  • The final amount is returned after applying a valid coupon

Final Words

By following these steps, you now have a fully functional coupon code system in Laravel. This feature not only improves your customer experience but also enhances your application's flexibility.

If you're looking for robust website development services or planning to integrate features like this in client projects, partnering with a professional web development company or offering custom web development can help you deliver powerful and scalable solutions.

Let this coupon module become an essential tool in your Laravel arsenal!

Tech Stack & Version

Frontend

  • HTML5
  • CSS3
  • Bootstrap

Backend

  • PHP 7.4
  • Laravel 8
  • MySQL

Deployment

  • DigitalOcean
  • Linode
  • AWS
img

©2025Digittrix Infotech Private Limited , All rights reserved.