Skip to content

Troubleshooting

{/* AUTO-GENERATED from ../docs/troubleshooting.md by scripts/sync-dev-docs.mjs — do not edit by hand. */}

Audience: merchants, agencies, and support staff resolving a discount issue on a live site. When to use this: a rule that looks correct in the admin is not firing on the storefront, totals appear stale, or a third-party plugin is interacting badly with Dino Discounts.

Start at §3 Debugging playbook if you do not yet have a hypothesis. Jump to §1 Plugin conflicts or §2 Cache gotchas if you already suspect one. The §4 FAQ answers the most common merchant questions.


Dino Discounts ships built-in detection for the four most common conflicting discount plugins and the eight most common full-page caching plugins (see includes/ConflictDetector.php). When one is active an admin notice is shown. The list below covers the symptoms and the fix for each category.

1.1 WP Rocket + discount cache invalidation

Section titled “1.1 WP Rocket + discount cache invalidation”
  • Symptom: rule edits do not take effect until the page cache expires. Shoppers on warmed pages see old prices; a fresh incognito window shows the new price.
  • Diagnosis: confirm WP Rocket is active. Browse to /cart/ signed out and view source for wp-rocket HTML comments at the bottom of the page.
  • Fix: exclude /cart/, /checkout/, /my-account/ and any page that renders a [woocommerce_*] shortcode from WP Rocket’s page cache (Settings → Advanced Rules → Never Cache URLs). Also add your cart/checkout pages to “Never Cache Cookies” for woocommerce_cart_hash. Hook your own purge on dino_discounts_cache_flush if you want a full-page purge on every rule save:
add_action( 'dino_discounts_cache_flush', function () {
if ( function_exists( 'rocket_clean_domain' ) ) {
rocket_clean_domain();
}
} );

1.2 W3 Total Cache + fragment caching of the mini-cart

Section titled “1.2 W3 Total Cache + fragment caching of the mini-cart”
  • Symptom: mini-cart widget shows the wrong discount amount, or shows no discount when the cart page shows one correctly.
  • Diagnosis: in W3TC → General → Fragment Cache, check whether fragment caching is enabled. Disable it temporarily and reload the mini-cart.
  • Fix: either disable fragment caching, or exclude the woocommerce fragment group. Ensure cart/checkout URLs are on W3TC’s “Never cache the following pages” list. The plugin also fires woocommerce_add_to_cart_fragments with a script#dino-discounts-mini-cart-data fragment — fragment-cache layers must not swallow it.

1.3 Page builders that wrap the cart (Elementor, Divi, Beaver Builder)

Section titled “1.3 Page builders that wrap the cart (Elementor, Divi, Beaver Builder)”
  • Symptom: virtual coupons apply (totals are correct) but the discount line item does not render on the cart page, or renders with a default label instead of the merchant-configured cart label.
  • Diagnosis: open the cart in a browser with no page-builder template active — switch the theme to Twenty Twenty-Four temporarily, or edit the cart page to remove the builder template. If the discount renders correctly there, the builder template is the cause.
  • Fix: the cart template must call wc_cart_totals_coupon_html() so Dino’s woocommerce_cart_totals_coupon_label filter has somewhere to run. Elementor’s “Cart” widget and Divi’s WooCommerce cart module both support this by default but are easy to override accidentally. Rebuild the cart page from the builder’s default WooCommerce template.

1.4 Other discount plugins running alongside

Section titled “1.4 Other discount plugins running alongside”

The four packages the plugin detects and names in a dismissible admin notice (see ConflictDetector::INCOMPATIBLE_PLUGINS):

  • WooCommerce Dynamic Pricing & Discounts
  • Discount Rules for WooCommerce
  • YITH Dynamic Pricing and Discounts
  • Smart Coupons for WooCommerce

Symptom: prices double-discount, discounts silently disappear, or customers receive error notices at checkout about “coupon already applied”.

Diagnosis: if the dismissible notice is present on any WooCommerce-related admin screen, that is your diagnosis. If you’ve dismissed it previously, check Plugins → Installed Plugins for any of the four packages above.

