This commit is contained in:
dev.daminik00 2025-12-24 21:42:28 +03:00
parent 331b9d8547
commit 654e8899a8
4 changed files with 119 additions and 5 deletions

View File

@ -18,6 +18,8 @@ import { getFormattedPrice } from "@/shared/utils/price";
import { formatPeriod, formatPeriodHyphen } from "@/shared/utils/period";
import { useState } from "react";
import { getTrackingCookiesForRedirect } from "@/shared/utils/cookies";
import { getClientSessionId } from "@/shared/session/sessionId";
import { getStateParamForRedirect } from "@/shared/utils/url";
interface SpecialOfferProps {
funnel: FunnelDefinition;
@ -70,9 +72,25 @@ export function SpecialOfferTemplate({
return;
}
setIsLoadingRedirect(true);
const redirectUrl = `${paymentUrl}?paywallId=${paywallId}&placementId=${placementId}&productId=${productId}&jwtToken=${token}&price=${(
// Build redirect URL with payment params
const baseParams = `paywallId=${paywallId}&placementId=${placementId}&productId=${productId}&jwtToken=${token}&price=${(
(trialPrice || 100) / 100
).toFixed(2)}&currency=${currency}&${getTrackingCookiesForRedirect()}`;
).toFixed(2)}&currency=${currency}`;
// Add sessionId
const sessionId = getClientSessionId();
const sessionParam = sessionId ? `&sessionId=${sessionId}` : "";
// Add state param with current UTM (base64 encoded JSON)
const stateParam = getStateParamForRedirect();
const stateStr = stateParam ? `&state=${stateParam}` : "";
// Add tracking cookies
const trackingCookies = getTrackingCookiesForRedirect();
const trackingStr = trackingCookies ? `&${trackingCookies}` : "";
const redirectUrl = `${paymentUrl}?${baseParams}${sessionParam}${stateStr}${trackingStr}`;
return window.location.replace(redirectUrl);
};

View File

@ -41,6 +41,8 @@ import { getFormattedPrice } from "@/shared/utils/price";
import { useClientToken } from "@/hooks/auth/useClientToken";
import { formatPeriod, formatPeriodHyphen } from "@/shared/utils/period";
import { getTrackingCookiesForRedirect } from "@/shared/utils/cookies";
import { getClientSessionId } from "@/shared/session/sessionId";
import { getStateParamForRedirect } from "@/shared/utils/url";
interface TrialPaymentTemplateProps {
funnel: FunnelDefinition;
@ -94,9 +96,25 @@ export function TrialPaymentTemplate({
return;
}
setLoadingButtonIndex(buttonIndex);
const redirectUrl = `${paymentUrl}?paywallId=${paywallId}&placementId=${placementId}&productId=${productId}&jwtToken=${token}&price=${(
// Build redirect URL with payment params
const baseParams = `paywallId=${paywallId}&placementId=${placementId}&productId=${productId}&jwtToken=${token}&price=${(
(trialPrice || 100) / 100
).toFixed(2)}&currency=${currency}&${getTrackingCookiesForRedirect()}`;
).toFixed(2)}&currency=${currency}`;
// Add sessionId
const sessionId = getClientSessionId();
const sessionParam = sessionId ? `&sessionId=${sessionId}` : "";
// Add state param with current UTM (base64 encoded JSON)
const stateParam = getStateParamForRedirect();
const stateStr = stateParam ? `&state=${stateParam}` : "";
// Add tracking cookies
const trackingCookies = getTrackingCookiesForRedirect();
const trackingStr = trackingCookies ? `&${trackingCookies}` : "";
const redirectUrl = `${paymentUrl}?${baseParams}${sessionParam}${stateStr}${trackingStr}`;
return window.location.replace(redirectUrl);
};

View File

@ -112,7 +112,7 @@ function trackFacebookPixelEvent(
// Map EnteredEmail to Lead for Facebook
const fbEvent = event === AnalyticsEvent.ENTERED_EMAIL ? AnalyticsEvent.LEAD : event;
window.fbq("track", fbEvent, options);
window.fbq?.("track", fbEvent, options);
console.log(`[FB] Event: ${fbEvent}`, options);
}

View File

@ -17,3 +17,81 @@ export const parseQueryParams = () => {
return result;
};
// Params that should NOT be included in state (they are passed separately)
const EXCLUDED_STATE_PARAMS = [
"paywallId",
"placementId",
"productId",
"jwtToken",
"price",
"currency",
"fb_pixels",
"sessionId",
"state",
// Tracking cookies (passed separately)
"_fbc",
"_fbp",
"_ym_uid",
"_ym_d",
"_ym_isad",
"_ym_visorc",
"yandexuid",
"ymex",
];
/**
* Get current query params that should be passed between screens and to payment
* Includes ALL params except internal ones (productId, placementId, etc.)
* Works with utm_*, fbclid, gclid, and any other marketing params
*/
export const getCurrentQueryParams = (): Record<string, string> => {
if (typeof window === "undefined") return {};
const params = parseQueryParams();
const utmParams: Record<string, string> = {};
for (const [key, value] of Object.entries(params)) {
const isExcluded =
EXCLUDED_STATE_PARAMS.includes(key) ||
key.startsWith("_ga") ||
key.startsWith("_gid");
if (!isExcluded && value) {
utmParams[key] = value;
}
}
return utmParams;
};
/**
* Encode params as base64 JSON for state parameter
* Uses URL-safe base64 encoding
*/
export const encodeStateParam = (params: Record<string, string>): string => {
if (Object.keys(params).length === 0) return "";
try {
const json = JSON.stringify(params);
// Use btoa for base64, replace unsafe chars for URL
const base64 = btoa(json)
.replace(/\+/g, "-")
.replace(/\//g, "_")
.replace(/=+$/, "");
return base64;
} catch {
return "";
}
};
/**
* Get base64-encoded state parameter with current query params
*/
export const getStateParamForRedirect = (): string => {
const params = getCurrentQueryParams();
return encodeStateParam(params);
};
// Backward compatibility alias
export const getCurrentUtmParams = getCurrentQueryParams;