Specifications

Under the hood

What the plugin writes, when it writes it, under which rules, and what can be plugged into it.

Requirements

Environment

ItemRequirementNote
WordPress6.0 or laterProven up to 7.1
WooCommerce8.0 or laterProven up to 10.9
PHP8.0 or laterFull test pass on 8.0, 8.2, 8.3 and 8.4
Order storageHPOS or wp_postsBoth, with identical counts
MultilingualWPML-friendlyValues are stored in English, translated on display
Multi-currencyyesThe currency is read from each order, never assumed
Interface languagesEnglish, FrenchAny other from the supplied .pot
Capabilitymanage_woocommerceChecked at menu registration and at render

No value is hard-coded in the plugin file. It installs on any shop without a line being touched. Only the conversion action name must be set before the first export.

Capture

Hooks and metadata

HookRole
woocommerce_checkout_order_createdCapture at classic checkout, priority 5
woocommerce_store_api_checkout_order_processedCapture at block checkout
woocommerce_thankyouSecond reading, for consent granted mid-journey

Metadata written on the order

MetaContent
_neant_gcl_awRaw _gcl_aw cookie, of the form GCL.<timestamp>.<gclid>
_neant_gcl_gb_gcl_gb cookie — iOS traffic, gbraid
_neant_gcl_auyes / no
_neant_consentyes, no, unknown, undetected
_neant_ty_gcl_aw, _neant_ty_consentThank-you page reading
_neant_gads_exported_at, …_export_batchExport timestamp and batch
_neant_gads_adjusted_at, …_adjust_batch, …_adjust_typeAdjustment sent

Capture writes even when no cookie is present. An order without _neant_consent means "capture did not run"; an order with an empty _neant_gcl_aw means "no Ads click". Conflating the two would make an interrupted collection indistinguishable from a month without Ads traffic.

Rules

Import eligibility

An order goes out if every condition holds.

Ref.ConditionReason if not met
E0Capture ranno capture
E1Click identifier present and parsableno gclid
E2Identifier not already retained in the batchduplicate of #…
E3Status in the configured liststatus … excluded
E4Amount strictly positivezero value
E5aOrder within the window following the clickorder outside the click window
E5bClick still importable todayclick older than 90 days
E6Not already exportedalready exported on …

E2 is judged among the orders that are otherwise exportable. If the older of two orders sharing an identifier is cancelled, the next one takes its place — otherwise the conversion would be lost between the two. At equal dates the order ID decides, so that two exports of the same batch retain the same row.

Consent does not enter these rules. It is displayed, never applied.

Adjustments

Ref.ConditionResult
A1Conversion already exportedotherwise, nothing to adjust
A2Status now cancelled, refunded, failedRETRACT
A3Refund covering the whole amountRETRACT, whatever the status
A4Partial refundRESTATE on the remainder
A5Conversion older than 24 hotherwise too recent
A6Conversion younger than 54 daysotherwise outside the adjustment window
A7No adjustment already sentexcept escalating RESTATERETRACT

A7 allows escalation in one direction only. A partial refund followed by a cancellation is common, and stopping at the first adjustment would leave the conversion counted for the remainder. The reverse is impossible: Google is explicit that a retracted conversion can no longer be adjusted.

Formats

The two files

Conversion import

Parameters:TimeZone=Europe/Brussels
"Google Click ID","Conversion Name","Conversion Time","Conversion Value","Conversion Currency","Order ID"
Cj0KCQjw…,Achat - import hors connexion,2026-07-31 07:46:18+02:00,335.00,EUR,66658

Order ID is optional for Google and decisive here: it is the recommended key for attaching a later adjustment. Without it, a retraction would rest on the identifier + timestamp pair, which the slightest time-zone drift misaligns.

Adjustments

"Order ID","Conversion Name","Adjustment Time","Adjustment Type","Adjusted Value","Adjusted Value Currency"
66658,Achat - import hors connexion,2026-09-12 14:22:03+02:00,RETRACT,,
66643,Achat - import hors connexion,2026-09-03 09:15:41+02:00,RESTATE,150.00,EUR

This file carries no Parameters:TimeZone= line: the zone is declared in each Adjustment Time. A retraction leaves value and currency empty — writing a zero there would read as a restatement to zero.

The two windows differ: 90 days to import from the click, 54 days to adjust from the conversion, of which 7 for bidding to take it into account. A conversion imported late can be born irretractable.

Extending

Filters

FilterUse
neant_gads_consent_adaptersPlug in a custom adapter. Placed first, it wins over the shipped ones.
neant_gads_axeptio_ad_vendorsAdapt the site's Axeptio vendor naming.
neant_gads_root_menu_labelRename the root entry in the admin menu.

Adjustments feed

ItemValue
Route/wp-json/neant-gads/v1/adjustments.csv
AuthenticationBasic, constant-time comparison
TransportHTTPS required, re-checked on every call
Rate limit30 requests per hour per address
ScopeFixed server-side, never driven by the URL
HEAD requestServed dry: it does not consume the queue

Translations

English is the source language. French ships with the plugin. Any other language is added by translating languages/neant-gads-offline.pot with Poedit or Loco Translate — no PHP involved. Regional variants fall back to the closest available translation of the same language, so an fr_CA site is not left in English for want of a catalogue in its own name.

Uninstalling

Options and the scheduled task are removed. No order metadata is deleted: it is the only trace of recoverable conversions, and an uninstall by mistake would make them permanently unrecoverable. The cost of a leftover row is nil; the cost of a deletion is not.