Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(billing): subscribe to all products (client side) #22768

Merged
merged 97 commits into from
Jun 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
97 commits
Select commit Hold shift + click to select a range
5f2498d
add subscription_level to billing type
zlwaterfield Jun 6, 2024
e876056
add SUBSCRIBE_TO_ALL_PRODUCTS ff
zlwaterfield Jun 6, 2024
85638c9
initial logic / copy for subscribe to all products behind f
zlwaterfield Jun 6, 2024
0b6e046
Update UI snapshots for `chromium` (1)
github-actions[bot] Jun 6, 2024
40f358f
Update UI snapshots for `chromium` (2)
github-actions[bot] Jun 6, 2024
b67d387
remove border focus
zlwaterfield Jun 6, 2024
dcfe33c
merge in main
zlwaterfield Jun 6, 2024
2664688
Update UI snapshots for `chromium` (2)
github-actions[bot] Jun 6, 2024
5be7cae
Update UI snapshots for `chromium` (1)
github-actions[bot] Jun 6, 2024
167a1a4
improve plan comparison for paid plan
zlwaterfield Jun 7, 2024
46fc301
improve unsub flow
zlwaterfield Jun 7, 2024
e2ddee1
paygate updates
zlwaterfield Jun 10, 2024
5aad083
improve upgrade url
zlwaterfield Jun 10, 2024
cb569e5
improve links and cta terminology
zlwaterfield Jun 12, 2024
c58e6ea
Improve pay gates
zlwaterfield Jun 12, 2024
f87d542
commit master in
zlwaterfield Jun 13, 2024
1c47b29
Update UI snapshots for `chromium` (1)
github-actions[bot] Jun 13, 2024
6cae8c2
Update UI snapshots for `chromium` (2)
github-actions[bot] Jun 13, 2024
5f7aa32
Make terminology more consistent
zlwaterfield Jun 13, 2024
4714396
Improve unsub UI
zlwaterfield Jun 13, 2024
41b8288
spacing
zlwaterfield Jun 13, 2024
df228ce
improve unsub modal langugae
zlwaterfield Jun 13, 2024
7966ac5
Merge branch 'zach/subscribe-to-all-products/plan-levels' of github.c…
zlwaterfield Jun 13, 2024
38af968
Update UI snapshots for `chromium` (1)
github-actions[bot] Jun 13, 2024
6ae4af6
Update UI snapshots for `chromium` (2)
github-actions[bot] Jun 13, 2024
6752bbe
improve terminology
zlwaterfield Jun 13, 2024
e5a8e1b
Update UI snapshots for `chromium` (2)
github-actions[bot] Jun 13, 2024
deb498c
Merge branch 'zach/subscribe-to-all-products/plan-levels' of github.c…
zlwaterfield Jun 13, 2024
b633bcb
Update UI snapshots for `chromium` (2)
github-actions[bot] Jun 13, 2024
8e99484
Update UI snapshots for `webkit` (2)
github-actions[bot] Jun 13, 2024
cab2fe2
Update UI snapshots for `chromium` (1)
github-actions[bot] Jun 13, 2024
b0204e4
Update UI snapshots for `chromium` (2)
github-actions[bot] Jun 13, 2024
8474464
remove trailing comma
zlwaterfield Jun 13, 2024
6c72341
Update UnsubscribeSurveyModal.tsx
zlwaterfield Jun 13, 2024
40cfff6
Merge branch 'zach/subscribe-to-all-products/plan-levels' of github.c…
zlwaterfield Jun 13, 2024
37c567a
Update UI snapshots for `chromium` (1)
github-actions[bot] Jun 13, 2024
09a7d54
Update UI snapshots for `chromium` (2)
github-actions[bot] Jun 13, 2024
f91a709
Update UI snapshots for `chromium` (1)
github-actions[bot] Jun 13, 2024
9123f05
Update UI snapshots for `chromium` (2)
github-actions[bot] Jun 13, 2024
90ede6a
Update UI snapshots for `chromium` (1)
github-actions[bot] Jun 13, 2024
dfd3548
Update UI snapshots for `chromium` (2)
github-actions[bot] Jun 13, 2024
4fc8db3
merge in main
zlwaterfield Jun 13, 2024
7a006fe
Update PayGateButton.tsx
zlwaterfield Jun 13, 2024
6713593
Update PayGateButton.tsx
zlwaterfield Jun 13, 2024
f646502
Update OnboardingBillingStep.tsx
zlwaterfield Jun 13, 2024
e23bbe5
Update UI snapshots for `chromium` (2)
github-actions[bot] Jun 13, 2024
a47e18c
Update PlanComparison.tsx
zlwaterfield Jun 13, 2024
155c5bb
Update UI snapshots for `chromium` (2)
github-actions[bot] Jun 13, 2024
dc8317f
Update UI snapshots for `chromium` (2)
github-actions[bot] Jun 13, 2024
05f72d1
Merge branch 'zach/subscribe-to-all-products/plan-levels' of github.c…
zlwaterfield Jun 14, 2024
8bda567
put plan comparison styles back
zlwaterfield Jun 14, 2024
6c7ad6b
update hero text and buttons
raquelmsmith Jun 18, 2024
23e6cca
update hero text
raquelmsmith Jun 18, 2024
02e8508
update unsubscribe card
raquelmsmith Jun 18, 2024
3958f69
fix type error
raquelmsmith Jun 18, 2024
9d30068
Update frontend/src/scenes/billing/BillingCTAHero.tsx
zlwaterfield Jun 18, 2024
91ca35a
Update frontend/src/scenes/billing/BillingCTAHero.tsx
zlwaterfield Jun 18, 2024
001269a
merge in main
zlwaterfield Jun 18, 2024
f22df82
turn ff into an experiment
zlwaterfield Jun 18, 2024
aeb78ed
put back plan comparison max width
zlwaterfield Jun 18, 2024
a1d69fe
hide unsub for subscribe to all products
zlwaterfield Jun 18, 2024
b4bb43b
remove extra padding
zlwaterfield Jun 18, 2024
09b413c
Update UI snapshots for `chromium` (1)
github-actions[bot] Jun 18, 2024
332f56c
Update UI snapshots for `chromium` (1)
github-actions[bot] Jun 18, 2024
dad5350
Improve addon ctas on billing page
zlwaterfield Jun 18, 2024
8320897
Update UI snapshots for `chromium` (1)
github-actions[bot] Jun 18, 2024
2d8dce6
feat: new compare plans table - showing all products (#22970)
zlwaterfield Jun 18, 2024
2b651e8
Merge branch 'master' into zach/subscribe-to-all-products/plan-levels
zlwaterfield Jun 18, 2024
7a99d9b
Update UI snapshots for `chromium` (1)
github-actions[bot] Jun 18, 2024
0375fd4
Update UI snapshots for `chromium` (2)
github-actions[bot] Jun 18, 2024
4d06d44
update some paid plan copy
zlwaterfield Jun 19, 2024
537f679
Fix unsub button checks
zlwaterfield Jun 19, 2024
318aa77
Update UI snapshots for `chromium` (1)
github-actions[bot] Jun 19, 2024
3a58918
Update UI snapshots for `chromium` (1)
github-actions[bot] Jun 19, 2024
b965e8a
Update UI snapshots for `chromium` (2)
github-actions[bot] Jun 19, 2024
8b51e24
fix unsub modal ff logic
zlwaterfield Jun 19, 2024
06e20aa
improve addon callout styling
zlwaterfield Jun 19, 2024
956848a
Merge branch 'zach/subscribe-to-all-products/plan-levels' of github.c…
zlwaterfield Jun 19, 2024
8ed987f
add hide icon prop to lemon banner
zlwaterfield Jun 19, 2024
e2501ab
Update UI snapshots for `chromium` (2)
github-actions[bot] Jun 19, 2024
2187d38
Update UI snapshots for `chromium` (2)
github-actions[bot] Jun 19, 2024
20c4e46
Update UI snapshots for `chromium` (2)
github-actions[bot] Jun 19, 2024
9b111a6
Update UI snapshots for `chromium` (2)
github-actions[bot] Jun 19, 2024
5ef30f1
Update UI snapshots for `chromium` (2)
github-actions[bot] Jun 19, 2024
c6dcd4c
Update UI snapshots for `chromium` (1)
github-actions[bot] Jun 19, 2024
385a5da
pass through intent product
zlwaterfield Jun 20, 2024
1855629
Update UI snapshots for `chromium` (2)
github-actions[bot] Jun 20, 2024
ad5b50a
Update UI snapshots for `chromium` (2)
github-actions[bot] Jun 20, 2024
881aee8
Update UI snapshots for `chromium` (1)
github-actions[bot] Jun 20, 2024
30ba4b7
Update UI snapshots for `chromium` (2)
github-actions[bot] Jun 20, 2024
d93c133
Update UI snapshots for `chromium` (2)
github-actions[bot] Jun 20, 2024
23a20ba
merge master in
zlwaterfield Jun 20, 2024
4e70fd5
Update UI snapshots for `chromium` (2)
github-actions[bot] Jun 20, 2024
a2b2734
Update UI snapshots for `chromium` (2)
github-actions[bot] Jun 20, 2024
7910905
merge master in
zlwaterfield Jun 21, 2024
b482705
merge master in
zlwaterfield Jun 21, 2024
2c0aeb0
Merge branch 'master' into zach/subscribe-to-all-products/plan-levels
zlwaterfield Jun 21, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions ee/api/billing.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ class ActivateSerializer(serializers.Serializer):
required=False
) # This is required but in order to support an error for the legacy 'plan' param we need to set required=False
redirect_path = serializers.CharField(required=False)
intent_product = serializers.CharField(required=False)

