Skip to content

Commit 9a60a95

Browse files
committed
stripe supa table integration PR
1 parent c62603a commit 9a60a95

File tree

15 files changed

+1269
-216
lines changed

15 files changed

+1269
-216
lines changed

package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,14 @@
2727
"@radix-ui/react-dropdown-menu": "^2.1.15",
2828
"@radix-ui/react-label": "^2.1.2",
2929
"@radix-ui/react-navigation-menu": "^1.2.13",
30+
"@radix-ui/react-progress": "^1.1.7",
3031
"@radix-ui/react-select": "^2.2.5",
3132
"@radix-ui/react-separator": "^1.1.2",
3233
"@radix-ui/react-slot": "^1.2.2",
3334
"@radix-ui/react-switch": "^1.2.4",
3435
"@radix-ui/react-tooltip": "^1.1.8",
36+
"@stripe/react-stripe-js": "^3.7.0",
37+
"@stripe/stripe-js": "^7.3.0",
3538
"@supabase/ssr": "^0.6.1",
3639
"@supabase/supabase-js": "^2.49.8",
3740
"class-variance-authority": "^0.7.1",
@@ -55,6 +58,7 @@
5558
"remark-gfm": "^4.0.1",
5659
"remark-math": "^6.0.0",
5760
"sonner": "^2.0.1",
61+
"stripe": "^18.1.1",
5862
"tailwind-merge": "^3.0.2",
5963
"tailwindcss-animate": "^1.0.7",
6064
"use-stick-to-bottom": "^1.0.46",

pnpm-lock.yaml

Lines changed: 78 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/app/api/[..._path]/route.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,6 @@ import { initApiPassthrough } from "langgraph-nextjs-api-passthrough";
55

66
export const { GET, POST, PUT, PATCH, DELETE, OPTIONS, runtime } =
77
initApiPassthrough({
8-
apiUrl: process.env.LANGGRAPH_API_URL, // default, if not defined it will attempt to read process.env.LANGGRAPH_API_URL
8+
apiUrl: process.env.LANGGRAPH_API_URL,
99
runtime: "edge", // default
1010
});
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
import { NextResponse } from "next/server"
2+
import Stripe from "stripe"
3+
import { supabaseServer } from "@/lib/auth/supabase-server"
4+
5+
type SessionCreateParams = Stripe.Checkout.SessionCreateParams
6+
7+
// Initialize Stripe
8+
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY || "")
9+
10+
export async function POST(request: Request) {
11+
try {
12+
const { priceId, userId, customerEmail } = await request.json()
13+
14+
console.log("Checkout session request:", { priceId, userId, customerEmail })
15+
16+
if (!priceId || !userId || !customerEmail) {
17+
console.error("Missing required parameters:", { priceId, userId, customerEmail })
18+
return NextResponse.json({ error: "Missing required parameters" }, { status: 400 })
19+
}
20+
21+
// Verify Stripe is initialized
22+
if (!process.env.STRIPE_SECRET_KEY) {
23+
console.error("STRIPE_SECRET_KEY is not set")
24+
return NextResponse.json({ error: "Stripe not configured" }, { status: 500 })
25+
}
26+
27+
// Get or create Stripe customer
28+
let customerId: string
29+
30+
// Check if user already has a Stripe customer ID
31+
const { data: userData, error: userError } = await supabaseServer
32+
.from("users")
33+
.select("stripe_customer_id, email")
34+
.eq("id", userId)
35+
.single()
36+
37+
if (userError && userError.code !== "PGRST116") {
38+
console.error("Error fetching user:", userError)
39+
return NextResponse.json({ error: "Failed to fetch user data" }, { status: 500 })
40+
}
41+
42+
if (userData?.stripe_customer_id) {
43+
customerId = userData.stripe_customer_id as string
44+
console.log("Using existing Stripe customer:", customerId)
45+
} else {
46+
// Create a new customer
47+
console.log("Creating new Stripe customer for:", customerEmail)
48+
const customer = await stripe.customers.create({
49+
email: customerEmail,
50+
metadata: {
51+
userId,
52+
},
53+
})
54+
55+
customerId = customer.id
56+
console.log("Created new Stripe customer:", customerId)
57+
58+
// Create or update user record in Supabase
59+
const { error: updateError } = await supabaseServer.from("users").upsert({
60+
id: userId,
61+
email: customerEmail,
62+
stripe_customer_id: customerId,
63+
credits_available: 0,
64+
subscription_status: "inactive",
65+
}, {
66+
onConflict: "id"
67+
})
68+
69+
if (updateError) {
70+
console.error("Error creating/updating user record:", updateError)
71+
// Don't fail the checkout, but log the error
72+
}
73+
}
74+
75+
// Create a checkout session
76+
const baseUrl = process.env.NEXT_PUBLIC_BASE_URL || "http://localhost:3000"
77+
78+
const sessionParams: SessionCreateParams = {
79+
customer: customerId,
80+
payment_method_types: ["card"],
81+
line_items: [
82+
{
83+
price: priceId,
84+
quantity: 1,
85+
},
86+
],
87+
mode: "subscription",
88+
success_url: `${baseUrl}/success?session_id={CHECKOUT_SESSION_ID}`,
89+
cancel_url: `${baseUrl}/pricing`,
90+
metadata: {
91+
userId,
92+
},
93+
subscription_data: {
94+
metadata: {
95+
userId,
96+
},
97+
},
98+
}
99+
100+
const session = await stripe.checkout.sessions.create(sessionParams)
101+
102+
return NextResponse.json({ sessionId: session.id })
103+
} catch (error) {
104+
console.error("Error creating checkout session:", error)
105+
return NextResponse.json({ error: "Failed to create checkout session" }, { status: 500 })
106+
}
107+
}
108+

