=== Open Tracked ===
Contributors: opentracked
Tags: tracking, analytics, attribution, woocommerce, conversions
Requires at least: 5.8
Tested up to: 6.8
Requires PHP: 7.4
Stable tag: 1.20.4
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

Connect WordPress and WooCommerce to Open Tracked: first-party tracking, a live dashboard and a CRM pipeline.

== Description ==

Open Tracked installs the Open Tracked pixel on your site and records what matters for your ads:

* Page views and the ad, campaign or channel each visit came from
* WooCommerce: product views, add to cart, remove from cart, checkout and purchases (order id, value, currency, items)
* Form leads from Contact Form 7, WPForms, Elementor Forms and Gravity Forms
* Every WooCommerce order and refund sent from your server (with an API token), so orders arrive even when a visitor's browser blocks tracking, plus a button that sends your last 90 days of orders
* Incomplete orders: email, phone and name of people who start the checkout but do not finish, with their cart and the required fields they left empty
* Logged-in customers are recognised, so a later purchase joins their earlier visits

The data goes to your own Open Tracked workspace, where you see revenue, ROAS, profit and your lead pipeline, and where workflows can send won deals back to Meta, Google Ads and Google Analytics.

You need an Open Tracked account (https://opentracked.io).

= Privacy =

The pixel sets two first-party cookies (a visitor id and a 30 minute session id). It does not run for visitors whose browser sends the Global Privacy Control signal, and your site can offer an opt-out by calling `openserver('optout')`.
If "Send the buyer or lead name, email and phone" is on, those details are sent to Open Tracked with purchases and leads. Emails inside analytics are stored only as a one-way hash; CRM contacts keep the readable details. Tell your visitors in your privacy policy and, where the law requires it, ask for consent first. To pause tracking until a visitor agrees, return false from the `open_tracked_active` filter.

== Installation ==

1. Upload the plugin folder to `/wp-content/plugins/` or install the zip under Plugins, Add New.
2. Activate it.
3. In Open Tracked open Settings, CRM and automation and create the connection key (it starts with osk_).
4. Open Settings, Open Tracked in WordPress, paste the key under Connection key and press Test connection. One key connects the pixel and your orders; Open Tracked tells the plugin which site it belongs to.
5. Save. "Send past orders" becomes available for WooCommerce.

== Frequently Asked Questions ==

= Does it work without WooCommerce? =
Yes. The pixel and form leads work on any WordPress site. The shopping events need WooCommerce.

= I already send GA4 ecommerce events with Google Site Kit or Google Tag Manager =
Open Tracked reads those events too. To avoid counting product views twice, turn off "WooCommerce" under What to track.

= I see no data =
Open Settings, Open Tracked and read the Status box. The most common reason: you are logged in as an administrator, and your own visits are skipped. Open your site in a private window. Also press Check my site, and clear your cache plugin or Cloudflare cache once.

= How do I stop tracking my own visits? =
Logged-in administrators and shop managers are skipped by default. You can also add your IP address in Open Tracked, Settings, Traffic rules.

= How do I wait for cookie consent? =
Add a filter that returns false until consent is given:
`add_filter( 'open_tracked_active', function () { return my_consent_given(); } );`

== Changelog ==

= 1.20.4 =
* An add to cart (and remove or quantity change) announced by the server is pushed once, even when WooCommerce's page and its cart refresh hand it out again on the next pages.

= 1.20.3 =
* Landing pages: no add_to_cart as the page opens, even when the funnel added the product on an earlier request; a form that is already on screen as the page opens waits for a click or typing; one add_to_cart per page however often the order button is pressed.

= 1.20.2 =
* The tagging server URL shows with https:// in the settings, as it was typed or downloaded.

= 1.20.1 =
* The landing page rules apply only when the funnel page itself puts the product in the cart; a CartFlows or FunnelKit global checkout reached with the shopper's own cart keeps the ordinary checkout events.

= 1.20.0 =
* Funnel landing pages with the checkout on them (CartFlows, FunnelKit): add_to_cart and begin_checkout no longer fire as the page opens just because the funnel put the product in the cart. add_to_cart fires when the visitor reaches the order form (any link or button that jumps to it, whatever its text, or the form half on screen for a second, or typing in it); begin_checkout when they start filling it in. Ordinary checkout pages are unchanged.

= 1.19.0 =
* Faster Tag Manager on custom loaders: the container file now comes from the shop's own address (cached there for 5 minutes), where the visitor is connected already, instead of a far tagging server. Events still go straight to the tagging server. If the shop cannot give the file, the tagging server's copy loads instead. On by default; Advanced, "Load the container file from this site".

= 1.18.3 =
* The theme's own notices are left alone again; the plugin shows no notice of its own on the dashboard.

= 1.18.2 =
* The theme's "This theme requires / recommends the following plugins" notice (TGM Plugin Activation) is hidden from the dashboard; turn it back on under Advanced. Other notices are untouched.

= 1.18.1 =
* No more "not connected yet" notice on the dashboard: the connection key is only needed for the Analytics plan.

= 1.18.0 =
* add_to_cart_ot from the browser the moment Add to cart is pressed, as Stape does: shop and category grids, the blocks' product grid, home page product lists, the product page with variations (their own id, price, SKU and options) and grouped products. A form that reloads the page is still announced by the server on the next page, and an add the browser already pushed is never pushed again by the server. The cart block and side carts that do not refresh WooCommerce's fragments no longer lose add_to_cart.
* select_item_ot when Add to cart is pressed in a list, too.

= 1.17.2 =
* Purchases on any thank-you page: purchase_ot and the pixel's purchase are pushed in the footer, for the order the page confirms (the order-received address with its key, CartFlows' thank-you page, or the order the visitor just placed), so a theme or Elementor thank-you page that skips WooCommerce's woocommerce_thankyou hook counts too. Still once per order.

