PHPackages                             remotedeveloper007/user-discounts - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. remotedeveloper007/user-discounts

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

remotedeveloper007/user-discounts
=================================

Production-ready discount management for Laravel with deterministic stacking, concurrency safety, and usage caps

v1.0.1(4mo ago)00MITPHPPHP ^8.2

Since Jan 7Pushed 4mo agoCompare

[ Source](https://github.com/remotedeveloper007/user-discounts)[ Packagist](https://packagist.org/packages/remotedeveloper007/user-discounts)[ Docs](https://github.com/remotedeveloper007/user-discounts)[ RSS](/packages/remotedeveloper007-user-discounts/feed)WikiDiscussions main Synced 1mo ago

READMEChangelogDependencies (3)Versions (3)Used By (0)

User Discounts
==============

[](#user-discounts)

A production-ready Laravel package for deterministic, concurrency-safe user-level discounts with stacking support, usage caps, and comprehensive auditing.

Features
--------

[](#features)

- ✅ **Deterministic Stacking**: Discounts apply in a predictable order (priority → id)
- ✅ **Concurrency Safe**: Database-level locking prevents race conditions
- ✅ **Usage Caps**: Per-user usage limits enforced at database level
- ✅ **Percentage Cap**: Configurable maximum total percentage discount
- ✅ **Full Auditing**: All assignments, revocations, and applications logged
- ✅ **Idempotent**: Safe to retry operations without side effects
- ✅ **Laravel 10/11/12 Compatible**

Requirements
------------

[](#requirements)

- PHP ^8.2
- Laravel ^10.0 | ^11.0 | ^12.0

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

[](#installation)

Install via Composer:

```
composer require remotedeveloper007/user-discounts
```

Publish the configuration file:

```
php artisan vendor:publish --tag=user-discounts-config
```

Publish and run migrations:

```
php artisan vendor:publish --tag=user-discounts-migrations
php artisan migrate
```

Configuration
-------------

[](#configuration)

The `config/user-discounts.php` file contains:

```
return [
    // How discounts stack: 'priority' (uses stacking_priority field)
    'stacking_order' => 'priority',

    // Maximum total percentage discount (prevents >100% discounts)
    'max_percentage_cap' => 50,

    // Rounding mode: 'round' | 'floor' | 'ceil'
    'rounding_mode' => 'round',

    // Decimal precision for final amounts
    'precision' => 2,
];
```

Usage
-----

[](#usage)

### Creating Discounts

[](#creating-discounts)

```
use Remotedeveloper007\UserDiscounts\Models\Discount;

$discount = Discount::create([
    'code' => 'WELCOME10',
    'type' => 'percentage', // or 'fixed'
    'value' => 10,
    'active' => true,
    'stacking_priority' => 1, // Lower = applied first
    'max_usage_per_user' => 3, // null = unlimited
    'starts_at' => now(),
    'ends_at' => now()->addDays(30),
]);
```

### Assigning Discounts to Users

[](#assigning-discounts-to-users)

```
use Remotedeveloper007\UserDiscounts\Services\DiscountService;

$service = app(DiscountService::class);
$user = auth()->user();

$service->assign($user, $discount);
```

### Checking Eligibility

[](#checking-eligibility)

```
if ($service->eligibleFor($user, $discount)) {
    // User can use this discount
}

// Get detailed eligibility with reason
$result = $service->eligibleForWithReason($user, $discount);
// Returns: ['eligible' => bool, 'reason' => string|null]
// Reasons: 'expired', 'inactive', 'revoked', 'usage_cap_reached', or null if eligible
```

**Eligibility Rules:**

- Discount must be active (`active = true`)
- Within valid date range (`starts_at` to `ends_at`)
- Not revoked for this user
- Usage cap not exceeded (if `max_usage_per_user` is set)

### Applying Discounts

[](#applying-discounts)

```
$originalPrice = 100.00;

// Simple apply (returns final amount only)
$finalPrice = $service->apply($user, $originalPrice);
// Automatically applies ALL eligible discounts in priority order

// Detailed apply (returns structured data for UI/API)
$result = $service->applyWithDetails($user, $originalPrice);
// Returns: [
//   'original_amount' => 100.00,
//   'final_amount' => 85.00,
//   'total_savings' => 15.00,
//   'applied' => [
//     ['code' => 'WELCOME10', 'type' => 'percentage', 'value' => 10, 'saved' => 10.00],
//     ['code' => 'SAVE5', 'type' => 'fixed', 'value' => 5, 'saved' => 5.00],
//   ],
//   'skipped' => [
//     ['code' => 'EXPIRED', 'reason' => 'expired'],
//   ],
// ]
```

### Revoking Discounts

[](#revoking-discounts)

```
$service->revoke($user, $discount);
```

How It Works
------------

[](#how-it-works)

### Lifecycle

[](#lifecycle)

1. **Create** - Admin creates a discount in the system
2. **Assign** - Discount is assigned to specific users
3. **Eligible** - Check if user can still use the discount (active, not expired, usage cap not reached)
4. **Apply** - Discount is applied to an amount, usage is incremented, audit logged
5. **Revoke** - Discount assignment is revoked (soft delete)

### Determinism

[](#determinism)

Same inputs always produce same outputs:

- Discounts are sorted by `stacking_priority` (ASC), then `id` (ASC)
- No randomness in discount selection or application
- Reproducible across multiple requests

### Idempotency

[](#idempotency)

Safe to retry operations:

- `assign()` - Uses `firstOrCreate`, won't duplicate
- `revoke()` - Sets timestamp, repeated calls have same effect
- `apply()` - Each call increments usage; designed for single application per transaction

### Concurrency Safety

[](#concurrency-safety)

Prevents race conditions:

- Uses database transactions
- `lockForUpdate()` on both discount and user\_discount rows
- Eligibility re-checked within lock
- Usage incremented atomically

### Example: Preventing Double Application

[](#example-preventing-double-application)

❌ **Without locking:**

```
Request A: Check usage (0/1) ✓ → Apply → Increment (1)
Request B: Check usage (0/1) ✓ → Apply → Increment (2) ← BUG!

```

✅ **With locking:**

```
Request A: Lock → Check (0/1) ✓ → Apply → Increment (1) → Unlock
Request B: Wait → Lock → Check (1/1) ✗ → Skip → Unlock

```

Events
------

[](#events)

Listen to these events in your application:

```
use Remotedeveloper007\UserDiscounts\Events\{
    DiscountAssigned,
    DiscountRevoked,
    DiscountApplied
};

// In EventServiceProvider
protected $listen = [
    DiscountAssigned::class => [
        SendWelcomeEmail::class,
    ],
    DiscountApplied::class => [
        LogRevenueImpact::class,
    ],
];
```

Database Schema
---------------

[](#database-schema)

### discounts

[](#discounts)

- Stores discount definitions (code, type, value, dates, etc.)

### user\_discounts

[](#user_discounts)

- Pivot table tracking which users have which discounts
- Stores `times_used` counter and revocation status

### discount\_audits

[](#discount_audits)

- Immutable audit log of all discount operations
- Records: assigned, revoked, applied actions with metadata

Testing
-------

[](#testing)

Run the test suite:

```
vendor/bin/phpunit
```

Example test validates usage cap enforcement:

```
$service->assign($user, $discount); // max_usage_per_user = 1
$this->assertEquals(90.00, $service->apply($user, 100.00)); // Works
$this->assertEquals(100.00, $service->apply($user, 100.00)); // Skipped (cap reached)
```

Production Considerations
-------------------------

[](#production-considerations)

### Indexes

[](#indexes)

All foreign keys and query columns are indexed for performance.

### Retry Safety

[](#retry-safety)

All operations are designed to be safe under retries and concurrent access.

### Extension Points

[](#extension-points)

- Add custom validation logic by extending `DiscountService`
- Create listeners for events to trigger side effects
- Add custom discount types by extending the `Discount` model

### Future Enhancements

[](#future-enhancements)

- Money library integration for precise currency handling
- Idempotency keys for duplicate request detection
- Discount facades for cleaner syntax
- Multi-currency support

License
-------

[](#license)

MIT License

Support
-------

[](#support)

For issues, questions, or contributions, please open an issue on GitHub.

###  Health Score

36

—

LowBetter than 81% of packages

Maintenance82

Actively maintained with recent releases

Popularity0

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity47

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~0 days

Total

2

Last Release

122d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/49d240172569d7bdee789251c7ea8316be4d1687a182cf6d2445688913ece16f?d=identicon)[Remote Developer](/maintainers/Remote%20Developer)

---

Top Contributors

[![remotedeveloper007](https://avatars.githubusercontent.com/u/40437843?v=4)](https://github.com/remotedeveloper007 "remotedeveloper007 (8 commits)")

---

Tags

laravelecommercediscountscouponspromotionsuser-discounts

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/remotedeveloper007-user-discounts/health.svg)

```
[![Health](https://phpackages.com/badges/remotedeveloper007-user-discounts/health.svg)](https://phpackages.com/packages/remotedeveloper007-user-discounts)
```

###  Alternatives

[barryvdh/laravel-ide-helper

Laravel IDE Helper, generates correct PHPDocs for all Facade classes, to improve auto-completion.

14.9k123.0M683](/packages/barryvdh-laravel-ide-helper)[sylius/promotion-bundle

Manage ecommerce promotions system in your Symfony application.

21426.2k3](/packages/sylius-promotion-bundle)[interaction-design-foundation/laravel-geoip

Support for multiple Geographical Location services.

17221.0k3](/packages/interaction-design-foundation-laravel-geoip)[nedwors/navigator

A Laravel package to ease defining navigation menus

433.1k](/packages/nedwors-navigator)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
