# Google Analytics 4

Source: https://developers.swell.is/app-guides/google-analytics-4

The Google Analytics 4 app sends your store's purchases, refunds and sign-ups to Google Analytics 4 from the server, with GA4's Measurement Protocol. The events are recorded even when an ad blocker or a closed tab stops browser tracking, and they can be joined to the shopper's browser session.

## How it works

| When | What the app does |
| --- | --- |
| An order is paid, or submitted if you choose | Sends a purchase event with the order number, total, tax, shipping, coupon and items, once per order, and records on the order when it was sent. |
| A payment of an order is refunded | Sends a refund event with that refund's amount, once per refund. |
| An order is canceled | With Refund canceled orders on, reports the part of the order total that isn't refunded yet as a refund. |
| A customer account is created | Sends a sign_up event. |
| You run Send to GA4 on orders | Sends the purchases of selected orders from the last 72 hours that weren't sent yet. |
| You run Validate setup | Sends a sample purchase to GA4's validation server and shows its answer. Nothing is recorded in GA4. |

## Before you begin

- A GA4 property with a web data stream, and access to its Admin settings to create a Measurement Protocol API secret.
- Optionally, access to your storefront's code, to save the shopper's GA4 client and session IDs on the cart. Without them, server events can't be joined to the shopper's browser session.

## Set up

Install Google Analytics 4 from the Swell App Store, and set it up in your test environment before the live one.

**1. Get the Measurement ID and an API secret**

In GA4, go to **Admin > Data streams** and open your web stream. Copy the **Measurement ID**, which looks like `G-XXXXXXXXXX`. Under **Measurement Protocol API secrets**, create a secret and copy its value.

**2. Add them to the app**

In Swell, go to **Apps > Google Analytics 4 > Settings**. Enter the **Measurement ID** and the **Measurement Protocol API secret**, and click **Save settings**.

**3. Validate the setup**

Choose **Actions > Validate setup**. The app sends a sample purchase to GA4's validation server, which checks the Measurement ID and the event, and records nothing. The validation server doesn't check the API secret, so the test order in step 6 confirms it.

**4. Choose the events**

On the **Events** tab, choose when purchases are sent and which other events to send. Then turn on **Enable Integration** and save.

**5. Join the shopper's session**

This step is optional. To count server events toward the same visitor, session and traffic source as the shopper's browser events, save the GA4 client and session IDs in the cart metadata from your storefront. Swell copies the cart metadata to the order.

```javascript
// Storefront: after gtag.js is loaded and the cart exists
const MEASUREMENT_ID = 'G-XXXXXXXXXX';
gtag('get', MEASUREMENT_ID, 'client_id', (clientId) => {
  gtag('get', MEASUREMENT_ID, 'session_id', (sessionId) => {
    swell.cart.update({ metadata: { ga_client_id: clientId, ga_session_id: sessionId } });
  });
});
```

For sign-ups, save the same values in the account metadata with `swell.account.update`.

**6. Place a test order**

On the **Advanced** tab, turn on **Debug mode** and save. Place a test order and pay for it. Within a minute, the purchase shows in GA4 under **Admin > DebugView**, and the order's **Google Analytics** tab shows when it was sent. Turn off Debug mode when you're done.

**7. Go live**

Settings are saved separately for each environment. In the live environment, repeat steps 2 to 4. Use a separate GA4 property or data stream for the test environment, so test orders stay out of your reports.

If your storefront also sends purchase events with gtag.js, turn those off, or GA4 may count purchases twice.

## Using Google Analytics 4

### Purchases

Each order is sent once, as a purchase event, when it's paid, or when it's submitted if **Send purchase when** is set to **Order is submitted**. Drafts and canceled orders aren't sent. The event has:

- `transaction_id`: the order number.
- `value`, `currency`, `tax` and `shipping`: the order's grand total, currency, tax total and shipping total, rounded to cents.
- `coupon`: the order's coupon code, if it has one.
- `items`: up to 200 items, each with the variant SKU, product SKU or product ID as `item_id`, the product name, the variant name, the unit price after item discounts and the quantity.

