What the plugin does
integrations/woocommerce/track-site is a single-file WordPress plugin (PHP 8.1+, WooCommerce 8+, compatible with high-performance order storage). It has three responsibilities and no others:
- Snippet. Prints the async Track loader with your tracking id in
wp_head; custom SDK and collector hosts only when you set them. - Purchase data layer. On the thank-you page it pushes a GA4-shaped
purchaseintowindow.dataLayer— transaction id and order id, value, currency, tax, shipping, coupon codes and line items — guarded by an order meta flag so a page reload does not push twice. The SDK observes the push through adata_layertrigger with keypurchase; this is the browser path that carries the visitor's consent and click ids. - Managed webhooks. On saving the settings it creates or updates two native WooCommerce webhooks,
order.createdandorder.updated, with REST v3 payloads, pointing at your connection's webhook URL and signed with the secret you enter. Deactivating the plugin deletes them again.
Payment details never travel: WooCommerce's order representation contains billing and shipping data, totals and items, not card numbers. If you prefer not to install a plugin, the same two webhooks can be created by hand under WooCommerce → Settings → Advanced → Webhooks with the same secret.
Verification
WooCommerce signs every delivery with X-WC-Webhook-Signature, the base64 HMAC-SHA256 of the raw body under the webhook secret. The collector recomputes it and compares in constant time. When a webhook is created, WooCommerce first sends a form-encoded ping (webhook_id=…); the collector verifies its signature and answers pong without creating an event, and the connection records the ping as its first topic.
From order status to events
WooCommerce fires order.updated on every change, so the mapping keys on status:
| Order status | Event |
|---|---|
processing, completed | purchase (once; the event id is deterministic per order) |
refunded, or any status with entries in refunds[] | one refund per refund entry, absolute amount |
pending, on-hold, cancelled, failed | ignored |
The purchase carries the paid timestamp (date_paid_gmt) rather than the creation time, the gateway transaction id, currency, totals, tax, shipping, discount and the first coupon code, line items keyed by variation id (or product id), and the billing e-mail, phone, name, city, postcode and country as raw matching data the router hashes. The customer id becomes external_id.
Pairing with the browser purchase
The webhook has no consent information. When it arrives, the router looks for a browser purchase with the same order id and lets the server event inherit its consent record, anonymous id, click ids and hashed identifiers. Both events are routed; vendors receive purchase:<order id> on both paths and count once. Without a browser purchase the server record is operational only and never reaches advertising destinations.
Because the plugin's data layer push and the webhook use the same order id, the pairing is automatic; nothing has to be configured on the shop side beyond the two settings.
Setup
- Site → Shop connection → WooCommerce: shop domain, save; copy the webhook URL; choose a secret and store it in the connection.
- Install and activate the plugin, open Settings → Track, enter tracking id, webhook URL and the same secret, save. The page lists the two managed webhooks.
- Place a test order and set it to processing. The debugger shows the browser purchase from the data layer and the verified woocommerce purchase with the same order id.
Known limits, stated plainly
- Manual status changes in the admin trigger
order.updatedlike any other change; the deterministic event id prevents a second purchase. - A second partial refund on the same order is deduplicated by order id in the conversion table; the first refund is recorded, later ones are visible in WooCommerce only.
- Subscriptions plugins that create renewal orders produce new order ids and therefore new purchases; map renewals to a separate conversion action if you do not want them in acquisition metrics.