a tool for shared writing and social publishing
at feature/analytics 63 lines 1.9 kB view raw
1"use server"; 2 3import { getIdentityData } from "./getIdentityData"; 4import { getStripe } from "stripe/client"; 5import { supabaseServerClient } from "supabase/serverClient"; 6import { getPriceId } from "stripe/products"; 7import { Ok, Err, type Result } from "src/result"; 8 9export async function createCheckoutSession( 10 cadence: "month" | "year", 11 returnUrl?: string, 12): Promise<Result<{ url: string }, string>> { 13 const identity = await getIdentityData(); 14 if (!identity) { 15 return Err("Not authenticated"); 16 } 17 18 const priceId = await getPriceId(cadence); 19 if (!priceId) { 20 return Err("No Stripe price found. Run the sync script first."); 21 } 22 23 // Check for existing Stripe customer 24 let customerId: string | undefined; 25 const { data: existingSub } = await supabaseServerClient 26 .from("user_subscriptions") 27 .select("stripe_customer_id") 28 .eq("identity_id", identity.id) 29 .single(); 30 31 if (existingSub?.stripe_customer_id) { 32 customerId = existingSub.stripe_customer_id; 33 } 34 35 const successUrl = new URL( 36 "/api/checkout/success", 37 process.env.NEXT_PUBLIC_APP_URL || "http://localhost:3000", 38 ); 39 successUrl.searchParams.set("session_id", "{CHECKOUT_SESSION_ID}"); 40 if (returnUrl) { 41 successUrl.searchParams.set("return", returnUrl); 42 } 43 44 const cancelUrl = returnUrl || process.env.NEXT_PUBLIC_APP_URL || "http://localhost:3000"; 45 46 const session = await getStripe().checkout.sessions.create({ 47 mode: "subscription", 48 line_items: [{ price: priceId, quantity: 1 }], 49 client_reference_id: identity.id, 50 ...(customerId 51 ? { customer: customerId } 52 : { customer_email: identity.email || undefined }), 53 subscription_data: { metadata: { identity_id: identity.id } }, 54 success_url: successUrl.toString(), 55 cancel_url: cancelUrl, 56 }); 57 58 if (!session.url) { 59 return Err("Failed to create checkout session"); 60 } 61 62 return Ok({ url: session.url }); 63}