⚡ (billing) Automatic usage-based billing (#924)
BREAKING CHANGE: Stripe environment variables simplified. Check out the new configs to adapt your existing system. Closes #906 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ### Summary by CodeRabbit **New Features:** - Introduced a usage-based billing system, providing more flexibility and options for users. - Integrated with Stripe for a smoother and more secure payment process. - Enhanced the user interface with improvements to the billing, workspace, and pricing pages for a more intuitive experience. **Improvements:** - Simplified the billing logic, removing additional chats and yearly billing for a more streamlined user experience. - Updated email notifications to keep users informed about their usage and limits. - Improved pricing and currency formatting for better clarity and understanding. **Testing:** - Updated tests and specifications to ensure the reliability of new features and improvements. **Note:** These changes aim to provide a more flexible and user-friendly billing system, with clearer pricing and improved notifications. Users should find the new system more intuitive and easier to navigate. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
@@ -46,23 +46,22 @@ const webhookHandler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
const metadata = session.metadata as unknown as
|
||||
| {
|
||||
plan: 'STARTER' | 'PRO'
|
||||
additionalChats: string
|
||||
workspaceId: string
|
||||
userId: string
|
||||
}
|
||||
| { claimableCustomPlanId: string; userId: string }
|
||||
if ('plan' in metadata) {
|
||||
const { workspaceId, plan, additionalChats } = metadata
|
||||
if (!workspaceId || !plan || !additionalChats)
|
||||
const { workspaceId, plan } = metadata
|
||||
if (!workspaceId || !plan)
|
||||
return res
|
||||
.status(500)
|
||||
.send({ message: `Couldn't retrieve valid metadata` })
|
||||
|
||||
const workspace = await prisma.workspace.update({
|
||||
where: { id: workspaceId },
|
||||
data: {
|
||||
plan,
|
||||
stripeId: session.customer as string,
|
||||
additionalChatsIndex: parseInt(additionalChats),
|
||||
isQuarantined: false,
|
||||
},
|
||||
include: {
|
||||
@@ -84,7 +83,6 @@ const webhookHandler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
userId: user.id,
|
||||
data: {
|
||||
plan,
|
||||
additionalChatsIndex: parseInt(additionalChats),
|
||||
},
|
||||
},
|
||||
])
|
||||
@@ -119,7 +117,6 @@ const webhookHandler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
userId,
|
||||
data: {
|
||||
plan: Plan.CUSTOM,
|
||||
additionalChatsIndex: 0,
|
||||
},
|
||||
},
|
||||
])
|
||||
@@ -148,7 +145,6 @@ const webhookHandler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
},
|
||||
data: {
|
||||
plan: Plan.FREE,
|
||||
additionalChatsIndex: 0,
|
||||
customChatsLimit: null,
|
||||
customStorageLimit: null,
|
||||
customSeatsLimit: null,
|
||||
@@ -172,7 +168,6 @@ const webhookHandler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
userId: user.id,
|
||||
data: {
|
||||
plan: Plan.FREE,
|
||||
additionalChatsIndex: 0,
|
||||
},
|
||||
},
|
||||
])
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
} from '@typebot.io/lib/api'
|
||||
import { getAuthenticatedUser } from '@/features/auth/helpers/getAuthenticatedUser'
|
||||
import { sendWorkspaceMemberInvitationEmail } from '@typebot.io/emails'
|
||||
import { isSeatsLimitReached } from '@typebot.io/lib/pricing'
|
||||
import { getSeatsLimit } from '@typebot.io/lib/billing/getSeatsLimit'
|
||||
import { env } from '@typebot.io/env'
|
||||
|
||||
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
@@ -37,11 +37,8 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
}),
|
||||
])
|
||||
if (
|
||||
isSeatsLimitReached({
|
||||
existingMembersAndInvitationsCount:
|
||||
existingMembersCount + existingInvitationsCount,
|
||||
...workspace,
|
||||
})
|
||||
getSeatsLimit(workspace) <=
|
||||
existingMembersCount + existingInvitationsCount
|
||||
)
|
||||
return res.status(400).send('Seats limit reached')
|
||||
if (existingUser) {
|
||||
|
||||
Reference in New Issue
Block a user