= 1.17.1 =
* Each Tag Manager container shows its ID, tagging server URL and loader file on three lines.

= 1.17.0 =
* The settings page has three tabs: Google Tag Manager, Open Tracked CRM Connect and Advanced, with shorter text. Nothing changes in what is saved or tracked.

= 1.16.4 =
* The note about a loader file without a tagging server URL shows once, not several times.

= 1.16.3 =
* Everyone is tracked by default, administrators too. Leaving roles out of Tag Manager, and administrators out of the pixel, moved to Advanced settings at the top.

= 1.16.2 =
* A loader file typed without a tagging server URL is kept (with a note to fill in the URL) instead of turning back into gtm.js.
* No empty extra container row after saving; "Add another container" adds one.

= 1.16.1 =
* The Advanced section (Open Tracked address) is no longer shown on the settings page.

= 1.16.0 =
* The server purchase, refunds, order_paid and order_completed to the tagging server are off until you tick them (they were on by default). The browser tracking, the data layer and Tag Manager are unchanged.

= 1.15.0 =
* Refunds go to your tagging server (refund, with the refunded amount and products) and the Open Tracked GTM files send them to GA4. On by default once the tagging server address and secret are set.
* Optional order_paid and order_completed events to the tagging server, for triggers of your own, like Stape's webhooks. The purchase now says new_customer true or false.
* A quantity changed on the cart page or in the cart block sends add_to_cart or remove_from_cart for the difference.
* Through this site, the Tag Manager loader and its service worker go as .load: web servers that answer .js from the disk no longer return a 404 for them.
* Google Ads customer list: no list ID needed. Leave it empty in Open Tracked and the first buyer makes a list named "Open Tracked - WordPress buyers".