src/app/api/user/credits/route.ts

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import { NextResponse } from "next/server"
2+
import { supabaseServer } from "@/lib/auth/supabase-server"
3+
4+
export async function GET(request: Request) {
5+
try {
6+
const { searchParams } = new URL(request.url)
7+
const userId = searchParams.get("userId")
8+
9+
if (!userId) {
10+
return NextResponse.json({ error: "Missing userId parameter" }, { status: 400 })
11+
}
12+
13+
console.log("Fetching credits for user:", userId)
14+
15+
// Get user's credit information from Supabase
16+
const { data: userData, error } = await supabaseServer
17+
.from("users")
18+
.select("credits_available, subscription_status, price_id")
19+
.eq("id", userId)
20+
.single()
21+
22+
if (error) {
23+
console.error("Error fetching user credits:", error)
24+
25+
// If user doesn't exist, create a basic record
26+
if (error.code === "PGRST116") {
27+
console.log("User not found in database, creating basic record...")
28+
29+
const { data: newUser, error: createError } = await supabaseServer
30+
.from("users")
31+
.insert({
32+
id: userId,
33+
credits_available: 0,
34+
subscription_status: "inactive"
35+
})
36+
.select("credits_available, subscription_status, price_id")
37+
.single()
38+
39+
if (createError) {
40+
console.error("Error creating user record:", createError)
41+
return NextResponse.json({ error: "Failed to create user record" }, { status: 500 })
42+
}
43+
44+
return NextResponse.json({
45+
credits: newUser.credits_available || 0,
46+
subscriptionStatus: newUser.subscription_status,
47+
priceId: newUser.price_id
48+
})
49+
}
50+
51+
return NextResponse.json({ error: "Failed to fetch credits" }, { status: 500 })
52+
}
53+
54+
return NextResponse.json({
55+
credits: userData.credits_available || 0,
56+
subscriptionStatus: userData.subscription_status,
57+
priceId: userData.price_id
58+
})
59+
60+
} catch (error) {
61+
console.error("Credits API error:", error)
62+
return NextResponse.json({ error: "Internal server error" }, { status: 500 })
63+
}
64+
}
65+

0 commit comments

Comments
 (0)