mirror of
https://github.com/sourcebot-dev/sourcebot.git
synced 2025-12-13 04:45:19 +00:00
save stripe session id and add manage subscription button in settings
This commit is contained in:
parent
33ae585327
commit
e7f8f51c05
8 changed files with 79 additions and 67 deletions
|
|
@ -1,2 +0,0 @@
|
|||
-- AlterTable
|
||||
ALTER TABLE "Org" ADD COLUMN "subscriptionId" TEXT;
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
-- AlterTable
|
||||
ALTER TABLE "Org" ADD COLUMN "stripeSessionId" TEXT;
|
||||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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 />;
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
)
|
||||
}
|
||||
|
|
@ -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>
|
||||
)
|
||||
}
|
||||
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
Loading…
Reference in a new issue