# Google Analytics

> Let track.js read the purchase from Google Analytics 4 with data-sale="ga4" – no code on the receipt page.

If your site already sends the purchase to Google Analytics 4 – via Google Tag Manager (`dataLayer`) or `gtag` – [track.js](https://www.affilyflow.com/docs/en/reference/track-js) can read the order number and amount from there. Nobody needs to know the template's field names, and there is nothing to add to the receipt.

## Turn it on

Add `data-sale="ga4"` to the script on every page. The exact line with your settings is under Setup in the store dashboard.

```html title="<head> · every page"
<script src="https://app.affilyflow.com/track.js" data-store="DIN_BUTIK" data-sale="ga4" data-sale-vat="25" async></script>
```

| Attribute | Value |
|---|---|
| `data-sale="ga4"` | Read the purchase from Google Analytics 4. Without it, Google Analytics is ignored entirely. |
| `data-sale-vat` | The VAT percentage included in the Google Analytics amounts. `25` if the amounts include Danish VAT; leave it out if they exclude VAT. |
| `data-sale-shipping` | `incl` if shipping is always included in `value`, `excl` if it never is. Without it, the script guesses. |

> [!WARNING]
> Use only one way to report the sale. If the site has the WooCommerce plugin or the [sale line](https://www.affilyflow.com/docs/en/reference/sale-line) on the receipt, don't turn on `data-sale="ga4"` – the same purchase could be reported twice with different order numbers. That's why the Google Analytics route is only active with the attribute.

## Formats that are read

The `purchase` event is read in all three forms:

```js title="Google Tag Manager · dataLayer"
dataLayer.push({
  event: 'purchase',
  ecommerce: {
    transaction_id: '1008',
    value: 675,
    shipping: 49,
    currency: 'DKK',
    items: [{ item_id: '317', price: 375, quantity: 2 }]
  }
});
```

```js title="gtag"
gtag('event', 'purchase', { transaction_id: '1008', value: 1000, currency: 'DKK' });
```

A `purchase` event in `dataLayer` without an `ecommerce` object is read too – the fields are then taken directly from the event.

- Events already in `dataLayer` when the script loads are read immediately.
- Events pushed later are caught straight away – also when Tag Manager loads after track.js. In addition, `dataLayer` is checked every second for 60 seconds.
- Other events (`view_item`, `add_to_cart` …) are ignored.
- A purchase without `transaction_id` is ignored.
- Each order number is reported once per page; the browser's and server's duplicate guards stop the rest.

### Fields used

| Field | Used for |
|---|---|
| `transaction_id` | The order number. Required. |
| `value` | The order amount. |
| `shipping` | Shipping, subtracted when it is included in `value`. |
| `items[].price` and `items[].quantity` | The sum of the item lines (`quantity` is 1 if missing). Used when `value` is missing, and to guess whether shipping is included. |
| `currency` | The currency. If missing, the first item's `currency` is used, otherwise `DKK`. |
| `coupon` and `items[].coupon` | The customer's discount codes. A personal discount code the store has given an affiliate earns commission, even without a click. See [track.js](https://www.affilyflow.com/docs/en/reference/track-js#personal-discount-codes). |

`tax` is not read. VAT is taken out with `data-sale-vat`.

## How the amount is calculated

1. The starting point is `value`. If it is 0 or missing, the item lines' sum is used.
2. Shipping is subtracted when `shipping` is above 0 and either `data-sale-shipping="incl"` is set, or (without `excl`) `value` is larger than the item lines' sum – then shipping is included in `value`.
3. With `data-sale-vat` it is divided by 1 + VAT/100.
4. The result is rounded to two decimals. If it isn't above 0, nothing is reported, and the console shows `Affilyflow: the purchase event has no amount – the sale was not recorded.`

| `value` | `shipping` | Item lines | Setting | Reported |
|---|---|---|---|---|
| 675 | 49 | 750 | VAT 25 | 540 |
| 799 | 49 | 750 | VAT 25 | 600 |
| 724 | 49 | 750 | VAT 25, shipping `incl` | 540 |
| 1000 | – | – | VAT 25 | 800 |
| 540 | – | – | no VAT | 540 |

> [!NOTE]
> A discount larger than the shipping can fool the guess: `value` ends up below the item lines even though shipping is included. Set `data-sale-shipping="incl"` (or choose it in Setup) if your `value` always includes shipping.

## The server recalculates

Besides the amount, the script sends Google Analytics' raw figures with the sale: `value`, `shipping` and the item lines' sum. If the store has saved its Google Analytics setup under Setup in the dashboard (VAT and shipping), the server recalculates the amount with it – using exactly the same rule as above.

- A change to VAT or shipping in Setup applies from the next sale. The script doesn't need to be redeployed.
- The server's amount is only used if it is above 0 and not above what Google Analytics itself reported. Otherwise the script's amount is used.
- Without a saved Google Analytics setup, the script's amount from the attributes is used.
- Only sales from the Google Analytics route are recalculated. Sales from the sale line, `affilyflow.conversion()` and the integrations are untouched.

The live test under Setup shows the amount that arrived, so you can see whether VAT and shipping are right.

## Consent and clicks

The Google Analytics route follows the same rules as the rest of track.js:

- Without an affiliate click in the browser nothing is sent – unless the purchase has a personal discount code.
- With `data-consent="required"` the sale waits until the visitor has consented to marketing. See [track.js](https://www.affilyflow.com/docs/en/reference/track-js#consent).

Kilde: https://www.affilyflow.com/docs/en/reference/google-analytics
