Skip to content

Commit 670b630

Browse files
committed
feat: Stripe customer update fixes
1 parent 2da01f7 commit 670b630

File tree

5 files changed

+139
-42
lines changed

5 files changed

+139
-42
lines changed

apps/web/actions/organization/manage-billing.ts

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { users } from "@cap/database/schema";
66
import { serverEnv } from "@cap/env";
77
import { stripe } from "@cap/utils";
88
import { eq } from "drizzle-orm";
9+
import type Stripe from "stripe";
910

1011
export async function manageBilling() {
1112
const user = await getCurrentUser();
@@ -16,13 +17,30 @@ export async function manageBilling() {
1617
}
1718

1819
if (!user.stripeCustomerId) {
19-
const customer = await stripe().customers.create({
20+
const existingCustomers = await stripe().customers.list({
2021
email: user.email,
21-
metadata: {
22-
userId: user.id,
23-
},
22+
limit: 1,
2423
});
2524

25+
let customer: Stripe.Customer;
26+
if (existingCustomers.data.length > 0 && existingCustomers.data[0]) {
27+
customer = existingCustomers.data[0];
28+
29+
customer = await stripe().customers.update(customer.id, {
30+
metadata: {
31+
...customer.metadata,
32+
userId: user.id,
33+
},
34+
});
35+
} else {
36+
customer = await stripe().customers.create({
37+
email: user.email,
38+
metadata: {
39+
userId: user.id,
40+
},
41+
});
42+
}
43+
2644
await db()
2745
.update(users)
2846
.set({

apps/web/app/api/desktop/[...route]/root.ts

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { zValidator } from "@hono/zod-validator";
77
import { eq } from "drizzle-orm";
88
import { Hono } from "hono";
99
import { PostHog } from "posthog-node";
10+
import type Stripe from "stripe";
1011
import { z } from "zod";
1112
import { withAuth } from "../../utils";
1213

@@ -72,7 +73,6 @@ app.get("/org-custom-domain", async (c) => {
7273
.leftJoin(organizations, eq(users.activeOrganizationId, organizations.id))
7374
.where(eq(users.id, user.id));
7475

75-
// Ensure custom domain has https:// prefix
7676
let customDomain = result?.customDomain ?? null;
7777
if (
7878
customDomain &&
@@ -151,19 +151,43 @@ app.post(
151151
let customerId = user.stripeCustomerId;
152152

153153
if (user.stripeCustomerId === null) {
154-
console.log("[POST] Creating new Stripe customer");
155-
const customer = await stripe().customers.create({
154+
console.log(
155+
"[POST] Checking for existing Stripe customer for email:",
156+
user.email,
157+
);
158+
159+
const existingCustomers = await stripe().customers.list({
156160
email: user.email,
157-
metadata: { userId: user.id },
161+
limit: 1,
158162
});
159163

164+
let customer: Stripe.Customer;
165+
if (existingCustomers.data.length > 0 && existingCustomers.data[0]) {
166+
customer = existingCustomers.data[0];
167+
console.log("[POST] Found existing Stripe customer:", customer.id);
168+
169+
customer = await stripe().customers.update(customer.id, {
170+
metadata: {
171+
...customer.metadata,
172+
userId: user.id,
173+
},
174+
});
175+
console.log("[POST] Updated existing customer metadata with userId");
176+
} else {
177+
console.log("[POST] Creating new Stripe customer");
178+
customer = await stripe().customers.create({
179+
email: user.email,
180+
metadata: { userId: user.id },
181+
});
182+
console.log("[POST] Created Stripe customer:", customer.id);
183+
}
184+
160185
await db()
161186
.update(users)
162187
.set({ stripeCustomerId: customer.id })
163188
.where(eq(users.id, user.id));
164189

165190
customerId = customer.id;
166-
console.log("[POST] Created Stripe customer:", customerId);
167191
}
168192

169193
console.log("[POST] Creating checkout session");

apps/web/app/api/settings/billing/manage/route.ts

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { serverEnv } from "@cap/env";
55
import { stripe } from "@cap/utils";
66
import { eq } from "drizzle-orm";
77
import { type NextRequest, NextResponse } from "next/server";
8+
import type Stripe from "stripe";
89

910
export async function POST(request: NextRequest) {
1011
const user = await getCurrentUser();
@@ -17,13 +18,30 @@ export async function POST(request: NextRequest) {
1718
}
1819

1920
if (!user.stripeCustomerId) {
20-
const customer = await stripe().customers.create({
21+
const existingCustomers = await stripe().customers.list({
2122
email: user.email,
22-
metadata: {
23-
userId: user.id,
24-
},
23+
limit: 1,
2524
});
2625

26+
let customer: Stripe.Customer;
27+
if (existingCustomers.data.length > 0 && existingCustomers.data[0]) {
28+
customer = existingCustomers.data[0];
29+
30+
customer = await stripe().customers.update(customer.id, {
31+
metadata: {
32+
...customer.metadata,
33+
userId: user.id,
34+
},
35+
});
36+
} else {
37+
customer = await stripe().customers.create({
38+
email: user.email,
39+
metadata: {
40+
userId: user.id,
41+
},
42+
});
43+
}
44+
2745
await db()
2846
.update(users)
2947
.set({

apps/web/app/api/settings/billing/subscribe/route.ts

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,13 @@ import { stripe, userIsPro } from "@cap/utils";
66
import { eq } from "drizzle-orm";
77
import type { NextRequest } from "next/server";
88
import { PostHog } from "posthog-node";
9+
import type Stripe from "stripe";
910

1011
export async function POST(request: NextRequest) {
11-
console.log("Starting subscription process");
1212
const user = await getCurrentUser();
1313
let customerId = user?.stripeCustomerId;
1414
const { priceId, quantity } = await request.json();
1515

16-
console.log("Received request with priceId:", priceId);
17-
console.log("Current user:", user?.id);
18-
1916
if (!priceId) {
2017
console.error("Price ID not found");
2118
return Response.json({ error: true }, { status: 400 });
@@ -33,28 +30,38 @@ export async function POST(request: NextRequest) {
3330

3431
try {
3532
if (!user.stripeCustomerId) {
36-
console.log("Creating new Stripe customer for user:", user.id);
37-
const customer = await stripe().customers.create({
33+
const existingCustomers = await stripe().customers.list({
3834
email: user.email,
39-
metadata: {
40-
userId: user.id,
41-
},
35+
limit: 1,
4236
});
4337

44-
console.log("Created Stripe customer:", customer.id);
38+
let customer: Stripe.Customer;
39+
if (existingCustomers.data.length > 0 && existingCustomers.data[0]) {
40+
customer = existingCustomers.data[0];
41+
42+
customer = await stripe().customers.update(customer.id, {
43+
metadata: {
44+
...customer.metadata,
45+
userId: user.id,
46+
},
47+
});
48+
} else {
49+
customer = await stripe().customers.create({
50+
email: user.email,
51+
metadata: {
52+
userId: user.id,
53+
},
54+
});
55+
}
4556

4657
await db()
4758
.update(users)
4859
.set({
4960
stripeCustomerId: customer.id,
5061
})
5162
.where(eq(users.id, user.id));
52-
53-
console.log("Updated user with Stripe customer ID");
5463
customerId = customer.id;
5564
}
56-
57-
console.log("Creating checkout session for customer:", customerId);
5865
const checkoutSession = await stripe().checkout.sessions.create({
5966
customer: customerId as string,
6067
line_items: [{ price: priceId, quantity: quantity }],
@@ -66,8 +73,6 @@ export async function POST(request: NextRequest) {
6673
});
6774

6875
if (checkoutSession.url) {
69-
console.log("Successfully created checkout session");
70-
7176
try {
7277
const ph = new PostHog(buildEnv.NEXT_PUBLIC_POSTHOG_KEY || "", {
7378
host: buildEnv.NEXT_PUBLIC_POSTHOG_HOST || "",

packages/database/auth/drizzle-adapter.ts

Lines changed: 45 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { STRIPE_AVAILABLE, stripe } from "@cap/utils";
22
import { and, eq } from "drizzle-orm";
33
import type { PlanetScaleDatabase } from "drizzle-orm/planetscale-serverless";
44
import type { Adapter } from "next-auth/adapters";
5+
import type Stripe from "stripe";
56
import { nanoId } from "../helpers";
67
import { accounts, sessions, users, verificationTokens } from "../schema";
78

@@ -25,17 +26,57 @@ export function DrizzleAdapter(db: PlanetScaleDatabase): Adapter {
2526
if (!row) throw new Error("User not found");
2627

2728
if (STRIPE_AVAILABLE()) {
28-
const customer = await stripe().customers.create({
29+
const existingCustomers = await stripe().customers.list({
2930
email: userData.email,
30-
metadata: {
31-
userId: nanoId(),
32-
},
31+
limit: 1,
32+
});
33+
34+
let customer: Stripe.Customer;
35+
if (existingCustomers.data.length > 0 && existingCustomers.data[0]) {
36+
customer = existingCustomers.data[0];
37+
38+
customer = await stripe().customers.update(customer.id, {
39+
metadata: {
40+
...customer.metadata,
41+
userId: row.id,
42+
},
43+
});
44+
} else {
45+
customer = await stripe().customers.create({
46+
email: userData.email,
47+
metadata: {
48+
userId: row.id,
49+
},
50+
});
51+
}
52+
53+
const subscriptions = await stripe().subscriptions.list({
54+
customer: customer.id,
55+
status: "active",
56+
limit: 100,
3357
});
3458

59+
const inviteQuota = subscriptions.data.reduce((total, sub) => {
60+
return (
61+
total +
62+
sub.items.data.reduce(
63+
(subTotal, item) => subTotal + (item.quantity || 1),
64+
0,
65+
)
66+
);
67+
}, 0);
68+
69+
const mostRecentSubscription = subscriptions.data[0];
70+
3571
await db
3672
.update(users)
3773
.set({
3874
stripeCustomerId: customer.id,
75+
...(mostRecentSubscription && {
76+
stripeSubscriptionId: mostRecentSubscription.id,
77+
stripeSubscriptionStatus: mostRecentSubscription.status,
78+
inviteQuota: inviteQuota || 1,
79+
}),
3980
})
4081
.where(eq(users.id, row.id));
4182
}
@@ -58,7 +99,6 @@ export function DrizzleAdapter(db: PlanetScaleDatabase): Adapter {
5899
.where(eq(users.email, email))
59100
.limit(1)
60101
.catch((e) => {
61-
console.log(e);
62102
throw e;
63103
});
64104
const row = rows[0];
@@ -182,29 +222,23 @@ export function DrizzleAdapter(db: PlanetScaleDatabase): Adapter {
182222
await db.delete(sessions).where(eq(sessions.sessionToken, sessionToken));
183223
},
184224
async createVerificationToken(verificationToken) {
185-
// First, check if a token for the given identifier already exists
186225
const existingTokens = await db
187226
.select()
188227
.from(verificationTokens)
189228
.where(eq(verificationTokens.identifier, verificationToken.identifier))
190229
.limit(1);
191230

192-
// If a token already exists, you can return the existing token
193-
// or handle it based on your business logic
194231
if (existingTokens.length > 0) {
195-
// For example, updating the existing token:
196232
await db
197233
.update(verificationTokens)
198234
.set({
199235
token: verificationToken.token,
200236
expires: verificationToken.expires,
201-
// you may update other fields as necessary
202237
})
203238
.where(
204239
eq(verificationTokens.identifier, verificationToken.identifier),
205240
);
206241

207-
// Return the updated token
208242
return await db
209243
.select()
210244
.from(verificationTokens)
@@ -215,14 +249,12 @@ export function DrizzleAdapter(db: PlanetScaleDatabase): Adapter {
215249
.then((rows) => rows[0]);
216250
}
217251

218-
// If the token does not exist, proceed to create a new one
219252
await db.insert(verificationTokens).values({
220253
expires: verificationToken.expires,
221254
identifier: verificationToken.identifier,
222255
token: verificationToken.token,
223256
});
224257

225-
// Retrieve and return the newly created token
226258
const rows = await db
227259
.select()
228260
.from(verificationTokens)

0 commit comments

Comments
 (0)