Here’s a scenario most merchants recognise. You generate five hundred unique coupon codes for a win-back campaign. They go out in batches over three weeks — some in the first send, some to people who open a follow-up a fortnight later. The offer is “20% off, valid for two weeks.”
Except it isn’t. In native Magento, a cart price rule has one To date, and every coupon generated under that rule inherits it. The customer who received their code on day one gets the full fortnight. The customer who received theirs on day nineteen gets about two days. Same campaign, same promise, wildly different offers — and the second group is considerably more likely to arrive at your checkout with a code that has already died.
The usual workaround is to split the campaign into several rules with staggered end dates, then keep track of which batch went to whom. It works, roughly, until someone resends a batch or a customer-service agent issues a code by hand.
We have released a small extension to deal with this properly. Magenable CouponExpiry is now live on the Adobe Commerce Marketplace, free and open source under the MIT licence.
Rolling validity, measured from issue
The idea is straightforward: instead of a fixed calendar date shared by every coupon, each coupon carries its own clock, starting when it was generated.
You add a Coupon validity (days) value — anywhere from 1 to 3650 — to an eligible auto-generated cart price rule. From then on, every new coupon that rule produces expires that many 24-hour periods after it was issued, calculated in UTC.

Two details matter more than they might appear. First, the issue time, the validity period and the resulting expiry are written as an immutable snapshot when the coupon is created. Change the rule to 30 days next month and previously issued coupons keep the 14 days they were promised — no retroactive rewriting of offers customers already hold. Second, leaving the field blank changes nothing, so adding the module to an existing rule is a no-op until you choose otherwise.
The harder half: making expiry actually stick
Adding a timestamp is the easy part. The genuinely fiddly problem is a coupon that expires while someone is checking out.
A customer applies a valid code at 11:58pm, works through shipping and payment, and submits at 12:03am. The discount was legitimate when it was applied and isn’t any more. Without careful handling you either honour a dead discount or, worse, take an order whose totals no longer match what was authorised.
CouponExpiry validates managed coupons at three points: during sales-rule processing, during totals collection, and again immediately before order submission. If validity ends mid-checkout, the discount is removed, totals are recalculated, and order placement is blocked until the customer has seen the updated cart. It also protects persisted checkout reviews, so a stale quote sitting in a browser tab can’t be submitted against the old figure.

Everything else about the parent rule still applies: website scope, customer group, usage limits and cart conditions all behave exactly as they always did. The module adds a constraint; it doesn’t replace Magento’s promotion logic.
What the admin sees
The Manage Coupon Codes grid gains columns for issue time, rolling expiry and current validity status. You can filter by status and include the expiry data in CSV and Excel XML exports — which matters when a customer-service agent needs to answer “was this code still live when they tried it?” without going to the database.
For partner and integration workflows there are authenticated REST endpoints for issuing coupons and looking up managed ones, with separate ACL resources for read and issue, and idempotency keys so a retried request doesn’t mint a second code. Both synchronous and queued generation are supported, using Magento’s own cron and message-queue infrastructure — the module starts no processes of its own and introduces no external service.
Compatibility and installation
- Magento Open Source 2.4.7-p10, 2.4.8-p5 and 2.4.9
- Adobe Commerce 2.4.8
- Mage-OS 3.5.0
- Storefront support for both Luma and Hyvä
composer require magenable/module-coupon-expiry:^1.0
bin/magento module:enable Magenable_CouponExpiry
bin/magento setup:upgrade
bin/magento setup:di:compile
bin/magento cache:flush
Two things worth knowing before you install
We would rather tell you these up front than have you discover them.
Queued generation depends on your consumer actually running. Admin mass generation publishes to Magento’s native sales_rule.codegenerator topic, and the codegeneratorProcessor consumer has to be running under your queue supervisor or cron consumer runner. That is standard Magento behaviour rather than anything we introduced, but it is the most common reason a batch appears to do nothing.
Native batch generation is not idempotent. Resubmitting a generation request can create a second batch, so check the pending queue before retrying. The REST issuing endpoint is idempotent — that protection exists where integrations need it, not in the Admin form.
Custom checkout implementations, multi-coupon customisations, direct database writers and third-party sales-rule replacements all need project-specific qualification. If you are running any of those, talk to us before rolling it out.
Where to get it
The extension is free, MIT-licensed, and passed all six Adobe Marketplace quality checks — installation and Varnish, coding standard, plagiarism, malware, marketing review and manual testing.
- Adobe Commerce Marketplace listing (version 1.0.4, with an eight-page user guide)
- Source on GitHub
If you have a campaign structure this doesn’t quite fit, or you want a hand qualifying it against a customised checkout, get in touch — and if you find a bug, the issue tracker on GitHub is the fastest way to reach us.