Fix: run one discount engine at a time. Either deactivate the other plugin, or scope Dino Discounts narrowly (e.g. only storefront tier tables) and leave cart-level discounts to the other plugin. We do not support running two coupon engines concurrently — the WooCommerce woocommerce_get_shop_coupon_data filter is a single handoff point and whichever plugin wins the priority war decides the outcome.

Not on the detection list but still worth knowing: Advanced Coupons and other plugins that only layer onto native WooCommerce coupons (rather than running their own engine) generally coexist, because Dino Discounts applies as virtual dino_dd_* coupons alongside the merchant’s native coupons. If you see an interaction, use the dino_discounts_incompatible_plugins filter to add it to the detection list and open a support issue.

Custom conflicts: the list is filterable. Use the dino_discounts_incompatible_plugins or dino_discounts_caching_plugins filters to add your own known bad interactions (see docs/hooks.md).

1.5 Translation plugins (WPML, Polylang, TranslatePress)

Section titled “1.5 Translation plugins (WPML, Polylang, TranslatePress)”
  • Symptom: a rule that targets a specific category or product works for the default language but not for translated languages — or vice versa.
  • Diagnosis: WPML and Polylang assign a separate post ID to each translation of a product or category. If your targeting tree references the default-language ID (e.g. category_id = 42), translated versions (ID 156, 237…) do not match. The plugin does not currently translate IDs in the targeting tree automatically.
  • Fix (preferred): scope targeting by product tag or attribute instead of by category or product ID. Tags and attributes are typically shared across translations, so one rule covers every language.
  • Fix (one rule per language): duplicate the rule and point each copy at the translated IDs. Rule count stays manageable for small catalogues.
  • Fix (programmatic, advanced): hook dino_discounts_targeting_result (see docs/hooks.md) and use WPML’s wpml_object_id filter inside your callback to rewrite the targeted ID to the current language’s equivalent before the tree evaluates.

1.6 Multi-currency plugins (Aelia, WOOCS, WPML Multi-Currency)

Section titled “1.6 Multi-currency plugins (Aelia, WOOCS, WPML Multi-Currency)”
  • Symptom: a fixed-amount discount (e.g. €5 off) applies to shoppers in a different currency as the wrong amount (e.g. $5 off for USD shoppers).
  • Diagnosis: Dino stores fixed-amount discounts in a single currency. The plugin does not read another plugin’s currency conversion rates — it applies the configured number verbatim.
  • Fix: use the dino_discounts_enabled_currencies filter to expose all shop currencies to the admin zone builder (see docs/hooks.md). Then scope each fixed-amount rule with a Zone that restricts to a specific currency. Percentage discounts do not have this issue — they convert naturally with the line-item price.

1.7 Payment gateways that recalculate cart totals (Stripe Payment Element, PayPal Express, Apple Pay / Google Pay)

Section titled “1.7 Payment gateways that recalculate cart totals (Stripe Payment Element, PayPal Express, Apple Pay / Google Pay)”
  • Symptom: the cart page shows the discount correctly, but the payment sheet (Stripe’s Payment Request button, PayPal Express popup, Apple Pay confirmation) shows a slightly different total — usually the undiscounted total or a stale one.
  • Diagnosis: these gateways query the cart via AJAX / Store API and render their own summary. If they query before a cart fragment update finishes, they see a pre-discount total.
  • Fix: ensure the gateway is using WC’s cart totals (WC()->cart->get_total()), not its own cached copy. For Stripe, enable “Update order via AJAX” in the plugin settings. For PayPal Express, make sure the woocommerce_add_to_cart_fragments hook fires before the button renders. If the mismatch persists, capture the gateway’s request to the Store API (/wp-json/wc/store/v1/cart) and compare its totals.total_price against the cart page — they should match.

Dino Discounts caches in four places. When a rule edit does not show up, work down this list.

LayerKey / groupTTLPurge mechanism
WP object cache — rules & settingsdino_discounts group, keys rules and settingsrequest lifetime (persists under Redis/Memcached)RulesEngine::invalidate_caches() runs on every rule/settings save
Engine evaluation transientsdino_eval_* (cart-fingerprint keyed)60 sCartCoupons::flush_eval_cache() on dino_discounts_cache_flush
Zone map transientdino_dd_zone_cache24 hDeleted on shipping-zone save, settings save
Storefront index transientdino_dd_storefront_index and dino_dd_sf_* / dino_dd_sf_html_* object-cache keys1 day / 1 hourStorefrontIndex::flush() on product save, rule save

