safemood/discountify
Laravel package for dynamic, condition-based discounts. Define custom conditions, apply percentage discounts, set global discount and tax rate, support coupon and class-based discounts, dynamic item field names, optional condition skipping, and event tracking for e-commerce totals.
composer require safemood/discountify
php artisan vendor:publish --tag="discountify-config"
AppServiceProvider:
use Safemood\Discountify\Facades\Condition;
public function boot()
{
Condition::define('test_discount', fn($items) => true, 10);
}
use Safemood\Discountify\Facades\Discountify;
$items = [['price' => 100, 'quantity' => 1]];
$total = Discountify::setItems($items)->total();
Discountify for core calculations (total(), totalDetailed()).Condition for defining rules.Coupon for coupon-based discounts.discountify:condition for scaffolding class-based conditions.Define Discount Rules:
// Inline conditions (ServiceProvider)
Condition::define('bulk_discount', fn($items) => count($items) > 5, 15);
// Class-based (auto-discovered)
// App/Conditions/SeasonalDiscount.php
class SeasonalDiscount implements ConditionInterface {
public function __invoke($items) { return now()->month === 12; }
}
Set Cart Items:
Discountify::setItems([
['price' => 50, 'quantity' => 2],
['price' => 100, 'quantity' => 1, 'type' => 'premium']
]);
Calculate Totals:
$total = Discountify::total(); // Basic total
$detailed = Discountify::totalDetailed(); // Array with subtotal, tax, savings
price/quantity keys via config or runtime:
Discountify::setFields(['price' => 'amount', 'quantity' => 'qty']);
Discountify::setGlobalDiscount(10)->setGlobalTaxRate(20);
Discountify::applyCoupon('SUMMER20')->total();
Listen for discount application events in EventServiceProvider:
Event::listen(DiscountAppliedEvent::class, function ($event) {
// Log discounts or trigger notifications
logger()->info('Discount applied', $event->discounts);
});
Slug Requirement:
Condition::define() requires a slug parameter. Older code may break:
// Old (deprecated)
Condition::define('test', fn($items) => true, 10);
// New
Condition::define('test_slug', fn($items) => true, 10);
Coupon Scoping:
userIds) must pass the user ID explicitly:
Discountify::applyCoupon('EXCLUSIVE', auth()->id());
storage/app/discountify/coupons.json for coupon state.Field Mismatches:
price/quantity keys don’t match config, calculations fail silently.setFields() dynamically or verify config:
$discountify->setFields(config('discountify.fields'));
dd(Discountify::getConditions()); // List all registered conditions
if (!Discountify::validateCoupon('INVALID')) {
// Handle invalid coupon
}
DiscountAppliedEvent or CouponAppliedEvent to verify applied discounts:
Event::listen(DiscountAppliedEvent::class, fn($e) => dd($e->discounts));
app()->make('Safemood\Discountify\Contracts\ConditionInterface');
storage/app/discountify/coupons.json. Clear it for testing:
php artisan discountify:clear-coupons
ConditionInterface for reusable logic:
class MinCartValueDiscount implements ConditionInterface {
public function __invoke($items) {
return array_sum(array_column($items, 'price')) > 100;
}
}
app()->bind('discountify.tax', fn() => new CustomTaxCalculator());
DiscountAppliedEvent to adjust discounts dynamically:
Event::listen(DiscountAppliedEvent::class, function ($event) {
$event->discounts[] = ['slug' => 'admin_override', 'value' => 5];
});
How can I help you explore Laravel packages today?