fix(web): fire checkout_started when the payment step is reached (all plans)
checkout_started only fired for the "full" plan (plus a mostly-unreachable sticky-CTA branch), so non-full-plan users reached payment without it ever firing — PostHog showed fewer checkout_started (1 person) than the downstream payment_initiated (3), which is impossible in a clean funnel. Move it to a step-driven effect that fires once when the payment step is reached, via any path (plan card, sticky CTA, or a prefilled pending plan), re-arming if the user returns to plan selection. Fixes the Monetization and Ödeme funnels at the checkout step and keeps plan -> checkout meaningful (brand-selection drop-off now shows up instead of a trivial 100% step). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -193,6 +193,7 @@ export function SubscriptionPage() {
|
||||
const [onboardingPhase, setOnboardingPhase] = useState<"provisioning" | "completed" | null>(null);
|
||||
const [animationEnded, setAnimationEnded] = useState(false);
|
||||
const hasFiredRef = useRef(false);
|
||||
const checkoutStartedRef = useRef(false);
|
||||
const [isDark] = useState(() => {
|
||||
const theme = getUserSettings().theme ?? "dark";
|
||||
return theme === "system"
|
||||
@@ -332,6 +333,22 @@ export function SubscriptionPage() {
|
||||
return () => clearTimeout(timer);
|
||||
}, [onboardingPhase]);
|
||||
|
||||
// checkout_started fires once when the user reaches the payment step, via any
|
||||
// path (plan card, sticky CTA, or a prefilled pending plan). It previously
|
||||
// fired only for the "full" plan, under-counting vs the downstream
|
||||
// payment_initiated; gating on the payment step fixes that and keeps the
|
||||
// plan -> checkout funnel step meaningful (brand-selection drop-off shows up).
|
||||
useEffect(() => {
|
||||
if (step === "plan") {
|
||||
checkoutStartedRef.current = false; // re-arm if the user restarts checkout
|
||||
return;
|
||||
}
|
||||
if (step === "payment" && selectedPlanKey && !checkoutStartedRef.current) {
|
||||
checkoutStartedRef.current = true;
|
||||
capture("checkout_started", { plan: selectedPlanKey, period: billingPeriod });
|
||||
}
|
||||
}, [step, selectedPlanKey, billingPeriod]);
|
||||
|
||||
useEffect(() => {
|
||||
if (search.stripe === "success" && !stripeResultRef.current) {
|
||||
stripeResultRef.current = true;
|
||||
@@ -423,9 +440,8 @@ export function SubscriptionPage() {
|
||||
capture("plan_selected", { plan: planKey });
|
||||
setSelectedPlanKey(planKey);
|
||||
setSelectedBrandIds([]);
|
||||
if (planKey === "full") {
|
||||
capture("checkout_started", { plan: planKey, period: billingPeriod });
|
||||
}
|
||||
// checkout_started now fires from the step-driven effect when the payment
|
||||
// step is reached (covers every plan + advance path), so we don't fire here.
|
||||
setStep(planKey === "full" ? "payment" : "brands");
|
||||
}
|
||||
|
||||
@@ -764,7 +780,6 @@ export function SubscriptionPage() {
|
||||
onAdvance={() => {
|
||||
if (step === "plan") {
|
||||
if (!selectedPlanKey) return;
|
||||
capture("checkout_started", { plan: selectedPlanKey, period: billingPeriod });
|
||||
setStep(isFullPlan ? "payment" : "brands");
|
||||
} else if (step === "brands") {
|
||||
handleAdvanceFromBrands();
|
||||
|
||||
Reference in New Issue
Block a user