= 1.14.0 =
* Google Ads customer list (off until you tick it), free on every plan: each new buyer (a guest, or an account's first paid order) is hashed in your shop and added to the customer list typed on the Google Ads card in Open Tracked. Choose email, phone and name with country and postcode, and the consent sent with them. "Add past buyers" sends the paid orders from before, 200 a minute. Local phone numbers get the country code first, so more of them match.
* Leave Tag Manager out for logged-in people with the roles you tick (administrator, shop manager …).
* Keep or drop _ot at the end of the event names. Plain names carry ot_plain, and the Open Tracked GTM files take them only from this plugin, so another plugin's events are never counted twice.
* Order ID in events: the order number, the order ID or a custom field, in the browser purchase and the server purchase alike.
* Brand in events: found by itself (WooCommerce Brands, Perfect Brands, YITH, a Brand attribute), a taxonomy you pick, a custom field, or none.
* The tagging server URL and loader file update by themselves from the Custom Loader in Open Tracked, every 3 hours and after each save.

= 1.13.0 =
* Send Tag Manager through this site (off until you tick it): the container script and every event go to a short path on your own site, which passes them on to your tagging server with the visitor's address, browser and location, so blockers see only your site and the tagging server's cookies belong to your site. Login, cart and session cookies are never passed on. The Open Tracked GTM files follow the path by themselves.
* Load Tag Manager only after consent (off until you tick it): nothing of Tag Manager loads until a consent banner grants analytics or ad storage (Google Consent Mode or the WP Consent API).

= 1.12.0 =
* Server purchase: each paid order is also sent from your own server to your tagging server (in the background, retried if the server is busy), so Meta, TikTok and the other platforms get the purchase even when a blocker stopped the browser. The browser's purchase and the server's carry the same event id (ot_purchase_<order id>), so every platform counts the order once; Google Analytics and Google Ads never get a second one. The address and secret come with the Open Tracked GTM files and are filled in when you download the plugin from your container's setup.
* Product ID in events: choose the product ID, the variation ID, the SKU or the GTIN, with an optional prefix (gla_, wc_post_id_ ...), so sales match the products in your Meta and Google catalogs.

= 1.11.0 =
* First-party address (on by default): the Open Tracked pixel loads from your own site (a short path of its own, different on every site, so blocker lists cannot know it), and an event a blocker stops on its way to app.opentracked.io is sent again through your site, with the visitor's address, browser and (behind Cloudflare) country. If your site cannot answer on that path, the pixel loads from app.opentracked.io as before.
* Order sync and incomplete orders are now off until you switch them on: they are for the Analytics plan (orders, CRM and POAS in Open Tracked).

= 1.10.2 =
* Lighter pages: every script of the plugin is now minified (the UTM script that sits in each page went from 5.5 KB to 3.4 KB, the listeners from 12 KB to 8 KB) and its licence banner is no longer repeated in every page.

= 1.10.1 =
* Data layer: select_item_ot when a visitor clicks a product in a shop or category list (with the product, the list name and its place in it). Billing and shipping addresses now include the second address line and the company. A store with three price decimals keeps all three. An add to cart or event that never reached a page is dropped after 5 minutes instead of being announced on some later page.

= 1.10.0 =
* Data layer: login_ot and sign_up_ot (with the person's details) after someone signs in or creates an account. Products carry their whole category path (item_category, item_category2 ... item_category5, the most specific category and the ones above it), and a variation also sends its own id as variant_id next to the product's item_id.

= 1.9.5 =
* Safari: the UTM tags, ad click ids and Meta's _fbp and _fbc are now also set by your own server (Safari lets a cookie set by a script live 7 days, one set by the server 90). It happens once per new visit, in Safari and on iPhone only, and sends nothing anywhere: the server hands the visitor's own cookies back to them.

= 1.9.4 =
* The click, scroll, time-on-page and video listeners are gone, so the plugin's script is about half the size. Phone, e-mail and WhatsApp clicks are counted by Tag Manager's own click trigger (the Open Tracked GTM files set it up). What is left under Listeners: forms, chat, popups and bookings, all off until you tick them. The script that remembers UTM tags and ad click ids is now part of the page itself (no separate file, no waiting), so no visit is missed.

= 1.9.3 =
* Nothing is lost for speed: the Open Tracked pixel is added the moment the page has been read (it still loads in the background), the form, click, chat and video listeners start at once, and product lists send up to 60 products again.

= 1.9.2 =
* Google Tag Manager rows now have a Remove button (press Save changes afterwards).

= 1.9.1 =
* New: search_submitted_ot with the search_term, sent on every site search results page, so Meta, TikTok, GA4 and the other platforms can count searches.

= 1.9.0 =
* Faster: none of the plugin's scripts can hold the page back any more. Every script waits until the page has been read, the Open Tracked pixel is fetched when the browser is idle, the listeners that look through the page for videos and chat widgets start when the browser is idle and only react to changes that bring in a video frame, and the settings are read once per page. Product lists on shop pages send at most 30 products. Every listener (forms, clicks, video, chat, popups, engagement) is now off until you tick it under Settings, Open Tracked, Listeners, so a new install loads no listener script at all.

= 1.8.2 =
* The buyer's address saved with an order is never a Cloudflare address any more (it used to be, on shops behind Cloudflare, which made Meta match the sale to the wrong place). The first address in the chain that is not Cloudflare's is used.

= 1.8.1 =
* The scroll and time-on-page listener is now off by default (tick Engagement under Listeners to use it). A fast scroll sends one scroll event for the highest mark reached instead of several at once.

= 1.8.0 =
* A copy downloaded from the Open Tracked dashboard comes with your Tag Manager container, tagging domain and loader file already filled in. Press Save changes to turn them on.
* The Tag Manager section is now at the top of the settings page, and the Open Tracked address moved under Advanced.
* Notices from other plugins no longer appear inside the purple header.

= 1.7.1 =
* A new look for the settings page: cards, switches and a tidier Tag Manager list.

= 1.7.0 =
* Orders now carry each product's SKU and main picture, so product pictures appear in Open Tracked. After updating, press Send past orders once to add the pictures to older orders.

= 1.6.0 =
* One key instead of two. Paste the connection key (osk_...) from Open Tracked, Settings, CRM and automation; the plugin asks Open Tracked for the site key itself. Sites already set up with a site key keep working.

= 1.5.0 =
* Ad tracking on every order: UTM tags, ad click ids (gclid, gbraid, wbraid, fbclid, ttclid, msclkid), Meta fbp and fbc, Google Analytics client and session ids, plus the buyer's IP address and browser, captured from cookies when the order is placed, shown on the order screen, sent to Open Tracked with the order and added to purchase_ot as "attribution". A small script keeps a first-party cookie with the landing touches and creates _fbp and _fbc when Meta's pixel has not. Switch it off under Settings, Ad tracking on orders

= 1.4.0 =
* New name: Open Tracked. Settings saved by older versions are picked up automatically; deactivate the old plugin first
* The data layer events now end in _ot (page_view_ot, view_item_ot, add_to_cart_ot, purchase_ot …)
* Google Tag Manager: add one or more containers from the settings page, each optionally loaded from your own domain (first-party loader), with a Test button
* Ready-made listeners, active on install: forms (Contact Form 7, WPForms, Gravity Forms, Elementor, Fluent Forms, Forminator, Formidable, HubSpot, Typeform, plain HTML), phone, email, WhatsApp, Messenger and Telegram clicks, file downloads, outbound links, scroll depth, time on page, video (HTML5, YouTube, Vimeo, Wistia), chat widgets (Tidio, Tawk.to, Crisp, Intercom, Drift), popups (Elementor, Popup Maker) and Calendly bookings. Each group can be switched off
* Data layer events and listeners also run for logged-in administrators, so Tag Manager preview works; every event carries user_role so a container can leave staff out

= 1.3.0 =
* Data layer: pushes page_view_ot, view_item_list_ot, view_item_ot, add_to_cart_ot, remove_from_cart_ot, view_cart_ot, begin_checkout_ot, add_shipping_info_ot, add_payment_info_ot and purchase_ot to the Google Tag Manager data layer (items, cart_state and user_data included). Works without a site key; switch it off under Settings, Data layer

= 1.2.0 =
* Incomplete orders: people who start the WooCommerce checkout (classic or Blocks) and leave are saved in Open Tracked with their cart and the fields they left empty; they are removed again if they order later

= 1.1.0 =
* Server-side order and refund sync with an API token, retries, and a button to send the last 90 days of orders
* Status box with a "Check my site" button; clear note when your own admin visits are skipped
* The pixel finds its key even when a script optimiser hides the script tag

= 1.0.0 =
* First release.