2.1 Object cache (Redis / Memcached) holding stale rule data

Section titled “2.1 Object cache (Redis / Memcached) holding stale rule data”
  • Why it happens: Redis/Memcached turn WordPress object cache into a persistent store. wp_cache_delete() does work — the plugin calls it on every save via update_option_dino_discounts_active_rulesRulesEngine::invalidate_caches(). But if a deploy pushes new code while an old PHP-FPM process holds a stale connection, that worker can serve the old rules key until it cycles.
  • Verify: wp eval 'var_dump( wp_cache_get( "rules", "dino_discounts" ) );' — output reflects what a request will see.
  • Clear manually: wp cache flush (site-wide), or targeted: wp cache delete rules dino_discounts && wp cache delete settings dino_discounts.
  • Multisite note: the dino_discounts group is intentionally not registered as a global group (see Plugin::run() — global groups share one namespace across a multisite network and would leak rules between subsites). Flushing per-site is correct.

2.2 Transient stacking with dino_eval_* keys

Section titled “2.2 Transient stacking with dino_eval_* keys”
  • Why it happens: AppliesDiscount writes a 60-second transient keyed by cart fingerprint to avoid re-running the engine for identical carts. When rules change mid-minute, carts that match a stored fingerprint get the old result for up to 60 seconds.
  • Verify (no persistent object cache): query SELECT option_name FROM wp_options WHERE option_name LIKE '_transient_dino_eval_%';, or run wp transient list --search='dino_eval_*' with the transient-extension command installed.
  • Verify (Redis / Memcached): on sites with a persistent object cache, set_transient() bypasses wp_options entirely — both the SQL query and wp transient list return nothing even when transients exist. Use wp eval 'var_dump( get_transient( "dino_eval_<fingerprint>" ) );' for a specific fingerprint, or just trust the flush_eval_cache call below.
  • Clear: saving any rule or setting fires dino_discounts_cache_flush, which wipes all dino_eval_* transients via CartCoupons::flush_eval_cache(). Important caveat: this method runs a direct DELETE FROM wp_options (see VirtualCouponFactory::flush_eval_cache()) — on persistent-object-cache sites (Redis/Memcached) the transients live in the object cache, not wp_options, so this SQL delete clears nothing and each stale fingerprint can persist up to 60s until its TTL expires. To force a full flush on those sites: wp cache flush after the rule save, or wp eval '\DinoDiscounts\Cart\CartCoupons::flush_eval_cache();' && wp cache flush.
  • Why it happens: WooCommerce stores the applied-coupons list on the customer session. After a rule deactivates, a shopper with that rule’s dino_dd_* virtual coupon still in their session can see stale discount data until the cart recalculates.
  • Verify: open an incognito window. If the new shopper gets correct totals and the existing shopper does not, it is a session issue.
  • Clear: the plugin invalidates WooCommerce’s cart fragment cache group on dino_discounts_cache_flush via WC_Cache_Helper::invalidate_cache_group( 'cart' ). Customers then get fresh totals on their next cart load. For a fully stuck session, clear the WC sessions table: wp db query "TRUNCATE TABLE wp_woocommerce_sessions;" (this logs everyone out of their cart, use sparingly).

2.4 CDN / edge-cached prices not re-evaluating

Section titled “2.4 CDN / edge-cached prices not re-evaluating”
  • Why it happens: Cloudflare, Bunny, or another edge cache is serving HTML that was generated before the rule change.
  • Verify: curl -I https://yoursite.com/cart/ — look at cf-cache-status, x-cache, or age headers.
  • Clear: purge /cart/, /checkout/, and any product pages that render tier tables from the edge. Configure the edge to bypass cache for those paths, or bypass-on-cookie for woocommerce_items_in_cart / woocommerce_cart_hash (either works — woocommerce_items_in_cart is set earlier in the request and is more reliable for bypass rules).

