diff --git a/src/app/(auth)/register/page.tsx b/src/app/(auth)/register/page.tsx
index 8094de9..7d29fbb 100644
--- a/src/app/(auth)/register/page.tsx
+++ b/src/app/(auth)/register/page.tsx
@@ -1,6 +1,8 @@
import { redirect } from "next/navigation";
+import { cookies } from "next/headers";
import { getSettings } from "@/lib/settings";
import { RegisterForm } from "./register-form";
+import { UTM_COOKIE, parseUtmCookie, pickUtm } from "@/lib/utm-cookie";
export default async function RegisterPage({
searchParams,
@@ -15,6 +17,11 @@ export default async function RegisterPage({
const sp = await searchParams;
const str = (v: string | string[] | undefined) => (Array.isArray(v) ? v[0] : v) ?? "";
+ // Метки: из адреса, а если их нет — из cookie лендинга (src/lib/utm-cookie.ts).
+ const utm = pickUtm(
+ { utmSource: str(sp.utm_source), utmMedium: str(sp.utm_medium), utmCampaign: str(sp.utm_campaign) },
+ parseUtmCookie((await cookies()).get(UTM_COOKIE)?.value),
+ );
return (
@@ -34,9 +41,9 @@ export default async function RegisterPage({
termsUrl={settings.termsUrl}
offerUrl={settings.offerUrl}
archetype={str(sp.archetype)}
- utmSource={str(sp.utm_source)}
- utmMedium={str(sp.utm_medium)}
- utmCampaign={str(sp.utm_campaign)}
+ utmSource={utm.utmSource}
+ utmMedium={utm.utmMedium}
+ utmCampaign={utm.utmCampaign}
gclid={str(sp.gclid)}
/>
diff --git a/src/lib/__tests__/utm-cookie.test.ts b/src/lib/__tests__/utm-cookie.test.ts
new file mode 100644
index 0000000..b29a909
--- /dev/null
+++ b/src/lib/__tests__/utm-cookie.test.ts
@@ -0,0 +1,40 @@
+import { describe, it, expect } from "vitest";
+import { cleanUtm, parseUtmCookie, pickUtm } from "../utm-cookie";
+
+describe("utm-cookie", () => {
+ it("разбирает сырую строку запроса из cookie", () => {
+ expect(parseUtmCookie("utm_source=youtube&utm_medium=video&utm_campaign=test-20261103&fbclid=x")).toEqual({
+ utmSource: "youtube",
+ utmMedium: "video",
+ utmCampaign: "test-20261103",
+ });
+ });
+
+ it("понимает percent-кодирование", () => {
+ expect(parseUtmCookie("utm_campaign=a%2Db_c.d").utmCampaign).toBe("a-b_c.d");
+ });
+
+ it("пустая и битая cookie — пустые метки", () => {
+ expect(parseUtmCookie(undefined)).toEqual({ utmSource: "", utmMedium: "", utmCampaign: "" });
+ expect(parseUtmCookie("")).toEqual({ utmSource: "", utmMedium: "", utmCampaign: "" });
+ expect(parseUtmCookie("%%%")).toEqual({ utmSource: "", utmMedium: "", utmCampaign: "" });
+ });
+
+ it("отбрасывает значения не из белого списка символов и слишком длинные", () => {
+ expect(cleanUtm("ok-1")).toBe("ok-1");
+ expect(cleanUtm("