Google Analytics 4 not showing revenue by channel: four checks that find the cause
Google Analytics 4 shows no revenue by channel when the purchase event lacks a value, UTMs get lost, or Stripe renewals skip the browser. Four checks find it.
Muzahid Maruf, FounderUpdated
On this page
Explore with AI
Opens this article inside the chosen assistant with a ready-made prompt.
If Google Analytics 4 is not showing revenue by channel, one of four things is usually behind it: the purchase event lacks a value or currency, it arrives with no campaign source, the report credits a different scope, or the money never passed through a browser, as with every Stripe renewal.
Configuration fixes clear the Direct, Unassigned and (not set) rows in an afternoon. Renewals, refunds and any trial longer than 7 days need events that Stripe creates and the Google Analytics tag never sees.
Key takeaways
- Google Analytics 4 counts a purchase toward Total revenue only when the event carries both a value and a currency.
- Stripe renewals, trial conversions and refunds fire no browser event. A $29 plan paid 12 times leaves Google Analytics 4 holding 1 payment, 8.3% of the year's revenue, unless your server posts the rest.
- Stripe as a referrer, Unassigned and (not set) rows are configuration problems. The fixes inside Google Analytics 4 are an unwanted-referrals entry, a custom channel group or a Session dimension.
- Traffic acquisition, User acquisition and Model comparison credit 1 sale to 3 different channels, because their user, session and event scopes differ.
- A 14-day trial is 336 hours, 7 times Google's 48-hour Measurement Protocol guidance and twice the 7 days Apple Safari keeps a script-written cookie.
- TrackRev reads revenue from Stripe and joins each charge to the original click by email, from $29 a month, while Google Analytics 4 stays in place for on-site behavior.
GA4 not showing revenue by channel: match your symptom
| What you see in Google Analytics 4 | Usual cause | Where to look |
|---|---|---|
| Total revenue is $0.00 on every row | Purchase event missing, or sent without a value or currency | Check 1 |
| Revenue appears a day late | Processing runs up to 24 to 48 hours | Check 1 |
| Google Analytics total far below Stripe | Renewals, trial charges and refunds fire no browser event | Check 2 |
| Most revenue under Direct | Cookie lost, UTMs stripped, domains not linked | Check 3 |
| Revenue under stripe.com / referral | checkout.stripe.com missing from unwanted referrals | Check 3 |
| Revenue under Unassigned | utm_medium matches no default channel rule | Check 3 |
| Source and medium read (not set) beside revenue | Event-scoped dimension used on a non-key event | Check 4 |
| 2 reports name different channels for 1 sale | Session, user and event scopes credit differently | Check 4 |
Check 1: Does the purchase event carry a value and a currency?
Open DebugView, send 1 test purchase and expand the event. Google's help page for the Traffic acquisition report says a purchase sent without both parameters never reaches Total revenue.
| Field | What Google expects | Example: $39 plan, $3.12 tax |
|---|---|---|
| value | Pre-tax amount, with shipping and tax left out | 39, never 42.12 |
| currency | ISO 4217 code, required whenever value is set | USD |
| transaction_id | Unique per order, because matching IDs are deduplicated | 1 per order, never an empty string |
From the purchase event reference and the transaction ID page. Deduplication works on web streams only.
Purchase data lags in reports, and the data freshness page lists 2 to 6 hours for intraday data, 12 hours for daily data on a standard property and 24 to 48 hours for full processing.
Stripe Checkout runs on Stripe's page
A Stripe Checkout page cannot run your tag.
Stripe's conversion funnel guide tags 3 pages (product, success and canceled) and counts begin_checkout events and success views, none with an amount, so build the revenue event on the success page or on your server.
End success_url with ?session_id={CHECKOUT_SESSION_ID}, as Stripe's redirect documentation shows, and look up the Checkout Session for its amount.
// after your server returns the Checkout Session for session_idgtag("event", "purchase", { transaction_id: "cs_live_a1B2c3", // Checkout Session ID, unique per order value: 39.0, // pre-tax amount in major units currency: "USD", items: [{ item_id: "starter-monthly", item_name: "Starter plan" }],});A success page can be reloaded or never reached, so it undercounts and double counts, and transaction_id fixes only the second.
A trial subscription counts as paid once its $0 trial invoice is processed, per the Checkout Session reference, so on day 1 the page has no revenue to report.
Check 2: Do Stripe's paid invoices match Google Analytics 4?
Export 7 days of paid invoices from the Stripe Dashboard and compare the count and pre-tax total with the Ecommerce purchases report. Stripe reports the smallest currency unit, so 3900 means $39.00.
Renewals, refunds and off-session charges
Stripe charges month 2 from a saved card with no browser open, and refunds the same way, so no tag fires for either.
Unless your server posts 1 purchase per paid invoice plus a refund event with the original transaction_id, Google Analytics adds a subscription's revenue once and never subtracts it.
Worked example: 40 subscribers at $29
A cohort of 40 customers clicks a Google Ads link and each chooses a $29 monthly plan. The success page reports 40 purchases and $1,160 in month 1. Over 12 months Stripe collects $13,920. Google Analytics holds $1,160 of it, 8.3%, and the other 11 payments per customer sit in Stripe with no channel row to land in.
Google asks that Measurement Protocol events joined to browser events arrive within 48 hours of the client-side event, and warns that later ones may not process as expected for conversion attribution.
A month 2 renewal lands about 720 hours after the click. UTMs on Measurement Protocol purchases can also override a Google Click ID source, so copy them only for a new campaign interaction.
The 6-step build for posting Stripe invoices to GA4 covers the webhook.
Trials longer than a week
Take a $29 plan with a 14-day trial and a Google Ads click on day 0. The hours between the click and each payment decide what Google Analytics can credit.
| Payment | Day | Hours after click | Inside 48 hours | Amount |
|---|---|---|---|---|
| Trial starts on the success page | 0 | 0 | Yes | $0 |
| Charge 1 | 14 | 336 | No | $29 |
| Charge 2 | 44 | 1,056 | No | $29 |
| Charge 12 | 344 | 8,256 | No | $29 |
Hours counted from the click, with 30-day months. The 48-hour limit is Google's Measurement Protocol guidance.
Session rules still allow credit: a direct return takes the last non-direct source the browser remembers, per Google's referral exclusion page, and Google says the key event lookback window, 90 days by default for most events, also applies to session attribution.
The cookie is the weak link.
Google Analytics 4 writes _ga from JavaScript, Google's cookie documentation says Apple Safari limits first-party cookies to 7 days without a return visit (Google Chrome allows 400), and WebKit's policy says Intelligent Tracking Prevention deletes script-written cookies after 7 days without interaction.
A Safari visitor who returns on day 12 arrives as a new user, and the paying session is credited to whatever brought that visit, often Direct.
In my view a trial longer than 7 days is where Google Analytics 4 stops working as a revenue tool.
Blocked tags and declined consent
A visitor who blocks the tag sends nothing to Google Analytics.
A visitor who declines cookies shows up only when consent mode modeling covers the property, and Google's behavioral modeling page sets the bar at 1,000 events a day with analytics_storage denied for 7 days plus 1,000 daily users with it granted on 7 of the previous 28 days.
Modeling covers user, session and new-user counts. A site that rarely reaches 1,000 consenting users a day cannot qualify.
Check 3: Where does the revenue sit?
Open Traffic acquisition, set the dimension to Session source / medium and read Total revenue to see which row holds the money.
Direct
Google Analytics 4 labels a session Direct when the source is exactly (direct) and the medium is (not set) or (none).
Google's page on (direct) / (none) traffic lists links without UTMs, redirects that strip them, shorteners such as bit.ly, typed URLs, offline documents and ad blockers, so some Direct revenue is genuine.
Checkout adds one more. Without cross-domain measurement each domain creates its own IDs, so 1 visitor moving from your marketing site to a billing domain becomes 2 users and 2 sessions unless a _gl linker parameter carries the IDs across.
Stripe as a referrer
A return from Stripe Checkout can arrive with checkout.stripe.com as the referrer and be filed as a referral. Add the domain to the unwanted referrals list on the web stream, which holds up to 50 entries.
Unassigned
Google Analytics 4 files a session under Unassigned when no channel rule matches, and the default channel group rules are literal about spelling.
Email needs the word email, or a variant such as e-mail, in the source or medium, Affiliates needs the medium affiliate, and Paid Search, Paid Social and Paid Video need a listed source plus a medium matching cp, ppc, retargeting or a paid prefix.
| utm_source / utm_medium | Channel assigned | Rule that decides it |
|---|---|---|
| weekly-digest / email | Medium is email | |
| weekly-digest / newsletter | Unassigned | No rule matches the word newsletter |
| acme-partners / affiliate | Affiliates | Medium is affiliate |
| acme-partners / partner | Unassigned | No rule matches the word partner |
| acmeads / cpc | Paid Other | A cpc medium with a source outside Google's lists |
Applied from Google's default channel group rules, October 2026. Source names are examples.
The default group cannot be edited. Tag new links with the mediums in the free UTM builder and cover live ones with a custom channel group.
A standard property holds 2 of them, with up to 50 channels each, and they apply retroactively.
Check 4: Which report and scope are you reading?
Three reports can credit 1 sale to 3 channels, and each is correct by its own rule.
| Report | Dimension | Scope | What it credits |
|---|---|---|---|
| Traffic acquisition | Session default channel group | Session | The current session, reset after 30 minutes of inactivity or on a return visit |
| User acquisition | First user default channel group | User | The channel that first brought the user, fixed for good |
| Model comparison | Default channel group | Event | Key events only, by data-driven or last click credit, with a 90-day default lookback |
Scopes as defined on Google's traffic-source scopes page, October 2026.
User and session scopes use the paid and organic channels last click model and ignore the attribution setting.
Event-scoped reports follow it, but since November 2023 the choices are data-driven and last click, so the First user dimensions answer what introduced a customer.
A subscriber who first arrived through Organic Search in March, then clicked a Paid Search ad in April and paid in that session, shows as Organic Search in User acquisition and Paid Search in Traffic acquisition.
Unprefixed Source and Medium are event-scoped and read (not set) for non-key events, so use Session source and Session medium, or mark purchase as a key event.
What you can fix in Google Analytics 4 and what you cannot
| Cause | Fix inside Google Analytics 4 | Google's limit | What it recovers |
|---|---|---|---|
| Purchase sent without value or currency | Add both parameters to the event | None | Purchases from the day it ships |
| checkout.stripe.com as a referrer | Add it to unwanted referrals | 50 per stream | Referral handling from the change |
| Unassigned channel | Custom channel group | 2 per standard property, 50 channels each | Past and future data |
| (not set) beside revenue | Switch to Session source / medium | None | Past data |
| Domains not linked | Cross-domain measurement with _gl | 100 conditions | New sessions |
| Renewals and refunds | Post purchase and refund events from a webhook | 48 hours after the browser event | Sales inside that 48-hour window |
| Blocked tags, declined consent | Consent mode modeling, for declined consent only | 1,000 events a day | Estimates for users and sessions |
Reading revenue by channel from Stripe instead
The alternative reverses the order: record the click first on a tracked link, read the payment from Stripe afterward, and join the two, so no browser ID has to survive a 14-day trial.
TrackRev, the affiliate software with revenue attribution built in, works this way, with a read-only restricted Stripe key and a 1-line pixel that binds each visitor to an email at signup or checkout.
It joins each paid charge to a click through that email, syncs new charges every hour, and credits every renewal to the channel of the original click, so month 12 of the $29 example lands with month 1.
Paddle Billing, Polar, Lemon Squeezy, Creem and Dodo Payments connect as well.
Revenue figures need a paid plan from $29 a month on Indie, while the free plan covers link tracking only, with 50 links and 1,000 tracked events a month. Credit rules are first click, last click or linear.
Because the join runs on email, test the buyer who pays under another address first. TrackRev does not measure page engagement or on-site funnels, so keep Google Analytics 4 for those.
For one-time purchases on your own domain, where the first payment is the whole sale, Google Analytics 4 alone is enough, and a team that wants only standalone attribution without a partner program may find another tool fits better.
Found this useful? Share it.
Frequently asked questions
- Google Analytics 4 expects the pre-tax amount in major units, while Stripe reports tax-inclusive totals in the smallest currency unit, so 4212 means $42.12. Stripe's balance is also net of processing fees. Compare against pre-tax paid invoices, excluding test mode.
- Only by 72 hours. Google accepts a timestamp_micros value up to 72 hours old and, under relaxed validation, overwrites an older one with a time 72 hours ago. Strict validation rejects it, so a payment on day 14 of a trial cannot be placed back on day 0.
- Stripe hosts that page, so no tag runs on it. Your server must send the purchase after Stripe confirms the payment, and the visitor's identifier can ride along in client_reference_id, which accepts up to 200 characters. A Google Analytics 4 client ID contains a period, which that field rejects, so swap it for a dash or Stripe silently drops the value.
- No. Google Analytics 4 exports only the events it collected, so the 11 later payments per customer in the worked example stay absent unless your server sent them, and modeled behavioral data is skipped.
- Google Analytics 4 folds the overflow into one (other) row when a dimension has more unique values than a report table can show. Reduce the date range, add a filter or build an Exploration to see the individual channels.
- Yes, for on-site behavior: page views, events and funnels. TrackRev ranks channels by the revenue Stripe records, which needs a paid plan from $29 a month.

Written by
Founder, TrackRev.io & Contant.io
Muzahid Maruf founded TrackRev.io, SaaS affiliate software with no limit on tracked revenue, and Contant.io. He writes about affiliate programs.
Writes about Marketing attribution · Link tracking · Revenue analytics · SaaS growth
Stop guessing where your revenue comes from.
Set up TrackRev in about five minutes. The free plan covers 1,000 events a month, no card needed.
Start free