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 five conflicting discount plugins and seven full-page caching plugins (see includes/ConflictDetector.php). When one is active an admin notice is shown (for caching plugins, only when cart and checkout are not already excluded from the page cache). 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. On every publish Dino already purges the affected product pages through clean_post_cache(), which WP Rocket honours. Hook your own purge on dino_discounts_cache_flush if you want a full-site purge on every publish instead:
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: first check Message Settings → Discount label → Show the discount line on. If a surface is unticked there, the discount still applies but no line is shown, by design. Otherwise, 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 five 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
  • Advanced 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 five packages above.

Fix: run one discount engine at a time. Either deactivate the other plugin, or keep each engine on products the other does not touch. Settings → Other discount plugins controls what Dino does while another engine or a native WooCommerce coupon is present: Warn of conflicts (default), Yield — step aside to avoid double discounts, or Take precedence — Dino applies first. 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.

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 a discount’s item scope references the default-language ID (e.g. category_id = 42), translated versions (ID 156, 237…) do not match. The plugin does not translate those IDs 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.

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: scope each fixed-amount discount to one currency with the Applies in currency… condition in its Where does it apply? section. That picker lists the currencies enabled on your store; if your currency plugin’s currencies are missing, add them with the dino_discounts_enabled_currencies filter (see docs/hooks.md). 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.

1.8 Theme or page-builder CSS that makes the cart/checkout notice overlap page content

Section titled “1.8 Theme or page-builder CSS that makes the cart/checkout notice overlap page content”
  • Symptom: the merchant cart-notice (the ”… applied” bar — e.g. “Valentine Saver 15% Applied”) renders on cart or checkout with its bottom edge sitting over the content below it — the next field/heading is partly hidden behind the notice box. Most often reported on block checkout (“the notice is over the top of the text”).
  • Diagnosis: the notice is a standard WooCommerce notice (wc_add_notice( …, 'notice' )) that WooCommerce renders as an ordinary banner (block: .wc-block-components-notice-banner; classic: .woocommerce-info). On a clean install it sits in normal flow with a positive margin and never overlaps. The overlap is produced by the active theme or page-builder overriding the WooCommerce notice banner (or its wrapper) with a negative/zero bottom margin, a fixed height + clipped overflow, or absolute/positioned placement. Confirm by switching to a default theme (Twenty Twenty-Five or Storefront) temporarily — if the overlap disappears, it is theme/builder CSS. In DevTools, inspect the notice banner’s Computed margin-bottom, height, overflow, and position.
  • Fix (built in): Dino ships a defensive CSS guard scoped to its own notices (the data-dino-cart-notice marker) that pins the notice back into normal flow — positive bottom margin, auto height, visible overflow, in-flow positioning — so it resists the common overrides automatically. No action needed in most cases.
  • Fix (when the guard is not enough): the built-in guard cannot override a theme rule that uses !important at equal-or-higher specificity, or one that negatively-margins the element after the notice rather than the notice itself. In those cases, remove or correct the offending theme/builder rule (target the notice banner / its wrapper), or add a small override in your child theme, e.g. .wc-block-components-notice-banner{ margin-bottom:16px !important; position:relative !important; overflow:visible !important; }. Full investigation + the surface matrix that was tested: docs/diagnostics/notice-overlap-investigation-2026-06-28.md.

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; dino_discounts_cache_flush also runs wp_cache_flush()
Engine evaluation transientsdino_eval_* (cart-fingerprint keyed; the fingerprint includes the rules version)60 sCartCoupons::flush_eval_cache() on dino_discounts_cache_flush
Zone map transientdino_dd_zone_cache24 hDeleted on shipping-zone save or delete, settings save
Storefront index transientdino_dd_storefront_index1 dayStorefrontIndex::invalidate() on product save, product category/tag edits, rule/settings 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_rules → RulesEngine::invalidate_caches(), and a publish from the admin also fires dino_discounts_cache_flush, which flushes the whole object cache. 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. The fingerprint includes the rules version (dino_discounts_rules_version), which goes up on every rule or settings save through the option API, so a publish never serves an old result. A stale result is only possible after a direct database write to dino_discounts_active_rules that bypasses the option API — fire do_action( 'dino_discounts_cache_flush' ) after such writes.
  • 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.
  • Clear: saving any rule or setting fires dino_discounts_cache_flush, which wipes all dino_eval_* transients via CartCoupons::flush_eval_cache(). That method runs a direct DELETE FROM wp_options (see VirtualCouponFactory::flush_eval_cache()), so on persistent-object-cache sites it clears nothing itself; the same action’s wp_cache_flush() clears them there. By hand: wp eval 'do_action( "dino_discounts_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 declares HPOS compatibility. Order lookups use wc_get_orders(), which transparently reads from the custom order tables when HPOS is enabled and wp_posts when it isn’t (see CustomerOrderCounter.php, CustomerStatsResolver.php, AnalyticsTracker.php, AnalyticsController.php, Privacy.php). No configuration is required.

