Skip to content

Commit 4a30b9c

Browse files
authored
Add Google Click ID tracking to Stripe checkout (#8180)
1 parent ef9cf34 commit 4a30b9c

File tree

2 files changed

+43
-9
lines changed

2 files changed

+43
-9
lines changed

apps/dashboard/src/@/actions/stripe-actions.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import "server-only";
22

3-
import { headers } from "next/headers";
3+
import { cookies, headers } from "next/headers";
44
import Stripe from "stripe";
55
import type { Team } from "@/api/team/get-team";
66
import {
@@ -88,6 +88,9 @@ export async function fetchClientSecret(team: Team) {
8888
throw new Error("No customer ID found");
8989
}
9090

91+
// try to get the gclid cookie
92+
const gclid = (await cookies()).get("gclid")?.value;
93+
9194
// Create Checkout Sessions from body params.
9295
const session = await stripe.checkout.sessions.create({
9396
ui_mode: "embedded",
@@ -117,6 +120,8 @@ export async function fetchClientSecret(team: Team) {
117120
missing_payment_method: "cancel",
118121
},
119122
},
123+
// if gclid exists, set it as a metadata field so we can attribute the conversion later
124+
metadata: gclid ? { gclid } : undefined,
120125
},
121126
});
122127

apps/dashboard/src/middleware.ts

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,20 @@ import { defineDashboardChain } from "@/lib/defineDashboardChain";
77
import { isLoginRequired } from "@/utils/auth";
88
import { LAST_VISITED_TEAM_PAGE_PATH } from "./app/(app)/team/components/last-visited-page/consts";
99

10+
type CookiesToSet = Record<
11+
string,
12+
| [string]
13+
| [
14+
string,
15+
{
16+
httpOnly: boolean;
17+
sameSite?: "lax" | "strict" | "none";
18+
secure: boolean;
19+
maxAge: number;
20+
},
21+
]
22+
>;
23+
1024
// ignore assets, api - only intercept page routes
1125
export const config = {
1226
matcher: [
@@ -24,12 +38,12 @@ export const config = {
2438
};
2539

2640
export async function middleware(request: NextRequest) {
27-
const { pathname } = request.nextUrl;
41+
const { pathname, searchParams } = request.nextUrl;
2842

2943
// nebula subdomain handling
3044
const paths = pathname.slice(1).split("/");
3145

32-
let cookiesToSet: Record<string, string> | undefined;
46+
let cookiesToSet: CookiesToSet = {};
3347

3448
const activeAccount = request.cookies.get(COOKIE_ACTIVE_ACCOUNT)?.value;
3549
const authCookie = activeAccount
@@ -56,12 +70,27 @@ export async function middleware(request: NextRequest) {
5670
cookiesToSet = {};
5771
}
5872

59-
cookiesToSet[key] = value;
73+
cookiesToSet[key] = [value];
6074
}
6175
}
6276
}
6377
}
6478

79+
// handle gclid (if it exists in search params)
80+
const gclid = searchParams.get("gclid");
81+
if (gclid) {
82+
cookiesToSet.gclid = [
83+
gclid,
84+
{
85+
// TODO: define conversion window, for now 7d should do fine
86+
maxAge: 7 * 24 * 60 * 60,
87+
httpOnly: false,
88+
sameSite: "lax",
89+
secure: true,
90+
},
91+
];
92+
}
93+
6594
// logged in paths
6695
if (isLoginRequired(pathname)) {
6796
// check if the user is logged in (has a valid auth cookie)
@@ -146,7 +175,7 @@ export async function middleware(request: NextRequest) {
146175
if (cookiesToSet) {
147176
const defaultResponse = NextResponse.next();
148177
for (const entry of Object.entries(cookiesToSet)) {
149-
defaultResponse.cookies.set(entry[0], entry[1]);
178+
defaultResponse.cookies.set(entry[0], entry[1][0], entry[1][1]);
150179
}
151180

152181
return defaultResponse;
@@ -162,15 +191,15 @@ function isPossibleAddressOrENSName(address: string) {
162191
function rewrite(
163192
request: NextRequest,
164193
relativePath: string,
165-
cookiesToSet: Record<string, string> | undefined,
194+
cookiesToSet: CookiesToSet,
166195
) {
167196
const url = request.nextUrl.clone();
168197
url.pathname = relativePath;
169198
const res = NextResponse.rewrite(url);
170199

171200
if (cookiesToSet) {
172201
for (const entry of Object.entries(cookiesToSet)) {
173-
res.cookies.set(entry[0], entry[1]);
202+
res.cookies.set(entry[0], entry[1][0], entry[1][1]);
174203
}
175204
}
176205

@@ -184,7 +213,7 @@ function redirect(
184213
| {
185214
searchParams?: string;
186215
permanent?: boolean;
187-
cookiesToSet?: Record<string, string> | undefined;
216+
cookiesToSet?: CookiesToSet;
188217
}
189218
| undefined,
190219
) {
@@ -196,7 +225,7 @@ function redirect(
196225

197226
if (options?.cookiesToSet) {
198227
for (const entry of Object.entries(options.cookiesToSet)) {
199-
res.cookies.set(entry[0], entry[1]);
228+
res.cookies.set(entry[0], entry[1][0], entry[1][1]);
200229
}
201230
}
202231

0 commit comments

Comments
 (0)