TrustLens – Fraud Prevention & Chargeback Defense for WooCommerce

Changelog

1.2.8

  • New: Video walkthrough — a short explainer showing how TrustLens turns real shopping behavior into a 0–100 trust score and surfaces returns, coupon, linked-account and card-testing abuse, now on the plugin page.
  • Update: Refreshed the plugin banner artwork.

1.2.7

  • New: Dispute deadline worklist on Chargeback Monitor — every open dispute with its response deadline and a live countdown, plus a due-soon count on the dashboard, so a chargeback response window never slips past you.
  • New: Ten new automation conditions — write rules on full and partial refunds, coupons used, first-order coupons, disputes won and lost, order edits, reviews-before-refund, and whether a customer is allowlisted or flagged.
  • New: Skip trusted customers — rules can now exclude allowlisted buyers with an is_allowlisted condition, so blanket rules don’t catch the people you’ve already vouched for.
  • New: Flagged for review is now a real customer status — the Flag action shows a badge, adds a filter on the Customers screen, and clears in one click, instead of leaving a note nobody could find.
  • Improvement: Automation Add Tag now writes a real customer tag — visible on the customer page and removable in bulk — instead of a hidden note.
  • Fix: Rules that quietly never fired now fire — first-order rules at checkout, and card-testing blocks against brand-new attacker emails, now work as configured.
  • Fix: Require Verification now actually holds the order for review (and flags it) instead of doing nothing.
  • Fix: Webhook activity is counted honestly — each delivery counts once (queued, then delivered or failed) instead of logging both a success and a failure for the same call, and queued deliveries are properly cancelled when the plugin is deactivated.
  • Fix: Allowlisting a customer now clears any review flag, so a trusted customer can’t stay flagged.
  • Fix: The rule builder no longer rejects valid rules — mixing 1 and true on a yes/no condition, or upper- and lower-case country codes, is understood correctly.
  • Fix: Choosing to remove all data on uninstall now also drops the disputes table, and automation log cleanup keeps running even after a Pro license lapses.

1.2.6

  • New: Quick Start setup card on the dashboard — three guided choices (block risky customers, email alerts, import past orders) get a new store protected without hunting through settings.
  • New: Block reason picker — when you block a customer you can record why, from a preset or your own note; it’s saved to the customer’s notes and history so you keep an audit trail.
  • New: Plain-language score summary on each customer — see at a glance what’s weighing a score down and what’s in their favour, instead of decoding raw signal bars.
  • New: Segment glossary and inline help — a built-in legend for VIP through Critical with their score bands, plus ? tips that explain terms like “fingerprint” right where you need them.
  • New: Recommended next actions on the dashboard and Card-Testing Defense — each screen now tells you what to do, not just what’s happening.
  • Improvement: Customer actions no longer reload the whole page — block and unblock update in place with a confirmation toast, and allowlist or recalculate keep your scroll position so you never lose your place.
  • Improvement: Panic Freeze remembers your chosen duration instead of resetting to 15 minutes each time, and the misleading “1 hour” option (which the server capped at 30) has been removed.
  • Improvement: Card-Testing Defense is now discoverable as the 8th detection module from the Modules tab in Settings.
  • Improvement: Signal explanations on the customer page are now keyboard-accessible, not hover-only.
  • Fix: The Automation tab now shows consistently in the in-app header, matching the WordPress admin menu.

1.2.5

Dashboard visual refresh — every chart now reads as one product.

Changed

  • All dashboard charts repainted in the TrustLens emerald palette to match the website and the rest of the admin — Trust Score Trends, Segment Distribution, Refund Activity, Hourly Activity, Category Returns, Monthly Protection, the Chargeback Monitor 12-month trend, and the Card-Testing Defense Attack History timeline. Replaces the previous mix of Bootstrap red/orange/green and a stray purple that had drifted in over earlier releases.
  • Chart tooltips switched from black bubbles to light cards with subtle slate borders and softer typography so they no longer overpower the data.
  • Line charts now use thinner 2px strokes with smoother curves and gradient fills that fade fully to transparent; bar charts have rounded 6px caps.
  • Snappier 600ms entry animation (was the Chart.js 1s default).
  • Hourly Activity chart now uses an emerald intensity gradient (low → slate, high → emerald) instead of a purple-to-violet ramp, so quiet hours read as quiet and busy hours pop.
  • New shared chart-theme.js module — every chart in the plugin pulls from one palette, so future charts pick up the same identity automatically.

Fixed

  • Segment doughnut’s “X customers” total now sits on the actual ring center. Previously, the bottom-positioned legend pushed the doughnut up but the label kept dividing the full canvas height in half, landing the text below the ring.
  • Segment-doughnut legend markers (VIP / Trusted / Normal / Caution / Risk / Critical) now render as perfect circles instead of slightly-oval boxes.
  • Empty charts now show a quiet “No data yet” message instead of a blank canvas, so a fresh install doesn’t look broken before any orders have been scored.