When the order is less than 72 hours old, the event is dated with the order's submission time.

### Refunds

With **Send refunds** on, every successful refund of an order's payment is sent as its own refund event, with that refund's amount and the Swell refund ID as `transaction_id`. An order refunded several times sends several refund events. Each event also carries the custom parameters `order_id` and `order_number`. To use them in GA4 reports, register them as event-scoped custom dimensions under **Admin > Custom definitions**.

With **Refund canceled orders** on, canceling an order reports the part of its total that isn't refunded yet, with the order number as `transaction_id`. Refunds are only sent for orders whose purchase was sent, never add up to more than the order total, and don't list items.

### Sign-ups

With **Send sign-ups** on, a sign_up event with `method: email` is sent when a customer account is created, including accounts created at checkout.

### Visitors and sessions

GA4 links events through the client ID. The app uses the client and session IDs your storefront saved in the cart or account metadata. Without them, it uses `swell.<customer ID>`, or `swell.<order ID>` for guest orders, and each event starts its own session. Those events still count in reports and show in Realtime and DebugView, but they're attributed to (direct) / (not set) traffic. With **Send user ID** on, the Swell customer ID is also sent as `user_id`, so GA4 can join a customer's devices.

### Sending missed orders

To send purchases that were missed, for example orders placed before you set up the app, open an order and choose **Actions > Send purchase to GA4**, or select up to 25 orders in the order list and choose **Send to GA4**. GA4 only accepts events up to 72 hours old, so older orders are skipped, along with orders that were already sent or aren't paid. The result lists the skipped orders.

### In the dashboard

| Where | What it's for |
| --- | --- |
| Apps > Google Analytics 4 > Settings | The Measurement ID and API secret, the Events and Advanced tabs, and Actions > Validate setup. |
| An order's Google Analytics tab | When the purchase was sent, the GA4 client ID it was sent with, the amount reported as refunded, when the last refund was sent, and the last error. |
| The order list | A GA4 column with the time the purchase was sent, and the Send to GA4 action for selected orders. |
| An order's Actions menu | Send purchase to GA4, until the order's purchase has been sent. |

## Settings

Settings are in **Apps > Google Analytics 4 > Settings** and are saved separately for each environment. Events and Advanced settings are on their own tabs.

| Setting | Description | Default |
| --- | --- | --- |
| Measurement ID | The Measurement ID of your GA4 web stream, like G-XXXXXXXXXX. Required. | None |
| Measurement Protocol API secret | An API secret created for the same web stream. Masked on the settings page. Required. | None |
| Enable Integration | Sends events to GA4 and allows the Send to GA4 actions. | Off |
| Send purchase when | Order is paid, or Order is submitted. | Order is paid |
| Send refunds | Sends a refund event for every refund, full or partial. | On |
| Refund canceled orders | Reports the rest of a canceled order's total as refunded. Needs Send refunds on. | On |
| Send sign-ups | Sends a sign_up event when a customer account is created. | On |
| Collection endpoint | Global (www.google-analytics.com) or EU (region1.google-analytics.com). | Global |
| Send user ID | Sends the Swell customer ID as user_id. | On |
| Debug mode | Shows events in GA4 under Admin > DebugView. Turn it off when you're done testing. | Off |

## Limitations

- GA4 accepts any well-formed event without reporting errors, and drops invalid ones silently. Use Validate setup and DebugView to check events.
- GA4 accepts events up to 72 hours old, so past orders can't be imported. A purchase sent later is recorded at the time it's sent.
- If your storefront also sends purchase events with gtag.js, GA4 may count purchases twice.
- Without the client and session IDs from your storefront, server events start their own sessions and are attributed to (direct) / (not set) traffic.
- The app doesn't send consent signals. If your site uses Consent Mode, check that collecting from the server matches your consent policy before you turn the app on.
- Refunds report an amount, not which items were returned, and GA4 reports them separately from the purchase, because their transaction ID is the Swell refund ID. Totals and net revenue are still correct.
- Refunds of payments that don't belong to an order, such as subscription invoices, aren't sent.
- Events that GA4 refuses with a 4xx error, for example because of a wrong Measurement ID, aren't retried.
- Realtime and DebugView show events within a minute. Standard reports take 24 to 48 hours.

