Laravel Discount: Coupons, Limits &amp; Stacking | Mohamed Said        [  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MH.png)   Mohamed Said Laravel Backend Engineer  ](https://msaied.com) [ Home ](https://msaied.com) [ Projects ](https://msaied.com/projects) [ Articles  ](https://msaied.com/articles) [ Certificates ](https://msaied.com/certificates) [ Contact ](https://msaied.com#contact-section) 

       [  ](https://github.com/EG-Mohamed)       

 [ Home ](https://msaied.com) [ Projects ](https://msaied.com/projects) [ Articles ](https://msaied.com/articles) [ Certificates ](https://msaied.com/certificates) [ Contact ](https://msaied.com#contact-section) 

  [ home ](https://msaied.com)    [ articles ](https://msaied.com/articles)    Laravel Discount: Coupon Codes, Usage Limits, and Stacking        On this page       1. [  Overview ](#overview)
2. [  Installation ](#installation)
3. [  Percentage and Fixed Discounts ](#percentage-and-fixed-discounts)
4. [  Coupon Codes, Expiry, and Usage Limits ](#coupon-codes-expiry-and-usage-limits)
5. [  Stackable Discounts and Model Attachment ](#stackable-discounts-and-model-attachment)
6. [  Cart Integration ](#cart-integration)
7. [  Validation and Exceptions ](#validation-and-exceptions)
8. [  Key Takeaways ](#key-takeaways)

  ![Laravel Discount: Coupon Codes, Usage Limits, and Stacking](https://cdn.msaied.com/539/74138a5313c0865164df1324379b1df5.png)

 [  Laravel ](https://msaied.com/articles?category=laravel) [  Composer Pacakge ](https://msaied.com/articles?category=composer-pacakge)  #Laravel   #Packages   #E-commerce   #Coupons   #Discounts  

 Laravel Discount: Coupon Codes, Usage Limits, and Stacking 
============================================================

     9 Aug 2026      4 min read    ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said  

       Table of contents

1. [  01   Overview  ](#overview)
2. [  02   Installation  ](#installation)
3. [  03   Percentage and Fixed Discounts  ](#percentage-and-fixed-discounts)
4. [  04   Coupon Codes, Expiry, and Usage Limits  ](#coupon-codes-expiry-and-usage-limits)
5. [  05   Stackable Discounts and Model Attachment  ](#stackable-discounts-and-model-attachment)
6. [  06   Cart Integration  ](#cart-integration)
7. [  07   Validation and Exceptions  ](#validation-and-exceptions)
8. [  08   Key Takeaways  ](#key-takeaways)

 Overview
--------

Building a discount system from scratch in Laravel means writing the same percentage-vs-fixed logic, expiry checks, and race-condition-safe redemption code over and over. [Laravel Discount](https://github.com/binafy/laravel-discount) by Milwad Khosravi packages all of that into Eloquent models evaluated through a single `LaravelDiscount` facade.

Installation
------------

The package requires PHP 8.1+ and Laravel 9–13.

```bash
composer require binafy/laravel-discount
php artisan migrate

```

Migrations create `discounts`, `discount_usages`, and `discountables` tables. Publish the config only if you need to change table names, the user model, or code-generation defaults:

```bash
php artisan vendor:publish --tag="laravel-discount-config"

```

Percentage and Fixed Discounts
------------------------------

A discount is a standard Eloquent model. Apply it through the facade and receive a `DiscountResult`:

```php
use Binafy\LaravelDiscount\Enums\DiscountType;
use Binafy\LaravelDiscount\Models\Discount;
use Binafy\LaravelDiscount\Facades\LaravelDiscount;

$discount = Discount::query()->create([
    'name'  => 'Summer Sale',
    'type'  => DiscountType::Percentage,
    'value' => 20,
]);

$result = LaravelDiscount::apply($discount, 200);
// originalAmount: 200.0 | discountAmount: 40.0 | payableAmount(): 160.0

```

Add `max_discount_amount` to cap a percentage discount — the classic "20% off, up to $100" scenario — without hardcoding anything in a controller.

Coupon Codes, Expiry, and Usage Limits
--------------------------------------

Setting a `code` column turns a discount into a promotional coupon. `applyCode()` validates the code and throws `DiscountNotFoundException` on a miss:

```php
$result = LaravelDiscount::applyCode('WELCOME10', 200, $user);

```

Bulk codes are generated with `random_int()`, stripping ambiguous characters like `0`/`O` and `1`/`I`:

```php
LaravelDiscount::generateCode();             // "8FJ2K9QW"
LaravelDiscount::generateCodes(100, 'VIP'); // Collection of 100 unique codes

```

Time windows use `starts_at` and `expires_at`. A `valid()` query scope filters to currently active discounts. Applying outside the window throws typed exceptions (`DiscountNotStartedException`, `DiscountExpiredException`) and fires a `DiscountExpired` event.

Usage limits are enforced at redemption time, not application time, using a database-level increment inside a transaction:

```php
LaravelDiscount::redeem($discount, $user, $result->discountAmount);

```

If two customers claim the last redemption simultaneously, only one succeeds — the database decides. Guests are tracked via session ID instead of a user model.

Stackable Discounts and Model Attachment
----------------------------------------

Mark a discount `is_stackable` and `applyMany()` resolves the optimal combination automatically: it sums all stackable discounts, finds the single best non-stackable one, and returns whichever side saves more.

```php
$result = LaravelDiscount::applyMany([$tenPercent, $tenFixed, $bigSolo], 100);
// $result->discounts  — the codes that actually applied
// $result->discountAmount — the winning total

```

The `HasDiscounts` trait attaches discounts to any Eloquent model via a polymorphic relationship:

```php
class Product extends Model
{
    use HasDiscounts;
}

$product->discounts()->attach($discount);
$result = $product->applyDiscounts($product->price);

```

Cart Integration
----------------

Install the companion `binafy/laravel-cart` package and a `CartDiscount` service handles cart-level and item-level discounts:

```php
$cartDiscount->applyToCart($cart, 'SUMMER-8FJ2K9QW');  // cart total
$cartDiscount->applyToItem($cartItem, $discount);       // single line item
$cartDiscount->applyItemDiscounts($cart);               // all items via HasDiscounts

```

Validation and Exceptions
-------------------------

A `ValidDiscountCode` form-request rule rejects invalid codes before any business logic runs, returning a specific failure reason rather than a generic message. Every failure case has its own exception extending `DiscountException`, each exposing the failing discount via `getDiscount()`:

```php
try {
    $result = LaravelDiscount::applyCode($code, $total, $user);
} catch (DiscountExpiredException $e) {
    return back()->withErrors("Code {$e->getDiscount()->code} has expired.");
} catch (DiscountException $e) {
    return back()->withErrors($e->getMessage());
}

```

Three lifecycle events — `DiscountApplied`, `DiscountRedeemed`, and `DiscountExpired` — carry enough data to drive analytics without additional queries.

Key Takeaways
-------------

- **Two discount types** (`Percentage`, `Fixed`) with an optional `max_discount_amount` cap.
- **Race-condition-safe redemption** via database-level transactional increments.
- **Guest support** through session ID tracking alongside nullable `user_id`.
- **Stacking resolver** automatically picks the combination that saves the customer the most.
- **`HasDiscounts` trait** attaches discounts polymorphically to any Eloquent model.
- **Artisan commands** `discount:generate` and `discount:prune` for terminal and scheduler use.

---

*Source: [Laravel Discount: Coupon Codes, Usage Limits, and Stacking — Laravel News](https://laravel-news.com/laravel-discount)*

 Found this useful?

          [  ](https://twitter.com/intent/tweet?url=https%3A%2F%2Fmsaied.com%2Farticles%2Flaravel-discount-coupon-codes-usage-limits-and-stacking&text=Laravel+Discount%3A+Coupon+Codes%2C+Usage+Limits%2C+and+Stacking) [  ](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fmsaied.com%2Farticles%2Flaravel-discount-coupon-codes-usage-limits-and-stacking) 

 Frequently Asked Questions 
----------------------------

  3 questions  

     Q01  How does Laravel Discount prevent two users from claiming the last redemption at the same time?        Redemption runs inside a database transaction that increments `used_count` only when the current count is still below the limit in the WHERE clause. If two requests arrive simultaneously, the database allows only one increment, and the other receives a `DiscountUsageLimitReachedException`. 

      Q02  How does the stacking resolver decide which discounts to apply?        `applyMany()` drops any discount that fails validation, splits the rest into stackable and non-stackable groups, sums the stackable ones (capped at the order total), finds the single best non-stackable discount, and returns whichever side produces the larger saving. 

      Q03  Can Laravel Discount be used for guest (unauthenticated) users?        Yes. Pass a session ID instead of a user model to both `applyCode()` and `redeem()`. The package tracks guest usage in the `session_id` column of the `discount_usages` table alongside the nullable `user_id`. 

  Continue reading

 More Articles 
---------------

 [ View all    ](https://msaied.com/articles) 

 [ ![Laravel AI SDK v0.10: 4 New Features Explained](https://cdn.msaied.com/540/e74363f048913d3782bc16a7292215db.png) Laravel AI SDK AI Agents Filesystem Tools 

### Laravel AI SDK v0.10: 4 New Features Explained

Laravel AI SDK v0.10 ships with filesystem tools for agents, human tool approval, and more. This walkthrough c...

  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said 

 12 Aug 2026     3 min read  

  Read    

 ](https://msaied.com/articles/laravel-ai-sdk-v010-4-new-features-explained) [ ![NightOwl: Laravel Monitoring With Flat Pricing and Your Own PostgreSQL Storage](https://cdn.msaied.com/538/60582948c0339b19e872f4f3c03171f2.png) Laravel Monitoring PostgreSQL 

### NightOwl: Laravel Monitoring With Flat Pricing and Your Own PostgreSQL Storage

NightOwl redirects Laravel Nightwatch telemetry into a PostgreSQL database you own, replacing per-event billin...

  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said 

 11 Aug 2026     3 min read  

  Read    

 ](https://msaied.com/articles/nightowl-laravel-monitoring-with-flat-pricing-and-your-own-postgresql-storage) [ ![Mock PHP Classes in Tests With the Double Library](https://cdn.msaied.com/537/be45e68dffd72aa9bd833c2f409fcb07.png) testing mocking phpunit 

### Mock PHP Classes in Tests With the Double Library

Double is a PHP 8.3 test double library by Jason McCreary that replaces mocks, spies, and partials with a sing...

  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said 

 11 Aug 2026     4 min read  

  Read    

 ](https://msaied.com/articles/mock-php-classes-in-tests-with-the-double-library) 

   [  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MH.png)   Mohamed Said Laravel Backend Engineer  ](https://msaied.com)Senior Backend Engineer specializing in Laravel, scalable SaaS platforms, APIs, and cloud infrastructure. I build secure, high-performance web applications that help businesses grow.

Explore

- [Home](https://msaied.com)
- [Projects](https://msaied.com/projects)
- [Articles](https://msaied.com/articles)
- [Certificates](https://msaied.com/certificates)
- [Contact](https://msaied.com#contact-section)

Connect

- [   hello@msaied.com ](mailto:hello@msaied.com)
- [   +20 109 461 9204 ](tel:+201094619204)

© 2026 Mohamed Said. All rights reserved.

 [  ](https://github.com/EG-Mohamed) [  ](https://www.linkedin.com/in/msaiedm/) [  ](https://wa.me/201094619204) [  ](mailto:hello@msaied.com) [  ](https://drive.google.com/file/u/0/d/1MF20IPRJyzfy32mhEutjL5EpSls0w2Q8/view)
