feat: add checkout attribution telemetry#8688
Conversation
📝 WalkthroughWalkthroughThis change implements checkout attribution tracking across the platform by introducing a new telemetry provider that captures attribution metadata from URL query parameters, persists it to storage, and plumbs it into subscription checkout requests. The changes extend metadata types and integrate attribution retrieval into the checkout flow. Changes
Sequence DiagramsequenceDiagram
participant Browser as Browser/Page View
participant TelemetryProvider as CheckoutAttributionTelemetryProvider
participant Attribution as checkoutAttribution Util
participant Storage as Local Storage
participant Checkout as Subscription Checkout
Browser->>TelemetryProvider: trackPageView() with search params
TelemetryProvider->>TelemetryProvider: extractSearchFromPath() or use window.location.search
TelemetryProvider->>Attribution: captureCheckoutAttributionFromSearch(search)
Attribution->>Attribution: Parse attribution fields from query params
Attribution->>Storage: Persist attribution to localStorage
Note over Browser,Storage: User initiates checkout
Checkout->>Attribution: getCheckoutAttribution()
Attribution->>Storage: Read persisted attribution
Attribution->>Checkout: Return attribution object
Checkout->>Checkout: Include attribution in request body
Checkout->>Browser: Send checkout request with attribution fields
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~22 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
🎨 Storybook Build Status✅ Build completed successfully! ⏰ Completed at: 02/08/2026, 04:41:58 AM UTC 🔗 Links🎉 Your Storybook is ready for review! |
🎭 Playwright Tests: ❌ FailedResults: 513 passed, 1 failed, 4 flaky, 8 skipped (Total: 526) ❌ Failed Tests📊 Browser Reports
|
There was a problem hiding this comment.
Pull request overview
This PR adds comprehensive checkout attribution tracking to capture marketing attribution data (UTM parameters, Impact affiliate tracking, and Google Ads click IDs) during user sessions. The attribution data is persisted to localStorage, captured on page views via a dedicated telemetry provider, and sent to the backend during subscription checkout flows.
Changes:
- Expanded attribution tracking from just Google Ads click IDs (gclid, gbraid, wbraid) to include UTM parameters and Impact affiliate tracking (im_ref)
- Added a new CheckoutAttributionTelemetryProvider that captures attribution data on page views
- Modified subscription checkout flows to send attribution data to the backend API
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| src/platform/telemetry/utils/checkoutAttribution.ts | Core attribution logic: renamed functions/types, added UTM and Impact tracking, added deduplication logic to prevent unnecessary storage writes |
| src/platform/telemetry/utils/tests/checkoutAttribution.test.ts | Comprehensive test coverage for attribution capture, storage, and deduplication scenarios |
| src/platform/telemetry/types.ts | New CheckoutAttributionMetadata interface with all attribution fields, extended BeginCheckoutMetadata |
| src/platform/telemetry/providers/cloud/CheckoutAttributionTelemetryProvider.ts | New telemetry provider that captures attribution from page view paths |
| src/platform/telemetry/initTelemetry.ts | Registered new CheckoutAttributionTelemetryProvider in telemetry initialization |
| src/platform/cloud/subscription/utils/subscriptionCheckoutUtil.test.ts | Updated tests with new attribution fields |
| src/platform/cloud/subscription/composables/useSubscription.ts | Modified initiateSubscriptionCheckout to send attribution data in request body |
| src/platform/cloud/subscription/composables/useSubscription.test.ts | Added mock for getCheckoutAttribution and verified attribution is sent in checkout requests |
| import { captureCheckoutAttributionFromSearch } from '@/platform/telemetry/utils/checkoutAttribution' | ||
|
|
||
| import type { PageViewMetadata, TelemetryProvider } from '../../types' | ||
|
|
||
| /** | ||
| * Internal cloud telemetry provider used to persist checkout attribution | ||
| * from query parameters during page view tracking. | ||
| */ | ||
| export class CheckoutAttributionTelemetryProvider implements TelemetryProvider { | ||
| trackPageView(_pageName: string, properties?: PageViewMetadata): void { | ||
| const search = this.extractSearchFromPath(properties?.path) | ||
|
|
||
| if (search) { | ||
| captureCheckoutAttributionFromSearch(search) | ||
| return | ||
| } | ||
|
|
||
| if (typeof window !== 'undefined') { | ||
| captureCheckoutAttributionFromSearch(window.location.search) | ||
| } | ||
| } | ||
|
|
||
| private extractSearchFromPath(path?: string): string { | ||
| if (!path || typeof window === 'undefined') return '' | ||
|
|
||
| try { | ||
| const url = new URL(path, window.location.origin) | ||
| return url.search | ||
| } catch { | ||
| const queryIndex = path.indexOf('?') | ||
| return queryIndex >= 0 ? path.slice(queryIndex) : '' | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
The CheckoutAttributionTelemetryProvider lacks test coverage. While the underlying captureCheckoutAttributionFromSearch function is well-tested, the provider's specific behavior should also be tested, particularly:
- The extractSearchFromPath method with various path formats
- The fallback to window.location.search when no path is provided
- Integration with the trackPageView lifecycle
This is important because the provider has its own logic for extracting search parameters from paths and choosing between the provided path and window.location.search.
a8df879 to
e38875c
Compare
0f32622 to
dafccab
Compare
dafccab to
4fb386d
Compare
72c94e9 to
3eccf3e
Compare
4fb386d to
73c0451
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In
`@src/platform/telemetry/providers/cloud/CheckoutAttributionTelemetryProvider.ts`:
- Around line 23-33: The SSR guard currently returns early in
extractSearchFromPath and blocks the manual fallback; instead ensure you only
early-return when path is falsy, then attempt to construct new URL only if
typeof window !== 'undefined' (wrap new URL in try/catch), and if window is
undefined or URL construction fails fall back to using path.indexOf('?') to
slice and return the query string; update extractSearchFromPath to perform path
check first, then try the window-dependent URL parsing, and finally the manual
indexOf fallback.
| private extractSearchFromPath(path?: string): string { | ||
| if (!path || typeof window === 'undefined') return '' | ||
|
|
||
| try { | ||
| const url = new URL(path, window.location.origin) | ||
| return url.search | ||
| } catch { | ||
| const queryIndex = path.indexOf('?') | ||
| return queryIndex >= 0 ? path.slice(queryIndex) : '' | ||
| } | ||
| } |
There was a problem hiding this comment.
SSR guard on Line 24 also blocks the window-independent fallback path.
When window is undefined but a valid path with query params is provided (e.g. "/checkout?utm_source=google"), the early return on Line 24 prevents reaching the manual indexOf('?') fallback (lines 30–31), which doesn't need window at all. This silently drops attribution params in SSR/test environments.
Move the window guard to only protect the new URL call:
Suggested fix
private extractSearchFromPath(path?: string): string {
- if (!path || typeof window === 'undefined') return ''
+ if (!path) return ''
try {
+ if (typeof window === 'undefined') throw new Error('no window')
const url = new URL(path, window.location.origin)
return url.search
} catch {
const queryIndex = path.indexOf('?')
return queryIndex >= 0 ? path.slice(queryIndex) : ''
}
}🤖 Prompt for AI Agents
In
`@src/platform/telemetry/providers/cloud/CheckoutAttributionTelemetryProvider.ts`
around lines 23 - 33, The SSR guard currently returns early in
extractSearchFromPath and blocks the manual fallback; instead ensure you only
early-return when path is falsy, then attempt to construct new URL only if
typeof window !== 'undefined' (wrap new URL in try/catch), and if window is
undefined or URL construction fails fall back to using path.indexOf('?') to
slice and return the query string; update extractSearchFromPath to perform path
check first, then try the window-dependent URL parsing, and finally the manual
indexOf fallback.

Summary
Persist checkout attribution metadata across cloud page views and include it in subscription checkout requests so affiliate/ad attribution survives multi-page journeys.
Changes
CheckoutAttributionTelemetryProviderand register it in telemetry init so page views capture attribution query params (with current-URL fallback).im_ref, UTM fields,gclid/gbraid/wbraid, andimpact_click_id./customers/cloud-subscription-checkoutrequest bodies.Review Focus
Screenshots (if applicable)
N/A (no UI changes)