## Troubleshooting

Each order's **Google Analytics** tab shows the last error the app got for it. The app also logs each event it sends. In the Swell dashboard, the logs are under **Developer > Console > Logs**.

### "Enter a Measurement ID like G-XXXXXXXXXX, save, then validate again."

Validate setup checks the saved settings. Copy the Measurement ID from your GA4 web stream, click **Save settings**, then run Validate setup again.

### "Enter the Measurement Protocol API secret, save, then validate again."

Create an API secret for the web stream in GA4, enter it, and save before you validate.

### "GA4 rejected the sample purchase"

The message lists what GA4's validation server didn't accept. Check that the Measurement ID is the one from your web stream.

### Validation passes, but nothing shows in GA4

The validation server doesn't check the API secret. Check that the secret belongs to the same web stream as the Measurement ID and that **Enable Integration** is on. Turn on **Debug mode** and look in **Admin > DebugView**, since standard reports take 24 to 48 hours.

### An order's purchase wasn't sent

Purchases are sent when the order is paid, or submitted if you chose that, and only while the integration is on. Drafts and canceled orders aren't sent. If the order is less than 72 hours old, choose **Actions > Send purchase to GA4** on the order.

### "older than 72 hours"

GA4 doesn't accept events older than 72 hours, so these orders can't be sent.

### Purchases are counted twice

Your storefront probably sends purchase events with gtag.js too. Turn those off.

### Events show as (direct) / (not set) traffic

The storefront doesn't save the shopper's GA4 client and session IDs. Add the snippet from step 5 of Set up.

### A refund isn't in GA4

Check that **Send refunds** is on and that the order's purchase was sent. Refunds of payments without an order aren't sent. In GA4, refunds have the Swell refund ID as their transaction ID, not the order number.

## Uninstalling

Uninstalling the app stops sending events to GA4, and removes the **Google Analytics** tab, the GA4 column and the Send to GA4 actions from your dashboard. Uninstalling applies to the environment you are in, so remove the app from the test and live environments separately.

Events already sent stay in GA4. If you no longer need the Measurement Protocol API secret, delete it in GA4.

## For developers

| Function | Runs on | What it does |
| --- | --- | --- |
| order-events | order.submitted, order.paid, order.canceled | Sends the purchase on the event Send purchase when names, and the refund for a canceled order. |
| refund-events | payment.refund.succeeded | Sends one refund for each refund of an order's payment. |
| account-events | account.created | Sends sign_up. |
| send-to-ga4 | Order record and bulk actions | Sends the purchases of up to 25 selected orders from the last 72 hours. |
| validate-event | Settings action | Checks a sample purchase with GA4's validation server. |

Events are posted to `https://www.google-analytics.com/mp/collect`, or `https://region1.google-analytics.com/mp/collect` with the EU endpoint.

The app keeps its status on orders under `$app.ga4`: `ga4_purchase_sent_at`, `ga4_client_id`, `ga4_refund_value`, `ga4_refunds` (the amounts reported, by Swell refund ID, or `canceled`), `ga4_refund_sent_at` and `ga4_error`. It saves them with `$events: false`, so they don't trigger order events.

The storefront fields it reads are `metadata.ga_client_id` and `metadata.ga_session_id`, on the cart, which Swell copies to the order, or on the account for sign-ups.

## Support

Swell builds and maintains the Google Analytics 4 app. For help with the app, email support@swell.is. For questions about your GA4 property, see Google Analytics Help.
