kishorthummar / module-cartrule-ipvalidation
Adds IP-address-based usage limiting to Cart Price Rules (Sales Rules / Coupons) in Adobe Commerce / Magento 2, enforced for both guest and logged-in customers.
Package info
github.com/kishorthummar/module-cartrule-ipvalidation
Type:magento2-module
pkg:composer/kishorthummar/module-cartrule-ipvalidation
Requires
- php: ~8.1.0||~8.2.0||~8.3.0||~8.4.0||~8.5.0
- magento/framework: *
- magento/module-checkout: *
- magento/module-config: *
- magento/module-sales: *
- magento/module-sales-rule: *
- magento/module-store: *
This package is auto-updated.
Last update: 2026-07-18 09:00:58 UTC
README
An Adobe Commerce / Magento 2 extension that adds IP-address-based usage limiting to Cart Price Rules (Sales Rules / Coupons), closing a gap in Magento's native coupon usage-limit logic — most notably for guest checkouts.
1. The Magento Limitation
Adobe Commerce ships with two coupon usage-limit fields on every Cart Price Rule:
| Field | What it actually limits |
|---|---|
| Uses per Coupon | Total number of times a single coupon code can be used, across all customers combined. |
| Uses per Customer | Number of times one logged-in customer account can use the coupon. |
The admin form itself is explicit about the gap — the "Uses per Customer" field carries the note:
"Usage limit enforced for logged in customers only."
This means:
- Guest checkouts are not covered at all. A customer who checks out as a guest can use a "one per customer" coupon an unlimited number of times, simply by not logging in.
- A logged-in customer can achieve the same result by creating multiple accounts with different
email addresses (trivial with disposable/plus-addressed emails), since the limit is keyed purely to
customer_id. - Magento does capture the customer's IP address on every order (
sales_order.remote_ip), but this value is never cross-referenced against coupon usage anywhere in core. It sits in the order record purely as metadata (fraud review, logs, admin grid) — there is no built-in mechanism that uses it to restrict rule/coupon reuse.
Net effect: any cart price rule intended as "one use per customer" is only reliably enforced for customers who log in and use one account — which, for public/marketing coupon codes, is often not the majority case.
2. How This Extension Resolves It
This module adds a secondary, independent usage-limit dimension based on IP address, layered on top of (not replacing) the existing "Uses per Customer" logic — enforced for both guests and logged-in customers.
What's added
- A new "Uses per IP" limit on each Cart Price Rule, separate from "Uses per Coupon" and "Uses per Customer" — so admins can tune each independently (e.g., 1 use per coupon code, unlimited per customer, but capped at 1 per IP).
- Per-rule opt-in toggle ("Enable IP Validation") — nothing changes for rules that don't explicitly turn this on; existing rules behave exactly as before.
- A global System Configuration master switch to enable/disable the entire feature store-wide, without touching any individual rule's settings — useful for emergency rollback or staged rollout.
- Usage tracking table (
salesrule_ip_usage) that records how many times each IP address has used each rule/coupon combination, populated at order placement — the same trustworthy point in the checkout flow that core Magento uses to recordsales_order.remote_ip. - No new/custom customer-facing error message. When an IP's usage cap is reached, the customer sees Magento's own standard coupon usage-limit message — the same one shown for "Uses per Customer" or "Uses per Coupon" violations today. No mention of IP, device, or location is ever surfaced, so customers have no signal that this method of validation exists or how to work around it.
- Zero effect on IP resolution logic. The extension reuses Magento's own
RemoteAddress::getRemoteAddress()service — the same class core uses forsales_order.remote_ip— so IP detection stays consistent with whatever trusted-proxy /X-Forwarded-Forconfiguration is already in place; it does not introduce a second, divergent way of resolving client IPs.
What this does not change
- "Uses per Coupon" and "Uses per Customer" continue to work exactly as before, for every rule, whether or not IP validation is enabled.
- Rules that don't opt in are completely unaffected — this is additive, not a replacement.
3. Installation
Option A — Composer (recommended)
composer require kishorthummar/module-cartrule-ipvalidation bin/magento module:enable KishorThummar_CartRuleIPValidation bin/magento setup:upgrade bin/magento setup:di:compile bin/magento setup:static-content:deploy -f bin/magento cache:flush
Option B — Manual installation
- Create the module directory:
mkdir -p app/code/KishorThummar/CartRuleIPValidation
- Copy/extract the module source into that directory, preserving the folder structure
(
Model/,Plugin/,Observer/,etc/,view/,Setup/,registration.php, etc.). - Enable and install:
bin/magento module:enable KishorThummar_CartRuleIPValidation bin/magento setup:upgrade bin/magento setup:di:compile bin/magento setup:static-content:deploy -f bin/magento cache:flush
Post-install configuration
- In the Admin panel, go to Stores → Configuration → Sales → Cart Rule IP Validation and set Enable to Yes. This is the global master switch — the feature is enabled by default.
- Go to Marketing → Cart Price Rules, open (or create) a rule, and locate the "Enable IP Validation" and "Uses per IP" fields directly below "Uses per Customer".
- Enable IP Validation on the rule and set a numeric Uses per IP value (leave blank for unlimited — usage will still be tracked but not enforced).
- Save the rule.
If the global switch (step 1) is left disabled, the two fields above will not appear on the Cart Price Rule form at all, and no IP validation or usage tracking occurs anywhere in the store.
4. Important Caveats (Limitations of IP-Based Validation)
IP-based validation is a supplementary fraud-reduction layer, not a precise or foolproof identity control. Please account for the following before enabling it broadly:
- Shared IP addresses cause false positives. Customers behind the same office network, university, public Wi-Fi, or mobile carrier-grade NAT (common with many mobile ISPs) can share a single public IP. A legitimate, distinct customer may be blocked because someone else on the same network already used the coupon.
- Dynamic/rotating IPs undermine enforcement. Many home and mobile connections change IP address periodically (or on router reboot), meaning the same customer can sometimes bypass the limit simply by reconnecting — without any deliberate intent to abuse the coupon.
- VPNs and proxies defeat it easily. Any customer using a VPN, proxy, or Tor can trivially present a different IP on each visit, bypassing this control entirely. This extension should not be treated as an anti-abuse measure against deliberate, technically capable bad actors.
- CDN/reverse-proxy misconfiguration can break it for everyone. If the store sits behind
Varnish, Cloudflare, a load balancer, or similar, and trusted proxies /
X-Forwarded-Forare not correctly configured, Magento may resolve the proxy's/CDN's IP instead of the real client IP — causing all customers to appear to share one IP and get blocked after the first use. Verify this configuration in a staging environment before enabling the feature in production. - IP addresses are personal data under privacy regulations (e.g., GDPR). Storing IP addresses in
salesrule_ip_usageshould be accounted for in the store's data retention and privacy policy — consider a retention/cleanup strategy (e.g., periodic purge) consistent with how the store already handlessales_order.remote_ipretention. - Not a replacement for "Uses per Customer." This extension is designed to complement the existing customer/account-based limit, not replace it. For the strongest practical protection, keep both enabled together — "Uses per Customer" as the primary control for logged-in accounts, and "Uses per IP" as an additional layer that also covers guest checkouts.
5. Support
For issues, questions, or feature requests related to this extension, please open an issue in the module's repository.