From 2aa05311f554d44663bc206f69420c79d439bc82 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 8 Nov 2026 16:15:57 +0000 Subject: [PATCH] feat(startups): send Meta InitiateCheckout and Subscribe from the checkout The Startups landing page now fires Lead on the click into the checkout, so the rest of the funnel needs its own events on the same pixel: InitiateCheckout when Paddle opens (plan list price in USD) and Subscribe on a completed checkout (Paddle total minus tax, in the checkout currency), with the transaction id as the eventID so a later Conversions API event can be deduplicated. trackEvent takes the params for this. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01FodmVu1MjgKhW4EZtmBozt --- packages/shared/src/contexts/PixelsContext.ts | 6 +++- .../components/checkout/StartupsCheckout.tsx | 28 +++++++++++++++++-- packages/webapp/context/PixelsContext.tsx | 9 ++++-- 3 files changed, 38 insertions(+), 5 deletions(-) diff --git a/packages/shared/src/contexts/PixelsContext.ts b/packages/shared/src/contexts/PixelsContext.ts index 61f9492a002..8bdbe4dc6a8 100644 --- a/packages/shared/src/contexts/PixelsContext.ts +++ b/packages/shared/src/contexts/PixelsContext.ts @@ -8,7 +8,11 @@ export interface PixelsContextData { currency: string, transactionId: string, ) => void; - trackEvent: (eventName: string) => void; + trackEvent: ( + eventName: string, + params?: Record, + options?: { eventId?: string }, + ) => void; } export const PixelsContext = createContext({ diff --git a/packages/shared/src/features/squads/components/checkout/StartupsCheckout.tsx b/packages/shared/src/features/squads/components/checkout/StartupsCheckout.tsx index f0942340f78..4f306b2ec03 100644 --- a/packages/shared/src/features/squads/components/checkout/StartupsCheckout.tsx +++ b/packages/shared/src/features/squads/components/checkout/StartupsCheckout.tsx @@ -34,6 +34,7 @@ import { useAuthContext } from '../../../../contexts/AuthContext'; import { useSquadCreate } from '../../../../hooks/squads/useSquadCreate'; import { useSquad } from '../../../../hooks/squads/useSquad'; import { usePaddlePayment } from '../../../../hooks/usePaddlePayment'; +import { usePixelsContext } from '../../../../contexts/PixelsContext'; import { formatCurrency } from '../../../../lib/utils'; import { useViewSize, ViewSize } from '../../../../hooks/useViewSize'; import type { Squad } from '../../../../graphql/sources'; @@ -59,6 +60,7 @@ import { getSquadPlanTitle, isSquadPlan, getSquadPlanCashback, + SQUAD_PLAN_LIST_PRICE_USD, SQUAD_PLAN_SEATS, SquadPlan, squadPlanDetails, @@ -579,9 +581,23 @@ const PaymentPanel = ({ onPaid: () => void; }): ReactElement => { const cashbackPromo = useStartupsCashbackPromo(); + const { trackEvent } = usePixelsContext(); const { openCheckout, isPaddleReady, checkoutTotals } = usePaddlePayment({ priceType: PurchaseType.Startups, - successCallback: onPaid, + successCallback: (event) => { + const totals = event?.data?.totals; + trackEvent( + 'Subscribe', + { + content_name: 'startups', + content_category: plan, + value: totals ? totals.total - totals.tax : 0, + currency: event?.data?.currency_code, + }, + { eventId: event?.data?.transaction_id }, + ); + onPaid(); + }, origin: Origin.StartupsCheckout, logExtra: { plan, source_id: company.id }, }); @@ -602,10 +618,18 @@ const PaymentPanel = ({ return; } + if (!openedRef.current) { + trackEvent('InitiateCheckout', { + content_name: 'startups', + content_category: plan, + value: SQUAD_PLAN_LIST_PRICE_USD[plan], + currency: 'USD', + }); + } // A second call while the frame is open swaps the item in place openedRef.current = priceId; openCheckout({ priceId, customData: { source_id: company.id } }); - }, [company.id, isPaddleReady, openCheckout, plan]); + }, [company.id, isPaddleReady, openCheckout, plan, trackEvent]); useEffect(() => { if (isPaddleReady) { diff --git a/packages/webapp/context/PixelsContext.tsx b/packages/webapp/context/PixelsContext.tsx index 2ae76b2af61..aa2c0470be4 100644 --- a/packages/webapp/context/PixelsContext.tsx +++ b/packages/webapp/context/PixelsContext.tsx @@ -76,12 +76,17 @@ function WebPixelsProvider({ // }); // } }, - trackEvent(eventName: string) { + trackEvent(eventName, params, options) { if (typeof globalThis.gtag === 'function') { globalThis.gtag('event', eventName.replace(/ /g, '_').toLowerCase()); } if (typeof globalThis.fbq === 'function') { - globalThis.fbq('track', eventName); + globalThis.fbq( + 'track', + eventName, + params, + options?.eventId ? { eventID: options.eventId } : undefined, + ); } }, };