Download the PHP package mvonline/discount-laravel without Composer
On this page you can find all versions of the php package mvonline/discount-laravel. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Download mvonline/discount-laravel
More information about mvonline/discount-laravel
Files in mvonline/discount-laravel
Package discount-laravel
Short Description A flexible Laravel package for managing discount codes and calculating discounts using a pipeline-based engine.
License MIT
Informations about the package discount-laravel
discount-laravel
A Laravel package for managing discount codes and calculating cart discounts using a pipeline of validation and application steps. Built for Laravel 12 and PHP 8.2+.
Repository: github.com/mvonline/discount-laravel
Package name: mvonline/discount-laravel
Root namespace: Mvonline\DiscountLaravel
Features
- CRUD-style HTTP API under
/api/discount-codes(enable/disable viadiscount-manager.routes.enabled) - Discount calculation via Laravel’s
Pipeline, with ordered pipes (validate code, expiry, usage limits, basket minimums, user/group/first-time rules, then apply amount) - DTOs for cart, customer, and calculation requests/results
DiscountTypeenum covering many strategies (full reference table below);DiscountType::...->isCalculationImplemented()marks types with built-in amount math inApplyDiscountCalculation- Migrations for
discount_codesand related tables (usage / conditions) for future use - Optional OpenAPI annotations and a
discount-manager:generate-docscommand (requireszircote/swagger-phpin dev)
Installation
From GitHub (before Packagist)
Then:
After Packagist publication
The service provider is auto-discovered. Publish config and migrations:
Configuration file: config/discount-manager.php. Important options:
| Key | Purpose |
|---|---|
models.discount_code |
Eloquent model class (extend DiscountCode in your app if needed). |
calculation.apply_mode |
stack (default): apply all requested codes in order. best_single: evaluate each requested code alone and return the best discount. |
routes.enabled |
true (default): register package routes. Set false to ship routes yourself. |
routes.middleware |
Default ['api']. Add e.g. auth:sanctum for protected admin APIs. |
pipeline.pipes |
Ordered list of pipeline classes. |
getMaximumDiscount() always evaluates each eligible code alone and returns the single best valid result (highest total_discount).
Supported discount types
Values are stored on discount_codes.type and match Mvonline\DiscountLaravel\Enums\DiscountType.
Built-in discount math in ApplyDiscountCalculation: percentage, fixed_amount, percentage_with_cap (DiscountType::...->isCalculationImplemented()). Other types return 0 from the default matcher until you extend pipes—many still use validation pipes and conditions / metadata.
All examples assume:
percentage
Percent off the applicable total (value = percent). Applied to the running remainder when multiple codes stack.
fixed_amount
Fixed currency amount off the total (cannot exceed what remains on the cart).
percentage_with_cap
Percent in value; max discount in metadata (see also Percentage with cap under Usage).
specific_user
Restricts redemption to listed user IDs (CheckSpecificUserDiscount). Pair with custom amount logic or extend ApplyDiscountCalculation—default amount for this type is 0.
specific_group
Requires the customer to be in one of the allowed groups (CheckSpecificGroupDiscount).
specific_product
Line-item / SKU rules—implement in your app or custom pipes using cart.items and conditions.
bogo
BOGO rules in metadata; fulfillment typically needs custom pipes or checkout logic.
buy_x_get_y
Threshold promotion; encode X/Y in metadata / conditions.
with_expiry
Campaign label; real validity is starts_at / expires_at (see CheckExpiryDate).
first_n_users
Cap redemptions with usage_limit (first N successful uses).
minimum_basket
CheckMinimumBasketValue enforces minimum_basket_value for any type. For built-in percent/fixed math, use PERCENTAGE or FIXED_AMOUNT with minimum_basket_value set. The MINIMUM_BASKET enum value is a semantic label if you extend amount logic yourself.
bundle
Define which SKUs must appear together; allocation in custom pipes.
category_based
Limit to categories; cart lines should carry category ids your pipes can read.
shipping
Flag shipping-focused promos; discount shipping via CartDTO + custom logic or fixed amount on shipping component.
loyalty_points
Map points to money in metadata; validate balance outside the package.
referral
Referral campaign id / terms in metadata.
bulk_purchase
Volume tiers in conditions / metadata.
payment_method
Allowed methods in conditions; validate in a custom pipe (e.g. read from request).
first_time_buyer
CheckFirstTimeBuyerDiscount requires CustomerDTO::isFirstTimeBuyer === true. Amount still needs PERCENTAGE/FIXED or custom pipe unless you extend the matcher.
seasonal
Label + date window for holiday/season campaigns.
limited_quantity
Global or per-code stock using usage_limit and your inventory rules.
location_based
Regions / stores in conditions.
customer_segment
Match CRM segments from CustomerDTO / conditions.
membership
Club / tier offers—often aligned with customer.groups.
gift_card
Treat as stored value; integrate ledger in your app.
tiered
Spend- or quantity-based tiers in metadata; implement tier resolution in a custom pipe.
flash
Short window flash sale.
user_anniversary
Eligibility from account/signup dates—validate in a custom pipe.
app_exclusive
Channel gate—pass e.g. channel in CustomerDTO::metadata.
free_gift
Adds a gift line in order management; amount here is often 0 until you model gift SKU in metadata.
upgrade
Upgrade path between products/plans.
subscription
Subscription billing hooks—eligibility flags for your biller.
milestone
Lifetime spend / order count thresholds.
refer_a_friend
Double-sided referral metadata for your referral service.
product_launch
Launch window + catalog flags.
donation_based
Round-up / charity—custom amount rules.
buy_more_save_more
Progressive table in metadata.
combo
Fixed combo price / discount for a preset basket mix.
exchange
Trade-in value and partner SKUs—integrate with ERP/POS.
Usage
Conditions & metadata: who passes what
There are two different places “conditions” and “metadata” show up:
| Where | When it is set | Purpose |
|---|---|---|
discount_codes.conditions and discount_codes.metadata |
When you create or update the code (admin, seeder, or POST /api/discount-codes) |
Rules and extra data for that code (allowed users, groups, caps, tiers, etc.). Shoppers do not send these at checkout—they are loaded from the database by code string. |
CartDTO::metadata, CustomerDTO::metadata, DiscountCalculationRequestDTO::metadata |
On each calculation request (checkout / quote) | Runtime context for your app or custom pipes (channel, device, payment method already chosen, A/B flags, etc.). Built-in pipes mostly use typed fields (customer.groups, customer.id, …), not these bags. |
At checkout the buyer only passes the code(s)—for example ['VIP2024']. The engine runs DiscountCode::whereIn('code', …), so stored conditions / metadata on each row are always applied automatically.
Built-in pipes and stored conditions keys (on the DiscountCode model):
| Pipe | Reads from the loaded code | Reads from DTOs |
|---|---|---|
| CheckSpecificUserDiscount | conditions['allowed_users'] |
customer.id |
| CheckSpecificGroupDiscount | conditions['allowed_groups'] |
customer.groups |
| CheckMinimumBasketValue | minimum_basket_value |
cart.total |
| ApplyDiscountCalculation (percentage with cap) | metadata['max_discount_amount'] or metadata['cap'] |
cart totals via context |
1) Define rules on the code (admin / API)
2) Apply codes at checkout (PHP)—only codes + cart + customer context
conditions / metadata on the code are not repeated in $request—they are read from the DiscountCode rows loaded for VIP15.
3) Same request via HTTP (POST /api/discount-codes/validate)
Optional fields match CartDTO::fromArray / CustomerDTO::fromArray: cart.metadata, cart.currency, customer.segments, customer.location, customer.payment_method, customer.metadata, and top-level metadata.
To set conditions / metadata on the code itself, use POST /api/discount-codes (create) or PUT /api/discount-codes/{id} with a JSON body that includes conditions and metadata:
Calculate a discount in PHP
HTTP API (package routes)
Routes are registered with the api middleware group and prefix api:
| Method | Path | Description |
|---|---|---|
| GET | /api/discount-codes |
Paginated list |
| POST | /api/discount-codes |
Create |
| GET | /api/discount-codes/{discountCode} |
Show |
| PUT | /api/discount-codes/{discountCode} |
Update |
| DELETE | /api/discount-codes/{discountCode} |
Soft delete |
| POST | /api/discount-codes/validate |
Validate cart + codes |
| POST | /api/discount-codes/maximum-discount |
Best single eligible code for the cart (highest discount) |
| POST | /api/discount-codes/{discountCode}/track-usage |
Increment usage_count |
Validation endpoints return the same shape as DiscountCalculationResultDTO::toArray() (snake_case keys).
Custom pipeline pipes
Override discount-manager.pipeline.pipes in config with your own classes extending Mvonline\DiscountLaravel\Pipeline\Pipe and implementing handle(DiscountCalculationContext $context, Closure $next).
Percentage with cap
Store the percentage in value and set a monetary cap in JSON metadata, for example:
Alternatively use the key cap for the same purpose.
Project layout
src/Services/DiscountCalculator.php— orchestrates the pipelinesrc/Pipeline/Pipes/*— individual stepssrc/Models/DiscountCode.php— Eloquent modelroutes/api.php— package routes
Testing
Uses Orchestra Testbench and PHPUnit 11.
Contributing
Issues and pull requests are welcome on GitHub.
License
See LICENSE (MIT).