- How do I install and configure Discountify in a Laravel application?
- Run composer require safemood/discountify, then publish the config with php artisan vendor:publish --tag=discountify-config. Laravel package auto-discovery registers the Discountify, Condition, and Coupon facades, so there are no migrations, queue workers, or external services to wire up. Most teams register their conditions inside an existing AppServiceProvider boot method.
- How do I define custom discount conditions in Discountify?
- Define a named condition on the Discountify facade with a closure that receives the cart items and returns a boolean. You then set the items, an optional global discount and tax rate, and call the calculation method to get totals, tax, savings, and a detailed breakdown. The discountify:condition Artisan generator scaffolds class-based conditions so onboarding stays consistent.
- Can I use class-based conditions instead of closures in Discountify?
- Yes. Any class implementing ConditionInterface is auto-discovered from the namespace and path configured in config/discountify.php, which defaults to App\Conditions and app_path('Conditions'). That keeps promotion rules in their own directory where they can be code-reviewed, versioned, and unit tested like any other business logic.
- Where does Discountify store coupons, and is that safe for production?
- Coupons are written to storage/app/discountify/coupons.json, a path you can override with the state_file_path config value or the DISCOUNTIFY_STATE_FILE_PATH environment variable. A JSON file is not transactional, is not shared between nodes, and is not durable on ephemeral container filesystems, so singleUse and usageLimit cannot be enforced atomically under concurrency. Treat it as unsuitable for a commercially meaningful coupon program and plan a database-backed replacement before launch.
- How do I create and apply a coupon with Discountify?
- Use Coupon::add() to persist a coupon definition, then applyCoupon() during the calculation. Since 1.0.0 the slug argument is mandatory on define and defineIf, so older examples without it will break. The README does not document whether an expired, exhausted, or wrong-user coupon throws or silently applies nothing, so cover those cases in your own tests.
- Does Discountify calculate tax before or after the discount?
- Tax handling is configurable: passing true as the second argument to calculateTaxAmount applies tax after the discount is taken. The underlying arithmetic type and rounding policy are not documented, so add golden tests that lock in the expected totals for your price points. The examples use integer percentages and integer prices throughout.
- How does Discountify handle multiple matching conditions, do discounts stack?
- The README shows many conditions plus a global discount but never states whether matching conditions sum, multiply, or take precedence, or whether stacking order is deterministic. That ambiguity is a revenue-leak and customer-dispute vector. Write tests that pin the combination behaviour before you ship any promotion on top of it.
- Does Discountify support multi-currency pricing or per-line discount allocation?
- No. Discountify is a pure computation engine over a plain array of items and owns no cart, order, invoice, or line-item model. It does not handle multi-currency, per-line discount allocation, or proration to refunds. Use it only for simple percentage-based promotions where pricing already lives in your application code.
- Does Discountify fire events I can listen to for discount tracking?
- Yes. The fire_events config key, backed by the DISCOUNTIFY_FIRE_EVENTS environment variable and enabled by default, lets you track applied discounts through events. Listen for them to build an audit trail of which conditions and coupons fired during a calculation. Keep event assertions in your test suite alongside the total amount checks.
- Which Laravel versions does Discountify support, and is it actively maintained?
- The README does not list explicit Laravel version constraints, so check the composer.json requirements on Packagist before committing. The package has a single maintainer and effectively no downstream consumers, which means little third-party production hardening. Pin an exact version, because the 0.4.0 to 1.0.0 upgrade made slug mandatory and shows minor-version pins will not protect you.