1.2.4

Card-Testing Defense — false-positive fix + WordPress 7.0 compatibility.

Fixed

  • Card-Testing Defense self-blocking legitimate shoppers on Blocks checkout. The velocity counter was incrementing on every Store API cart write — add-item, update-item, apply-coupon, select-shipping-rate, update-customer — not only on actual payment submissions. A normal shopper rapidly adjusting quantities or applying a coupon in a Blocks cart could cross the 10-submissions-per-60s threshold and target their own fingerprint for a 90-second lockdown, locking themselves out of checkout. Root cause: the gate context had no way to distinguish cart-write intent from checkout-submit intent, so the velocity recorder treated everything as a payment attempt. The gate now carries an explicit intent field populated from the route; the velocity recorder only counts true checkout submissions, and the card-testing module short-circuits on cart writes (which also removes 2 unnecessary COUNT(*) queries from every cart click). The email blocklist continues to fire on cart writes per the 1.3.0 design — known-bad customers still cannot add items to cart. No configuration change required; existing thresholds keep working but are now fed only real payment attempts.

Compatibility

  • Tested up to WordPress 7.0 (“Armstrong”). Full audit completed: PHPMailer 7.0.2 update, Backbone 1.6.1, CodeMirror v5 / Espree, Interactivity API state.navigation deprecation, Block API version, Block Hooks REST move, REST permission_callback coverage, early-init translation, and PHP-8 deprecation surfaces. No plugin changes were needed for 7.0.

1.2.3

Security and reliability hardening. Closes several issues surfaced during a pre-release audit.