Dino Discounts is HPOS-compatible. Every order lookup uses wc_get_orders(), which transparently reads from the custom order tables when HPOS is enabled and wp_posts when it isn’t (see TargetingEvaluator.php, CouponCodeManager.php, AnalyticsTracker.php, AnalyticsController.php, Privacy.php). No configuration is required.

If you see order-based targeting (e.g. first-order rules, N-order-customer rules) behave inconsistently after enabling HPOS: the most likely cause is a site that has not yet run the HPOS sync to completion. Go to WooCommerce → Settings → Advanced → Features → Custom Order Tables and verify the sync status is 100%. Behaviour during partial sync is not supported.

The dino_discounts object-cache group is intentionally not registered as a global group (see Plugin::run()). Each subsite has its own dino_discounts_active_rules option and its own cache namespace. This means:

  • Rule saves on one subsite do not invalidate caches on other subsites — each subsite is independent.
  • If you network-activate the plugin, each subsite admin still manages its own rules. There is no super-admin view of all rules across the network.
  • wp cache flush --url=<subsite> targets one subsite; wp cache flush at the network level flushes everything.
  • The dino_dd_zone_cache transient is also per-subsite.

The engine is designed for tens to hundreds of rules. Each cart evaluation iterates every active rule in priority order. If you have 500+ rules:

  • Enable the performance monitor in Discounts → Settings and review the performance_tracking output.
  • Consider consolidating overlapping rules (e.g. one tiered rule instead of five separate product-percentage rules).
  • The dino_eval_* 60-second cache only helps if cart fingerprints are stable — carts with highly variable quantities bypass the cache.

WooCommerce’s default logger threshold suppresses debug and info levels in production. The engine’s debug_log() emits at debug level, so a fresh install sees nothing in the log even when the engine runs. Set WC_LOG_THRESHOLD=debug (environment variable or wp-config.php constant) to see these entries, or use the woocommerce_logger_log_message filter to capture them.


Work down this list in order.

In wp-config.php:

define( 'WP_DEBUG', true );
define( 'WP_DEBUG_LOG', true );
define( 'WP_DEBUG_DISPLAY', false );

PHP notices and warnings now land in wp-content/debug.log.

Dino Discounts writes to WooCommerce’s logger with source dino-discounts. The log handler is configured in WooCommerce → Settings → Advanced → Features → Logger.

  • Default handler (File). Logs land at wp-content/uploads/wc-logs/dino-discounts-YYYY-MM-DD-<hash>.log. Read them via WooCommerce → Status → Logs or tail them directly:

    Terminal window
    tail -f wp-content/uploads/wc-logs/dino-discounts-*.log
  • Database handler (WC 8.x default option). If WooCommerce is configured to use the database log handler, entries live in the wp_wc_admin_note_actions / wp_woocommerce_log tables — read them via WooCommerce → Status → Logs, not the filesystem. tail -f will find nothing on those sites.

The engine’s debug_log() emits at debug level on every non-trivial branch of the rule evaluator (see RulesEngine::debug_log() and TargetingEvaluator::debug_log()). Visibility depends on the WooCommerce logger threshold, not WP_DEBUG — by default WC suppresses debug and info levels. Set the environment variable WC_LOG_THRESHOLD=debug (or filter woocommerce_logger_log_message) to see them.

Step 3. Dump engine state for a specific cart

Section titled “Step 3. Dump engine state for a specific cart”

The plugin exposes a REST endpoint that runs the engine against a hypothetical cart. Call it with a manage_woocommerce user’s nonce/auth:

Terminal window
curl -X POST 'https://yoursite.com/wp-json/dino-discounts/v1/preview' \
-H 'Content-Type: application/json' \
-H 'X-WP-Nonce: <nonce>' \
-d '{
"cart": [ { "product_id": 42, "quantity": 2 } ],
"rules": [ /* paste active rules here */ ],
"cart_total": 120.00,
"currency": "EUR",
"country": "GR"
}'