If you see order-based conditions (e.g. First-time customers, Customers with N+ past orders) 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 and verify the order-data sync has finished. Behaviour during partial sync is not supported. Customer order counts are cached; if they look wrong after a migration or a third-party order import, run wp dino-discounts rebuild-order-counters to clear the cache (counts rebuild on the next cart evaluation).

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:

  • Each subsite’s rules and Dino cache keys are independent. One exception: a publish fires dino_discounts_cache_flush, whose wp_cache_flush() clears the shared object cache for the whole network.
  • 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 clears the object cache for the whole network, even with --url. To target one subsite, use wp cache delete rules dino_discounts --url=<subsite> (and the same for settings).
  • 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 the order they appear on the Discounts tab (drag reorder is the source of truth — there is no separate priority field). If you have 500+ rules:

  • Tick Enable Performance Tracking on the Dev Tools tab (the performance_tracking setting). It writes a hidden HTML comment with timing and cache data to the page footer (View Page Source) and fills the performance log on that tab.
  • 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.

The engine’s debug_log() only writes when WP_DEBUG is true or the dino_discounts_debug option is truthy, and it writes at debug level. WooCommerce’s logger threshold can also drop debug entries. Set the threshold to debug (the WC_LOG_THRESHOLD constant in wp-config.php, or the level setting under WooCommerce → Status → Logs) to see them, 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 under WooCommerce → Status → Logs (Settings).

  • 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. If WooCommerce is configured to use the database log handler, entries live in the wp_woocommerce_log table — 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 non-trivial branches of the rule evaluator (see RulesEngine::debug_log()). It only writes when WP_DEBUG is on (Step 1) or the dino_discounts_debug option is truthy, and the WooCommerce logger threshold must also let debug through (see Logger threshold).

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, "price": 60.00 } ],
"rules": [ /* paste active rules here */ ],
"cart_total": 120.00,
"currency": "EUR",
"country": "GR",
"debug": true
}'