Fixed

  • Card-Testing Defense — VIP bypass too permissive. Previously, any customer with at least one completed order was permanently exempt from card-testing velocity blocks — meaning a fraud actor who completed a single order gained immunity from then on. The threshold now matches the plugin-wide trustlens_min_orders setting (default 3 orders) AND customers in risk or critical segments no longer bypass card-testing defense regardless of order count.
  • Chargeback meta not HPOS-compatible. Manual chargeback writes used update_post_meta() / get_post_meta() directly, which silently target the wrong table on stores with WooCommerce High-Performance Order Storage enabled. Switched to WC_Order::update_meta_data() / WC_Order::get_meta() so the chargeback indicator and Record Manual Chargeback form work correctly on HPOS stores.
  • IP spoofing via forwarding headers. HTTP_X_FORWARDED_FOR (request gate) and HTTP_CLIENT_IP + HTTP_X_FORWARDED_FOR (payment-method controls) were trusted unconditionally, letting an attacker send X-Forwarded-For: 1.2.3.4 to rotate their apparent IP and defeat per-IP velocity rules. Both code paths now default to REMOTE_ADDR. Sites legitimately behind a trusted reverse proxy (Cloudflare, load balancer, Sucuri) can opt in to X-Forwarded-For via the new trustlens/trust_proxy_headers filter — the last entry in the header is used (the IP the closest trusted hop observed).
  • Webhook signing secret exposed in DOM. The “Test” button on the Pro Webhooks settings page rendered the signing secret as a data-secret HTML attribute, making it readable by any browser extension or XSS payload running in the admin panel. The secret is no longer rendered to the page; the AJAX handler now looks up the endpoint server-side from the stored config.
  • Webhook async dispatch could pile up duplicates. Automation rule webhooks used as_enqueue_async_action() without dedup, so a rapid burst of identical triggers (e.g. score_updated firing several times during a batch refund) queued multiple deliveries for the same rule+customer. Now dedup’d via wstl_ensure_single_action; retries from inside the dispatch handler still carry a distinct retry counter and bypass dedup so failed deliveries still get their 60s / 120s / 240s attempts.
  • Score-update queue race. wstl_queue_score_update() used a read-then-write pattern that could double-schedule the score recalculation under concurrent events for the same customer. Replaced with wstl_ensure_single_action, which uses unschedule-then-schedule semantics and is race-free.
  • Chargeback record double-increment under concurrency. The manual chargeback AJAX path ran two separate UPDATE statements (one for total_disputes, one for the outcome counter). Two concurrent calls for the same customer could leave disputes_lost exceeding total_disputes. Now done as a single atomic UPDATE when the outcome is known at record time.
  • Shipping anomaly re-entrancy. The trustlens/shipping_anomaly action fired (and address_anomaly_detected was logged) from inside get_signal(), which runs on every score recalculation. That could spawn re-entrant Action Scheduler jobs through automation rules. Both events now fire once from handle_order_completed, so each detection produces exactly one event per order completion.
  • Guest-order automation actions silently dropped. Customer-level actions (send email, block customer, tag, etc.) on order-bound rules silently returned when the order was a guest checkout. Now a ‘skipped’ row is written to the rule log with the reason “Guest order: no customer email hash” — the inspector can finally answer “why didn’t my rule fire?”.
  • Lockdown targets transient race. Card-Testing Defense stored every targeted device fingerprint in one shared transient map; concurrent attacks from different devices could clobber each other on write. Switched to one transient per fingerprint so concurrent target writes never conflict. Admin listing and “any-target-active” check use indexed wp_options LIKE queries.
  • Automation is_first_order matched 0-order customers. Condition now requires total_orders === 1 (exact), so rules don’t fire against brand-new customer records that exist before any order has been counted.
  • Chargeback signal ignored min-orders threshold. A one-time buyer who filed a legitimate dispute could trigger the -30 chargeback penalty before any other signals existed. The chargeback module now honors trustlens_min_orders like the returns, coupons, and shipping modules.
  • Dispute Report didn’t validate the hash format. $_GET[‘hash’] is now checked against wstl_is_email_hash() before being passed to the lookup, matching the rest of the codebase.
  • Webhook log table escaping. Endpoint URL inside <code> was escaped with esc_url() (an attribute-context function) instead of esc_html(). Switched to the correct function for text content.
  • Automation retention cron not cleared on deactivation. The trustlens/automation/retention_cleanup event survived Deactivate, leaving an orphan WP-Cron entry. Now cleared alongside the other scheduled events.
  • Duplicate score-save logic. process_score_calculation() and TrustLens_Score_Calculator::recalculate_score() each contained their own copy of the save-and-fire(score_updated / segment_changed) flow. The Action Scheduler callback now delegates to the calculator method so the two paths can’t drift.
  • Redundant order re-fetches. class-module-orders and class-module-shipping-anomalies registered woocommerce_order_status_completed with 1 arg and immediately called wc_get_order() on the order ID. Hooks now register with 2 args and use the WC_Order instance WooCommerce passes — with a defensive fallback for third-party callers firing the hook with one argument.
  • Card-testing defense bypass via client fingerprint rotation. Bots that rotated their JavaScript-side fingerprint per request avoided the per-fingerprint velocity threshold (each rotated hash had count 1, never tripping the limit). Declines are now also recorded under the server-fallback fingerprint (IP + User-Agent + Accept-Language) — which stays stable across client-hash rotation — so the velocity detector accumulates and targets even rotating attackers. Lockdown checks test both hashes on every request, so an attacker who got the server hash targeted on attempt 3 stays blocked even if they rotate the client hash on attempt 4.
  • Panic Freeze duration ceiling. The duration the panic-freeze AJAX accepted from the admin form was clamped to 3600s (1 hour). An admin mis-entering the value could accidentally block checkout for an hour. The server-side ceiling is now 30 minutes by default, filterable via trustlens/card_testing/panic_max_duration for sites that genuinely need longer.
  • Cron reconciliation on every page load. ensure_notification_schedules() was hooked to init and ran on every frontend request, writing to wp_options on stores with notifications disabled. The reconciliation now self-throttles to once per hour, with explicit invalidation on notification-setting changes so toggles still take effect immediately.
  • Automation customer_age_days / days_since_last_order timezone drift. Conditions mixed local-time (current_time(‘timestamp’)) with UTC-stored MySQL timestamps, producing up to ±14 hours of drift on non-UTC sites — enough to push a daily-granularity condition off by a full day. Both sides now anchor to UTC.
  • Webhook endpoints option marked autoload=no. The endpoint config (which contains plaintext HMAC signing secrets) was autoloaded on every request. It’s now loaded only when a webhook actually needs to fire.
  • Card-Testing Defense not actually enabled by default. The readme advertised “ships enabled with sensible thresholds” but the activation flow never set the trustlens_module_card_testing_enabled option, so the module sat dormant until merchants found the toggle in Settings. New installs now enable card-testing defense and the VIP customer bypass automatically, matching the documented promise. Existing sites keep whatever value they already have — no surprise behavior changes.
  • Welcome email never sent on default-off installs. The 24-hour-post-activation welcome summary was gated behind the master trustlens_enable_notifications switch, which ships disabled. The handler silently returned and the carefully-built welcome email was dropped on every fresh install. The welcome email is now gated only by its own trustlens_notify_welcome_summary opt-out (which already defaults to on), so the onboarding email actually fires.
  • Plugin row “Dashboard” / “Settings” shortcuts. The Plugins screen now surfaces direct links to the TrustLens dashboard and Settings page in the plugin row, matching standard WordPress plugin UX.
  • Dashboard onboarding card now signals active protection. When a fresh install lands on the empty dashboard, a small pill next to the onboarding steps confirms that the detection modules are already scoring incoming orders, so merchants know protection is live and not deferred until they finish setup.

