a tool for shared writing and social publishing
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 || "https://leaflet.pub",
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 || "https://leaflet.pub";
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 allow_promotion_codes: true,
55 success_url: successUrl.toString(),
56 cancel_url: cancelUrl,
57 });
58
59 if (!session.url) {
60 return Err("Failed to create checkout session");
61 }
62
63 return Ok({ url: session.url });
64}