The response includes the matched discounts and the per-rule trace when trace mode is enabled (RulesEngine::enable_trace_mode()). The admin’s built-in Cart Previewer calls this same endpoint — see includes/API/PreviewController.php for the full parameter list.

  • Cart previewer (admin): Discounts → Cart Previewer. Paste or build a cart, toggle scenarios, see which rules fire and why.
  • Live storefront: sign out, open an incognito window, add the same products. If the admin previewer shows a discount applying but the storefront does not, the divergence is almost always one of: session state (§2.3), caching (§2.1 or §2.4), or a theme/builder override hiding the rendered line (§1.3).

In rough order of likelihood:

  1. Rule priority. Rules with lower priority numbers evaluate first. If a higher-priority rule stops the cascade, lower-priority rules never fire. Check the rules list — the priority column is sortable.
  2. Stacking mode. Global setting stacking_mode: all_additive (default), first_match, best_only, or disable_all. If first_match, only the first matched rule applies. If disable_all, nothing applies — often set accidentally during a trial.
  3. WooCommerce coupon kill-switch. Global setting wc_coupon_kill_switch: off (default) or disable_dino_when_wc_coupon_applied. When enabled and a shopper enters a real (non-dino_dd_*) coupon, all Dino rules back off. Easily mistaken for a bug.
  4. Zone mismatch. Rules scoped to a zone compare the shopper’s resolved country against the zone. The country source is set globally (country_source: billing_shipping_store, shipping_billing_store, shipping, billing). If a shopper has no address yet, the default source falls back to the WooCommerce store country.
  5. Product meta drift. Targeting trees reference categories, tags, and products by ID. A category deletion or product trashing leaves the rule with a dangling reference that silently never matches. Re-open the rule in the wizard — missing references show as warnings.
  6. Currency / tax interaction. Fixed-amount rules are in one currency. A multi-currency plugin showing the same rule to a shopper in a different currency will apply the fixed amount as if it were in their currency — scope fixed-amount rules with a zone to prevent this. Tax-inclusive vs tax-exclusive cart totals also matter: Dino evaluates using WooCommerce’s subtotal, which honours the store’s Prices entered with tax setting.

1. Why are my discounts not applying on the cart page?

Section titled “1. Why are my discounts not applying on the cart page?”

Most common cause: the shopper’s cart does not match the rule’s targeting. Open Discounts → Cart Previewer, build a cart that matches the rule, and watch which condition fails. Second most common cause: a full-page cache plugin is serving a stale cart (see §1.1 and §2.4).

2. Why do I see dino_dd_<rule-id> as a coupon code instead of my campaign name?

Section titled “2. Why do I see dino_dd_<rule-id> as a coupon code instead of my campaign name?”

This is the virtual coupon’s internal ID. It should be replaced with your rule’s Cart Label on the cart/checkout page. If you see the raw dino_dd_ code, your theme is either overriding cart/cart.php without using wc_cart_totals_coupon_html(), or a page builder is rendering the cart (see §1.3). Set the Cart Label on the rule itself in the wizard.

3. I installed another discount plugin and now nothing works. What gives?

Section titled “3. I installed another discount plugin and now nothing works. What gives?”

Dino Discounts is not compatible with other discount/pricing engines running concurrently. The admin shows a notice when it detects one of: WooCommerce Dynamic Pricing & Discounts, Discount Rules for WooCommerce, YITH Dynamic Pricing and Discounts, or Smart Coupons for WooCommerce. Deactivate one — see §1.4.

4. How do I clear the cache so my rule change takes effect?

Section titled “4. How do I clear the cache so my rule change takes effect?”

Saving any rule or setting already flushes the plugin’s caches (dino_discounts object-cache group + dino_eval_* transients + WC cart fragment cache). If a full-page cache plugin is active, you also need to purge its cache for /cart/ and /checkout/. Run wp cache flush to clear the WordPress object cache when in doubt. See §2.

5. Why can I enter my campaign code but nothing happens?

Section titled “5. Why can I enter my campaign code but nothing happens?”

Three possibilities, in order:

  1. The campaign’s targeting tree has extra conditions (country, user role, minimum spend) that your cart does not satisfy. Open the rule in the wizard.
  2. The global wc_coupon_kill_switch setting is disable_dino_when_wc_coupon_applied and the shopper has already entered another coupon, so Dino rules back off.
  3. The campaign was archived, or its coupon code was deleted. Check Discounts → Coupon Codes.

