Implementing Aliasing for User Tracking with Device ID and Custom Events
🎫 I tried to implement aliasing (//Attach identity…)so that the user starting the funnel by device id would match the user after they convert and are assigned a new unique ID. I also tried creating a few custom events (// Track Stripe payment) and (// Track plan button) rather than rely on Page Views as follows:
Script for Mixpanel Auto Track appears here…
<script>
function initCustomJSCode() {
// Begin custom code, CIrcle wraps in function
// disable Mixpanel in page edit mode
if (document.querySelector('[data-testid="builder-ui-shell"]')) {
console.warn('Mixpanel disabled in Circle edit mode');
return;
}
// Ensure Mixpanel library is available
if (!window.mixpanel || typeof mixpanel.init !== 'function') {
return;
}
// Initialize Mixpanel once
if (!window.__mixpanel_initialized__) {
mixpanel.init('TOKEN_HERE', {
record_sessions_percent: 100,
record_heatmap_data: true,
autocapture: {
pageview: "full-url",
click: true,
input: true,
scroll: true,
submit: true,
capture_text_content: true
}
});
window.__mixpanel_initialized__ = true;
}
// Attach identity using Circle publicUid
function attachIdentityWhenReady() {
if (
!window.mixpanel ||
!window.circleUser ||
!window.circleUser.publicUid
) {
return false;
}
const user = window.circleUser;
const userId = user.publicUid;
const currentId = mixpanel.get_distinct_id();
// Alias only if currently anonymous
if (currentId && currentId.startsWith("$device:")) {
mixpanel.alias(userId);
}
mixpanel.identify(userId);
mixpanel.people.set({
$email: user.email,
$name: user.name,
firstName: user.firstName,
lastName: user.lastName,
isAdmin: user.isAdmin === 'true',
isModerator: user.isModerator === 'true',
profileUrl: user.profileUrl
});
return true;
}
// Poll until Circle user object becomes available
let identityAttempts = 0;
const identityInterval = setInterval(function () {
if (attachIdentityWhenReady()) {
clearInterval(identityInterval);
}
identityAttempts++;
if (identityAttempts > 20) {
clearInterval(identityInterval);
}
}, 500);
// Process paywall conversion safely on every page load
(function processPaywallConversion() {
const raw = localStorage.getItem("__circle_paywall_conversion__");
if (!raw) return;
if (!window.mixpanel || typeof mixpanel.track !== "function") {
return;
}
let data;
try {
data = JSON.parse(raw);
} catch (e) {
localStorage.removeItem("__circle_paywall_conversion__");
return;
}
const priceUsd = data.plan_price_amount
? data.plan_price_amount / 100
: 0;
mixpanel.people.set({
plan_internal_name: data.plan_internal_name,
plan_display_name: data.plan_display_name,
plan_price_amount: data.plan_price_amount,
plan_price_amount_usd: priceUsd,
plan_price_interval: data.plan_price_interval,
plan_status: "active",
last_checkout_date: data.captured_at
});
mixpanel.track("Paywall Converted", {
plan_internal_name: data.plan_internal_name,
plan_display_name: data.plan_display_name,
plan_price_amount: data.plan_price_amount,
plan_price_amount_usd: priceUsd,
plan_price_interval: data.plan_price_interval,
amount_paid: data.amount_paid,
coupon_code: data.plan_coupon_code,
source: "circle_paywall"
});
localStorage.removeItem("__circle_paywall_conversion__");
})();
// Track plan button clicks
document.addEventListener("click", function (e) {
if (!window.mixpanel) return;
const btn = e.target.closest("a[href*='/checkout/']");
if (!btn) return;
const href = btn.getAttribute("href") || "";
let plan = null;
if (href.includes("/checkout/basic")) {
plan = "Basic";
} else if (href.includes("/checkout/plus")) {
plan = "Plus";
} else if (href.includes("/checkout/premium")) {
plan = "Premium";
}
if (!plan) return;
mixpanel.track("Plan Button Clicked", {
plan: plan,
destination: href,
page_path: window.location.pathname
});
});
// Track Stripe payment form submission
document.addEventListener("submit", function (e) {
if (!window.mixpanel) return;
const form = e.target;
if (!form.matches("form[data-testid='checkout-form']")) return;
const path = window.location.pathname;
let plan = null;
if (path.includes("/checkout/basic")) {
plan = "Basic";
} else if (path.includes("/checkout/plus")) {
plan = "Plus";
} else if (path.includes("/checkout/premium")) {
plan = "Premium";
}
mixpanel.track("Payment Submitted", {
plan: plan,
page_path: path
});
});
// Circle end wrapper
}
document.addEventListener("DOMContentLoaded", initCustomJSCode);
</script>The Plan Button Clicked event appears in the Event Feed, but I cannot use it to create an Insight Report or Funnel - the 'Plan Button Clicked' event does not appear as an Event choice, even though it is in the feed. From what I can see, my attempt to match the user from start to finish in the funnel is not working either.
