Models
Shopper uses two models to manage discounts:| Model | Purpose |
|---|---|
Shopper\Core\Models\Discount | The discount definition (code, type, value, rules) |
Shopper\Core\Models\DiscountDetail | Polymorphic link connecting discounts to specific products or customers |
Shopper\Core\Models\Contracts\Discount. Neither model is configurable via config/shopper/models.php.
Database Schema
Discount Table
| Column | Type | Nullable | Default | Description |
|---|---|---|---|---|
id | bigint | no | auto | Primary key |
code | string | no | - | Unique discount code |
type | string | no | - | Discount type enum |
value | integer | no | - | Discount value (percentage or cents) |
is_active | boolean | no | false | Discount visibility |
apply_to | string | no | - | Application scope enum |
min_required | string | no | - | Requirement type enum |
min_required_value | string | yes | null | Minimum value required |
eligibility | string | no | - | Eligibility type enum |
usage_limit | integer | yes | null | Maximum total uses |
usage_limit_per_user | boolean | no | false | Limit one use per customer |
total_use | integer | no | 0 | Current total usage count |
start_at | datetime | no | - | Discount start date |
end_at | datetime | yes | null | Discount end date (null = no expiry) |
zone_id | bigint | yes | null | FK to zone (null = all zones) |
metadata | json | yes | null | Additional custom data |
created_at | timestamp | yes | null | Creation timestamp |
updated_at | timestamp | yes | null | Last update timestamp |
Discountable Table (Pivot)
| Column | Type | Nullable | Description |
|---|---|---|---|
id | bigint | no | Primary key |
condition | string | yes | Condition type (apply_to, eligibility) |
total_use | integer | no | Usage count for this relation |
discountable_id | bigint | no | Polymorphic relation ID |
discountable_type | string | no | Polymorphic relation type |
discount_id | bigint | no | FK to discount |
Discount Type
TheDiscountType enum defines value types:
| Type | Database Value | Value Handling |
|---|---|---|
| Percentage | percentage | Stored as-is (10 = 10%) |
| Fixed Amount | fixed_amount | Stored in cents (1000 = $10.00) |
Discount Application
TheDiscountApplyTo enum defines where the discount applies:
| Scope | Database Value | Description |
|---|---|---|
| Order | order | Discount applies to total order amount |
| Products | products | Discount applies only to linked products |
Discount Eligibility
TheDiscountEligibility enum defines who can use the discount:
| Eligibility | Database Value | Description |
|---|---|---|
| Everyone | everyone | Any customer can use |
| Customers | customers | Only linked customers can use |
Discount Requirements
TheDiscountRequirement enum defines minimum conditions:
| Requirement | Database Value | min_required_value |
|---|---|---|
| None | none | Not used |
| Price | price | Minimum amount in cents |
| Quantity | quantity | Minimum item count |
Discount Condition
TheDiscountCondition enum is used on the DiscountDetail pivot model to distinguish whether a linked record represents a product (the discount applies to) or a customer (who is eligible):
Relationships
Items (DiscountDetail)
Zone
Value Handling
Fixed amount values are stored in cents. Percentage values are stored as the percentage number. For a $10 fixed discount, store1000 (cents). For a 15% percentage discount, store 15:
Usage Limit Checking
Creating Discounts
Percentage Discount for All Orders
Fixed Amount Discount with Minimum Order
Product-Specific Discount
VIP Customer Discount
Retrieving Discounts
Discount Validation
When a coupon is applied to a cart viaCartManager::applyCoupon(), the Cart package validates the discount automatically during the calculation pipeline. You do not need to build manual validation logic. The built-in DiscountValidator checks all rules (active status, date range, usage limits, eligibility, zone, minimum amounts) and produces clear error messages.
For cases outside the cart pipeline where you need to check a discount programmatically:
Configuration
Disabling Discounts
If your store doesn’t use discount codes, disable the feature inconfig/shopper/features.php:
Permissions
The admin panel generates five permissions for discount management:| Permission | Description |
|---|---|
browse_discounts | View the discounts list |
read_discounts | View a single discount |
add_discounts | Create new discounts |
edit_discounts | Edit existing discounts |
delete_discounts | Delete discounts |
Components
To customize the admin UI for discount management:config/shopper/components/discount.php:
Storefront Example
Applying a discount code on the storefront uses the Cart package API. TheCartManager::applyCoupon() method validates the code exists, and the calculation pipeline handles the rest (validation rules, discount calculation, and adjustment creation):
Use Cases
| Scenario | Type | Apply To | Eligibility | Requirement |
|---|---|---|---|---|
| Site-wide sale | Percentage | Order | Everyone | None |
| Free shipping over $50 | Fixed | Order | Everyone | Price |
| Buy 3+ get 10% off | Percentage | Order | Everyone | Quantity |
| VIP member exclusive | Percentage | Order | Customers | None |
| Product clearance | Percentage | Products | Everyone | None |
| First order discount | Fixed | Order | Customers | None |