The response’s discounts array lists what applied. With "debug": true it also carries a per-rule trace and a global_settings_trace. The admin’s Cart Preview panel calls this same endpoint — see includes/API/PreviewController.php for the full parameter list.

  • Cart Preview (admin): the panel on the right of the Discounts tab and the discount editor. Build a cart, pick a customer scenario, and see which discounts apply and why each other one didn’t qualify. It previews your unsaved edits; the storefront runs only what you have published.
  • 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 order and exclusivity. There is no priority number. Order on the Discounts tab matters when overlapping discounts are set to First (drag rows to change it). A discount with Exclusive — applies on its own ticked (Controls section) blocks every other discount when it applies.
  2. Stacking mode. The When discounts overlap: dropdown at the top of the Discounts tab (stacking_mode): Apply All (all_additive, default), Best Only (best_only), First (first_match), or Pause All (disable_all). With First, only the first qualifying discount applies. With Pause All, nothing applies — often set accidentally during a trial.
  3. WooCommerce coupon kill-switch. Settings → WooCommerce coupon interaction (wc_coupon_kill_switch): Apply Dino discounts alongside (default) or Suppress all Dino discounts when a coupon is applied. With Suppress, a shopper entering a real (non-dino_dd_*) coupon silences every Dino discount. Easily mistaken for a bug.
  4. Zone mismatch. Discounts scoped to a zone compare the shopper’s resolved country against the zone. The address used is set by Settings → Country matching uses (country_source): Shipping → Billing → Store (default), Billing → Shipping → Store, Shipping address only, or Billing address only. The default resolves shipping first, then billing; if a shopper has no address yet it falls back to the WooCommerce store country.
  5. Product meta drift. Discounts reference categories, tags, and products by ID. A category deletion or product trashing leaves the discount with a dangling reference that silently never matches. Open the discount and check that the products and categories in its item pickers still exist.
  6. Currency / tax interaction. Fixed-amount discounts are in one currency. A multi-currency plugin showing the same discount to a shopper in a different currency will apply the fixed amount as if it were in their currency — scope fixed-amount discounts with the Applies in currency… condition 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 discount’s conditions. Open the Cart Preview panel on the Discounts tab, build a cart that should qualify, and read the reason shown for the discount that didn’t apply. 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 coupon name?

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

This is the virtual coupon’s internal ID. It should be replaced with your discount’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 in the discount’s Messaging section.

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, Smart Coupons for WooCommerce, or Advanced 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). It also purges affected product pages in WP Rocket, WP Super Cache, LiteSpeed Cache, W3 Total Cache and WP-Optimize. Other full-page caches (and CDNs) need purging yourself, at least 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 coupon code but nothing happens?

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

