diff --git a/frontend/__snapshots__/scenes-app-insights--stickiness-edit--webkit.png b/frontend/__snapshots__/scenes-app-insights--stickiness-edit--webkit.png index 62e04313ddc65..509a482b5eeda 100644 Binary files a/frontend/__snapshots__/scenes-app-insights--stickiness-edit--webkit.png and b/frontend/__snapshots__/scenes-app-insights--stickiness-edit--webkit.png differ diff --git a/frontend/src/mocks/fixtures/_billing_v2_with_flat_fee.json b/frontend/src/mocks/fixtures/_billing_v2_with_flat_fee.json new file mode 100644 index 0000000000000..cb3a7fa06ef5a --- /dev/null +++ b/frontend/src/mocks/fixtures/_billing_v2_with_flat_fee.json @@ -0,0 +1,1515 @@ +{ + "available_features": [ + "surveys_unlimited_surveys", + "surveys_all_question_types", + "surveys_user_targeting", + "surveys_user_sampling", + "surveys_styling", + "surveys_text_html", + "surveys_api_mode", + "surveys_results_analysis", + "surveys_templates", + "zapier", + "slack_integration", + "microsoft_teams_integration", + "discord_integration", + "apps", + "app_metrics", + "boolean_flags", + "persist_flags_cross_authentication", + "feature_flag_payloads", + "multiple_release_conditions", + "release_condition_overrides", + "targeting_by_group", + "local_evaluation_and_bootstrapping", + "flag_usage_stats", + "console_logs", + "recordings_playlists", + "recordings_performance", + "recordings_file_export", + "group_analytics", + "dashboards", + "funnels", + "graphs_trends", + "paths", + "subscriptions", + "paths_advanced", + "dashboard_permissioning", + "dashboard_collaboration", + "ingestion_taxonomy", + "correlation_analysis", + "tagging", + "behavioral_cohort_filtering", + "tracked_users", + "data_retention", + "team_members", + "organizations_projects", + "api_access", + "project_based_permissioning", + "social_sso", + "sso_enforcement", + "white_labelling", + "community_support", + "dedicated_support", + "email_support", + "terms_and_conditions", + "security_assessment" + ], + "license": { + "plan": "cloud" + }, + "customer_id": "cus_1234", + "deactivated": false, + "has_active_subscription": true, + "billing_period": { + "current_period_start": "2023-10-13T09:54:24Z", + "current_period_end": "2023-11-13T09:54:24Z", + "interval": "month" + }, + "available_product_features": [ + { + "key": "surveys_unlimited_surveys", + "name": "Unlimited surveys", + "description": "Create as many surveys as you want.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "surveys_all_question_types", + "name": "All question types", + "description": "Rating scale (for NPS and the like), multiple choice, single choice, emoji rating, link, free text.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "surveys_user_targeting", + "name": "User property targeting", + "description": "Target users based on any of their user properties.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "surveys_user_sampling", + "name": "User sampling", + "description": "Sample users to only survey a portion of the users who match the criteria.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "surveys_styling", + "name": "Custom colors & positioning", + "description": "Customize the colors of your surveys to match your brand and set survey position.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "surveys_text_html", + "name": "Custom HTML text", + "description": "Add custom HTML to your survey text.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "surveys_api_mode", + "name": "API mode", + "description": "Create surveys via the API.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "surveys_results_analysis", + "name": "Results analysis", + "description": "Analyze your survey results including completion rates and drop offs.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "surveys_templates", + "name": "Templates", + "description": "Use our templates to get started quickly with NPS, customer satisfaction surveys, user interviews, and more.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "zapier", + "name": "Zapier", + "description": "Zapier lets you connect PostHog with thousands of the most popular apps, so you can automate your work and have more time for what matters most—no code required.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "slack_integration", + "name": "Slack", + "description": "Get notified about new actions in Slack.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "microsoft_teams_integration", + "name": "Microsoft Teams", + "description": "Get notified about new actions in Microsoft Teams.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "discord_integration", + "name": "Discord", + "description": "Get notified about new actions in Discord.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "apps", + "name": "CDP + Apps library", + "description": "Connect your data with 50+ apps including BigQuery, Redshift, and more.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "app_metrics", + "name": "App metrics", + "description": "Get metrics on your apps to see their usage, reliability, and more.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "boolean_flags", + "name": "Boolean feature flags", + "description": "Turn features on and off for specific users.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "persist_flags_cross_authentication", + "name": "Persist flags across authentication", + "description": "Persist feature flags across authentication events so that flag values don't change when an anonymous user logs in and becomes identified.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "feature_flag_payloads", + "name": "Payloads", + "description": "Send additional pieces of information (any valid JSON) to your app when a flag is matched for a user.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "multiple_release_conditions", + "name": "Multiple release conditions", + "description": "Target multiple groups of users with different release conditions for the same feature flag.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "release_condition_overrides", + "name": "Release condition overrides", + "description": "For any release condition, specify which flag value the users or groups in that condition should receive.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "targeting_by_group", + "name": "Flag targeting by groups", + "description": "Target feature flag release conditions by group properties, not just user properties.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "local_evaluation_and_bootstrapping", + "name": "Local evaluation & bootstrapping", + "description": "Bootstrap flags on initialization so all flags are available immediately, without having to make extra network requests.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "flag_usage_stats", + "name": "Flag usage stats", + "description": "See how many times a flag has been evaluated, how many times each variant has been returned, and what values users received.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "console_logs", + "name": "Console logs", + "description": "Diagnose issues by inspecting errors in the user's network console", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "recordings_playlists", + "name": "Recording playlists", + "description": "Create playlists of certain session recordings to easily find and watch them again in the future.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "recordings_performance", + "name": "Network performance on recordings", + "description": "See your end-user's network performance and information alongside session recordings.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "recordings_file_export", + "name": "Recordings file export", + "description": "Save session recordings as a file to your local filesystem.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "group_analytics", + "name": "Group analytics", + "description": "Associate events with a group - such as a company, community, or project - and analyze them in that context.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "dashboards", + "name": "Dashboards", + "description": "Save trends, funnels, and other insights for easy reference by your whole team.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "funnels", + "name": "Funnels", + "description": "Visualize user dropoff between a sequence of events.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "graphs_trends", + "name": "Graphs & trends", + "description": "Plot any number of events or actions over time.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "paths", + "name": "Paths", + "description": "Limited paths excludes: customizing path insights by setting the maximum number of paths, number of people on each path, how path names appear", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "subscriptions", + "name": "Insight & dashboard subscriptions", + "description": "Create a subscription for any insight or dashboard in PostHog to receive regular reports with their updates.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "paths_advanced", + "name": "Advanced paths", + "description": "Customize your path insights by setting the maximum number of paths, number of people on each path, and how path names should appear.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "dashboard_permissioning", + "name": "Dashboard permissions", + "description": "Restrict access to dashboards within the organization to only those who need it.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "dashboard_collaboration", + "name": "Tags & text cards", + "description": "Keep organized by adding tags to your dashboards, cohorts and more. Add text cards and descriptions to your dashboards to provide context to your team.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "ingestion_taxonomy", + "name": "Ingestion taxonomy", + "description": "Ingestion taxonomy", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "correlation_analysis", + "name": "Correlation analysis", + "description": "Automatically highlight significant factors that affect the conversion rate of users within a funnel.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "tagging", + "name": "Dashboard tags", + "description": "Organize dashboards with tags.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "behavioral_cohort_filtering", + "name": "Lifecycle cohorts", + "description": "Group users based on their long term behavior, such as whether they frequently performed an event, or have recently stopped performing an event.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "tracked_users", + "name": "Tracked users", + "description": "Track users across devices and sessions.", + "unit": null, + "limit": null, + "note": "Unlimited" + }, + { + "key": "data_retention", + "name": "Data retention", + "description": "Keep a historical record of your data.", + "unit": null, + "limit": null, + "note": "7 years" + }, + { + "key": "team_members", + "name": "Team members", + "description": "PostHog doesn't charge per seat add your entire team!", + "unit": null, + "limit": null, + "note": "Unlimited" + }, + { + "key": "organizations_projects", + "name": "Projects", + "description": "Create silos of data within PostHog. All data belongs to a single project and all queries are project-specific.", + "unit": null, + "limit": null, + "note": "Unlimited" + }, + { + "key": "api_access", + "name": "API access", + "description": "Access your data via our developer-friendly API.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "project_based_permissioning", + "name": "Project permissions", + "description": "Restrict access to data within the organization to only those who need it.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "social_sso", + "name": "SSO via Google, Github, or Gitlab", + "description": "Log in to PostHog with your Google, Github, or Gitlab account.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "sso_enforcement", + "name": "Enforce SSO login", + "description": "Users can only sign up and log in to your PostHog organization with your specified SSO provider.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "white_labelling", + "name": "White labeling", + "description": "Use your own branding in your PostHog organization.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "community_support", + "name": "Community support", + "description": "Get help from other users and PostHog team members in our Community forums.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "dedicated_support", + "name": "Slack (dedicated channel)", + "description": "Get help firectly from our support team in a dedicated Slack channel shared between you and the PostHog team.", + "unit": null, + "limit": null, + "note": "$2k/month spend or above" + }, + { + "key": "email_support", + "name": "Direct access to engineers", + "description": "Get help directly from our product engineers via email. No wading through multiple support people before you get help.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "terms_and_conditions", + "name": "Terms and conditions", + "description": "Terms and conditions", + "unit": null, + "limit": null, + "note": "Standard" + }, + { + "key": "security_assessment", + "name": "Security assessment", + "description": "Security assessment", + "unit": null, + "limit": null, + "note": null + } + ], + "current_total_amount_usd": "543.72", + "current_total_amount_usd_after_discount": "543.72", + "products": [ + { + "name": "Product analytics + data stack", + "description": "Trends, funnels, path analysis, CDP + more.", + "price_description": null, + "usage_key": "events", + "image_url": "https://posthog.com/images/product/product-icons/product-analytics.svg", + "icon_key": "IconGraph", + "docs_url": "https://posthog.com/docs/product-analytics", + "subscribed": true, + "plans": [], + "type": "product_analytics", + "free_allocation": 0, + "tiers": [ + { + "flat_amount_usd": "200", + "unit_amount_usd": "0", + "up_to": 5000000, + "current_amount_usd": "200.00", + "current_usage": 5000000, + "projected_usage": 5000000, + "projected_amount_usd": "200.00" + }, + { + "flat_amount_usd": "0", + "unit_amount_usd": "0.000075", + "up_to": 25000000, + "current_amount_usd": "343.72", + "current_usage": 4582922, + "projected_usage": 7082316, + "projected_amount_usd": "531.17" + }, + { + "flat_amount_usd": "0", + "unit_amount_usd": "0.000005", + "up_to": 50000000, + "current_amount_usd": "0.00", + "current_usage": 0, + "projected_usage": 0, + "projected_amount_usd": "0.00" + }, + { + "flat_amount_usd": "0", + "unit_amount_usd": "0.0000025", + "up_to": null, + "current_amount_usd": "0.00", + "current_usage": 0, + "projected_usage": 0, + "projected_amount_usd": "0.00" + } + ], + "tiered": true, + "unit_amount_usd": null, + "current_amount_usd_before_addons": "543.72", + "current_amount_usd": "543.72", + "current_usage": 10101970, + "usage_limit": 13000000, + "has_exceeded_limit": false, + "percentage_usage": 0.7770746153846154, + "projected_usage": 12082316, + "projected_amount_usd": "731.17", + "unit": "event", + "addons": [ + { + "name": "Group analytics", + "description": "Associate events with a group or entity - such as a company, community, or project. Analyze these events as if they were sent by that entity itself. Great for B2B, marketplaces, and more.", + "price_description": null, + "image_url": "https://posthog.com/images/product/product-icons/group-analytics.svg", + "icon_key": "IconPeople", + "docs_url": "https://posthog.com/docs/product-analytics/group-analytics", + "type": "group_analytics", + "tiers": [], + "tiered": false, + "included_with_main_product": true, + "subscribed": false, + "unit": "event", + "unit_amount_usd": null, + "current_amount_usd": null, + "current_usage": 0, + "projected_usage": 0, + "projected_amount_usd": null, + "plans": [], + "contact_support": false + } + ], + "contact_support": false, + "inclusion_only": false + }, + { + "name": "Session replay", + "description": "Searchable recordings of people using your app or website with console logs and behavioral bucketing.", + "price_description": null, + "usage_key": "recordings", + "image_url": "https://posthog.com/images/product/product-icons/session-replay.svg", + "icon_key": "IconRewindPlay", + "docs_url": "https://posthog.com/docs/session-replay", + "subscribed": true, + "plans": [ + { + "plan_key": "free-20230117", + "product_key": "session_replay", + "name": "Session replay", + "description": "Searchable recordings of people using your app or website with console logs and behavioral bucketing.", + "image_url": "https://posthog.com/images/product/product-icons/session-replay.svg", + "docs_url": "https://posthog.com/docs/session-replay", + "note": null, + "unit": "recording", + "free_allocation": 15000, + "features": [ + { + "key": "console_logs", + "name": "Console logs", + "description": "Diagnose issues by inspecting errors in the user's network console", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "recordings_playlists", + "name": "Recording playlists", + "description": "Create playlists of certain session recordings to easily find and watch them again in the future.", + "unit": "playlists", + "limit": 5, + "note": null + } + ], + "tiers": null, + "current_plan": false, + "included_if": null + }, + { + "plan_key": "paid-20230117", + "product_key": "session_replay", + "name": "Session replay", + "description": "Searchable recordings of people using your app or website with console logs and behavioral bucketing.", + "image_url": "https://posthog.com/images/product/product-icons/session-replay.svg", + "docs_url": "https://posthog.com/docs/session-replay", + "note": null, + "unit": "recording", + "free_allocation": null, + "features": [ + { + "key": "console_logs", + "name": "Console logs", + "description": "Diagnose issues by inspecting errors in the user's network console", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "recordings_playlists", + "name": "Recording playlists", + "description": "Create playlists of certain session recordings to easily find and watch them again in the future.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "recordings_performance", + "name": "Network performance on recordings", + "description": "See your end-user's network performance and information alongside session recordings.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "recordings_file_export", + "name": "Recordings file export", + "description": "Save session recordings as a file to your local filesystem.", + "unit": null, + "limit": null, + "note": null + } + ], + "tiers": [ + { + "flat_amount_usd": "0", + "unit_amount_usd": "0", + "up_to": 15000, + "current_amount_usd": "0.00", + "current_usage": 0, + "projected_usage": null, + "projected_amount_usd": null + }, + { + "flat_amount_usd": "0", + "unit_amount_usd": "0.005", + "up_to": 50000, + "current_amount_usd": "0.00", + "current_usage": 0, + "projected_usage": null, + "projected_amount_usd": null + }, + { + "flat_amount_usd": "0", + "unit_amount_usd": "0.0045", + "up_to": 150000, + "current_amount_usd": "0.00", + "current_usage": 0, + "projected_usage": null, + "projected_amount_usd": null + }, + { + "flat_amount_usd": "0", + "unit_amount_usd": "0.004", + "up_to": 500000, + "current_amount_usd": "0.00", + "current_usage": 0, + "projected_usage": null, + "projected_amount_usd": null + }, + { + "flat_amount_usd": "0", + "unit_amount_usd": "0.0035", + "up_to": null, + "current_amount_usd": "0.00", + "current_usage": 0, + "projected_usage": null, + "projected_amount_usd": null + } + ], + "current_plan": true, + "included_if": null + } + ], + "type": "session_replay", + "free_allocation": 0, + "tiers": [ + { + "flat_amount_usd": "0", + "unit_amount_usd": "0", + "up_to": 15000, + "current_amount_usd": "0.00", + "current_usage": 0, + "projected_usage": null, + "projected_amount_usd": null + }, + { + "flat_amount_usd": "0", + "unit_amount_usd": "0.005", + "up_to": 50000, + "current_amount_usd": "0.00", + "current_usage": 0, + "projected_usage": null, + "projected_amount_usd": null + }, + { + "flat_amount_usd": "0", + "unit_amount_usd": "0.0045", + "up_to": 150000, + "current_amount_usd": "0.00", + "current_usage": 0, + "projected_usage": null, + "projected_amount_usd": null + }, + { + "flat_amount_usd": "0", + "unit_amount_usd": "0.004", + "up_to": 500000, + "current_amount_usd": "0.00", + "current_usage": 0, + "projected_usage": null, + "projected_amount_usd": null + }, + { + "flat_amount_usd": "0", + "unit_amount_usd": "0.0035", + "up_to": null, + "current_amount_usd": "0.00", + "current_usage": 0, + "projected_usage": null, + "projected_amount_usd": null + } + ], + "tiered": true, + "unit_amount_usd": null, + "current_amount_usd_before_addons": "0.00", + "current_amount_usd": "0.00", + "current_usage": 0, + "usage_limit": 15000, + "has_exceeded_limit": false, + "percentage_usage": 0.0, + "projected_usage": 0, + "projected_amount_usd": "0.00", + "unit": "recording", + "addons": [], + "contact_support": false, + "inclusion_only": false + }, + { + "name": "Feature flags & A/B testing", + "description": "Safely roll out new features and run experiments on changes.", + "price_description": null, + "usage_key": "feature_flag_requests", + "image_url": "https://posthog.com/images/product/product-icons/feature-flags.svg", + "icon_key": "IconToggle", + "docs_url": "https://posthog.com/docs/feature-flags", + "subscribed": false, + "plans": [ + { + "plan_key": "free-20230712", + "product_key": "feature_flags", + "name": "Feature flags & A/B testing", + "description": "Safely roll out new features and run experiments on changes.", + "image_url": "https://posthog.com/images/product/product-icons/feature-flags.svg", + "docs_url": "https://posthog.com/docs/feature-flags", + "note": null, + "unit": "request", + "free_allocation": 10000000, + "features": [ + { + "key": "boolean_flags", + "name": "Boolean feature flags", + "description": "Turn features on and off for specific users.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "persist_flags_cross_authentication", + "name": "Persist flags across authentication", + "description": "Persist feature flags across authentication events so that flag values don't change when an anonymous user logs in and becomes identified.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "feature_flag_payloads", + "name": "Payloads", + "description": "Send additional pieces of information (any valid JSON) to your app when a flag is matched for a user.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "multiple_release_conditions", + "name": "Multiple release conditions", + "description": "Target multiple groups of users with different release conditions for the same feature flag.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "release_condition_overrides", + "name": "Release condition overrides", + "description": "For any release condition, specify which flag value the users or groups in that condition should receive.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "targeting_by_group", + "name": "Flag targeting by groups", + "description": "Target feature flag release conditions by group properties, not just user properties.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "local_evaluation_and_bootstrapping", + "name": "Local evaluation & bootstrapping", + "description": "Bootstrap flags on initialization so all flags are available immediately, without having to make extra network requests.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "flag_usage_stats", + "name": "Flag usage stats", + "description": "See how many times a flag has been evaluated, how many times each variant has been returned, and what values users received.", + "unit": null, + "limit": null, + "note": null + } + ], + "tiers": null, + "current_plan": true, + "included_if": null + }, + { + "plan_key": "paid-20230712", + "product_key": "feature_flags", + "name": "Feature flags & A/B testing", + "description": "Safely roll out new features and run experiments on changes.", + "image_url": "https://posthog.com/images/product/product-icons/feature-flags.svg", + "docs_url": "https://posthog.com/docs/feature-flags", + "note": null, + "unit": "request", + "free_allocation": null, + "features": [ + { + "key": "boolean_flags", + "name": "Boolean feature flags", + "description": "Turn features on and off for specific users.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "multivariate_flags", + "name": "Multivariate feature flags & experiments", + "description": "Create three or more variants of a feature flag to test or release different versions of a feature.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "persist_flags_cross_authentication", + "name": "Persist flags across authentication", + "description": "Persist feature flags across authentication events so that flag values don't change when an anonymous user logs in and becomes identified.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "feature_flag_payloads", + "name": "Payloads", + "description": "Send additional pieces of information (any valid JSON) to your app when a flag is matched for a user.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "multiple_release_conditions", + "name": "Multiple release conditions", + "description": "Target multiple groups of users with different release conditions for the same feature flag.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "release_condition_overrides", + "name": "Release condition overrides", + "description": "For any release condition, specify which flag value the users or groups in that condition should receive.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "targeting_by_group", + "name": "Flag targeting by groups", + "description": "Target feature flag release conditions by group properties, not just user properties.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "local_evaluation_and_bootstrapping", + "name": "Local evaluation & bootstrapping", + "description": "Bootstrap flags on initialization so all flags are available immediately, without having to make extra network requests.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "flag_usage_stats", + "name": "Flag usage stats", + "description": "See how many times a flag has been evaluated, how many times each variant has been returned, and what values users received.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "experimentation", + "name": "A/B testing", + "description": "Test changes to your product and evaluate the impacts those changes make.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "group_experiments", + "name": "Group experiments", + "description": "Target experiments to specific groups of users so everyone in the same group gets the same variant.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "funnel_experiments", + "name": "Funnel & trend experiments", + "description": "Measure the impact of a change on a aggregate values or a series of events, like a signup flow.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "secondary_metrics", + "name": "Secondary experiment metrics", + "description": "Track additional metrics to see how your experiment affects other parts of your app or different flows.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "statistical_analysis", + "name": "Statistical analysis", + "description": "Get a statistical analysis of your experiment results to see if the results are significant, or if they're likely just due to chance.", + "unit": null, + "limit": null, + "note": null + } + ], + "tiers": [ + { + "flat_amount_usd": "0", + "unit_amount_usd": "0", + "up_to": 10000000, + "current_amount_usd": "0.00", + "current_usage": 0, + "projected_usage": null, + "projected_amount_usd": null + }, + { + "flat_amount_usd": "0", + "unit_amount_usd": "0.000025", + "up_to": 50000000, + "current_amount_usd": "0.00", + "current_usage": 0, + "projected_usage": null, + "projected_amount_usd": null + }, + { + "flat_amount_usd": "0", + "unit_amount_usd": "0.00001", + "up_to": null, + "current_amount_usd": "0.00", + "current_usage": 0, + "projected_usage": null, + "projected_amount_usd": null + } + ], + "current_plan": false, + "included_if": null + } + ], + "type": "feature_flags", + "free_allocation": 10000000, + "tiers": null, + "tiered": true, + "unit_amount_usd": null, + "current_amount_usd_before_addons": null, + "current_amount_usd": null, + "current_usage": 4576, + "usage_limit": 10000000, + "has_exceeded_limit": false, + "percentage_usage": 0.0004576, + "projected_usage": 5603, + "projected_amount_usd": null, + "unit": "request", + "addons": [], + "contact_support": false, + "inclusion_only": false + }, + { + "name": "Surveys", + "description": "Collect feedback from your users. Multiple choice, rating, open text, and more.", + "price_description": null, + "usage_key": "survey_responses", + "image_url": "https://posthog.com/images/product/product-icons/surveys.svg", + "icon_key": "IconMessage", + "docs_url": "https://posthog.com/docs/surveys", + "subscribed": false, + "plans": [ + { + "plan_key": "free-20230928-beta-users", + "product_key": "surveys", + "name": "Surveys", + "description": "Collect feedback from your users. Multiple choice, rating, open text, and more.", + "image_url": "https://posthog.com/images/product/product-icons/surveys.svg", + "docs_url": "https://posthog.com/docs/surveys", + "note": null, + "unit": "survey response", + "free_allocation": 1000, + "features": [ + { + "key": "surveys_unlimited_surveys", + "name": "Unlimited surveys", + "description": "Create as many surveys as you want.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "surveys_all_question_types", + "name": "All question types", + "description": "Rating scale (for NPS and the like), multiple choice, single choice, emoji rating, link, free text.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "surveys_user_targeting", + "name": "User property targeting", + "description": "Target users based on any of their user properties.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "surveys_user_sampling", + "name": "User sampling", + "description": "Sample users to only survey a portion of the users who match the criteria.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "surveys_styling", + "name": "Custom colors & positioning", + "description": "Customize the colors of your surveys to match your brand and set survey position.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "surveys_text_html", + "name": "Custom HTML text", + "description": "Add custom HTML to your survey text.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "surveys_api_mode", + "name": "API mode", + "description": "Create surveys via the API.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "surveys_results_analysis", + "name": "Results analysis", + "description": "Analyze your survey results including completion rates and drop offs.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "surveys_templates", + "name": "Templates", + "description": "Use our templates to get started quickly with NPS, customer satisfaction surveys, user interviews, and more.", + "unit": null, + "limit": null, + "note": null + } + ], + "tiers": null, + "current_plan": true, + "included_if": null + }, + { + "plan_key": "paid-20230928-beta-users", + "product_key": "surveys", + "name": "Surveys", + "description": "Collect feedback from your users. Multiple choice, rating, open text, and more.", + "image_url": "https://posthog.com/images/product/product-icons/surveys.svg", + "docs_url": "https://posthog.com/docs/surveys", + "note": null, + "unit": "survey response", + "free_allocation": null, + "features": [ + { + "key": "surveys_unlimited_surveys", + "name": "Unlimited surveys", + "description": "Create as many surveys as you want.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "surveys_all_question_types", + "name": "All question types", + "description": "Rating scale (for NPS and the like), multiple choice, single choice, emoji rating, link, free text.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "surveys_multiple_questions", + "name": "Multiple questions", + "description": "Create multiple questions in a single survey.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "surveys_user_targeting", + "name": "User property targeting", + "description": "Target users based on any of their user properties.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "surveys_user_sampling", + "name": "User sampling", + "description": "Sample users to only survey a portion of the users who match the criteria.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "surveys_styling", + "name": "Custom colors & positioning", + "description": "Customize the colors of your surveys to match your brand and set survey position.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "surveys_text_html", + "name": "Custom HTML text", + "description": "Add custom HTML to your survey text.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "surveys_api_mode", + "name": "API mode", + "description": "Create surveys via the API.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "surveys_results_analysis", + "name": "Results analysis", + "description": "Analyze your survey results including completion rates and drop offs.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "surveys_templates", + "name": "Templates", + "description": "Use our templates to get started quickly with NPS, customer satisfaction surveys, user interviews, and more.", + "unit": null, + "limit": null, + "note": null + } + ], + "tiers": [ + { + "flat_amount_usd": "0", + "unit_amount_usd": "0", + "up_to": 1000, + "current_amount_usd": "0.00", + "current_usage": 0, + "projected_usage": null, + "projected_amount_usd": null + }, + { + "flat_amount_usd": "0", + "unit_amount_usd": "0.035", + "up_to": 10000, + "current_amount_usd": "0.00", + "current_usage": 0, + "projected_usage": null, + "projected_amount_usd": null + }, + { + "flat_amount_usd": "0", + "unit_amount_usd": "0.015", + "up_to": 20000, + "current_amount_usd": "0.00", + "current_usage": 0, + "projected_usage": null, + "projected_amount_usd": null + }, + { + "flat_amount_usd": "0", + "unit_amount_usd": "0.01", + "up_to": null, + "current_amount_usd": "0.00", + "current_usage": 0, + "projected_usage": null, + "projected_amount_usd": null + } + ], + "current_plan": false, + "included_if": null + } + ], + "type": "surveys", + "free_allocation": 1000, + "tiers": null, + "tiered": true, + "unit_amount_usd": null, + "current_amount_usd_before_addons": null, + "current_amount_usd": null, + "current_usage": 8, + "usage_limit": 1000, + "has_exceeded_limit": false, + "percentage_usage": 0.008, + "projected_usage": 10, + "projected_amount_usd": null, + "unit": "survey response", + "addons": [], + "contact_support": false, + "inclusion_only": false + }, + { + "name": "Integrations + CDP", + "description": "Connect PostHog to your favorite tools.", + "price_description": null, + "usage_key": null, + "image_url": "https://posthog.com/images/product/product-icons/integrations.svg", + "icon_key": "IconBolt", + "docs_url": "https://posthog.com/docs/apps", + "subscribed": null, + "plans": [ + { + "plan_key": "free-20230117", + "product_key": "integrations", + "name": "Integrations + CDP", + "description": "Connect PostHog to your favorite tools.", + "image_url": "https://posthog.com/images/product/product-icons/integrations.svg", + "docs_url": "https://posthog.com/docs/apps", + "note": null, + "unit": null, + "free_allocation": null, + "features": [ + { + "key": "zapier", + "name": "Zapier", + "description": "Zapier lets you connect PostHog with thousands of the most popular apps, so you can automate your work and have more time for what matters most—no code required.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "slack_integration", + "name": "Slack", + "description": "Get notified about new actions in Slack.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "microsoft_teams_integration", + "name": "Microsoft Teams", + "description": "Get notified about new actions in Microsoft Teams.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "discord_integration", + "name": "Discord", + "description": "Get notified about new actions in Discord.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "apps", + "name": "CDP + Apps library", + "description": "Connect your data with 50+ apps including BigQuery, Redshift, and more.", + "unit": null, + "limit": null, + "note": null + } + ], + "tiers": null, + "current_plan": false, + "included_if": "no_active_subscription" + }, + { + "plan_key": "paid-20230117", + "product_key": "integrations", + "name": "Integrations + CDP", + "description": "Connect PostHog to your favorite tools.", + "image_url": "https://posthog.com/images/product/product-icons/integrations.svg", + "docs_url": "https://posthog.com/docs/apps", + "note": null, + "unit": null, + "free_allocation": null, + "features": [ + { + "key": "zapier", + "name": "Zapier", + "description": "Zapier lets you connect PostHog with thousands of the most popular apps, so you can automate your work and have more time for what matters most—no code required.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "slack_integration", + "name": "Slack", + "description": "Get notified about new actions in Slack.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "microsoft_teams_integration", + "name": "Microsoft Teams", + "description": "Get notified about new actions in Microsoft Teams.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "discord_integration", + "name": "Discord", + "description": "Get notified about new actions in Discord.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "apps", + "name": "CDP + Apps library", + "description": "Connect your data with 50+ apps including BigQuery, Redshift, and more.", + "unit": null, + "limit": null, + "note": null + }, + { + "key": "app_metrics", + "name": "App metrics", + "description": "Get metrics on your apps to see their usage, reliability, and more.", + "unit": null, + "limit": null, + "note": null + } + ], + "tiers": null, + "current_plan": true, + "included_if": "has_subscription" + } + ], + "type": "integrations", + "free_allocation": 0, + "tiers": null, + "tiered": false, + "unit_amount_usd": null, + "current_amount_usd_before_addons": null, + "current_amount_usd": null, + "current_usage": 0, + "usage_limit": 0, + "has_exceeded_limit": false, + "percentage_usage": 0, + "projected_usage": 0, + "projected_amount_usd": null, + "unit": null, + "addons": [], + "contact_support": false, + "inclusion_only": true + }, + { + "name": "Platform and support", + "description": "SSO, permission management, and support.", + "price_description": null, + "usage_key": null, + "image_url": "https://posthog.com/images/product/product-icons/platform.svg", + "icon_key": "IconStack", + "docs_url": "https://posthog.com/docs", + "subscribed": null, + "plans": [], + "type": "platform_and_support", + "free_allocation": 0, + "tiers": null, + "tiered": false, + "unit_amount_usd": null, + "current_amount_usd_before_addons": null, + "current_amount_usd": null, + "current_usage": 0, + "usage_limit": 0, + "has_exceeded_limit": false, + "percentage_usage": 0, + "projected_usage": 0, + "projected_amount_usd": null, + "unit": null, + "addons": [], + "contact_support": true, + "inclusion_only": true + } + ], + "custom_limits_usd": { + "session_replay": "0", + "product_analytics": "800" + }, + "usage_summary": { + "events": { + "usage": 9582922, + "limit": 13000000 + }, + "recordings": { + "usage": 0, + "limit": 15000 + }, + "feature_flag_requests": { + "usage": 4576, + "limit": 10000000 + }, + "survey_responses": { + "usage": 8, + "limit": 1000 + } + }, + "free_trial_until": null, + "discount_percent": null, + "discount_amount_usd": null, + "amount_off_expires_at": null, + "never_drop_data": false, + "stripe_portal_url": "https://billing.stripe.com/p/session/live_YWNjdF8xSElNRERFdUlhdFJYU2R6LF9PeG9wUmZuUFpmcTlvaWVjeUd0ZlhFN2hHbjJUWDR30100sm6GDMjx" +} diff --git a/frontend/src/scenes/billing/billing-utils.spec.ts b/frontend/src/scenes/billing/billing-utils.spec.ts index 9680f47886738..a28188ba901a3 100644 --- a/frontend/src/scenes/billing/billing-utils.spec.ts +++ b/frontend/src/scenes/billing/billing-utils.spec.ts @@ -8,6 +8,7 @@ import { import tk from 'timekeeper' import { dayjs } from 'lib/dayjs' import billingJson from '~/mocks/fixtures/_billing_v2.json' +import billingJsonWithFlatFee from '~/mocks/fixtures/_billing_v2_with_flat_fee.json' describe('summarizeUsage', () => { it('should summarise usage', () => { @@ -148,6 +149,34 @@ describe('convertUsageToAmountWithPercentDiscount', () => { } ) }) + +const amountToUsageMappingWithFirstTierFlatFee = [ + { usage: 5_000_000, amount: '200.00' }, + { usage: 10_000_000, amount: '575.00' }, + { usage: 30_000_000, amount: '1725.00' }, +] +describe('amountToUsageMappingWithFirstTierFlatFee', () => { + it.each(amountToUsageMappingWithFirstTierFlatFee)( + 'should convert usage to an amount based on the tiers', + (mapping) => { + if (billingJsonWithFlatFee.products[0].tiers) { + expect(convertUsageToAmount(mapping.usage, [billingJsonWithFlatFee.products[0].tiers])).toEqual( + mapping.amount + ) + } + } + ) + it.each(amountToUsageMappingWithFirstTierFlatFee)( + 'should convert amount to a usage based on the tiers', + (mapping) => { + if (billingJsonWithFlatFee.products[0].tiers) { + expect(convertAmountToUsage(mapping.amount, [billingJsonWithFlatFee.products[0].tiers])).toEqual( + mapping.usage + ) + } + } + ) +}) describe('convertAmountToUsageWithPercentDiscount', () => { it.each(amountToUsageMappingWithPercentDiscount)( 'should convert amount to a usage based on the tiers', diff --git a/frontend/src/scenes/billing/billing-utils.ts b/frontend/src/scenes/billing/billing-utils.ts index fd4c0d25bcd56..4b55f893bf04e 100644 --- a/frontend/src/scenes/billing/billing-utils.ts +++ b/frontend/src/scenes/billing/billing-utils.ts @@ -50,12 +50,15 @@ export const convertUsageToAmount = ( const tiers = productAndAddonTiers[0].map((tier, index) => { const allAddonsTiers = productAndAddonTiers.slice(1) let totalAmount = parseFloat(tier.unit_amount_usd) + let flatFee = parseFloat(tier.flat_amount_usd || '0') for (const addonTiers of allAddonsTiers) { totalAmount += parseFloat(addonTiers[index].unit_amount_usd) + flatFee += parseFloat(addonTiers[index].flat_amount_usd || '0') } return { ...tier, unit_amount_usd: totalAmount.toString(), + flat_amount_usd: flatFee.toString(), } }) @@ -66,9 +69,13 @@ export const convertUsageToAmount = ( const tierUsageMax = tier.up_to ? tier.up_to - (previousTier?.up_to || 0) : Infinity const amountFloatUsd = parseFloat(tier.unit_amount_usd) + const tierFlatFee = parseFloat(tier.flat_amount_usd || '0') const usageThisTier = Math.min(remainingUsage, tierUsageMax) remainingUsage -= usageThisTier amount += amountFloatUsd * usageThisTier + if (tierFlatFee) { + amount += tierFlatFee + } previousTier = tier } @@ -95,12 +102,15 @@ export const convertAmountToUsage = ( const tiers = productAndAddonTiers[0].map((tier, index) => { const allAddonsTiers = productAndAddonTiers.slice(1) let totalAmount = parseFloat(tier.unit_amount_usd) + let flatFee = parseFloat(tier.flat_amount_usd || '0') for (const addonTiers of allAddonsTiers) { totalAmount += parseFloat(addonTiers[index].unit_amount_usd) + flatFee += parseFloat(addonTiers[index].flat_amount_usd || '0') } return { ...tier, unit_amount_usd: totalAmount.toString(), + flat_amount_usd: flatFee.toString(), } }) @@ -134,10 +144,14 @@ export const convertAmountToUsage = ( const tierUsageMax = tier.up_to ? tier.up_to - (previousTier?.up_to || 0) : Infinity const amountFloatUsd = parseFloat(tier.unit_amount_usd) + const tierFlatFee = parseFloat(tier.flat_amount_usd || '0') const usageThisTier = Math.min(remainingAmount / amountFloatUsd, tierUsageMax) usage += usageThisTier remainingAmount -= amountFloatUsd * usageThisTier + if (tierFlatFee) { + remainingAmount -= tierFlatFee + } previousTier = tier }