Skip to content

Commit

Permalink
🐛 (stripe) Fix plan update and management
Browse files Browse the repository at this point in the history
  • Loading branch information
baptisteArno committed Sep 27, 2022
1 parent f83e0ef commit 6384a3a
Show file tree
Hide file tree
Showing 12 changed files with 99 additions and 105 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
Button,
Heading,
} from '@chakra-ui/react'
import { useToast } from 'components/shared/hooks/useToast'
import { PlanTag } from 'components/shared/PlanTag'
import { Plan } from 'db'
import React, { useState } from 'react'
Expand All @@ -26,38 +27,48 @@ export const CurrentSubscriptionContent = ({
const [isCancelling, setIsCancelling] = useState(false)
const [isRedirectingToBillingPortal, setIsRedirectingToBillingPortal] =
useState(false)
const { showToast } = useToast()

const cancelSubscription = async () => {
if (!stripeId) return
setIsCancelling(true)
await cancelSubscriptionQuery(stripeId)
const { error } = await cancelSubscriptionQuery(stripeId)
if (error) {
showToast({ description: error.message })
return
}
onCancelSuccess()
setIsCancelling(false)
}

const isSubscribed = (plan === Plan.STARTER || plan === Plan.PRO) && stripeId

if (isCancelling) return <Spinner colorScheme="gray" />
return (
<Stack gap="2">
<Heading fontSize="3xl">Subscription</Heading>
<HStack>
<Text>Current workspace subscription: </Text>
<PlanTag plan={plan} />
{isSubscribed && (
<Link
as="button"
color="gray.500"
textDecor="underline"
fontSize="sm"
onClick={cancelSubscription}
>
Cancel my subscription
</Link>
{isCancelling ? (
<Spinner color="gray.500" size="xs" />
) : (
<>
<PlanTag plan={plan} />
{isSubscribed && (
<Link
as="button"
color="gray.500"
textDecor="underline"
fontSize="sm"
onClick={cancelSubscription}
>
Cancel my subscription
</Link>
)}
</>
)}
</HStack>

{isSubscribed && (
{isSubscribed && !isCancelling && (
<>
<Stack gap="1">
<Text fontSize="sm">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,18 @@ export const ChangePlanForm = () => {
selectedStorageLimitIndex === undefined
)
return
await pay({
const response = await pay({
stripeId: workspace.stripeId ?? undefined,
user,
plan,
workspaceId: workspace.id,
additionalChats: selectedChatsLimitIndex,
additionalStorage: selectedStorageLimitIndex,
})
if (typeof response === 'object' && response?.error) {
showToast({ description: response.error.message })
return
}
refreshCurrentSubscriptionInfo({
additionalChatsIndex: selectedChatsLimitIndex,
additionalStorageIndex: selectedStorageLimitIndex,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ type UpgradeProps = {
export const pay = async ({
stripeId,
...props
}: UpgradeProps): Promise<{ newPlan: Plan } | undefined | void> =>
}: UpgradeProps): Promise<{ newPlan?: Plan; error?: Error } | void> =>
isDefined(stripeId)
? updatePlan({ ...props, stripeId })
: redirectToCheckout(props)
Expand All @@ -31,13 +31,13 @@ export const updatePlan = async ({
workspaceId,
additionalChats,
additionalStorage,
}: Omit<UpgradeProps, 'user'>): Promise<{ newPlan: Plan } | undefined> => {
}: Omit<UpgradeProps, 'user'>): Promise<{ newPlan?: Plan; error?: Error }> => {
const { data, error } = await sendRequest<{ message: string }>({
method: 'PUT',
url: '/api/stripe/subscription',
body: { workspaceId, plan, stripeId, additionalChats, additionalStorage },
})
if (error || !data) return
if (error || !data) return { error }
return { newPlan: plan }
}

Expand Down
25 changes: 9 additions & 16 deletions apps/builder/pages/_app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { SupportBubble } from 'components/shared/SupportBubble'
import { WorkspaceContext } from 'contexts/WorkspaceContext'
import { toTitleCase } from 'utils'
import { Session } from 'next-auth'
import { Plan } from 'db'

const { ToastContainer, toast } = createStandaloneToast(customTheme)

Expand All @@ -35,7 +36,14 @@ const App = ({
}, [pathname])

useEffect(() => {
displayStripeCallbackMessage(query.stripe?.toString(), toast)
const newPlan = query.stripe?.toString()
if (newPlan === Plan.STARTER || newPlan === Plan.PRO)
toast({
position: 'bottom-right',
status: 'success',
title: 'Upgrade success!',
description: `Workspace upgraded to ${toTitleCase(status)} 🎉`,
})
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isReady])

Expand Down Expand Up @@ -68,19 +76,4 @@ const App = ({
)
}

const displayStripeCallbackMessage = (
status: string | undefined,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
toast: any
) => {
if (status && ['pro', 'team'].includes(status)) {
toast({
position: 'bottom-right',
status: 'success',
title: 'Upgrade success!',
description: `Workspace upgraded to ${toTitleCase(status)} 🎉`,
})
}
}

export default App
53 changes: 33 additions & 20 deletions apps/builder/pages/api/stripe/subscription.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,12 @@ const getSubscriptionDetails =
})
return {
additionalChatsIndex:
subscriptions.data[0].items.data.find(
subscriptions.data[0]?.items.data.find(
(item) =>
item.price.id === process.env.STRIPE_ADDITIONAL_CHATS_PRICE_ID
)?.quantity ?? 0,
additionalStorageIndex:
subscriptions.data[0].items.data.find(
subscriptions.data[0]?.items.data.find(
(item) =>
item.price.id === process.env.STRIPE_ADDITIONAL_STORAGE_PRICE_ID
)?.quantity ?? 0,
Expand Down Expand Up @@ -100,33 +100,34 @@ const createCheckoutSession = (req: NextApiRequest) => {
}

const updateSubscription = async (req: NextApiRequest) => {
const { customerId, plan, workspaceId, additionalChats, additionalStorage } =
(typeof req.body === 'string' ? JSON.parse(req.body) : req.body) as {
customerId: string
workspaceId: string
additionalChats: number
additionalStorage: number
plan: 'STARTER' | 'PRO'
}
const { stripeId, plan, workspaceId, additionalChats, additionalStorage } = (
typeof req.body === 'string' ? JSON.parse(req.body) : req.body
) as {
stripeId: string
workspaceId: string
additionalChats: number
additionalStorage: number
plan: 'STARTER' | 'PRO'
}
if (!process.env.STRIPE_SECRET_KEY)
throw Error('STRIPE_SECRET_KEY var is missing')
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY, {
apiVersion: '2022-08-01',
})
const { data } = await stripe.subscriptions.list({
customer: customerId,
customer: stripeId,
})
const subscription = data[0]
const currentStarterPlanItemId = subscription.items.data.find(
const subscription = data[0] as Stripe.Subscription | undefined
const currentStarterPlanItemId = subscription?.items.data.find(
(item) => item.price.id === process.env.STRIPE_STARTER_PRICE_ID
)?.id
const currentProPlanItemId = subscription.items.data.find(
const currentProPlanItemId = subscription?.items.data.find(
(item) => item.price.id === process.env.STRIPE_PRO_PRICE_ID
)?.id
const currentAdditionalChatsItemId = subscription.items.data.find(
const currentAdditionalChatsItemId = subscription?.items.data.find(
(item) => item.price.id === process.env.STRIPE_ADDITIONAL_CHATS_PRICE_ID
)?.id
const currentAdditionalStorageItemId = subscription.items.data.find(
const currentAdditionalStorageItemId = subscription?.items.data.find(
(item) => item.price.id === process.env.STRIPE_ADDITIONAL_STORAGE_PRICE_ID
)?.id
const items = [
Expand Down Expand Up @@ -155,9 +156,18 @@ const updateSubscription = async (req: NextApiRequest) => {
deleted: additionalStorage === 0,
},
].filter(isDefined)
await stripe.subscriptions.update(subscription.id, {
items,
})

if (subscription) {
await stripe.subscriptions.update(subscription.id, {
items,
})
} else {
await stripe.subscriptions.create({
customer: stripeId,
items,
})
}

await prisma.workspace.update({
where: { id: workspaceId },
data: {
Expand Down Expand Up @@ -187,7 +197,10 @@ const cancelSubscription =
const existingSubscription = await stripe.subscriptions.list({
customer: workspace.stripeId,
})
await stripe.subscriptions.del(existingSubscription.data[0].id)
const currentSubscriptionId = existingSubscription.data[0]?.id
if (currentSubscriptionId)
await stripe.subscriptions.del(currentSubscriptionId)

await prisma.workspace.update({
where: { id: workspace.id },
data: {
Expand Down
9 changes: 6 additions & 3 deletions apps/builder/playwright/services/database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {
Workspace,
} from 'db'
import { readFileSync } from 'fs'
import { createFakeResults } from 'utils'
import { injectFakeResults } from 'utils'
import { encrypt } from 'utils/api'
import Stripe from 'stripe'

Expand Down Expand Up @@ -75,7 +75,10 @@ export const addSubscriptionToWorkspace = async (
customer: stripeId,
items,
default_payment_method: paymentId,
currency: 'usd',
currency: 'eur',
})
await stripe.customers.update(stripeId, {
invoice_settings: { default_payment_method: paymentId },
})
await prisma.workspace.update({
where: { id: workspaceId },
Expand Down Expand Up @@ -264,7 +267,7 @@ export const updateUser = (data: Partial<User>) =>
},
})

export const createResults = createFakeResults(prisma)
export const createResults = injectFakeResults(prisma)

export const createFolder = (workspaceId: string, name: string) =>
prisma.dashboardFolder.create({
Expand Down
4 changes: 2 additions & 2 deletions apps/viewer/playwright/services/database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
} from 'models'
import { GraphNavigation, Plan, PrismaClient, WorkspaceRole } from 'db'
import { readFileSync } from 'fs'
import { createFakeResults } from 'utils'
import { injectFakeResults } from 'utils'
import { encrypt } from 'utils/api'

const prisma = new PrismaClient()
Expand Down Expand Up @@ -221,7 +221,7 @@ export const importTypebotInDatabase = async (
})
}

export const createResults = createFakeResults(prisma)
export const createResults = injectFakeResults(prisma)

export const createSmtpCredentials = (
id: string,
Expand Down
19 changes: 12 additions & 7 deletions packages/scripts/index.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,23 @@
import { PrismaClient } from 'db'
import path from 'path'
import fs from 'fs'
import { injectFakeResults } from 'utils'

require('dotenv').config({
path: path.join(
__dirname,
process.env.NODE_ENV === 'production'
? '.env.production'
: process.env.NODE_ENV === 'staging'
? '.env.staging'
: '.env.local'
process.env.NODE_ENV === 'staging' ? '.env.staging' : '.env.local'
),
})

const main = async () => {}
const prisma = new PrismaClient()

const main = async () => {
await injectFakeResults(prisma)({
count: 150,
typebotId: 'cl89sq4vb030109laivd9ck97',
isChronological: false,
idPrefix: 'batch2',
})
}

main().then()
6 changes: 2 additions & 4 deletions packages/scripts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,14 @@
"scripts": {
"start:local": "ts-node index.ts",
"start:staging": "NODE_ENV=staging ts-node index.ts",
"start:prod": "NODE_ENV=production ts-node index.ts",
"start:workspaces:migration": "ts-node workspaceMigration.ts",
"start:workspaces:migration:recover": "ts-node workspaceMigrationRecover.ts"
"start:prod": "NODE_ENV=production ts-node index.ts"
},
"devDependencies": {
"@types/node": "18.7.16",
"db": "workspace:*",
"models": "workspace:*",
"ts-node": "^10.9.1",
"typescript": "^4.8.3",
"utils": "*"
"utils": "workspace:*"
}
}
33 changes: 0 additions & 33 deletions packages/scripts/prepareEmojis.ts

This file was deleted.

2 changes: 1 addition & 1 deletion packages/utils/playwright.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ type CreateFakeResultsProps = {
fakeStorage?: number
}

export const createFakeResults =
export const injectFakeResults =
(prisma: PrismaClient) =>
async ({
count,
Expand Down
Loading

0 comments on commit 6384a3a

Please sign in to comment.