Three possibilities, in order:

  1. The discount the code is linked to has other conditions (zone, user role, minimum spend) that your cart does not satisfy. Open the discount and check its Who, Where and What triggers it? sections.
  2. Settings → WooCommerce coupon interaction is set to Suppress all Dino discounts when a coupon is applied and the shopper has already entered another coupon, so Dino discounts back off.
  3. The coupon was archived, or its code was deleted. Check the Coupons tab.

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). The plugin requires WooCommerce 8.8 or newer. Diagnosis when block cart and shortcode cart disagree:

  1. Look at the block cart’s Store API response: in the shopper’s browser, open DevTools → Network and find the request to /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 Preview but the block UI still shows the wrong chip, the issue is on the client (a theme or plugin overriding the block checkout filters registered with registerCheckoutFilters, 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 (Apply Dino discounts alongside (default), wc_coupon_kill_switch = off). Choose Suppress all Dino discounts when a coupon is applied if you want every Dino discount to back off whenever a WooCommerce coupon is applied. The setting is WooCommerce coupon interaction on the Settings tab.

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 When discounts overlap: at the top of the Discounts tab:

  • Apply All (all_additive) — every matched discount applies (the default).
  • First (first_match) — only the first qualifying discount in the list applies (drag rows to control which one wins).
  • Best Only (best_only) — only the single highest-value discount applies.
  • Pause All (disable_all) — no discounts apply (emergency kill switch).

To keep one discount from combining with any other, tick Exclusive — applies on its own in its Controls section instead.

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 discount is not published, or its Enabled toggle is off.
  3. Nudges are off for that surface: check Message Settings → Spend-more nudge (Enable spend-more nudges, and the slot for the mini-cart, cart page or checkout is not Not shown), and that the discount’s Include in spend-more nudges box in its Messaging section is ticked.
  4. The cart does not satisfy the discount’s other conditions at all (zone / user role / product scope). Check the Cart Preview — if the discount doesn’t even match, the nudge won’t render.
  5. Only one nudge renders per surface, and another discount was the better candidate. The dino_discounts_nudge_rules filter can change the candidate list.
  6. The storefront page (cart or mini-cart) is full-page cached — purge it and reload.

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

10. I enabled a rule but Cart Preview shows “0 applied”. Why?

Section titled “10. I enabled a rule but Cart Preview shows “0 applied”. Why?”

Check the discount’s Enabled toggle in its Controls section, and that the discount is complete (Cart Preview lists unfinished discounts as incomplete). If both are correct, the discount has a condition the preview cart does not satisfy — Cart Preview shows the reason for each discount that didn’t qualify. Cart Preview runs your unsaved edits; the storefront only runs what you have published.

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 template (Create a discount on the Discounts tab). Set its spend threshold (set to 0 for “free shipping on every order”). Add Customers in specific zones in the Where does it apply? section to restrict it to a shipping region, otherwise the discount applies to every country.

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 discounts with the Applies in currency… condition in Where does it apply?, or use a percentage discount instead. If your currencies are missing from that picker, add them with the dino_discounts_enabled_currencies filter (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 / role template (Create a discount → Customer segments), or tick Customers in specific user roles in any discount’s Who is this for? section. For multi-tier wholesale (Retailer / Distributor / Reseller), create one discount per role — each targets a different role. Logged-out shoppers have no role, so a role condition effectively limits the discount to logged-in 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 Welcome template, or tick First-time customers in the discount’s Who is this for? section. The discount applies only when the customer has not yet placed a paid order (failed and pending orders don’t count).

15. Do rules expire automatically at a date?

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

Yes. Tick Within a date window in the discount’s When does it apply? section and set From and Until (times optional, in your site timezone). After the end passes, the discount stops firing and shows as Ended — no need to remember to toggle it off. The Flash sale template sets this up for you.

16. Why does my Dino coupon code apply a different WooCommerce coupon, or not apply at all?

Section titled “16. Why does my Dino coupon code apply a different WooCommerce coupon, or not apply at all?”

A native WooCommerce coupon (shop_coupon post) exists with the same code string as one of your Dino coupons. The plugin’s CouponCodeInterceptor detects this, skips that code so the two don’t conflict, and shows an admin notice naming it.

To fix: go to WooCommerce → Marketing → Coupons, find the conflicting code, and either delete that native coupon or rename it. Alternatively, rename the Dino coupon on the Coupons tab. Discounts link to coupons by ID, so no discount needs re-saving after a rename.

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 matching uses), user role, and cart contents may all differ. Compare field-by-field.
  2. The change isn’t published. Cart Preview runs your unsaved edits; the storefront runs only published discounts.
  3. A full-page or edge cache is serving stale HTML. See §2.4.

18. Does this plugin work with WooCommerce Subscriptions?

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

Partially. Dino discounts apply to the initial order only. Dino evaluates the signup cart the same way as any other cart, and the discount applies to the signup payment. Renewals are charged at full price: Dino stays out of the recurring totals WooCommerce Subscriptions builds, and on wcs_renewal_order_created it strips any Dino attribution copied onto a renewal order so the renewal is not counted as a fresh redemption. The discount editor says so on discounts that can apply to subscription products.

For a discount that repeats on every renewal, use a WooCommerce Subscriptions recurring coupon instead.

Yes. Every publish writes a snapshot of your discounts and promotions (see SnapshotsController). Open the History tab to preview or restore one. For a file backup, use Download Backup (under Export Discounts & Zones) on the same tab, and Import Backup… (under Import & Merge) to load one: matching IDs are overwritten, new ones are added, and anything not in the file is kept. Coupon codes are tracked separately and are not in snapshots or backup files. From WP-CLI, wp option get dino_discounts_active_rules --format=json > rules.json dumps the published discount set.

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 coupons and rules connect.
  • Error UX inventory — every user-visible failure path in the plugin, classified.

Document last updated: 2026-10-15. 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.

WooCommerce is a trademark of Automattic, Inc. WordPress is a trademark of the WordPress Foundation. Dino Discounts is not affiliated with or endorsed by Automattic or the WordPress Foundation.

Contact · Report a bug