save stripe session id and add manage subscription button in settings

This commit is contained in:
msukkari 2025-02-11 19:06:40 -08:00
parent 33ae585327
commit e7f8f51c05
8 changed files with 79 additions and 67 deletions

View file

@ -1,2 +0,0 @@
-- AlterTable
ALTER TABLE "Org" ADD COLUMN "subscriptionId" TEXT;

View file

@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "Org" ADD COLUMN "stripeSessionId" TEXT;

View file

@ -105,20 +105,20 @@ model Invite {
}
model Org {
id Int @id @default(autoincrement())
name String
domain String @unique
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
members UserToOrg[]
connections Connection[]
repos Repo[]
secrets Secret[]
id Int @id @default(autoincrement())
name String
domain String @unique
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
members UserToOrg[]
connections Connection[]
repos Repo[]
secrets Secret[]
subscriptionId String?
stripeSessionId String?
/// List of pending invites to this organization
invites Invite[]
invites Invite[]
}
enum OrgRole {

View file

@ -98,7 +98,7 @@ export const checkIfOrgDomainExists = async (domain: string): Promise<boolean> =
return !!org;
}
export const createOrg = async (name: string, domain: string, subscriptionId?: string): Promise<{ id: number } | ServiceError> => {
export const createOrg = async (name: string, domain: string, stripeSessionId?: string): Promise<{ id: number } | ServiceError> => {
const session = await auth();
if (!session) {
return notAuthenticated();
@ -119,7 +119,7 @@ export const createOrg = async (name: string, domain: string, subscriptionId?: s
data: {
name,
domain,
subscriptionId,
stripeSessionId,
members: {
create: {
userId: session.user.id,
@ -422,4 +422,30 @@ export async function fetchStripeClientSecret(name: string, domain: string) {
export async function fetchStripeSession(sessionId: string) {
const stripeSession = await stripe.checkout.sessions.retrieve(sessionId);
return stripeSession;
}
export async function createCustomerPortalSession() {
const orgId = await getCurrentUserOrg();
if (isServiceError(orgId)) {
return orgId;
}
const org = await prisma.org.findUnique({
where: {
id: orgId,
},
});
if (!org || !org.stripeSessionId) {
return notFound();
}
const origin = (await headers()).get('origin')
const stripeSession = await fetchStripeSession(org.stripeSessionId);
const portalSession = await stripe.billingPortal.sessions.create({
customer: stripeSession.customer as string,
return_url: `${origin}/settings/billing`,
});
return portalSession;
}

View file

@ -35,14 +35,12 @@ export default async function OnboardComplete({ searchParams }: OnboardCompleteP
}
const stripeSession = await fetchStripeSession(sessionId);
const stripeSubscription = stripeSession.subscription;
if(stripeSession.payment_status !== "paid" || !stripeSubscription) {
if(stripeSession.payment_status !== "paid") {
console.error("Invalid stripe session");
return <ErrorPage />;
}
const subscriptionId = stripeSubscription as string;
const res = await createOrg(orgName, orgDomain, subscriptionId);
const res = await createOrg(orgName, orgDomain, stripeSession.id);
if (isServiceError(res)) {
console.error("Failed to create org");
return <ErrorPage />;

View file

@ -1,46 +0,0 @@
"use client"
import { useState } from "react"
import { useRouter } from "next/navigation"
import { isServiceError } from "@/lib/utils"
import { useToast } from "@/components/hooks/use-toast"
import { Button } from "@/components/ui/button"
import { createCheckoutSession } from "../../../actions"
export function CheckoutButton() {
const [isLoading, setIsLoading] = useState(false)
const router = useRouter()
const { toast } = useToast()
const handleCheckoutSession = async () => {
setIsLoading(true)
try {
const session = await createCheckoutSession()
if (isServiceError(session)) {
console.log("Failed to create checkout session: ", session)
toast({
title: "Error",
description: "Failed to create checkout session. Please try again.",
variant: "destructive",
})
} else {
router.push(session.url!)
}
} catch (error) {
console.error("Error creating checkout session:", error)
toast({
title: "Error",
description: "An unexpected error occurred. Please try again.",
variant: "destructive",
})
} finally {
setIsLoading(false)
}
}
return (
<Button onClick={handleCheckoutSession} disabled={isLoading}>
{isLoading ? "Checkout out..." : "Checkout"}
</Button>
)
}

View file

@ -0,0 +1,34 @@
"use client"
import { useState } from "react"
import { useRouter } from "next/navigation"
import { isServiceError } from "@/lib/utils"
import { Button } from "@/components/ui/button"
import { createCustomerPortalSession } from "../../../actions"
export function ManageSubscriptionButton() {
const [isLoading, setIsLoading] = useState(false)
const router = useRouter()
const redirectToCustomerPortal = async () => {
setIsLoading(true)
try {
const session = await createCustomerPortalSession()
if (isServiceError(session)) {
console.log("Failed to create portal session: ", session)
} else {
router.push(session.url!)
}
} catch (error) {
console.error("Error creating portal session:", error)
} finally {
setIsLoading(false)
}
}
return (
<Button onClick={redirectToCustomerPortal} disabled={isLoading}>
{isLoading ? "Creating customer portal..." : "Manage Subscription"}
</Button>
)
}

View file

@ -1,12 +1,12 @@
'use server'
import { CheckoutButton } from "./checkoutButton"
import { ManageSubscriptionButton } from "./manageSubscriptionButton"
export default async function BillingPage() {
return (
<div>
<h1>Billing</h1>
<CheckoutButton />
<ManageSubscriptionButton />
</div>
);
}