diff --git a/global.d.ts b/global.d.ts index e0a154c3112..b46bc541805 100644 --- a/global.d.ts +++ b/global.d.ts @@ -11,6 +11,12 @@ interface ImpactQueueFunction { a?: unknown[][] } +interface RewardfulGlobal { + referral?: string + affiliate?: { id?: string; token?: string; name?: string } + campaign?: { id?: string; name?: string } +} + type GtagGetFieldName = 'client_id' | 'session_id' | 'session_number' interface GtagGetFieldValueMap { @@ -63,6 +69,7 @@ interface Window { gtag?: GtagFunction ire_o?: string ire?: ImpactQueueFunction + Rewardful?: RewardfulGlobal } interface Navigator { diff --git a/src/platform/telemetry/types.ts b/src/platform/telemetry/types.ts index e606379eb5f..71d47c3607b 100644 --- a/src/platform/telemetry/types.ts +++ b/src/platform/telemetry/types.ts @@ -325,6 +325,7 @@ export interface CheckoutAttributionMetadata { ga_session_id?: string ga_session_number?: string im_ref?: string + rewardful_referral?: string utm_source?: string utm_medium?: string utm_campaign?: string diff --git a/src/platform/telemetry/utils/__tests__/checkoutAttribution.test.ts b/src/platform/telemetry/utils/__tests__/checkoutAttribution.test.ts index a17522c0443..79a67928338 100644 --- a/src/platform/telemetry/utils/__tests__/checkoutAttribution.test.ts +++ b/src/platform/telemetry/utils/__tests__/checkoutAttribution.test.ts @@ -15,6 +15,7 @@ describe('getCheckoutAttribution', () => { } window.gtag = undefined window.ire = undefined + window.Rewardful = undefined window.history.pushState({}, '', '/') }) @@ -228,4 +229,47 @@ describe('getCheckoutAttribution', () => { expect(attribution.im_ref).toBeUndefined() }) + + it('captures Rewardful referral from window.Rewardful', async () => { + window.Rewardful = { + referral: 'rwd-abc-123' + } + + const attribution = await getCheckoutAttribution() + + expect(attribution.rewardful_referral).toBe('rwd-abc-123') + }) + + it('returns undefined Rewardful referral when window.Rewardful is absent', async () => { + const attribution = await getCheckoutAttribution() + + expect(attribution.rewardful_referral).toBeUndefined() + }) + + it('returns undefined Rewardful referral when window.Rewardful.referral is empty', async () => { + window.Rewardful = { referral: '' } + + const attribution = await getCheckoutAttribution() + + expect(attribution.rewardful_referral).toBeUndefined() + }) + + it('captures Rewardful referral alongside Impact attribution', async () => { + window.history.pushState( + {}, + '', + '/?im_ref=impact-url-id&utm_source=affiliate' + ) + window.Rewardful = { + referral: 'rwd-xyz-789' + } + + const attribution = await getCheckoutAttribution() + + expect(attribution).toMatchObject({ + im_ref: 'impact-url-id', + utm_source: 'affiliate', + rewardful_referral: 'rwd-xyz-789' + }) + }) }) diff --git a/src/platform/telemetry/utils/checkoutAttribution.ts b/src/platform/telemetry/utils/checkoutAttribution.ts index c525a35a104..fd9b5640ae1 100644 --- a/src/platform/telemetry/utils/checkoutAttribution.ts +++ b/src/platform/telemetry/utils/checkoutAttribution.ts @@ -180,6 +180,11 @@ async function getGeneratedClickId(): Promise { } } +function getRewardfulReferral(): string | undefined { + if (typeof window === 'undefined') return undefined + return asNonEmptyString(window.Rewardful?.referral) +} + export function captureCheckoutAttributionFromSearch(search: string): void { const fromUrl = readAttributionFromUrl(search) const storedAttribution = readStoredAttribution() @@ -213,11 +218,13 @@ export async function getCheckoutAttribution(): Promise