Checkout Bouncer

Notes

WooCommerce order attribution: what it records, and what it proves

WooCommerce records where each order came from, but the browser collects that data and submits it with the order. What the fields are, and what an odd profile is actually worth.

6 min read

Every order in a modern store carries a short story about where it came from. WooCommerce order attribution is the feature that writes that story down, and it has shipped in core since version 8.5. Because Woo built it for marketing analytics, most writing about it covers channels and campaigns. But the same values double as a rough behavioural fingerprint, which is the more interesting use when a run of orders looks wrong.

So it is worth knowing three things: what the feature records, where those values come from, and how far they hold up. The third question turns out to matter most.

What WooCommerce order attribution records

WooCommerce keeps a fixed set of fields on the order, each one under a meta key beginning _wc_order_attribution_. The default list lives in the OrderAttributionMeta trait in WooCommerce core, which carries an @since 8.5.0 tag on the trait itself.

  • source_type, which drives the origin label. Core knows utm, organic, referral, typein, mobile_app, admin and pos, shown as Source, Organic, Referral, Direct, Mobile app, Web admin and Point of Sale. Anything else shows as Unknown.
  • referrer, described in the docs as "The URL of the site that referred the visitor to your site."
  • Nine UTM fields, from utm_source and utm_medium through to utm_marketing_tactic.
  • Four session fields: session_entry, session_start_time, session_pages and session_count.
  • user_agent, which core turns into the device type shown in the admin.

Session page views is the one to pause on, because the docs define it as "The number of page views in the session before placing the order." That is a count of browsing, and browsing is the very thing a bot skips.

Where it shows up in the admin

WooCommerce order attribution surfaces in two places, both added by core. First, the orders list gains an origin column, which the order attribution tracking documentation puts like this: "This column reveals the origin of each order, providing immediate insight into which channels are driving sales." So you can scan a screen of failures without opening any of them.

Second, the single order screen gains a meta box called Order attribution. The docs list its contents as "Origin, Source type, UTM campaign (if source type is UTM), and Medium, Device type, and Session page views."

Neither view works backwards. As the docs put it, "Order Attribution Tracking data is available only to orders generated when this feature is enabled."

How the values actually reach the order

Here the story changes, and this part decides what the data is worth. WooCommerce order attribution starts with the Sourcebuster JavaScript library, loaded on the front end. Sourcebuster writes first-party cookies as a visitor moves around the shop, and the docs are plain about what happens next: "The order attribution information is stored temporarily using cookies in visitors' browsers. Only in the event of an order, this data will be read and saved as order metadata."

Notably, that reading also happens in the browser. On the classic checkout, the front end script pulls values out of the Sourcebuster object. Then it stamps them into hidden inputs inside a wc-order-attribution-inputs element in the checkout form. On the block checkout, the same values travel as extension data under the namespace woocommerce/order-attribution. Either way, the browser sends them.

PHP then takes whatever arrived. The classic path reads the prefixed fields out of $_POST as the order is created. Meanwhile the block path reads the extensions parameter on the Store API request. Both clean up the strings, then save them as order meta. Neither one checks them against anything the server saw itself.

One detail deserves spelling out. The device type does not come from the request's own User-Agent header. Instead, core reads the user_agent field the client sent, which is a different thing wearing the same name.

A signal, never a verdict

Attribution reaches the order from the browser, so it is a signal and never a verdict. A real shopper's browser reports honestly, because the script has no reason to do otherwise. But a program that posts straight to the checkout endpoint owns every one of these fields. It can send a tidy organic origin with eleven page views as easily as it can send nothing at all.

Equally, an empty profile has innocent causes. When a field does not arrive, core swaps in (none) and skips storing it, so the origin falls through to Unknown. WooCommerce's own script names two of the causes in a comment: "Returns object full of `null`s if tracking is disabled or if sourcebuster.js is blocked." Consent tools, privacy add-ons and page caching all leave the same blank.

Therefore treat Unknown as a question, not an answer. And treat a full, tidy profile the same way, because it is no harder to fake than a blank one.

What an odd profile is worth as evidence

Support, mostly. Nothing in WooCommerce order attribution settles a case alone. Still, a pattern across a run of orders tells you something.

  1. First, look for repeats. Forty failed orders in an hour, all reporting Direct with one page view, describe forty people who each opened your checkout cold and paid at once.
  2. Second, look for conflict. A sent user_agent that disagrees with the browser string stored elsewhere on the same order does not add up.
  3. Third, look at the sessions. session_pages of one, on every order in a shop where real customers browse first, is a shape and not a fluke.

Then set all of it beside evidence the client did not write. A gateway's own decline reasons in the order notes panel come from outside the browser, and so does the pattern of statuses those orders end up in. Do not stack two client-fed fields, though. The stored address has the same flaw, since the customer IP on an order is read from a header and not from the network. For the wider method, reading card testing out of your own order data covers the rest.

Where Checkout Bouncer fits

Checkout Bouncer works one step earlier than any of this. Because it scores the checkout request with Google reCAPTCHA v3, the traffic it turns away never becomes an order, so it never gets an attribution profile at all. The plugin is free, GPL and listed on WordPress.org.

Its limits matter as much. The plugin guards the WooCommerce checkout only, which leaves login, registration, comments and contact forms outside its remit. Nor is it a firewall or a malware scanner. And it uses reCAPTCHA v3, never v2 or any other provider.

WooCommerce order attribution and a checkout guard answer different questions. One describes an order that already exists, using values the client handed over. Meanwhile the other decides whether that order should exist at all. To see which routes into your own checkout answer today, the free scanner reports in about two minutes.

See which doors into your checkout are standing open