def validate(self, data):
plan = data.get("plan")
Expand Down Expand Up @@ -137,6 +138,10 @@ def handle_activate(self, request: Request, *args: Any, **kwargs: Any) -> HttpRe
products = serializer.validated_data.get("products")
url = f"{url}&products={products}"

intent_product = serializer.validated_data.get("intent_product")
if intent_product:
url = f"{url}&intent_product={intent_product}"

if license:
billing_service_token = build_billing_token(license, organization)
url = f"{url}&token={billing_service_token}"
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
45 changes: 40 additions & 5 deletions frontend/src/lib/components/PayGateMini/PayGateButton.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import { LemonButton } from '@posthog/lemon-ui'
import { useValues } from 'kea'
import { FEATURE_FLAGS } from 'lib/constants'
import { featureFlagLogic, FeatureFlagsSet } from 'lib/logic/featureFlagLogic'
import { urls } from 'scenes/urls'

import { BillingProductV2AddonType, BillingProductV2Type, BillingV2FeatureType, BillingV2Type } from '~/types'

Expand All @@ -8,6 +12,7 @@ interface PayGateButtonProps {
featureInfo: BillingV2FeatureType
onCtaClick: () => void
billing: BillingV2Type | null
isAddonProduct?: boolean
scrollToProduct: boolean
}

