Forum Replies Created

Viewing 15 replies - 1 through 15 (of 31 total)
  • Plugin Author shan

    (@suhanduman)

    Hi Leslie,

    Looked at all three screenshots — they tell me more than I expected. There are actually three separate issues, all rooted in the same fix:

    1. The dedup rate (36.27%)

    Your Event Deduplication panel shows two things that matter:

    • “Connected with PixelYourSite” — Meta is treating PYS as your CAPI
      integration partner, not CAPI Suite. Even if your PYS settings say
      “pixel only”, please double-check PYS → Conversions API tab and confirm
      there’s no API access token or pixel ID configured there. If PYS has
      ANY CAPI configuration on the Meta side, it’s silently sending server
      events too, and that’s why the event_ids don’t match — three sources
      (PYS pixel, PYS CAPI, CAPI Suite CAPI), three different ID schemes.
    • Server events (34) outnumber browser events (13) on PageView by 2.6×.
      That’s the opposite of a healthy ratio — normally browser ≥ server.
      It tells me the browser pixel isn’t firing for most visitors. On your
      stack, the most likely cause is WP-Rocket’s aggressive lazy-loading
      (I noticed earlier that even jQuery loads with rocketlazyloadscript
      on your site) combined with whichever pixel tag IS firing being
      delayed past the moment Meta wants to see it.

    2. The price data warning (“All Purchase events sending the same price”)

    This one I want to flag — Meta detected it Jun 16. There are three possible causes worth checking:

    • PYS is sending Purchase with a default/static value (free PYS versions
      often do this on Purchase events). CAPI Suite reads $order->get_total()
      per order, so the server-side value is real per-purchase.
    • Your product catalog feed (the one connected to your Meta Catalog)
      has wrong or static prices. If the feed says every product is €0 or
      every product is the same price, Meta uses the feed as the source of
      truth for ad reporting and the event-level value gets ignored. Worth
      opening Meta Commerce Manager → Catalog → Products and checking that
      each product shows its correct individual price.
    • The content_ids in your Purchase events don’t match the product IDs
      in your catalog feed. When Meta can’t match the event’s content_id to
      a catalog item, it normalizes the price (often to whatever default
      fallback your feed configuration uses), making every Purchase look
      identical. If you can share one Purchase event from the Test Events
      tab (just the custom_data.contents field), I can verify the IDs are
      formatted correctly.

    The dedup fix below will eliminate the PYS-default-value path. The feed checks above eliminate the other two. Worth ruling out all three.

    3. Match quality (4.4–4.8/10 on most events)

    Separate issue — that’s about which user parameters (hashed email, phone, click ID, FBP cookie) you’re sending. Don’t worry about this one until dedup and price are fixed; once Meta sees a clean stream, match quality will improve on its own. If it’s still low after 7–10 days, we can dig in.

    The clean fix — three steps, in this order:

    Step 1: WP Admin → CAPI Suite → Settings → GTM Template → download container.json. Import it into Google Tag Manager in Merge mode. (If you have not done it yet)
    This installs the Meta Pixel, Pinterest Tag, TikTok Pixel, GA4, and Google Ads tags — all wired to the same event_id that CAPI Suite uses server-side. Browser ↔ server deduplication becomes automatic.

    Step 2: WP-Rocket → File Optimization → JavaScript. You’ll need to add exclusions in two places:

    a) “Excluded JavaScript Files” — add these URL patterns so they’re not
    minified/combined:
    googletagmanager.com/gtm.js
    mcapi-frontend-events.js

    b) If “Delay JavaScript Execution” is enabled (it’s on by default in
    newer WP-Rocket versions, and it’s what’s lazy-loading even jQuery
    on your site), add these script IDs to its exclusion list so they
    fire on first paint instead of on user interaction:
    mcapi-pageview-init
    mcapi-viewcontent-events
    mcapi-viewcategory-events
    mcapi-frontend-events
    googletagmanager.com/gtm.js

    These are the inline + external scripts CAPI Suite pushes events
    through. Without these exclusions, the dataLayer push that the Meta
    Pixel tag in GTM listens for can fire AFTER the visitor has already
    left the page, so the browser pixel never gets a chance to send the
    event. CAPI Suite shows this as an admin notice when it detects
    WP-Rocket — if you haven’t seen it, it may have been dismissed.

    Step 3: In PixelYourSite, disable the Meta Pixel and Facebook Conversions API features entirely. Settings → Meta tab → toggle off.
    You can keep PYS for any other purpose but Meta should come exclusively from CAPI Suite + its GTM
    template.

    After all three are done, give Meta 3–7 days. The dedup rate should jump from 36% to >85%, the price-data warning should clear (if it was the PYS path), and the quality score should climb back toward your 7.66 goal. If the price warning persists after dedup is fixed, that points to the feed side and we’ll dig in there.

    Pinterest — same principle. Step 1 (the GTM template) installs a Pinterest tag with the correct event_id, so Pinterest ↔ CAPI Suite dedup becomes automatic too. If you currently have a different Pinterest tag in GTM (manually built or from another plugin), remove it after the template imports — the bundled one handles everything.

    If anything’s unclear or PYS settings don’t match what I described, just share a screenshot of PYS → Settings → Meta tab and I’ll walk you through it.

    Kind regards,
    Suhan

    Plugin Author shan

    (@suhanduman)

    Hi Leslie,

    Found it. Your Scheduled Actions screenshot was the smoking gun — that “failed after 300 seconds” entry on June 12 12:12 UTC told the whole story.
    Thank you for taking the time to pull it; that one screenshot saved me days of guessing.

    Here’s the full picture, including why this whole “deferred events” machinery exists in the first place — because the answer is intertwined with the bug.

    Why we have a deferred-events table at all.
    The plugin maintains a ~9,500-CIDR datacenter IP list (AWS, GCP, Azure, ColoCrossing, OVH, etc.).
    Events arriving from those IPs are usually scrapers, headless browsers, ad-fraud bots — but not always. Apple iCloud Private Relay traffic rides on Apple’s own datacenters.
    VPN shoppers often exit through commercial hosting ranges. A logged-in customer using a corporate VPN looks identical to a scraper at the network layer.
    We can’t just drop those events — we’d lose real shoppers.

    So instead of dropping or sending immediately, ambiguous events get parked in mcapi_deferred_events while the plugin watches that visitor for “real human” signals: paced page views, behavioral keepalive proof, a click-ID cookie, eventually a Purchase.
    If any of those land, the plugin “graduates” the visitor to trusted and replays the entire deferred funnel into the queue — so Meta/Pinterest see PageView → ViewContent → AddToCart → InitiateCheckout → Purchase as a complete chain instead of an orphaned Purchase.
    If the signals never land, the deferred rows age out without ever being sent. Real shoppers preserved, scrapers excluded.

    Where 3.7.1 broke this.
    I added a pacing check to the soft-trust graduation logic — 3 events with at least 3-second average gaps instead of just “3 events”. That’s correct anti-bot hardening (a bot firing 3 events in 200ms shouldn’t earn trust).
    But the side effect was that more events accumulate in the deferred table before graduation.
    And when a visitor finally graduates, the recovery function would dump ALL of their accumulated deferred events into the queue in one shot — no cap.

    Combined with the second problem: the plugin’s admin Event Log uses WordPress transients for caching, and 3.7.1 added two new transient keys (mcapi_logs_byip_*) for the new By-IP view. Each log insert fired 5 delete_transient() calls. On a queue tick processing 100 recovered events × 3 platforms × 5 deletes = 1,500 DELETE queries against wp_options in one cron tick. On a site with bloated wp_options (many plugins, lots of options), those DELETEs stack up, combined with HTTP timeouts to Meta/Pinterest, the batch can exceed Action Scheduler’s 300-second wall. Action Scheduler then marks the action “failed” — and it doesn’t auto-reschedule. Silent halt.

    Your downgrade-to-3.7.0 fixed it because WordPress runs the activation hook on a fresh install, which re-registered the cron from scratch.

    3.7.2 just shipped to ww.wp.xz.cn with two fixes:

    1. Per-event invalidation is gone — replaced with a single invalidation call at the end of each queue batch. ~1,500 DELETEs → ~30 DELETEs per batch.
    2. Deferred-event recovery now caps at 50 rows per call (filterable via mcapi_deferred_recover_limit). A single trust graduation can’t dump a saturated backlog into one cron tick anymore — anything beyond 50 drains naturally on the next tick.

    When you update from 3.7.0 to 3.7.2, the activation hook fires and re-registers the cron (same way your downgrade did), so dispatch resumes on the new code automatically. No manual intervention needed.

    You can update whenever you’re comfortable — there’s no urgency because you’re already on 3.7.0 and events are flowing. If you do update and see anything off, please write back and I’ll dig in immediately.

    One more thing: your diagnostic work here directly led to the fix. If anyone else hit the same silent failure mode they hadn’t reported it — your screenshots are the reason every user gets this fix today.

    Genuinely grateful.

    Kind regards,
    Suhan

    Plugin Author shan

    (@suhanduman)

    Hi Leslie,

    Thanks so much for taking the time to gather all of this — the timeline, the queue health snapshot, and the API Conversions screenshots are exactly what helps me investigate.
    I really appreciate it.

    Tech stack of your site:

    • WP 6.8.5, WC 10.7.0, PHP 8.4 (bleeding edge), Apache, France
    • Theme: Uncode premium + uncode-child + uncode-js_composer (Loads its own woocommerce-add-to-cart.js file)
    • Plugins: easy-meta-capi + PixelYourSite + GTM (duracelltomi) + jetpack + tiktok-for-business + wp-rocket + ultimate-member + contact-form-7 + uncode-privacy
    • WP-Rocket uses very aggressive lazy-loading — all JS, including jQuery, is loaded with rocketlazyloadscript. (This is a notorious footgun in CAPI plugins)

    I’ve spent the last hour going through the actual code diff between 3.7.0 and 3.7.1, and I genuinely cannot find a path by which 3.7.1 would cause all events to stop reaching Meta/Pinterest. The changes between those two versions are:

    1. An AddToCart JavaScript refactor (affects only AddToCart, not Purchase/Checkout/ViewContent — those are server-side hooks that didn’t change)
    2. A tighter datacenter-IP filter (only kicks in for visitors whose IP is in a known hosting-provider range — residential shoppers from France would never hit it)
    3. An admin cache-invalidation tweak (only affects what you see in the Event Log UI; does not touch the dispatch loop at all)
    4. An autoload=false hardening on one option

    None of those touch the path that sends events to Meta/Pinterest for a normal shopper.

    That doesn’t mean you’re wrong — the correlation you’re seeing is real and reproducible on your site, and “downgrade fixes it” is hard evidence.
    It means the cause is most likely something I can’t see from your screenshots alone. (and also i could not reproduce the problem on my own system)

    Also thank you so much for the detailed timeline and the screenshots. They actually told me something really interesting that I want to share with you, because it might change what we’re looking for.
    Looking at the Pinterest API Conversions graphs you attached: June 12 = 143 PageVisits flowing normally, June 13 and 14 = 0, June 15 = 297 recovered. That part absolutely confirms the dispatch gap is real. No question about that.

    But here’s the thing I noticed in your local Event Log screenshot (the one showing “Last 24 Hours = 0”).
    The most recent event captured before the freeze is dated June 12, 14:03.
    That’s an afternoon timestamp. You mentioned you updated to 3.7.1 on Friday morning, June 13 — which would put the update roughly 16 to 20 hours AFTER the last successful event.

    If 3.7.1 were the cause, the dispatch would have stopped at the moment of the update (Friday morning) — not the previous afternoon. The fact that events stopped well before 3.7.1 was even on your site means the trigger is something else that happened around June 12 at 14:03.

    As for why downgrading “fixed” it — I think the most likely explanation is that downgrading a plugin in WordPress goes through a full deactivate → uninstall → reinstall → reactivate cycle.
    That cycle re-fires the plugin activation hook, which re-registers the Action Scheduler cron that runs the event queue. If the original cron entry got into a “failed” or “stuck” state around June 12 14:03, downgrading would have refreshed it as a side effect — which is why events came back. Reactivating 3.7.1 (without downgrading) would probably have done the same.

    If you want to test this theory quickly: temporarily re-update to 3.7.1, then check Tools → Scheduled Actions, filter by the hook “mcapi_process_event_queue”, and see if the action is “Pending” with a future run date. If it is, dispatch should keep working on 3.7.1 too. If you see “Failed” or no entries at all, that’s the actual problem and I can ship a hardening fix that auto-recovers stuck schedules.

    To narrow it down, could you help me with a couple of things?

    1. PHP error log: anything from June 12 evening through June 14? Even warnings/deprecation notices from PHP 8.4 would be useful here. Path is usually /wp-content/debug.log if WP_DEBUG_LOG is on, or your host’s error_log.
    2. Action Scheduler status: WP Admin → Tools → Scheduled Actions, then filter by hook “mcapi_process_event_queue”. If any rows are marked “failed” during the 3.7.1 window — that’s our smoking gun. A screenshot of that page (or copy of the Status + Scheduled Date + Log columns) would be huge.
    3. WP-Rocket: I noticed your site uses WP-Rocket with lazy-load on JavaScript. Did anything change in WP-Rocket settings on June 12, or did WP-Rocket itself update around that date? A WP-Rocket cache rebuild can mask a lot of state.
    4. PixelYourSite: I see PYS is also installed alongside CAPI Suite. Was PYS sending Meta CAPI as well, or pixel-only? If both are sending CAPI, Meta’s dashboard would show events from EITHER source — so for the graph to go to zero, BOTH plugins would have had to stop. That’s an important data point.
    5. Auto-updates: was 3.7.1 a manual update or did it auto-update? If auto-update, do you know exactly when it ran? (Activity log plugin or wp_options “auto_update_plugins” could tell us.)
    6. If you can also temporarily turn on WP_DEBUG_LOG and re-update to 3.7.1 for an hour or two on a low-traffic day, we’d capture exactly where it breaks. I know that’s asking a lot — only if you’re comfortable.

    Bottom line: I want to fix this for you, but I want to fix the right thing.
    Rolling back 3.7.1 for all users without understanding the mechanism would risk breaking it for everyone else who isn’t seeing this problem.
    If we can find the actual cause, I can ship a targeted fix immediately.

    Kind regards,
    Suhan

    Plugin Author shan

    (@suhanduman)

    Hi Leslie,

    Really sorry to hear about the tracking impact — that’s a painful 3 days, and I want to help you get to the bottom of it.

    Quick context up front: we haven’t seen similar reports from other users on 3.7.1 yet, so this might be something specific to your environment, or there could be a regression we haven’t seen elsewhere. Either way I’d like to walk through the diagnostic together rather than guess.

    A few things that would help me narrow it down:

    1. Walk me through the timeline. When exactly did you first notice the events stopped flowing, and when did you update to 3.7.1? Sometimes the update timing is coincidental with another change on the site (a host migration, a security plugin update, cron service change, SSL renewal) and the diagnostic gets easier once we map the sequence out.
    2. What the most recent Event Log rows show. In CAPI Suite → Event Log → Detailed Event Log, the most recent rows should have a status (“success” / “failed”) and a response_data column. If there are rows with failure responses (HTTP 401, 5xx, timeout etc.), those error messages usually point right at the cause. Could you share the last 5–10 row statuses and their response_data values?
    3. Queue health. On the WP Dashboard widget (CAPI Suite section), what do these show?
    • Queue size
    • Oldest pending
    • Last successful dispatch
    • Background tasks pending If queue size is high but “Last successful dispatch” is days old, the queue processor (Action Scheduler / WP-Cron) likely isn’t firing. You can force a manual run from WP-CLI: wp action-scheduler run –hooks=mcapi_process_event_queue
    1. Outbound HTTP reachability. Some hosts silently block outbound HTTPS after security/firewall changes. Quick test from WP-CLI: wp eval ‘var_dump( wp_remote_get( “https://graph.facebook.com/v25.0/”, array( “timeout” => 5 ) ) );’ A network error or timeout here would explain a dispatch stall regardless of plugin version.
    2. Pixel ID + Access Token still valid? If the access token expired or was rotated, dispatch would fail silently with 401 responses — and those would show up in the Event Log rows from item 2. A 60-day Meta system-user token rotation, a Business Manager permission change, or an account reorganization can all cause this on a date unrelated to the plugin update.
    3. Anything else around the same time. Plugin updates, theme change, server move, security plugin install, .htaccess edit, SSL renewal, cron service change, WP-Cron disable — worth listing anything that changed in the same window.
    4. (Optional) Your site URL. A quick look at the front-end lets us spot the theme, caching plugin, and security plugin setup — sometimes a security plugin’s recent rule update silently blocks outbound HTTP to graph.facebook.com, and the setup often tells us where to look without you needing to dig through configs. For context: 3.7.1’s changes were a browser-side fix for the AddToCart dataLayer push, a tightening of the behavioral bot filter for datacenter IPs, and a couple of admin-side cleanups (cache invalidation + an option autoload flag). None of those touch the server-side dispatch loop that POSTs events to Meta or Pinterest — so I’m puzzled by the symptom and want to make sure we’re not chasing the wrong cause. If the diagnostic data above points back at the plugin, we’ll absolutely ship a hotfix and pull the affected version; if it points elsewhere (which is what the changeset suggests), we’ll know exactly where to look together. Whenever you can share the items above — even partial info helps — I’ll dig in.

      Thanks for the detailed report.
      Kind regards,
      Suhan
    Plugin Author shan

    (@suhanduman)

    Hello soorta;

    Perfect solution.
    And it pointed me to a simple complete solution.
    I’ll add that kind of a fragment read fix to the code to fix the issue for all users.

    Regards,
    Suhan

    Plugin Author shan

    (@suhanduman)


    UPDATE:

    I took a quick look at https://eurocharms.sk source. Your stack is Elementor Pro + a custom child theme (FinestudioEcommerce1). This is almost certainly the root cause: Elementor Pro’s WooCommerce widgets (Products grid, Loop Grid, custom Add to Cart buttons) implement their own AJAX flow that bypasses both WooCommerce’s standard fragments mechanism and the jQuery added_to_cart event.

    What that means specifically:
    – The cart updates correctly (Elementor calls woocommerce_add_to_cart PHP hook directly), so server-side CAPI is still firing — please confirm by checking the plugin’s Event Log tab; you should see AddToCart rows with Success (Meta) after testing
    – But the browser-side dataLayer push doesn’t happen because our JS listener depends on the standard jQuery added_to_cart event, which Elementor doesn’t trigger

    Three options to fix the browser-side / GTM half:

    1. Switch the product cards to native WC blocks (or the classic shortcode [products]). This is the simplest fix — WC’s native rendering uses standard AJAX and our plugin works out of the box. Best if you don’t need Elementor-specific layout tricks.

    2. Add a custom dataLayer push bound to Elementor’s event. Drop this in your child theme’s functions.php:

    add_action( ‘wp_footer’, function() {
    if ( ! function_exists( ‘is_woocommerce’ ) || ! ( is_shop() || is_product_category() || is_product_tag() || is_product() ) ) return;
    ?>
    <script>
    jQuery(document.body).on(‘elementor/widget/loop_grid/add_to_cart/success added_to_cart’, function(event, fragments, cart_hash, $button) {
    // Re-check the mcapi fragment in case Elementor’s response did include it
    var $el = jQuery(‘.mcapi-atc-datalayer’);
    if ($el.length && $el.attr(‘data-mcapi-payload’)) {
    try {
    var payload = JSON.parse($el.attr(‘data-mcapi-payload’));
    $el.removeAttr(‘data-mcapi-payload’);
    window.dataLayer = window.dataLayer || [];
    window.dataLayer.push(payload);
    } catch(e) {}
    }
    });
    </script>
    <?php
    });


    2. This rebinds the listener to Elementor’s success event in addition to WC’s standard one. If Elementor’s AJAX response doesn’t carry our fragment, this won’t help on its own (option 3 below covers that).

    3. Force the dataLayer push server-side without depending on fragments, by directly enqueuing the AddToCart push for the next page load. Add to child theme functions.php:

    add_action( ‘woocommerce_add_to_cart’, function( $cart_item_key, $product_id, $quantity ) {
    if ( ! function_exists( ‘WC’ ) || ! WC()->session ) return;
    $product = wc_get_product( $product_id );
    if ( ! $product ) return;
    $value = (float) $product->get_price() * (int) $quantity;
    $payload = array(
    ‘event’ => ‘add_to_cart’,
    ‘event_id’ => ‘addtocart_’ . $cart_item_key . ‘_’ . uniqid(),
    ‘ecommerce’ => array(
    ‘currency’ => get_woocommerce_currency(),
    ‘value’ => $value,
    ‘items’ => array( array(
    ‘id’ => (string) $product_id,
    ‘item_id’ => (string) $product_id,
    ‘item_name’ => $product->get_name(),
    ‘price’ => (float) $product->get_price(),
    ‘quantity’ => (int) $quantity,
    ) ),
    ),
    );
    // Stash for the next page load — gets emitted by Elementor’s redirect or page refresh
    WC()->session->set( ‘mcapi_pending_atc_datalayer’, $payload );
    }, 5, 3 ); // priority 5 = before plugin’s hook at 10

    add_action( ‘wp_footer’, function() {
    if ( ! function_exists( ‘WC’ ) || ! WC()->session ) return;
    $payload = WC()->session->get( ‘mcapi_pending_atc_datalayer’ );
    if ( empty( $payload ) ) return;
    WC()->session->set( ‘mcapi_pending_atc_datalayer’, null );
    echo ‘<script>window.dataLayer=window.dataLayer||[];window.dataLayer.push(‘ . wp_json_encode( $payload ) . ‘);</script>’;
    });

    3. This fires once on the next page load (e.g., when Elementor redirects to the cart or refreshes a fragment). Less ideal than real-time but works regardless of which AJAX flow Elementor uses.

    My recommendation: start with option 1 if your design allows it — least code, zero ongoing maintenance. If you need Elementor’s layout, try option 2 first (1-min test in browser console will tell you if Elementor includes the fragment); fall back to option 3 if it doesn’t.

    Either way, please first verify server-side CAPI is firing via the Event Log — that tells us whether the cart-add itself is reaching to the plug-in, even though the dataLayer push isn’t.

    Regards,
    Suhan

    Plugin Author shan

    (@suhanduman)

    Hi Tim;

    Thanks for the suggestions; advice from a real user is incredibly valuable in a UI/UX context.
    All done and version 3.7.0 was released.

    Regards,
    Suhan

    Plugin Author shan

    (@suhanduman)

    1. Plugin’de zaten “Datacenter IP filter” var.

    Açıksa plugin her gün ~9,500 CIDR (AWS, GCP, Azure, Cloudflare, DigitalOcean, Linode, Vultr, Hetzner, Alibaba…) indirip otomatik filtreliyor. Ad-fraud bot’larının yaklaşık %85-90’ı bu cloud datacenter’larından geliyor.

    1. Country-filter, datacenter filter ile büyük ölçüde overlap eder.

    TR mağazası için, gelen trafik örnekleri:


    ┌────────────────────────────────────────────────┬───────────────────┬──────────────────────────┐
    │ Senaryo │ Datacenter filter │ Country-filter (TR-only)
    ├────────────────────────────────────────────────┼───────────────────┼──────────────────────────┤
    │ AWS Virginia'dan bot │ ✅ yakalar │ ✅ yakalar (aynı IP)
    │ Hetzner Almanya bot │ ✅ yakalar │ ✅ yakalar (aynı IP)
    │ Rus residential proxy ağı │ ❌ kaçar │ ✅ yakalar
    │ TR datacenter'da yerel scraper │ ✅ yakalar │ ❌ kaçar (TR sayılır)
    │ Almanya'dan alışveriş yapan Türk gurbetçi │ ✅ geçer │ ❌ KESER ⚠
    │ Apple Private Relay ile NL çıkışlı iOS müşteri │ ✅ bypass var │ ❌ KESER ⚠
    │ TR'de VPN açarak gezen gerçek müşteri │ ✅ bypass var │ ❌ KESER ⚠

    Yani country-filter, datacenter filter’ın yakalamadığı çok küçük bir niş için fayda sağlıyor; karşılığında yurt
    dışı gerçek müşterileri (gurbetçi, turist, VPN-kullanıcı, Apple Relay iOS) false-positive olarak kesiyor.

    Plugin’in datacenter filter’ı bu son üç grup için zaten özel bypass mekanizmaları içeriyor: varsa cookie kontrolü, Apple Private Relay IP whitelist, login customer bypass.
    Country-filter bu bypass’ları yok sayar — hepsini “yabancı IP = bot” sayar.

    Sonuç: Country-filter gereksiz, datacenter filter zaten asıl sorunu çözüyor. Excluded Traffic sekmesinde “Auto-fetched (daily)” aktif edersen büyük ihtimalle “tek tek IP girmek çok zor” sorunun orada çözülecek.

    Not: Meta ve Google reklam onayında Landing page crawl yapar.

    • Bizim filter sadece “datacenter IP’den POST /wp-json/mcapi/v1/event çağrılarını” filtreliyor. GET /landing-page’i kimseyi engellemiyor. Meta’nın crawler’ı landing page’e ulaşabiliyor, pixel’i HTML’de görüyor, onay süreci sorunsuz işliyor.
    • Bizim datacenter blocklist’imiz cloud sağlayıcılar (AWS, GCP, Azure, Cloudflare, DigitalOcean, Linode, Vultr, Hetzner, Alibaba, Oracle, Fastly) odaklı.
    • Meta’nın kendi AS’ı (31.13.0.0/16 civarı, AS32934) ve Google’ın ad crawler AS’ları (AS15169 Googlebot ranges) listede YOK.
    • facebookexternalhit Meta IP’sinden gelirse → datacenter filter’a uğramaz, normal şekilde sayfayı alır
    • Googlebot veya AdsBot-Google Google IP’sinden gelirse → datacenter filter’a uğramaz
    • Hatta GCP’den çıkan bir test (nadir senaryo) → datacenter filter’a uğrar AMA facebookexternalhit veya Googlebot UA’sı zaten ayrıca yakalanır → Purchase event muafiyeti dışında zaten event göndermiyoruz → net etki sıfır

    Block’lanan IP’ler de aslında block’lanmıyor, sadece o olay eğer gerçek kullanıcı değilse meta’ya gönderilmiyor. (yeni versiyonda block ismi değişti, block kelimesi yanlış çağrışım yapıyor)

    Plugin Author shan

    (@suhanduman)

    Hi Tim,

    3.6.0 just shipped — should resolve everything you flagged.
    The queue-leak you spotted is fixed (rows now correctly drop when an event’s send_to targets a platform with no credentials configured, instead of accumulating).
    I also ran end-to-end tests against the timezone scenario you mentioned and the plugin behaves correctly across the time-difference cases I could reproduce.

    Really appreciate the thorough review — these were exactly the kind of edge cases that would have been painful to debug in production.

    Regards,
    Suhan



    Plugin Author shan

    (@suhanduman)

    Hi Tim,

    Thank you for the incredibly detailed reports. The PHP error log, the queue dump, the Action Scheduler stats — that level of detail let me identify exactly what’s going on. Quick summary, then specifics:

    1. The “spurious” PageView and AddToCart events — confirmed bot/scraper activity, not a plugin bug

    I went through the queue CSV you sent. The traffic profile is unmistakable:

    14,036 AddToCart vs only 871 PageView events. Real shoppers always generate far more PageViews than AddToCarts (typical ratio is 5–20× the other way). This inversion means clients are calling the cart endpoint directly without ever navigating the site. (which is impossible)

    Top source IPs are all known hosting providers:

    2a01:4f8:151:841e::2 (171 hits) → Hetzner IPv6

    43.130.139.214, 43.130.107.148, 43.131.63.234, 8.219.99.6 (96+ hits combined) → Tencent Cloud (Singapore / HK)

    47.237.254.73, 47.236.133.154 (57+ hits) → Alibaba Cloud

    User-agents are realistic Chrome/macOS strings — that’s why our existing UA-based bot filter in 3.5.3 lets them through. The bots are paying attention to fingerprint detection.

    This isn’t visitor activity. These are competitor-research scrapers, ad-fraud bots, and price-monitoring tools hitting your add_to_cart endpoint directly.

    1. What 3.6.0 does about it (already coded and tested, still working on an elegant solution)

    The next version adds a server-side datacenter IP filter with the following stack: (real users don’t use datacenters)

    A 9,000+ CIDR blocklist covering AWS, GCP, Azure, DigitalOcean, Hetzner, Tencent, Alibaba, Linode, Vultr, OVH, Oracle, Fastly — the exact providers your CSV is dominated by. Updates daily from a CC0-licensed upstream.

    A “real-user bypass” so legitimate visitors behind VPNs/corporate proxies don’t get caught: ad-click IDs (fbclid/gclid/ttclid), Apple iCloud Private Relay egress IPs (whitelisted from Apple’s published list), logged-in WooCommerce customers, and visitors with a _fbp (Meta Pixel) or _ga (Google Analytics) cookie set from a previous visit.

    Purchase events are never blocked — your revenue tracking stays clean by design.

    A funnel-chain recovery mechanism: if a real user happens to be filtered (rare edge case) and then completes a Purchase, the plugin replays their full funnel (PageView → ViewContent → AddToCart → InitiateCheckout) so Meta still sees the complete journey.

    I’ve tested this end-to-end against synthetic bot traffic and against real edge cases (Apple Private Relay, click-ID bypass, etc.). On a sample run, ~95% of datacenter-source events get filtered before they reach the queue or your logs.

    In the meantime, the Wordfence rule you mentioned will help — blocking known scraper ASNs (AS24940 Hetzner, AS45090 Tencent, AS37963 Alibaba) at the firewall is the right interim move.

    1. GTM template “Unrecognized value [EVENT]” import error — fixed

    This was a real bug in the 3.5.3 template. The container had a built-in variable entry of type “EVENT” that GTM’s deserializer rejected on import in some regions. The variable wasn’t actually used by any tag or trigger — just dead weight in the JSON. Removed in 3.6.0.

    1. Event time showing 2 hours ahead — I’d like to confirm one thing on your side

    The plugin stores all timestamps in UTC and converts to your site’s local timezone via WordPress’s wp_date() for display. That logic is correct as far as I can verify it. To rule out a config issue: in your WP admin, Settings → General → Timezone, what is the value set to? Specifically:

    Is it set to a city like Europe/Prague?

    Or is it set to a manual offset like UTC+2?

    If it’s the manual offset, that can produce the exact +2h drift you’re seeing because the offset gets applied twice in some code paths. Switching to Europe/Prague (which respects DST automatically) usually fixes it. If it’s already on Europe/Prague, that’s unexpected and I’ll need to dig further.

    1. Log “stopped at exactly 1,000 entries” — this isn’t expected, I’d like more info

    The plugin doesn’t have a 1,000-row hard limit anywhere in the code. The retention cleanup runs once per day and is based on age (15 days by default), not row count. So a hard stop at 1,000 is unusual.
    Is the wp_mcapi_logs table actually capped at 1,000 rows, or are you seeing only 1,000 in the admin panel display (which has a UI cap) while the table itself has more? A quick SELECT COUNT(*) FROM wp_mcapi_logs; would clarify.

    1. Bonus answer to your queue draining concern

    3.6.0 ships a Sending Method toggle in Event Management → Behavior:

    Asyn (default, current behavior): events batched every 60s via Action Scheduler. Good when cron is reliable.

    Sync: events sent synchronously on the same HTTP request that triggered them. Useful if you ever again see queue backlog without drain. Trade-off is +500ms on add-to-cart and checkout requests, but you’ll never have a “queue stuck” state.

    For your situation today (15K backlog), the queue is draining — the Action Scheduler stats you sent show 3,808 successful runs. The reason it’s not catching up is bots are arriving faster than the dispatcher can drain at 60s ticks. Once you’re on 3.6.0, the datacenter filter will drop ~95% of those bot events at the door, so the queue stays small.

    Thanks again for the detailed bug reports — exactly the kind of feedback that improves the plugin for everyone.

    Best,
    Suhan

    Plugin Author shan

    (@suhanduman)

    1. Forkunda ciddi bir bug var, dedup sorunu ondan yaşanıyor. Fork kodu (line 1224-1233 + 1292-1294 + 1325):
      // AS enqueue — payload’da event_time yok:
      as_enqueue_async_action(‘mcapi_async_send_event’, array(
      ‘event_name’ => $event_name,
      ‘user_data’ => $user_data,
      // … event_time GEÇİLMİYOR
      ), ‘mcapi_events’); // AS handler çalıştığında:
      function mcapi_fb_send_event(…, $event_timestamp = null) {
      if (is_null($event_timestamp)) {
      $event_timestamp = time(); // ← AS dispatch zamanı, click zamanı değil
      }
      // …
      “event_time” => min(time(), (int)$event_timestamp),
      }

    Meta’nın dedupe algoritması event_time tolerance’ına bağlıdır, bu mismatch dedupe’u kırar.

    1. Ülke bazlı filtre çok basit implementasyon, VPN, proxy, header manipulation şeklinde çok rahat bypass edilir.

    Versiyon 3.6.0 yakınca yayınlanacak, bot aktivitesini çok daha kapsamlı çözecektir.

    Plugin Author shan

    (@suhanduman)

    Hi Tim — thanks for the update.

    (1) Glad it sorted itself out. Old Meta Pixel plug-ins will be updated with the template.
    0c574 is the correct version.

    (2) The free-shipping progress bar plugin is a strong lead. 3.5.3 (shipping today)
    already includes a fix for one variant of the cart-widget replay issue: the AJAX
    add-to-cart payload was previously emitted as an inline script inside a
    WooCommerce fragment, and WC caches fragments in browser sessionStorage, so any
    later page load that re-rendered the cart widget would silently fire the script
    again. 3.5.3 moves the payload to a self-clearing data attribute, so cached
    fragments are inert on replay.

    That should cover the browser-side noise. If the spurious events continue after
    3.5.3, the next likely vector is your progress bar plugin re-invoking
    woocommerce_add_to_cart on cart-page render. To confirm or rule out, after
    upgrading could you check the Event Log on the cart page (no actual add-to-cart
    action) and let me know if any AddToCart rows appear with source_info matching
    items already in your cart?

    (3) Event Log lives in a custom database table (wp_mcapi_logs by default — your
    table prefix may differ). You can view/filter it from WP Admin → All-in-one CAPI
    → Logs. Filter by status=failed and event_name=PageView (or any event you suspect
    bots are spamming), then click any row to see the full User-Agent. Two or three
    real samples and I’ll extend the bot regex tightly.

    Thanks again for the patience — 3.5.3 should be live in your auto-update channel
    within a couple of hours.

    Plugin Author shan

    (@suhanduman)

    Hi Tim — thanks for the detailed status, very useful.

    (1) Meta template update error — Could you grab the exact message (or screenshot) when GTM offers the 19af9 → 0c574 update and you accept it? GTM’s “wait, terms of
    service for this template” or schema-validation message would tell me exactly which field it’s unhappy about. Without that I’d be guessing.

    (2) Spurious add_to_cart on cart page — Probably needs an improvement, if we hit such a condition:
    The woocommerce_add_to_cart PHP hook the plugin uses can fire on cart page reload when a third-party plugin (subscriptions, free gifts, cart persistence) re-adds items in the cart-page lifecycle. I can ship 3.5.3 with an early-exit when is_cart() or is_checkout() is true — real add-to-cart actions happen BEFORE the visitor lands on /cart/, so suppressing fires from those two pages is safe.

    (3) Bot / price-scraper traffic — They are still on the list. The current regex catches self-identifying crawlers (Googlebot, AhrefsBot, etc.) but commercial scrapers spoof real browsers. Could you paste a couple of example User-Agent strings from your Event Log’s failed/spurious rows? With real samples I can extend the regex tightly instead of casting too wide a net and catching real visitors.

    Regards,
    Suhan

    Plugin Author shan

    (@suhanduman)

    v3.5.2 just shipped — full GTM API schema rebuild. (template is working)

    The previous 3.5.x template wasn’t importing into modern GTM workspaces (Unknown entity type errors).
    I tested the new build end-to-end in a fresh GTM workspace and it now imports cleanly with all 19 tags, 10 triggers, 13 variables, and the Meta Pixel custom template.

    Please re-download the template from the plugin settings and re-import.
    Thanks for the patience while we chased this through. : )

    Regards,
    Suhan

    Plugin Author shan

    (@suhanduman)

    Actually it’s a bug on CookieYes. (class_exist function)
    But i have solved it on my side.
    Released 3.5.1 for you.

Viewing 15 replies - 1 through 15 (of 31 total)