From 0fe740362559632673b22f9e22dd28b673894daf Mon Sep 17 00:00:00 2001 From: Robert Ing Date: Fri, 10 Apr 2026 14:08:24 -0400 Subject: [PATCH] feat: Add base64-encoded MPID query parameter for Google Marketing Platform cookie sync When the cookie sync partner is DoubleclickDFP (module ID 41), append a `google_hm` query parameter containing the base64-encoded MPID to the cookie sync URL. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/cookieSyncManager.ts | 7 ++- src/utils.ts | 16 ++++-- test/jest/cookieSyncManager.spec.ts | 86 +++++++++++++++++++++++++++++ 3 files changed, 102 insertions(+), 7 deletions(-) diff --git a/src/cookieSyncManager.ts b/src/cookieSyncManager.ts index 7a4ea9b63..d69e4c246 100644 --- a/src/cookieSyncManager.ts +++ b/src/cookieSyncManager.ts @@ -137,8 +137,11 @@ export default function CookieSyncManager( // It is optional but to simplify the code, we add it for all Trade // // Desk cookie syncs. const domain = moduleId === PARTNER_MODULE_IDS.TradeDesk ? window.location.hostname : undefined; - // Add domain parameter for Trade Desk - const fullUrl = createCookieSyncUrl(mpid, pixelUrl, redirectUrl, domain); + + // Google Marketing Platform requires a base64-encoded MPID query parameter + const base64Mpid = moduleId === PARTNER_MODULE_IDS.DoubleclickDFP ? btoa(mpid) : undefined; + + const fullUrl = createCookieSyncUrl(mpid, pixelUrl, redirectUrl, domain, base64Mpid); self.performCookieSync( fullUrl, diff --git a/src/utils.ts b/src/utils.ts index 69d26ec2d..ad35419d8 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -207,7 +207,8 @@ const createCookieSyncUrl = ( mpid: MPID, pixelUrl: string, redirectUrl?: string, - domain?: string + domain?: string, + base64Mpid?: string ): string => { const modifiedPixelUrl = replaceAmpWithAmpersand(pixelUrl); const modifiedDirectUrl = redirectUrl @@ -215,18 +216,23 @@ const createCookieSyncUrl = ( : null; let url = replaceMPID(modifiedPixelUrl, mpid); - + const redirect = modifiedDirectUrl ? replaceMPID(modifiedDirectUrl, mpid) : ''; - + let fullUrl = url + encodeURIComponent(redirect); - + if (domain) { const separator = fullUrl.includes('?') ? '&' : '?'; fullUrl += `${separator}domain=${domain}`; } - + + if (base64Mpid) { + const separator = fullUrl.includes('?') ? '&' : '?'; + fullUrl += `${separator}google_hm=${base64Mpid}`; + } + return fullUrl; }; diff --git a/test/jest/cookieSyncManager.spec.ts b/test/jest/cookieSyncManager.spec.ts index 71139ca7f..ce237044e 100644 --- a/test/jest/cookieSyncManager.spec.ts +++ b/test/jest/cookieSyncManager.spec.ts @@ -776,6 +776,92 @@ describe('CookieSyncManager', () => { ); }); }); + + describe('Google Marketing Platform (DoubleclickDFP) base64 MPID', () => { + it('should add base64-encoded MPID parameter for DoubleclickDFP (module ID 41)', () => { + const gmpPixelSettings: IPixelConfiguration = { + ...pixelSettings, + moduleId: PARTNER_MODULE_IDS.DoubleclickDFP, // 41 + pixelUrl: 'https://cm.g.doubleclick.net/pixel?google_nid=abc123', + redirectUrl: '', + }; + + const mockMPInstance = ({ + _Store: { + webviewBridgeEnabled: false, + pixelConfigurations: [gmpPixelSettings], + }, + _CookieConsentManager: { getNoFunctional: jest.fn().mockReturnValue(false) }, + _Persistence: { + getPersistence: () => ({testMPID: { + csd: {} + }}), + }, + _Consent: { + isEnabledForUserConsent: jest.fn().mockReturnValue(true), + }, + Identity: { + getCurrentUser: jest.fn().mockReturnValue({ + getMPID: () => testMPID, + }), + }, + } as unknown) as IMParticleWebSDKInstance; + + const cookieSyncManager = new CookieSyncManager(mockMPInstance); + cookieSyncManager.performCookieSync = jest.fn(); + + cookieSyncManager.attemptCookieSync(testMPID, true); + + expect(cookieSyncManager.performCookieSync).toHaveBeenCalledWith( + `https://cm.g.doubleclick.net/pixel?google_nid=abc123&google_hm=${btoa(testMPID)}`, + '41', + testMPID, + {}, + ); + }); + + it('should not add base64 MPID parameter for non-DoubleclickDFP partners', () => { + const appNexusPixelSettings: IPixelConfiguration = { + ...pixelSettings, + moduleId: PARTNER_MODULE_IDS.AppNexus, // 50 + pixelUrl: 'https://ib.adnxs.com/cookie_sync?adv=abc123', + redirectUrl: '', + }; + + const mockMPInstance = ({ + _Store: { + webviewBridgeEnabled: false, + pixelConfigurations: [appNexusPixelSettings], + }, + _CookieConsentManager: { getNoFunctional: jest.fn().mockReturnValue(false) }, + _Persistence: { + getPersistence: () => ({testMPID: { + csd: {} + }}), + }, + _Consent: { + isEnabledForUserConsent: jest.fn().mockReturnValue(true), + }, + Identity: { + getCurrentUser: jest.fn().mockReturnValue({ + getMPID: () => testMPID, + }), + }, + } as unknown) as IMParticleWebSDKInstance; + + const cookieSyncManager = new CookieSyncManager(mockMPInstance); + cookieSyncManager.performCookieSync = jest.fn(); + + cookieSyncManager.attemptCookieSync(testMPID, true); + + expect(cookieSyncManager.performCookieSync).toHaveBeenCalledWith( + 'https://ib.adnxs.com/cookie_sync?adv=abc123', + '50', + testMPID, + {}, + ); + }); + }); }); describe('PARTNER_MODULE_IDS', () => {