feat: new platform addons (#32624)

Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
This commit is contained in:
Zach Waterfield
2025-05-26 14:08:39 -04:00
committed by GitHub
parent b83fbc2be6
commit 2ee43f3bed
18 changed files with 182 additions and 159 deletions

View File

@@ -2813,7 +2813,7 @@
{
"plan_key": "free-20230117",
"product_key": "platform_and_support",
"name": "Totally free",
"name": "Free",
"description": "SSO, permission management, and support.",
"image_url": "https://posthog.com/images/product/product-icons/platform.svg",
"docs_url": "https://posthog.com/docs",

View File

@@ -2934,7 +2934,7 @@
{
"plan_key": "free-20230117",
"product_key": "platform_and_support",
"name": "Totally free",
"name": "Free",
"description": "SSO, permission management, and support.",
"image_url": "https://posthog.com/images/product/product-icons/platform.svg",
"docs_url": "https://posthog.com/docs",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 103 KiB

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 104 KiB

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 63 KiB

After

Width:  |  Height:  |  Size: 62 KiB

View File

@@ -87,14 +87,14 @@ const SupportResponseTimesTable = ({
link?: string
}[] = [
{
name: 'Totally free',
name: 'Free',
current_plan: billing?.subscription_level === 'free' && !hasActiveTrial && !hasEnterprisePlan,
features: [{ note: 'Community support only' }],
plan_key: 'free',
link: 'https://posthog.com/questions',
},
{
name: 'Ridiculously cheap',
name: 'Pay-as-you-go',
current_plan:
billing?.subscription_level === 'paid' && !teamsAddonActive && !hasEnterprisePlan && !hasActiveTrial,
features: [{ note: '2 business days' }],
@@ -359,8 +359,8 @@ export function SidePanelSupport(): JSX.Element {
<Section title="">
<h3>Can't find what you need in the docs?</h3>
<p>
With the totally free plan you can ask the community via the link below, or
explore your upgrade choices for the ability to email a support engineer.
With the free plan you can ask the community via the link below, or explore your
upgrade choices for the ability to email a support engineer.
</p>
</Section>
)}

View File

@@ -2635,7 +2635,7 @@ export const billingJson: BillingType = {
{
plan_key: 'free-20230117',
product_key: 'platform_and_support',
name: 'Totally free',
name: 'Free',
description: 'SSO, permission management, and support.',
image_url: 'https://posthog.com/images/product/product-icons/platform.svg',
docs_url: 'https://posthog.com/docs',

View File

@@ -3338,7 +3338,7 @@
{
"plan_key": "free-20230117",
"product_key": "platform_and_support",
"name": "Totally free",
"name": "Free",
"description": "SSO, permission management, and support.",
"image_url": "https://posthog.com/images/product/product-icons/platform.svg",
"docs_url": "https://posthog.com/docs",

View File

@@ -22,7 +22,9 @@ import { PlanComparisonModal } from './PlanComparison'
const PLAN_BADGES: Record<BillingPlan, string> = {
[BillingPlan.Free]: planFree,
[BillingPlan.Paid]: planPaid,
[BillingPlan.Teams]: planTeams,
[BillingPlan.Teams]: planTeams, // Legacy
[BillingPlan.Boost]: planTeams, // TODO: Add Boost badge
[BillingPlan.Scale]: planTeams, // TODO: Add Scale badge
[BillingPlan.Enterprise]: planEnterprise,
}
@@ -53,27 +55,49 @@ const BADGE_CONFIG: Record<BillingPlan | StartupProgramLabel, CopyVariation> = {
},
[BillingPlan.Paid]: {
title: 'Good call!',
subtitle: "You're on the Ridiculously Cheap™ plan.",
subtitle: "You're on the Pay-as-you-go plan.",
backgroundColor: 'bg-warning-highlight',
getDescription: (_billingPlan: BillingPlan, scrollToProduct: (productType: string) => void) => (
<p>
If you're growing like crazy, you might want to check out the{' '}
If you're growing like crazy, you might want to check out our{' '}
{scrollToProduct ? (
<>
<Link onClick={() => scrollToProduct('teams')}>Teams</Link>
{' or '}
<Link onClick={() => scrollToProduct('enterprise')}>Enterprise</Link>
<Link onClick={() => scrollToProduct('platform_and_support')}>Platform add-ons</Link>
</>
) : (
'Teams or Enterprise'
)}{' '}
plan.
'Platform add-ons'
)}
.
</p>
),
},
[BillingPlan.Teams]: {
title: 'Good call!',
subtitle: "You're on the Teams plan.",
subtitle: "You're on the Pay-as-you-go plan (with Teams add-on).",
backgroundColor: 'bg-warning-highlight',
getDescription: (_billingPlan: BillingPlan, scrollToProduct: (productType: string) => void) => (
<p>
If you're growing like crazy, you might want to check out the{' '}
{scrollToProduct ? <Link onClick={() => scrollToProduct('enterprise')}>Enterprise</Link> : 'Enterprise'}{' '}
plan.
</p>
),
},
[BillingPlan.Boost]: {
title: 'Good call!',
subtitle: "You're on the Pay-as-you-go plan (with Boost add-on).",
backgroundColor: 'bg-warning-highlight',
getDescription: (_billingPlan: BillingPlan, scrollToProduct: (productType: string) => void) => (
<p>
If you're growing like crazy, you might want to check out the{' '}
{scrollToProduct ? <Link onClick={() => scrollToProduct('enterprise')}>Enterprise</Link> : 'Enterprise'}{' '}
plan.
</p>
),
},
[BillingPlan.Scale]: {
title: 'Good call!',
subtitle: "You're on the Pay-as-you-go plan (with Scale add-on).",
backgroundColor: 'bg-warning-highlight',
getDescription: (_billingPlan: BillingPlan, scrollToProduct: (productType: string) => void) => (
<p>
@@ -93,23 +117,15 @@ const BADGE_CONFIG: Record<BillingPlan | StartupProgramLabel, CopyVariation> = {
title: 'Good for you!',
subtitle: "You're on the startup plan.",
backgroundColor: 'bg-warning-highlight',
getDescription: (billingPlan: BillingPlan, scrollToProduct: (productType: string) => void) => (
getDescription: (_billingPlan: BillingPlan, scrollToProduct: (productType: string) => void) => (
<p>
If you're growing like crazy, you might want to check out the{' '}
{billingPlan !== BillingPlan.Teams ? (
<>
{scrollToProduct ? (
<>
<Link onClick={() => scrollToProduct('teams')}>Teams</Link>
{' or '}
</>
) : (
'Teams or '
)}
</>
) : null}
{scrollToProduct ? <Link onClick={() => scrollToProduct('enterprise')}>Enterprise</Link> : 'Enterprise'}{' '}
plan.
If you're growing like crazy, you might want to check out our{' '}
{scrollToProduct ? (
<Link onClick={() => scrollToProduct('platform_and_support')}>Platform add-ons</Link>
) : (
'Platform add-ons'
)}
.
</p>
),
},
@@ -117,32 +133,20 @@ const BADGE_CONFIG: Record<BillingPlan | StartupProgramLabel, CopyVariation> = {
title: 'Lucky you!',
subtitle: "You're on the YC plan.",
backgroundColor: 'bg-warning-highlight',
getDescription: (billingPlan: BillingPlan, scrollToProduct: (productType: string) => void) => (
getDescription: (_billingPlan: BillingPlan, scrollToProduct: (productType: string) => void) => (
<>
<p>
Enjoy your founder merch, and don't forget to say hello in the{' '}
<Link to="https://posthog.slack.com/archives/C04J1TJ11UZ">Founders Club!</Link>
</p>
<p>
If you're growing like crazy, you might want to check out the{' '}
{billingPlan !== BillingPlan.Teams ? (
<>
{scrollToProduct ? (
<>
<Link onClick={() => scrollToProduct('teams')}>Teams</Link>
{' or '}
</>
) : (
'Teams or '
)}
</>
) : null}
If you're growing like crazy, you might want to check out our{' '}
{scrollToProduct ? (
<Link onClick={() => scrollToProduct('enterprise')}>Enterprise</Link>
<Link onClick={() => scrollToProduct('platform_and_support')}>Platform add-ons</Link>
) : (
'Enterprise'
)}{' '}
plan.
'Platform add-ons'
)}
.
</p>
</>
),

View File

@@ -54,6 +54,7 @@ export const BillingProduct = ({ product }: { product: BillingProductV2Type }):
surveyID,
billingProductLoading,
isSessionReplayWithAddons,
visibleAddons,
} = useValues(billingProductLogic({ product }))
const {
setShowTierBreakdown,
@@ -100,7 +101,10 @@ export const BillingProduct = ({ product }: { product: BillingProductV2Type }):
<div className="border border-primary rounded w-full bg-surface-primary" ref={productRef}>
<div className="border-b border-primary rounded-t p-4">
<div className="flex gap-4 items-center justify-between">
{/* Product icon */}
{getProductIcon(product.name, product.icon_key, 'text-2xl')}
{/* Product name and description */}
<div>
<h3 className="font-bold mb-0 flex items-center gap-x-2">
{product.name}{' '}
@@ -110,6 +114,8 @@ export const BillingProduct = ({ product }: { product: BillingProductV2Type }):
</h3>
<div>{product.description}</div>
</div>
{/* Product actions */}
<div className="flex grow justify-end gap-x-2 items-center">
{product.docs_url && (
<LemonButton
@@ -170,12 +176,15 @@ export const BillingProduct = ({ product }: { product: BillingProductV2Type }):
</div>
</div>
<div className="px-8 pb-8 sm:pb-0">
{/* Exceeded limit notice */}
{product.percentage_usage > 1 && (
<LemonBanner className="mt-6" type="error">
You have exceeded the {hasCustomLimitSet ? 'billing limit' : 'free tier limit'} for this
product.
</LemonBanner>
)}
{/* Usage and projected usage */}
<div className="sm:flex w-full items-center gap-x-8">
{product.contact_support && (!product.subscribed || isUnlicensedDebug) ? (
<div className="py-8">
@@ -329,15 +338,36 @@ export const BillingProduct = ({ product }: { product: BillingProductV2Type }):
)
)}
</div>
{product.price_description ? (
<LemonBanner type="info">
<span dangerouslySetInnerHTML={{ __html: product.price_description }} />
</LemonBanner>
) : null}
{/* Table with tiers */}
{showTierBreakdown && <BillingProductPricingTable product={product} />}
{/* Add-ons */}
{product.addons?.length > 0 && (
<div className="pb-8">
{/* Legacy teams addon */}
{product.type === 'platform_and_support' &&
product.addons.find((addon) => addon.legacy_product && addon.subscribed) && (
<LemonBanner type="warning" className="my-4" hideIcon>
<p>
You're currently subscribed to our legacy{' '}
{
product.addons.find((addon) => addon.legacy_product && addon.subscribed)
?.name
}{' '}
add-on. If you'd like to move to one of our new add-ons please subscribe
below.
</p>
</LemonBanner>
)}
{/* Add-ons title */}
<h4 className="my-4">Add-ons</h4>
{billing?.subscription_level == 'free' && (
<LemonBanner type="warning" className="text-sm mb-4" hideIcon>
@@ -376,28 +406,18 @@ export const BillingProduct = ({ product }: { product: BillingProductV2Type }):
</LemonBanner>
)}
<div className="gap-y-4 flex flex-col">
{product.addons
// TODO: enhanced_persons: remove this filter
.filter((addon) => {
if (addon.inclusion_only) {
if (featureFlags[FEATURE_FLAGS.PERSONLESS_EVENTS_NOT_SUPPORTED]) {
return false
}
}
return true
})
.filter((addon) => {
const hideAddonFlag = `billing_hide_addon_${addon.type}`
return featureFlags[hideAddonFlag] !== true
})
.map((addon, i) => {
return <BillingProductAddon key={i} addon={addon} />
})}
{visibleAddons.map((addon: BillingProductV2AddonType, i: number) => {
return <BillingProductAddon key={i} addon={addon} />
})}
</div>
</div>
)}
</div>
{/* Billing limit */}
{!isTemporaryFreeProduct && <BillingLimit product={product} />}
{/* Feature flag usage notice */}
<FeatureFlagUsageNotice product={product} />
</div>
<ProductPricingModal

View File

@@ -1,8 +1,7 @@
import { IconCheckCircle, IconChevronDown, IconChevronRight } from '@posthog/icons'
import { LemonButton, LemonModal, LemonSelectOptions, LemonTag, Link, Tooltip } from '@posthog/lemon-ui'
import { IconCheckCircle, IconChevronDown, IconChevronRight, IconInfo } from '@posthog/icons'
import { LemonButton, LemonSelectOptions, LemonTag, Link, Tooltip } from '@posthog/lemon-ui'
import clsx from 'clsx'
import { useActions, useValues } from 'kea'
import { supportLogic } from 'lib/components/Support/supportLogic'
import { capitalizeFirstLetter, humanFriendlyCurrency } from 'lib/utils'
import { ReactNode, useRef } from 'react'
import { getProductIcon } from 'scenes/products/Products'
@@ -34,17 +33,13 @@ export const formatFlatRate = (flatRate: number, unit: string | null): string |
export const BillingProductAddon = ({ addon }: { addon: BillingProductV2AddonType }): JSX.Element => {
const productRef = useRef<HTMLDivElement | null>(null)
const { billing } = useValues(billingLogic)
const { isPricingModalOpen, currentAndUpgradePlans, surveyID, trialModalOpen, trialLoading, showTierBreakdown } =
useValues(billingProductLogic({ product: addon, productRef }))
const { toggleIsPricingModalOpen, setTrialModalOpen, activateTrial, setShowTierBreakdown } = useActions(
billingProductLogic({ product: addon })
const { isPricingModalOpen, currentAndUpgradePlans, surveyID, showTierBreakdown } = useValues(
billingProductLogic({ product: addon, productRef })
)
const { openSupportForm } = useActions(supportLogic)
const { toggleIsPricingModalOpen, setShowTierBreakdown } = useActions(billingProductLogic({ product: addon }))
const logic = billingProductAddonLogic({ addon })
const { gaugeItems } = useValues(logic)
const upgradePlan = currentAndUpgradePlans?.upgradePlan
const productType = { plural: `${addon.unit}s`, singular: addon.unit }
const tierDisplayOptions: LemonSelectOptions<string> = [
{ label: `Per ${productType.singular}`, value: 'individual' },
@@ -98,6 +93,13 @@ export const BillingProductAddon = ({ addon }: { addon: BillingProductV2AddonTyp
</div>
)
)}
{addon.legacy_product && (
<div>
<LemonTag type="highlight" icon={<IconInfo />}>
Legacy add-on
</LemonTag>
</div>
)}
</div>
<p className="ml-0 mb-0">
{addon.description}{' '}
@@ -204,60 +206,6 @@ export const BillingProductAddon = ({ addon }: { addon: BillingProductV2AddonTyp
{/* Unsubscribe survey modal */}
{surveyID && <UnsubscribeSurveyModal product={addon} />}
{/* Trial modal */}
{/* Not currently used but keeping around incase we need it again */}
<LemonModal
isOpen={trialModalOpen}
onClose={() => setTrialModalOpen(false)}
title={`Start your ${addon.name} trial`}
description={`You'll have ${addon.trial?.length} days to try it out before being charged.`}
footer={
<>
<LemonButton type="secondary" onClick={() => setTrialModalOpen(false)}>
Cancel
</LemonButton>
<LemonButton type="primary" onClick={activateTrial} loading={trialLoading}>
Start trial
</LemonButton>
</>
}
>
<p className="mb-1.5">Here's some stuff about the trial:</p>
<ul className="flex flex-col gap-0.5">
<li className="ml-2">
🎉 It's <b>free!</b>
</li>
<li className="ml-2">
📅 The trial is for <b>{addon.trial?.length} days</b>
</li>
<li className="ml-2">
🚀 You'll get access to <b>all the features</b> of the plan immediately
</li>
<li className="ml-2">
📧 3 days before the trial ends, you'll be emailed a reminder that you'll be charged
</li>
<li className="ml-2">
🚫 If you don't want to be charged, you can cancel anytime before the trial ends
</li>
<li className="ml-2">
💵 At the end of the trial, you'll be be subscribed and charged{' '}
{formatFlatRate(Number(upgradePlan?.unit_amount_usd), upgradePlan?.unit)}
</li>
<li className="ml-2">
☎️ If you have any questions, you can{' '}
<Link
onClick={() => {
setTrialModalOpen(false)
openSupportForm({ kind: 'support', target_area: 'billing' })
}}
className="cursor-pointer"
>
contact us
</Link>
</li>
</ul>
</LemonModal>
</div>
)
}

View File

@@ -21,7 +21,7 @@ interface BillingProductAddonActionsProps {
export const BillingProductAddonActions = ({ addon, productRef }: BillingProductAddonActionsProps): JSX.Element => {
const { billing, billingError, timeTotalInSeconds, timeRemainingInSeconds } = useValues(billingLogic)
const { currentAndUpgradePlans, billingProductLoading, trialLoading } = useValues(
const { currentAndUpgradePlans, billingProductLoading, trialLoading, isSubscribedToAnotherAddon } = useValues(
billingProductLogic({ product: addon, productRef })
)
@@ -121,7 +121,7 @@ export const BillingProductAddonActions = ({ addon, productRef }: BillingProduct
(billingError && billingError.message) ||
(billing?.subscription_level === 'free' && 'Upgrade to add add-ons')
}
loading={billingProductLoading === addon.type}
loading={billingProductLoading === addon.type || trialLoading}
onClick={
isTrialEligible
? () => activateTrial()
@@ -152,7 +152,7 @@ export const BillingProductAddonActions = ({ addon, productRef }: BillingProduct
return null
}
if (isTrialEligible) {
if (isTrialEligible && !isSubscribedToAnotherAddon) {
return (
<p className="mt-2 text-xs text-secondary text-right">
You'll have {addon.trial?.length} days to try it out. Then you'll be charged{' '}
@@ -161,7 +161,7 @@ export const BillingProductAddonActions = ({ addon, productRef }: BillingProduct
)
}
if (isProrated) {
if (isProrated && !isSubscribedToAnotherAddon) {
return (
<p className="mt-2 text-xs text-secondary text-right">
Pay ~${prorationAmount} today (prorated) and
@@ -192,8 +192,9 @@ export const BillingProductAddonActions = ({ addon, productRef }: BillingProduct
Contact support
</LemonButton>
)
} else if (!billing?.trial) {
} else if (!billing?.trial && !isSubscribedToAnotherAddon) {
// Customer is not subscribed to any trial
// We don't allow multiple add-ons to be subscribed to at the same time so this checks if the customer is subscribed to another add-on
// TODO: add support for when a customer has a Paid Plan trial
content = renderPurchaseActions()
}

View File

@@ -2,6 +2,7 @@ import { LemonDialog, lemonToast } from '@posthog/lemon-ui'
import { actions, connect, events, kea, key, listeners, path, props, reducers, selectors } from 'kea'
import { forms } from 'kea-forms'
import api from 'lib/api'
import { FEATURE_FLAGS } from 'lib/constants'
import { featureFlagLogic } from 'lib/logic/featureFlagLogic'
import posthog from 'posthog-js'
import React from 'react'
@@ -11,6 +12,7 @@ import {
BillingProductV2AddonType,
BillingProductV2Type,
BillingTierType,
BillingType,
SurveyEventName,
} from '~/types'
@@ -109,7 +111,6 @@ export const billingProductLogic = kea<billingProductLogicType>([
}),
activateTrial: true,
cancelTrial: true,
setTrialModalOpen: (isOpen: boolean) => ({ isOpen }),
setTrialLoading: (loading: boolean) => ({ loading }),
setUnsubscribeModalStep: (step: number) => ({ step }),
resetUnsubscribeModalStep: true,
@@ -193,12 +194,6 @@ export const billingProductLogic = kea<billingProductLogicType>([
toggleIsPlanComparisonModalOpen: (_, { highlightedFeatureKey }) => highlightedFeatureKey || null,
},
],
trialModalOpen: [
false,
{
setTrialModalOpen: (_, { isOpen }) => isOpen,
},
],
trialLoading: [
false,
{
@@ -220,6 +215,32 @@ export const billingProductLogic = kea<billingProductLogicType>([
],
}),
selectors(({ values }) => ({
isSubscribedToAnotherAddon: [
(s, p) => [s.billing, p.product],
(billing: BillingType, addon: BillingProductV2AddonType) => {
const subscribed = addon.subscribed
if (subscribed) {
// They are subscribed to this addon so can't be subscribed to another one
return false
}
const parentProduct = billing?.products.find((product: any) =>
product.addons.find((a: BillingProductV2AddonType) => a.type === addon.type)
)
if (!parentProduct) {
return false
}
if (parentProduct?.type !== 'platform_and_support') {
// Only platform and support can have multiple add-ons
return false
}
// Check if they are subscribed to another add-on that is not a legacy add-on
// This is because if they are on a legacy add-on, we want them to be able to move to a new add-on.
return parentProduct.addons.some((a: BillingProductV2AddonType) => a.subscribed && !a.legacy_product)
},
],
customLimitUsd: [
(s, p) => [s.billing, p.product],
(billing, product) => {
@@ -230,6 +251,34 @@ export const billingProductLogic = kea<billingProductLogicType>([
return product.usage_key ? billing?.custom_limits_usd?.[product.usage_key] ?? null : null
},
],
visibleAddons: [
(s, p) => [s.featureFlags, p.product],
(featureFlags: Record<string, any>, product: BillingProductV2Type) => {
if (!product.addons?.length) {
return []
}
return product.addons.filter((addon: BillingProductV2AddonType) => {
// Filter out inclusion-only addons if personless events are not supported
if (addon.inclusion_only && featureFlags[FEATURE_FLAGS.PERSONLESS_EVENTS_NOT_SUPPORTED]) {
return false
}
// Filter out legacy addons for platform_and_support if not subscribed
if (product.type === 'platform_and_support' && addon.legacy_product && !addon.subscribed) {
return false
}
// Filter out addons that are hidden by feature flag
const hideAddonFlag = `billing_hide_addon_${addon.type}`
if (featureFlags[hideAddonFlag]) {
return false
}
return true
})
},
],
hasCustomLimitSet: [
(s) => [s.customLimitUsd],
(customLimitUsd) => (!!customLimitUsd || customLimitUsd === 0) && customLimitUsd >= 0,
@@ -407,13 +456,12 @@ export const billingProductLogic = kea<billingProductLogicType>([
target: props.product.type,
})
lemonToast.success('Your trial has been activated!')
} catch (e) {
lemonToast.error('There was an error activating your trial. Please try again or contact support.')
} finally {
await breakpoint(400)
window.location.reload()
} catch (e) {
lemonToast.error('There was an error activating your trial. Please try again or contact support.')
actions.setTrialLoading(false)
actions.setTrialModalOpen(false)
actions.loadBilling()
}
},
cancelTrial: async () => {
@@ -421,13 +469,11 @@ export const billingProductLogic = kea<billingProductLogicType>([
try {
await api.create(`api/billing/trials/cancel`)
lemonToast.success('Your trial has been cancelled!')
} catch (e) {
console.error(e)
lemonToast.error('There was an error cancelling your trial. Please try again or contact support.')
} finally {
actions.loadBilling()
window.location.reload()
} catch (e) {
lemonToast.error('There was an error cancelling your trial. Please try again or contact support.')
actions.setTrialLoading(false)
actions.loadBilling()
}
},
triggerMoreHedgehogs: async (_, breakpoint) => {

View File

@@ -197,7 +197,7 @@ export const PlanCard: React.FC<PlanCardProps> = ({ planData, product, highlight
const PLANS_DATA: PlanData[] = [
{
title: 'Totally free',
title: 'Free',
plan: Plan.TOTALLY_FREE,
billingPlanKeyPrefix: 'free',
subtitle: 'No credit card required',
@@ -212,7 +212,7 @@ const PLANS_DATA: PlanData[] = [
ctaAction: 'next',
},
{
title: 'Ridiculously cheap',
title: 'Pay-as-you-go',
plan: Plan.RIDICULOUSLY_CHEAP,
billingPlanKeyPrefix: 'paid',
subtitle: 'Usage-based pricing after free tier',

View File

@@ -226,7 +226,9 @@ export enum LicensePlan {
export enum BillingPlan {
Free = 'free',
Paid = 'paid',
Teams = 'teams',
Teams = 'teams', // Legacy
Boost = 'boost',
Scale = 'scale',
Enterprise = 'enterprise',
}
@@ -1806,6 +1808,7 @@ export interface BillingProductV2Type {
// addons-only: if this addon is included with the base product and not subscribed individually. for backwards compatibility.
included_with_main_product?: boolean
trial?: BillingTrialType
legacy_product?: boolean | null
}
export interface BillingProductV2AddonType {
@@ -1837,6 +1840,7 @@ export interface BillingProductV2AddonType {
included_if?: 'no_active_subscription' | 'has_subscription' | null
usage_limit?: number | null
trial?: BillingTrialType
legacy_product?: boolean | null
}
export interface BillingType {
customer_id: string

View File

@@ -2813,7 +2813,7 @@
{
"plan_key": "free-20230117",
"product_key": "platform_and_support",
"name": "Totally free",
"name": "Free",
"description": "SSO, permission management, and support.",
"image_url": "https://posthog.com/images/product/product-icons/platform.svg",
"docs_url": "https://posthog.com/docs",

View File

@@ -2934,7 +2934,7 @@
{
"plan_key": "free-20230117",
"product_key": "platform_and_support",
"name": "Totally free",
"name": "Free",
"description": "SSO, permission management, and support.",
"image_url": "https://posthog.com/images/product/product-icons/platform.svg",
"docs_url": "https://posthog.com/docs",