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.

Maintainers

Package info

github.com/kishorthummar/module-cartrule-ipvalidation

Type:magento2-module

pkg:composer/kishorthummar/module-cartrule-ipvalidation

Transparency log

Statistics

Installs: 1

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

1.0.0 2026-07-18 08:42 UTC

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 record sales_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 for sales_order.remote_ip — so IP detection stays consistent with whatever trusted-proxy / X-Forwarded-For configuration 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

  1. Create the module directory:
    mkdir -p app/code/KishorThummar/CartRuleIPValidation
  2. Copy/extract the module source into that directory, preserving the folder structure (Model/, Plugin/, Observer/, etc/, view/, Setup/, registration.php, etc.).
  3. 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

  1. 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.
  2. 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".
  3. 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).
  4. 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-For are 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_usage should 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 handles sales_order.remote_ip retention.
  • 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.