Changed (potentially breaking for existing webhook receivers)

  • Webhook signature scheme v2. Outgoing webhook signatures now cover timestamp + ‘.’ + body instead of body alone, and a new X-TrustLens-Timestamp header carries the Unix epoch. This lets receivers reject replayed deliveries by checking the timestamp falls within a short window (recommended: ±5 minutes). Verification on the receiver side: compute ‘sha256=’ + hmac_sha256(timestamp + ‘.’ + body, secret) and constant-time-compare against X-TrustLens-Signature. If you have an existing webhook receiver, update its verification code before upgrading.

Internal

  • Centralized client-IP retrieval in wstl_get_client_ip() so future fraud modules don’t have to re-solve the spoofable-header problem.
  • Centralized webhook signature computation in TrustLens_Webhooks::compute_signature() so the three send sites (settings test, classic webhooks, automation webhooks) can’t drift apart.
  • Defensive: replaced ActionScheduler_Store::STATUS_PENDING constant reference with the literal ‘pending’ in wstl_queue_score_update() so the function survives unusual AS bootstrap orderings.

1.2.2

Automation Rules — reliability rewrite + major capability expansion. Plus Card-Testing Defense admin UX consolidation.

Automation

  • Added triggers: Chargeback Filed · Dispute Recorded · Linked Accounts Detected · Card Testing Attack · Shipping Anomaly.
  • Added condition fields: Total Order Value · Total Disputes · Linked Accounts · Coupon Then Refund · Cancelled Orders · Customer Type · Is Blocked · Customer Age · Days Since Last Order · Payment Method · Shipping Country · Billing Country · Country Mismatch · Coupon Total.
  • Added actions: Allowlist Customer · Cancel Order.
  • Added: Save-time validator blocks rules that can never fire — unsatisfiable conditions, schema-bound violations, trigger-state contradictions, invalid operators for the field type, incomplete actions — each with a specific inline reason.
  • Added: Inspector shows SKIP status on evaluations that didn’t execute, with the reason (“Cooldown active” / “Condition not met: trust_score > 50”). Directly answers “why didn’t my rule fire?”.
  • Changed: Webhooks now dispatch async with automatic retry (60s/120s/240s backoff) and are HMAC-SHA256 signed by default.
  • Changed: Rule editor no longer full-page-reloads on save or delete; errors appear inline inside the modal.
  • Fixed: Concurrent rule saves were last-write-wins — now serialized via advisory lock.
  • Fixed: A failed action locked the rule out for an hour via cooldown — now clears on error so the next event retries.
  • Fixed: Rules with an unknown condition field silently matched everything (catastrophic for block_customer rules). Now rejected.
  • Fixed: Timezone drift between log timestamps and inspector counters when MySQL server TZ ≠ site TZ.
  • Fixed: Operators <, <=, <> couldn’t save at all.
  • Fixed: “Send Email” action ignored the recipient field; now honors it as a per-rule override (falls back to site notification email when blank).
  • Fixed: “Refund Processed” trigger silently dropped order context — order-only actions/conditions never fired on refunds.

Admin UX — Card-Testing Defense + Dashboard

  • Changed: Card-Testing Defense page consolidated from four tabs into a single live view — panic controls, live state, and targeted fingerprints visible without clicking.
  • Added: Dashboard alert band for active Panic Freeze, targeted lockdowns, and card-network programs over chargeback threshold.
  • Added: Module-status pill row on the dashboard (on/off + one stat for each subsystem).
  • Added: Persistent plugin-wide admin header with unified nav, live status pill, notifications bell, and ⌘K command palette.
  • Fixed: Unchecking “Enable Card-Testing Defense” or “VIP bypass” didn’t save (Settings API checkbox quirk).
  • Fixed: Slack webhook delivery failures are now logged instead of swallowed.
  • Fixed: Uninstall clears card-testing options and cron hooks; deactivation unschedules card-testing crons.
  • Fixed: Card-testing attacks with an identifiable customer email now fire trustlens/checkout_blocked (once per newly-targeted fingerprint) so Notifications / Automation / Webhooks can react.

Safe additive upgrade — new composite index added idempotently, no data migration.

For the complete changelog of earlier versions, visit the full changelog.

Plugin Website
Visit website

Author
webstepper
Version:
1.2.8
Last Updated
June 20, 2026
Requires
WordPress 6.4
Tested Up To
WordPress 7.0
Requires PHP
7.4

Share Post

Join our newsletter.

Get insights into what’s happening at ChangelogWP right in your inbox. We don’t believe in spam.