Expand All @@ -17,16 +22,27 @@ export const PayGateButton = ({
featureInfo,
onCtaClick,
billing,
isAddonProduct,
scrollToProduct = true,
}: PayGateButtonProps): JSX.Element => {
const { featureFlags } = useValues(featureFlagLogic)
return (
<LemonButton
to={getCtaLink(gateVariant, productWithFeature, featureInfo, scrollToProduct)}
to={getCtaLink(
gateVariant,
productWithFeature,
featureInfo,
featureFlags,
billing?.subscription_level,
isAddonProduct,
scrollToProduct
)}
disableClientSideRouting={gateVariant === 'add-card' && !isAddonProduct}
type="primary"
center
onClick={onCtaClick}
>
{getCtaLabel(gateVariant, billing)}
{getCtaLabel(gateVariant, billing, featureFlags)}
</LemonButton>
)
}
Expand All @@ -35,9 +51,21 @@ const getCtaLink = (
gateVariant: 'add-card' | 'contact-sales' | 'move-to-cloud' | null,
productWithFeature: BillingProductV2AddonType | BillingProductV2Type,
featureInfo: BillingV2FeatureType,
featureFlags: FeatureFlagsSet,
subscriptionLevel?: BillingV2Type['subscription_level'],
isAddonProduct?: boolean,
scrollToProduct: boolean = true
): string | undefined => {
if (gateVariant === 'add-card') {
if (
gateVariant === 'add-card' &&
!isAddonProduct &&
featureFlags[FEATURE_FLAGS.SUBSCRIBE_TO_ALL_PRODUCTS] === 'test' &&
subscriptionLevel === 'free'
) {
return `/api/billing/activate?products=all_products:&redirect_path=${urls.organizationBilling()}&intent_product=${
productWithFeature.type
}`
} else if (gateVariant === 'add-card') {
return `/organization/billing${scrollToProduct ? `?products=${productWithFeature.type}` : ''}`
} else if (gateVariant === 'contact-sales') {
return `mailto:[email protected]?subject=Inquiring about ${featureInfo.name}`
Expand All @@ -49,9 +77,16 @@ const getCtaLink = (

const getCtaLabel = (
gateVariant: 'add-card' | 'contact-sales' | 'move-to-cloud' | null,
billing: BillingV2Type | null
billing: BillingV2Type | null,
featureFlags: FeatureFlagsSet
): string => {
if (gateVariant === 'add-card') {
if (
gateVariant === 'add-card' &&
featureFlags[FEATURE_FLAGS.SUBSCRIBE_TO_ALL_PRODUCTS] === 'test' &&
billing?.subscription_level === 'free'
) {
return 'Upgrade now'
} else if (gateVariant === 'add-card') {
return billing?.has_active_subscription ? 'Upgrade now' : 'Subscribe now'
} else if (gateVariant === 'contact-sales') {
return 'Contact sales'
Expand Down
35 changes: 32 additions & 3 deletions frontend/src/lib/components/PayGateMini/PayGateMini.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,21 @@ import { IconInfo, IconOpenSidebar } from '@posthog/icons'
import { LemonButton, Link, Tooltip } from '@posthog/lemon-ui'
import clsx from 'clsx'
import { useActions, useValues } from 'kea'
import { FEATURE_FLAGS } from 'lib/constants'
import { featureFlagLogic, FeatureFlagsSet } from 'lib/logic/featureFlagLogic'
import posthog from 'posthog-js'
import { useEffect } from 'react'
import { billingLogic } from 'scenes/billing/billingLogic'
import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic'
import { getProductIcon } from 'scenes/products/Products'

import { AvailableFeature, BillingProductV2AddonType, BillingProductV2Type, BillingV2FeatureType } from '~/types'
import {
AvailableFeature,
BillingProductV2AddonType,
BillingProductV2Type,
BillingV2FeatureType,
BillingV2Type,
} from '~/types'

import { upgradeModalLogic } from '../UpgradeModal/upgradeModalLogic'
import { PayGateButton } from './PayGateButton'
Expand Down Expand Up @@ -95,6 +103,7 @@ export function PayGateMini({
productWithFeature={productWithFeature}
isGrandfathered={isGrandfathered}
isAddonProduct={isAddonProduct}
billing={billing}
featureInfoOnNextPlan={featureInfoOnNextPlan}
handleCtaClick={handleCtaClick}
>
Expand All @@ -106,6 +115,7 @@ export function PayGateMini({
onCtaClick={handleCtaClick}
billing={billing}
scrollToProduct={scrollToProduct}
isAddonProduct={isAddonProduct}
/>
{docsLink && isCloudOrDev && (
<LemonButton
Expand Down Expand Up @@ -135,6 +145,7 @@ interface PayGateContentProps {
productWithFeature: BillingProductV2AddonType | BillingProductV2Type
isGrandfathered?: boolean
isAddonProduct?: boolean
billing: BillingV2Type | null
featureInfoOnNextPlan?: BillingV2FeatureType
children: React.ReactNode
handleCtaClick: () => void
Expand All @@ -149,10 +160,12 @@ function PayGateContent({
productWithFeature,
isGrandfathered,
isAddonProduct,
billing,
featureInfoOnNextPlan,
children,
handleCtaClick,
}: PayGateContentProps): JSX.Element {
const { featureFlags } = useValues(featureFlagLogic)
return (
<div
className={clsx(
Expand All @@ -171,6 +184,8 @@ function PayGateContent({
gateVariant,
featureInfo,
productWithFeature,
billing,
featureFlags,
isAddonProduct,
handleCtaClick
)}
Expand All @@ -187,6 +202,8 @@ const renderUsageLimitMessage = (
gateVariant: 'add-card' | 'contact-sales' | 'move-to-cloud' | null,
featureInfo: BillingV2FeatureType,
productWithFeature: BillingProductV2AddonType | BillingProductV2Type,
billing: BillingV2Type | null,
featureFlags: FeatureFlagsSet,
isAddonProduct?: boolean,
handleCtaClick?: () => void
): JSX.Element => {
Expand Down Expand Up @@ -223,9 +240,13 @@ const renderUsageLimitMessage = (
.
</p>
</>
) : featureFlags[FEATURE_FLAGS.SUBSCRIBE_TO_ALL_PRODUCTS] === 'test' &&
billing?.subscription_level === 'free' &&
!isAddonProduct ? (
<p>Upgrade to create more {featureInfo.name}</p>
) : (
<p>
Please upgrade your <b>{productWithFeature.name}</b> plan to create more {featureInfo.name}
Upgrade your <b>{productWithFeature.name}</b> plan to create more {featureInfo.name}
</p>
)}
</div>
Expand All @@ -234,14 +255,16 @@ const renderUsageLimitMessage = (
return (
<>
<p className="max-w-140">{featureInfo.description}</p>
<p>{renderGateVariantMessage(gateVariant, productWithFeature, isAddonProduct)}</p>
<p>{renderGateVariantMessage(gateVariant, productWithFeature, billing, featureFlags, isAddonProduct)}</p>
</>
)
}

const renderGateVariantMessage = (
gateVariant: 'add-card' | 'contact-sales' | 'move-to-cloud' | null,
productWithFeature: BillingProductV2AddonType | BillingProductV2Type,
billing: BillingV2Type | null,
featureFlags: FeatureFlagsSet,
isAddonProduct?: boolean
): JSX.Element => {
if (gateVariant === 'move-to-cloud') {
Expand All @@ -252,7 +275,13 @@ const renderGateVariantMessage = (
Subscribe to the <b>{productWithFeature?.name}</b> addon to use this feature.
</>
)
} else if (
featureFlags[FEATURE_FLAGS.SUBSCRIBE_TO_ALL_PRODUCTS] === 'test' &&
billing?.subscription_level === 'free'
) {
return <>Upgrade to use this feature.</>
}

return (
<>
Upgrade your <b>{productWithFeature?.name}</b> plan to use this feature.
Expand Down
1 change: 1 addition & 0 deletions frontend/src/lib/constants.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@ export const FEATURE_FLAGS = {
PERSONLESS_EVENTS_NOT_SUPPORTED: 'personless-events-not-supported', // owner: @raquelmsmith
SESSION_REPLAY_UNIVERSAL_FILTERS: 'session-replay-universal-filters', // owner: #team-replay
ALERTS: 'alerts', // owner: github.com/nikitaevg
SUBSCRIBE_TO_ALL_PRODUCTS: 'subscribe-to-all-products', // owner: #team-growth
ERROR_TRACKING: 'error-tracking', // owner: #team-replay
SETTINGS_BOUNCE_RATE_PAGE_VIEW_MODE: 'settings-bounce-rate-page-view-mode', // owner: @robbie-c
SURVEYS_BRANCHING_LOGIC: 'surveys-branching-logic', // owner: @jurajmajerik #team-feature-success
Expand Down
13 changes: 8 additions & 5 deletions frontend/src/lib/lemon-ui/LemonBanner/LemonBanner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export interface LemonBannerProps {
className?: string
/** If provided, the banner will be dismissed and hidden when the key is set in localStorage. */
dismissKey?: string
hideIcon?: boolean
}

/** Generic alert message. */
Expand All @@ -29,6 +30,7 @@ export function LemonBanner({
action,
className,
dismissKey = '',
hideIcon = false,
}: LemonBannerProps): JSX.Element | null {
const logic = lemonBannerLogic({ dismissKey })
const { isDismissed } = useValues(logic)
Expand All @@ -49,11 +51,12 @@ export function LemonBanner({
return (
<div className={clsx('LemonBanner @container', `LemonBanner--${type}`, className)}>
<div className="flex items-center gap-2 grow @md:px-1">
{type === 'warning' || type === 'error' ? (
<IconWarning className="LemonBanner__icon hidden @md:block" />
) : (
<IconInfo className="LemonBanner__icon hidden @md:block" />
)}
{!hideIcon &&
(type === 'warning' || type === 'error' ? (
<IconWarning className="LemonBanner__icon hidden @md:block" />
) : (
<IconInfo className="LemonBanner__icon hidden @md:block" />
))}
<div className="grow overflow-hidden">{children}</div>
{action && <LemonButton className="hidden @md:flex" type="secondary" {...action} />}
{showCloseButton && <LemonButton size="small" icon={<IconX />} onClick={_onClose} aria-label="close" />}
Expand Down
1 change: 1 addition & 0 deletions frontend/src/mocks/fixtures/_billing.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3835,4 +3835,5 @@ export const billingJson: BillingV2Type = {
custom_limits_usd: {},
stripe_portal_url:
'https://billing.stripe.com/p/session/test_YWNjdF8xSElNRERFdUlhdFJYU2R6LF9QaEVJR3VyemlvMDZzRzdiQXZrc1AxSjNXZk1BellP0100ZsforDQG',
subscription_level: 'paid',
}
Loading
Loading