Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Coupondiscount Laravel Package

codeboxr/coupondiscount

Coupon and discount management for Laravel. Create coupon codes with percentage or fixed discounts, expiry dates, usage limits, and minimum cart rules, then validate and apply them to carts and orders. A simple, lightweight way to add promotional pricing to your store.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and publish

composer require codeboxr/coupondiscount
php artisan vendor:publish --provider="Codeboxr\CouponDiscount\CouponDiscountServiceProvider"
php artisan migrate

Publish before migrating — this package ships migrations, and if you migrate first you'll end up hand-writing the table afterwards. Check config/coupondiscount.php (or similarly named) for table names, currency, and any model bindings before you touch anything.

Where to look first

This is a 12-star, 0-dependent package with a single 2024 release. Treat the README as a hint, not documentation, and read in this order:

  1. src/ — the service provider and the main facade/service class. That's your whole public API.
  2. database/migrations/ — the actual schema. This tells you more about capabilities (percentage vs. fixed, min cart value, usage caps, date windows) than the README will.
  3. config/ — anything swappable.

Budget 30–45 minutes for this. You are not evaluating a framework; you're reading ~a few hundred lines of code.

First use case

The canonical flow is: create a coupon (admin/seed) → validate a code against a cart total → apply the discount → record redemption against an order. Get that end-to-end in a test before wiring UI:

$coupon = Coupon::create([
    'code' => 'EID25',
    'type' => 'percentage',
    'value' => 25,
    'expires_at' => now()->addMonth(),
]);

$discount = CouponDiscount::apply('EID25', $cartTotal); // verify actual signature

If apply() throws rather than returns a result object, wrap it — see gotchas.

Implementation Patterns

Keep the coupon math behind one service

Don't let controllers call the package directly. Write a thin CouponService of your own that owns validation order (exists → active → within date window → under usage cap → meets minimum → currency matches), because you will almost certainly need to add a rule the package doesn't have (per-user limits, first-order-only, product/category scoping). Isolating the package means swapping or forking it later is a one-file change.

Validate in a FormRequest, apply in a domain action

Two distinct moments:

// CartController — "is this code valid right now?"
public function check(ApplyCouponRequest $request)
{
    return $this->coupons->preview($request->code, $request->user(), $cart);
}

// CheckoutController — "commit the discount"
$order = DB::transaction(function () use ($cart, $code) {
    $discount = $this->coupons->apply($code, $cart); // re-validate, don't trust the session
    return $this->orders->create($cart, $discount);
});

Never carry "coupon applied" as a boolean in the session and trust it at checkout. Re-resolve it inside the transaction. This is the single most important pattern with any discount package.

Persist the redemption, not just the coupon

The package's schema will have a coupons table; it may or may not have a redemptions/pivot table. If it does, write a row per order with coupon_id, order_id, user_id, amount_discounted. If it doesn't, add your own table. You need this for usage caps, audit, and refunds — and computing "how many times has this been used" with a COUNT on orders is a query you'll regret.

Snapshot the discount onto the order

Store coupon_code, discount_amount, and discount_type as columns on the order. Coupons get edited and deleted; the order must remain explainable years later. This also makes refunds and partial refunds tractable.

Admin CRUD is yours to build

Expect no UI. A simple resource controller with index/create/edit and a Coupon::query() listing is a couple of hours. Generate codes server-side (uppercase alphanumeric, retry on unique-constraint collision) rather than letting admins type duplicates.

Testing

Fake the clock, not the package, for expiry tests — $this->travelTo(...) around the expiry boundary. For the rest, integration tests against a real SQLite/MySQL connection are more valuable than mocking the service, because the bugs here live in SQL and date comparison, not in PHP branching.

Gotchas and Tips

The low score is about support, not necessarily quality. 12 stars, 0 dependents, one release in 2024 means: no Stack Overflow answers, no issues triage, and breaking changes arrive silently. Pin the exact version in composer.json ("codeboxr/coupondiscount": "1.0.*" or the precise tag) and plan to fork. Vendoring it into app/ or a private namespace is a legitimate choice for something this small — you'll modify it anyway.

Race conditions on usage limits. Two simultaneous checkouts can both pass a uses < max_uses check. Fix it at the database level: either a SELECT ... FOR UPDATE on the coupon row inside the checkout transaction, or a unique index on (coupon_id, user_id) for single-use-per-user coupons and catch the duplicate-key exception. Do not rely on the package's counter increment alone.

Timezone drift on expiry. If expires_at is a DATETIME and your app timezone differs from UTC (or from the DB session timezone), coupons expire hours early or late. Confirm what the package writes and compare against now() consistently; safest is to store UTC and evaluate UTC. Test the exact boundary second.

Rounding and currency mismatch. Percentage discounts on odd totals produce fractions. Decide once — round half-up at the line level, or floor to the minor unit — and apply the same rule everywhere (preview, order, refund). Also verify the package doesn't hardcode a currency or a decimal precision; if it does, that's a fork-worthy issue.

Floating point money. If the package uses floats for value or the computed discount, cast to integer minor units in your own service layer before persisting. 19.99 * 0.85 will eventually cost you a reconciliation bug.

Migration collisions. If you already have a coupons table, publishing will either fail or silently no-op depending on the migration's guard. Rename via config if supported; otherwise edit the published migration before running it. Never edit it after it's run in production without a follow-up migration.

Exception-based validation. Many small packages throw a generic \Exception for "coupon expired" and "coupon not found" alike. Catch broadly in your service and re-throw a domain exception (InvalidCouponException) so your controller can render a clean 422. Otherwise users see stack traces for typos.

Extension points are thin. Realistically you get: config, migrations, and possibly a model binding in config. If there's no configurable model, you cannot extend Coupon with your own relationships — you'll be adding columns via separate migrations and querying the package's model directly. That's fine, but decide it deliberately rather than discovering it in month three.

Debugging. When a coupon "doesn't work," log the resolved coupon row and each validation gate result. Add a temporary ->dd() chain or a debug flag in your own service. Nine times out of ten it's a soft-deleted coupon, a lowercase/uppercase code mismatch (decide: store uppercase, normalize input with strtoupper(trim($code))), or a timezone-off expiry.

Watch the code comparison. If the package does a plain where('code', $input), your MySQL collation may make it case-insensitive while SQLite makes it case-sensitive — tests pass, production behaves differently, or vice versa. Normalize on write and on read.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib