I have GA4 running on my site, and in my backend I store the user’s client_id and session_id. With measurement protocol on our backend, I send a custom trial started event when a user starts their trial, and I send a custom subscription paid event when a user pays for a full month. Both of these events use the same function in my code, so I know nothing is shifting between them:
# sends google analytics event
def send_ga4_event(event_name, customer_id):
measurement_id = os.getenv("GA_MEASUREMENT_ID")
api_secret = os.getenv("GA_MP_SECRET")
ga_debug_mode = os.getenv("GA_DEBUG_MODE") == "true"
user = get_user(stripe_id=customer_id, tag="send_ga4_event")
if not user:
logging.warn("send_ga4_event: User doesn't exist in database")
return False
elif not user.stripe_email:
logging.warn("send_ga4_event: No stripe email associated with user")
return False
elif not user.ga_client_id:
logging.warn("send_ga4_event: No ga_client_id associated with user")
return False
if not user.ga_session_id:
logging.warn("send_ga4_event: No ga session id associated with user, continuing anyway.")
"""Send an event to GA4 with a hashed email."""
hashed_email = hashlib.sha256(user.stripe_email.lower().encode()).hexdigest()
params = {"debug_mode": ga_debug_mode}
if user.ga_session_id:
# logging.debug(f"send_ga4_event: USER {user}")
params["session_id"] = user.ga_session_id
event_data = {
"client_id": user.ga_client_id,
"user_id": hashed_email,
"non_personalized_ads": False,
"events": [{"name": event_name, "params": params}],
}
headers = {"Content-Type": "application/json"}
url = f"https://www.google-analytics.com/mp/collect?measurement_id={measurement_id}&api_secret={api_secret}"
response = requests.post(url, headers=headers, data=json.dumps(event_data))
if response.ok:
logging.debug(
f"send_ga4_event: event_name {event_name}, customer_id {customer_id}, email {hashed_email}, measure id {measurement_id}, api secret {api_secret} [{response.status_code}, {response.text}]"
)
return True
else:
logging.error(
f"send_ga4_event: event_name {event_name}, customer_id {customer_id}, email {hashed_email}, measure id {measurement_id}, api secret {api_secret} [{response.status_code}, {response.text}]"
)
return False
When I say attribution isn’t correct, I mean the subscription paid event shows up in the traffic report on GA4 as ‘unassigned’. This issue originally happened for trial started events too, but once I added session_id to the events, it attributed correctly.
In the data being sent, I send their client_id, user_id (which is their email hashed), and inside ‘events’ i send ‘session_id’.
From reading the docs, I was under the impression I should be updating the session_id and client_id stored in our backend every time it’s changed on their browser. The docs sound like it should connect any sessions that go back 90 days. The time between a trial start and a subscription paid is one week, so this shouldn’t be an issue.
Has anyone experienced this? if so, how do you fix it?