6. Does Dino Discounts work with the block-based Cart and Checkout?

Section titled “6. Does Dino Discounts work with the block-based Cart and Checkout?”

Yes, via the WooCommerce Store API extension (StoreApiExtension). You need WooCommerce 7.6 or newer. Diagnosis when block cart and shortcode cart disagree:

  1. Open the block cart’s Store API response directly: curl -H 'X-WC-Store-API-Nonce: <nonce>' https://yoursite.com/wp-json/wc/store/v1/cart. The extensions.dino-discounts object shows what the extension returned.
  2. If the extensions.dino-discounts payload matches the admin Cart Previewer but the block UI still shows the wrong chip, the issue is on the client (theme override of the @woocommerce/blocks-registry filter, or a JS error — check the browser console).
  3. If the Store API response itself is wrong, enable debug logging (§3 Step 2) and look for dino-discounts entries with the request’s session.

7. Do discounts stack with WooCommerce coupons I created manually?

Section titled “7. Do discounts stack with WooCommerce coupons I created manually?”

By default, yes (wc_coupon_kill_switch = off). Change the global setting to disable_dino_when_wc_coupon_applied if you want Dino rules to back off whenever any WooCommerce coupon is applied. The global setting lives under Discounts → Settings.

8. How do I stop two of my rules from both firing on the same cart?

Section titled “8. How do I stop two of my rules from both firing on the same cart?”

Set the Stacking Mode in global settings:

  • all_additive — every matched rule applies (the default).
  • first_match — only the first rule (by priority) applies.
  • best_only — only the single highest-value discount applies.
  • disable_all — no rules apply (emergency kill switch).

9. Why does my tiered “spend & save” rule not show the next-tier nudge?

Section titled “9. Why does my tiered “spend & save” rule not show the next-tier nudge?”

Work through these causes in order:

  1. The cart is already in the top tier — by design, the nudge is about the next tier.
  2. The rule is not Active, or the Enabled toggle is off.
  3. The cart does not satisfy the rule’s targeting at all (country / user role / product scope). Check the Cart Previewer — if the rule doesn’t even match, the nudge won’t render.
  4. Another rule is a stronger nudge candidate and the dino_discounts_nudge_rules filter selected it instead.
  5. The storefront page (cart or mini-cart) is full-page cached — edit the rule and reload.

Override the rendered HTML via the dino_discounts_nudge_html filter (see docs/hooks.md).

10. I enabled a rule but the admin shows “0 rules applied” in the cart previewer. Why?

Section titled “10. I enabled a rule but the admin shows “0 rules applied” in the cart previewer. Why?”

The rule may be set to Draft — only Active rules evaluate. Also check the rule’s Enabled toggle at the top of the wizard. If both are correct, the targeting tree has a condition the previewer’s cart does not satisfy — the previewer highlights the failing node.

11. How do I add free shipping when the cart hits a threshold?

Section titled “11. How do I add free shipping when the cart hits a threshold?”

Use the Free Shipping recipe. Set the spend threshold (set to 0 for “free shipping on every order”). Scope to a Zone to restrict to a specific shipping region, otherwise the rule applies to every country. See the recipe tip text in the wizard and docs/template-overrides.md for rendering overrides.

12. Why is my fixed-amount discount showing the wrong currency?

Section titled “12. Why is my fixed-amount discount showing the wrong currency?”

Fixed-amount discounts are expressed in one currency. Shoppers in other currencies see that same numeric amount as if it were in their currency (e.g. €5 off becomes $5 off for a USD shopper). Scope fixed-amount rules with a Zone that filters by currency, or use a percentage discount instead. Multi-currency plugins should hook dino_discounts_enabled_currencies (see docs/hooks.md).

13. How do I give a discount only to logged-in customers with a specific WordPress role?

Section titled “13. How do I give a discount only to logged-in customers with a specific WordPress role?”

