2023-03-15 08:35:16 +01:00
|
|
|
import type { Workspace } from '@typebot.io/prisma'
|
|
|
|
import { Plan } from '@typebot.io/prisma'
|
2022-09-17 16:37:33 +02:00
|
|
|
|
|
|
|
const infinity = -1
|
|
|
|
|
2023-04-13 11:39:10 +02:00
|
|
|
export const priceIds = {
|
|
|
|
[Plan.STARTER]: {
|
|
|
|
base: {
|
|
|
|
monthly: process.env.STRIPE_STARTER_MONTHLY_PRICE_ID,
|
|
|
|
yearly: process.env.STRIPE_STARTER_YEARLY_PRICE_ID,
|
|
|
|
},
|
|
|
|
chats: {
|
|
|
|
monthly: process.env.STRIPE_STARTER_CHATS_MONTHLY_PRICE_ID,
|
|
|
|
yearly: process.env.STRIPE_STARTER_CHATS_YEARLY_PRICE_ID,
|
|
|
|
},
|
|
|
|
storage: {
|
|
|
|
monthly: process.env.STRIPE_STARTER_STORAGE_MONTHLY_PRICE_ID,
|
|
|
|
yearly: process.env.STRIPE_STARTER_STORAGE_YEARLY_PRICE_ID,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
[Plan.PRO]: {
|
|
|
|
base: {
|
|
|
|
monthly: process.env.STRIPE_PRO_MONTHLY_PRICE_ID,
|
|
|
|
yearly: process.env.STRIPE_PRO_YEARLY_PRICE_ID,
|
|
|
|
},
|
|
|
|
chats: {
|
|
|
|
monthly: process.env.STRIPE_PRO_CHATS_MONTHLY_PRICE_ID,
|
|
|
|
yearly: process.env.STRIPE_PRO_CHATS_YEARLY_PRICE_ID,
|
|
|
|
},
|
|
|
|
storage: {
|
|
|
|
monthly: process.env.STRIPE_PRO_STORAGE_MONTHLY_PRICE_ID,
|
|
|
|
yearly: process.env.STRIPE_PRO_STORAGE_YEARLY_PRICE_ID,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2022-09-17 16:37:33 +02:00
|
|
|
export const prices = {
|
|
|
|
[Plan.STARTER]: 39,
|
|
|
|
[Plan.PRO]: 89,
|
|
|
|
} as const
|
|
|
|
|
|
|
|
export const chatsLimit = {
|
2023-04-13 11:39:10 +02:00
|
|
|
[Plan.FREE]: { totalIncluded: 200 },
|
2022-09-17 16:37:33 +02:00
|
|
|
[Plan.STARTER]: {
|
2023-04-13 11:39:10 +02:00
|
|
|
graduatedPrice: [
|
|
|
|
{ totalIncluded: 2000, price: 0 },
|
|
|
|
{
|
|
|
|
totalIncluded: 2500,
|
|
|
|
price: 10,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
totalIncluded: 3000,
|
|
|
|
price: 20,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
totalIncluded: 3500,
|
|
|
|
price: 30,
|
|
|
|
},
|
|
|
|
],
|
2022-09-17 16:37:33 +02:00
|
|
|
},
|
|
|
|
[Plan.PRO]: {
|
2023-04-13 11:39:10 +02:00
|
|
|
graduatedPrice: [
|
|
|
|
{ totalIncluded: 10000, price: 0 },
|
|
|
|
{ totalIncluded: 15000, price: 50 },
|
|
|
|
{ totalIncluded: 25000, price: 150 },
|
|
|
|
{ totalIncluded: 50000, price: 400 },
|
|
|
|
],
|
2022-09-17 16:37:33 +02:00
|
|
|
},
|
2022-12-13 08:21:18 +01:00
|
|
|
[Plan.CUSTOM]: {
|
|
|
|
totalIncluded: 2000,
|
|
|
|
increaseStep: {
|
|
|
|
amount: 500,
|
|
|
|
price: 10,
|
|
|
|
},
|
|
|
|
},
|
2022-09-17 16:37:33 +02:00
|
|
|
[Plan.OFFERED]: { totalIncluded: infinity },
|
|
|
|
[Plan.LIFETIME]: { totalIncluded: infinity },
|
2023-01-27 15:00:07 +01:00
|
|
|
[Plan.UNLIMITED]: { totalIncluded: infinity },
|
2022-09-17 16:37:33 +02:00
|
|
|
} as const
|
|
|
|
|
|
|
|
export const storageLimit = {
|
|
|
|
[Plan.FREE]: { totalIncluded: 0 },
|
|
|
|
[Plan.STARTER]: {
|
2023-04-13 11:39:10 +02:00
|
|
|
graduatedPrice: [
|
|
|
|
{ totalIncluded: 2, price: 0 },
|
|
|
|
{
|
|
|
|
totalIncluded: 3,
|
|
|
|
price: 2,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
totalIncluded: 4,
|
|
|
|
price: 4,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
totalIncluded: 5,
|
|
|
|
price: 6,
|
|
|
|
},
|
|
|
|
],
|
2022-09-17 16:37:33 +02:00
|
|
|
},
|
|
|
|
[Plan.PRO]: {
|
2023-04-13 11:39:10 +02:00
|
|
|
graduatedPrice: [
|
|
|
|
{ totalIncluded: 10, price: 0 },
|
|
|
|
{
|
|
|
|
totalIncluded: 15,
|
|
|
|
price: 8,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
totalIncluded: 25,
|
|
|
|
price: 24,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
totalIncluded: 40,
|
|
|
|
price: 49,
|
|
|
|
},
|
|
|
|
],
|
2022-09-17 16:37:33 +02:00
|
|
|
},
|
2022-12-13 08:21:18 +01:00
|
|
|
[Plan.CUSTOM]: {
|
|
|
|
totalIncluded: 2,
|
|
|
|
increaseStep: {
|
|
|
|
amount: 1,
|
|
|
|
price: 2,
|
|
|
|
},
|
|
|
|
},
|
2022-09-17 16:37:33 +02:00
|
|
|
[Plan.OFFERED]: { totalIncluded: 2 },
|
|
|
|
[Plan.LIFETIME]: { totalIncluded: 10 },
|
2023-01-27 15:00:07 +01:00
|
|
|
[Plan.UNLIMITED]: { totalIncluded: infinity },
|
2022-09-17 16:37:33 +02:00
|
|
|
} as const
|
|
|
|
|
|
|
|
export const seatsLimit = {
|
2022-10-27 11:32:21 +02:00
|
|
|
[Plan.FREE]: { totalIncluded: 1 },
|
2022-09-17 16:37:33 +02:00
|
|
|
[Plan.STARTER]: {
|
|
|
|
totalIncluded: 2,
|
|
|
|
},
|
|
|
|
[Plan.PRO]: {
|
|
|
|
totalIncluded: 5,
|
|
|
|
},
|
2022-12-13 08:21:18 +01:00
|
|
|
[Plan.CUSTOM]: {
|
|
|
|
totalIncluded: 2,
|
|
|
|
},
|
2022-09-17 16:37:33 +02:00
|
|
|
[Plan.OFFERED]: { totalIncluded: 2 },
|
|
|
|
[Plan.LIFETIME]: { totalIncluded: 8 },
|
2023-01-27 15:00:07 +01:00
|
|
|
[Plan.UNLIMITED]: { totalIncluded: infinity },
|
2022-09-17 16:37:33 +02:00
|
|
|
} as const
|
|
|
|
|
|
|
|
export const getChatsLimit = ({
|
|
|
|
plan,
|
|
|
|
additionalChatsIndex,
|
2022-10-27 11:32:21 +02:00
|
|
|
customChatsLimit,
|
|
|
|
}: Pick<Workspace, 'additionalChatsIndex' | 'plan' | 'customChatsLimit'>) => {
|
2022-12-13 08:21:18 +01:00
|
|
|
if (customChatsLimit) return customChatsLimit
|
2023-04-13 11:39:10 +02:00
|
|
|
const totalIncluded =
|
2022-09-17 16:37:33 +02:00
|
|
|
plan === Plan.STARTER || plan === Plan.PRO
|
2023-04-13 11:39:10 +02:00
|
|
|
? chatsLimit[plan].graduatedPrice[additionalChatsIndex].totalIncluded
|
|
|
|
: chatsLimit[plan].totalIncluded
|
|
|
|
return totalIncluded
|
2022-09-17 16:37:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
export const getStorageLimit = ({
|
|
|
|
plan,
|
|
|
|
additionalStorageIndex,
|
2022-10-27 11:32:21 +02:00
|
|
|
customStorageLimit,
|
|
|
|
}: Pick<
|
|
|
|
Workspace,
|
|
|
|
'additionalStorageIndex' | 'plan' | 'customStorageLimit'
|
|
|
|
>) => {
|
2022-12-13 08:21:18 +01:00
|
|
|
if (customStorageLimit) return customStorageLimit
|
2023-04-13 11:39:10 +02:00
|
|
|
const totalIncluded =
|
2022-09-17 16:37:33 +02:00
|
|
|
plan === Plan.STARTER || plan === Plan.PRO
|
2023-04-13 11:39:10 +02:00
|
|
|
? storageLimit[plan].graduatedPrice[additionalStorageIndex].totalIncluded
|
|
|
|
: storageLimit[plan].totalIncluded
|
|
|
|
return totalIncluded
|
2022-09-17 16:37:33 +02:00
|
|
|
}
|
2022-09-18 19:01:37 +02:00
|
|
|
|
2022-10-27 11:32:21 +02:00
|
|
|
export const getSeatsLimit = ({
|
|
|
|
plan,
|
|
|
|
customSeatsLimit,
|
|
|
|
}: Pick<Workspace, 'plan' | 'customSeatsLimit'>) => {
|
2022-12-13 08:21:18 +01:00
|
|
|
if (customSeatsLimit) return customSeatsLimit
|
2022-10-27 11:32:21 +02:00
|
|
|
return seatsLimit[plan].totalIncluded
|
|
|
|
}
|
|
|
|
|
2023-02-13 11:13:05 +01:00
|
|
|
export const isSeatsLimitReached = ({
|
|
|
|
existingMembersCount,
|
|
|
|
existingInvitationsCount,
|
|
|
|
plan,
|
|
|
|
customSeatsLimit,
|
|
|
|
}: { existingMembersCount: number; existingInvitationsCount: number } & Pick<
|
|
|
|
Workspace,
|
|
|
|
'plan' | 'customSeatsLimit'
|
|
|
|
>) => {
|
|
|
|
const seatsLimit = getSeatsLimit({ plan, customSeatsLimit })
|
|
|
|
return (
|
|
|
|
seatsLimit !== infinity &&
|
|
|
|
seatsLimit <= existingMembersCount + existingInvitationsCount
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2022-09-18 19:01:37 +02:00
|
|
|
export const computePrice = (
|
|
|
|
plan: Plan,
|
|
|
|
selectedTotalChatsIndex: number,
|
2023-04-13 11:39:10 +02:00
|
|
|
selectedTotalStorageIndex: number,
|
|
|
|
frequency: 'monthly' | 'yearly'
|
2022-09-18 19:01:37 +02:00
|
|
|
) => {
|
|
|
|
if (plan !== Plan.STARTER && plan !== Plan.PRO) return
|
2023-04-13 11:39:10 +02:00
|
|
|
const price =
|
2022-09-18 19:01:37 +02:00
|
|
|
prices[plan] +
|
2023-04-13 11:39:10 +02:00
|
|
|
chatsLimit[plan].graduatedPrice[selectedTotalChatsIndex].price +
|
|
|
|
storageLimit[plan].graduatedPrice[selectedTotalStorageIndex].price
|
|
|
|
return frequency === 'monthly' ? price : price - price * 0.16
|
2022-09-18 19:01:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
const europeanUnionCountryCodes = [
|
|
|
|
'AT',
|
|
|
|
'BE',
|
|
|
|
'BG',
|
|
|
|
'CY',
|
|
|
|
'CZ',
|
|
|
|
'DE',
|
|
|
|
'DK',
|
|
|
|
'EE',
|
|
|
|
'ES',
|
|
|
|
'FI',
|
|
|
|
'FR',
|
|
|
|
'GR',
|
|
|
|
'HR',
|
|
|
|
'HU',
|
|
|
|
'IE',
|
|
|
|
'IT',
|
|
|
|
'LT',
|
|
|
|
'LU',
|
|
|
|
'LV',
|
|
|
|
'MT',
|
|
|
|
'NL',
|
|
|
|
'PL',
|
|
|
|
'PT',
|
|
|
|
'RO',
|
|
|
|
'SE',
|
|
|
|
'SI',
|
|
|
|
'SK',
|
|
|
|
]
|
|
|
|
|
|
|
|
const europeanUnionExclusiveLanguageCodes = [
|
|
|
|
'fr',
|
|
|
|
'de',
|
|
|
|
'it',
|
|
|
|
'el',
|
|
|
|
'pl',
|
|
|
|
'fi',
|
|
|
|
'nl',
|
|
|
|
'hr',
|
|
|
|
'cs',
|
|
|
|
'hu',
|
|
|
|
'ro',
|
|
|
|
'sl',
|
|
|
|
'sv',
|
|
|
|
'bg',
|
|
|
|
]
|
|
|
|
|
2023-04-13 11:39:10 +02:00
|
|
|
export const guessIfUserIsEuropean = () => {
|
|
|
|
if (typeof window === 'undefined') return false
|
|
|
|
return window.navigator.languages.some((language) => {
|
2022-09-18 19:01:37 +02:00
|
|
|
const [languageCode, countryCode] = language.split('-')
|
|
|
|
return countryCode
|
|
|
|
? europeanUnionCountryCodes.includes(countryCode)
|
|
|
|
: europeanUnionExclusiveLanguageCodes.includes(languageCode)
|
|
|
|
})
|
2023-04-13 11:39:10 +02:00
|
|
|
}
|
2022-09-18 19:01:37 +02:00
|
|
|
|
2022-11-29 13:17:46 +01:00
|
|
|
export const formatPrice = (price: number, currency?: 'eur' | 'usd') => {
|
2022-09-18 19:01:37 +02:00
|
|
|
const isEuropean = guessIfUserIsEuropean()
|
|
|
|
const formatter = new Intl.NumberFormat(isEuropean ? 'fr-FR' : 'en-US', {
|
|
|
|
style: 'currency',
|
2022-11-29 13:17:46 +01:00
|
|
|
currency: currency?.toUpperCase() ?? (isEuropean ? 'EUR' : 'USD'),
|
2022-09-18 19:01:37 +02:00
|
|
|
maximumFractionDigits: 0, // (causes 2500.99 to be printed as $2,501)
|
|
|
|
})
|
|
|
|
return formatter.format(price)
|
|
|
|
}
|