🎫 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.
This post is now synced! Check it out here: _Implementing Aliasing for User Tracking with Device ID and Custom Events_ ⤵️ Any future comments will be automatically posted to the thread
Thanks for your question! Hopefully, your peers in the community can help answer your questions.
However, if you need some review from our Support team, leave 🎫 on your question and we should reach out shortly! If you do this, please make sure to not send duplicate tickets through the Support webform. Appreciate it!
In the meantime, let us know if this feedback is similar to any previous discussions below:
UPDATE II: After 39 tests and 2 head-smashing days of trying to get this to work, I now have it working! If anyone ever sees this or replies, I will do my best to explain the solution. In summary, the problem was that I was trying to rely on [Auto] Page View and filtering by URL, BUT the Page View event happened before the get_distinct_id matching could fire. I replaced [Auto] Page View with a custom event Landing Page Viewed and set this event to not fire until get_distinct_id could fire:
<script>
document.addEventListener("DOMContentLoaded", function () {
function waitForIdentity() {
if (!window.mixpanel) {
setTimeout(waitForIdentity, 200);
return;
}
if (typeof mixpanel.get_distinct_id !== "function") {
setTimeout(waitForIdentity, 200);
return;
}
var id = mixpanel.get_distinct_id();
if (!id) {
setTimeout(waitForIdentity, 200);
return;
}
mixpanel.track("Landing Page Viewed", {
landing_page: "/apply-now"
});
}
waitForIdentity();
});
</script>This is all very messy, but seemingly necessary, and will need to be updated for other funnel entry points. I think I will create a new custom event that looks for any button that links to the checkout pages by looking at the href.
This issue has been resolved. Here is what I did:
Removed ALL event creation from the < script>
Rely on Mixpanel UI to create custom events
Use <script> to ensure identity is managed before and after account creation so that the same user can complete all funnel steps as the same person.