Use the VIP / Member recipe (from Recipes → Targeted → VIP / Member), or add a user_role condition to any rule’s targeting tree. For multi-tier wholesale (Retailer / Distributor / Reseller), create one rule per role — each targets a different role. Logged-out shoppers never match a user_role condition, so the rule is effectively gated to authenticated customers.

14. Can I run a discount only for first-time customers?

Section titled “14. Can I run a discount only for first-time customers?”

Yes. Use the First-order discount recipe, or add a first_order targeting node manually. The rule fires only on orders where the customer has no previous completed orders.

15. Do rules expire automatically at a date?

Section titled “15. Do rules expire automatically at a date?”

Yes. Every rule supports optional Start date and End date fields. After the end date passes, the rule stops firing — no need to remember to toggle it off. See the Seasonal / scheduled sale recipe.

16. Why does the customer see “Coupon code already applied” when I haven’t used that code?

Section titled “16. Why does the customer see “Coupon code already applied” when I haven’t used that code?”

A native WooCommerce coupon (shop_coupon post) exists with the same code string as one of your Dino campaigns. The plugin’s CouponCodeInterceptor detects this and shows an admin notice naming the conflicting code.

To fix: go to WooCommerce → Marketing → Coupons, find the conflicting code, and either delete that native coupon or rename it. Alternatively, rename the Dino campaign at Discounts → Coupon Codes. After renaming, re-save any rule that referenced the old code so the targeting tree picks up the new one.

17. How do I debug a rule that passes in the previewer but fails on the storefront?

Section titled “17. How do I debug a rule that passes in the previewer but fails on the storefront?”

Work through §3 Debugging playbook step by step. Three common causes, in order of likelihood:

  1. The carts are different. The previewer lets you pick an arbitrary country, user, and cart contents. The live shopper’s resolved country (from billing/shipping address per country_source), user role, and cart contents may all differ. Compare field-by-field.
  2. A full-page or edge cache is serving stale HTML. See §2.4.
  3. A persistent object cache (Redis/Memcached) is holding a 60-second stale evaluation transient. See §2.2.

18. Does this plugin work with WooCommerce Subscriptions?

Section titled “18. Does this plugin work with WooCommerce Subscriptions?”

Partially. Dino evaluates the initial cart (the one containing the subscription product at signup) the same way as any other cart, and the discount applies to the signup payment. Recurring renewal orders are not currently re-evaluated against Dino rules — there is no integration with the wcs_renewal_order_created hook. The first-payment discount is frozen into the subscription by WooCommerce Subscriptions itself and repeats on each renewal, but any rule changes you make afterwards do not affect existing subscriptions.

Workarounds:

  • For a recurring percentage discount, set the rule up before the customer signs up and leave it active — WC Subscriptions will reuse the first-payment discount on renewals.
  • For a per-renewal re-evaluation, hook wcs_renewal_order_created and call dino_discounts_cache_flush yourself, then rely on your own custom targeting. This is not a supported path; test thoroughly.

Yes. Every rule save writes a snapshot to the rule history (see SnapshotsController). Go to Discounts → Rules → History to restore a previous snapshot. For a full export, wp option get dino_discounts_active_rules --format=json > rules.json dumps the active rule set; wp option update dino_discounts_active_rules --format=json < rules.json restores it.

20. I deactivated the plugin but I still see dino_dd_* codes in old orders. Is that a problem?

Section titled “20. I deactivated the plugin but I still see dino_dd_* codes in old orders. Is that a problem?”

No. Those are historical references to the coupon that applied at the time of the order. The codes are inert when the plugin is inactive — WooCommerce simply cannot resolve them, and they display as the raw code. Reactivating the plugin restores the original label.


  • Shopper-facing string style guide — copy conventions for nudges, tier tables, and applied-coupon labels.
  • Template override reference — how to customise storefront markup via theme templates.
  • Hook reference — every filter and action available for developer customisation.
  • Coupon ↔ Rule linking — how campaigns and rules connect via the targeting tree.
  • Error UX inventory — every user-visible failure path in the plugin, classified.

Document last updated: 2026-04-18. If you add a new known-bad interaction or a new cache layer, add an entry here so the next support request lands in a concrete fix.