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

⚡ (billing) Automatic usage-based billing #924

Merged
merged 3 commits into from
Oct 17, 2023

Conversation

baptisteArno
Copy link
Owner

@baptisteArno baptisteArno commented Oct 13, 2023

BREAKING CHANGE: Stripe environment variables simplified. Check out the new configs to adapt your existing system.

Closes #906

Summary by CodeRabbit

New Features:

  • Introduced a usage-based billing system, providing more flexibility and options for users.
  • Integrated with Stripe for a smoother and more secure payment process.
  • Enhanced the user interface with improvements to the billing, workspace, and pricing pages for a more intuitive experience.

Improvements:

  • Simplified the billing logic, removing additional chats and yearly billing for a more streamlined user experience.
  • Updated email notifications to keep users informed about their usage and limits.
  • Improved pricing and currency formatting for better clarity and understanding.

Testing:

  • Updated tests and specifications to ensure the reliability of new features and improvements.

Note: These changes aim to provide a more flexible and user-friendly billing system, with clearer pricing and improved notifications. Users should find the new system more intuitive and easier to navigate.

@coderabbitai
Copy link

coderabbitai bot commented Oct 13, 2023

Walkthrough

This pull request introduces significant changes to the billing system, transitioning from a fixed plan structure to a usage-based model. It also includes updates to the Stripe integration, UI/UX improvements, test updates, email notifications, database schema changes, and pricing/currency formatting adjustments.

Changes

File(s) Summary
.../billing/api/createCheckoutSession.ts
.../billing/api/getSubscription.ts
.../billing/api/updateSubscription.ts
Simplified billing logic and removed additional chats and yearly billing.
.../billing/api/getUsage.ts
.../lib/billing/getChatsLimit.ts
.../lib/billing/getSeatsLimit.ts
Introduced usage-based billing and updated Stripe integration.
.../billing/components/ChangePlanForm.tsx
.../billing/components/ProPlanPricingCard.tsx
.../billing/components/StarterPlanPricingCard.tsx
Updated UI components to reflect new billing model.
.../billing/billing.spec.ts
.../results/components/ResultsPage.tsx
Updated tests and specs to accommodate new billing logic.
.../emails/AlmostReachedChatsLimitEmail.tsx Updated email notifications to align with usage-based billing.
.../features/billing/subscription.ts
.../features/telemetry.ts
Updated database schemas to support new billing model.
.../lib/billing/formatPrice.ts
.../lib/billing/guessIfUserIsEuropean.ts
Adjusted pricing and currency formatting.
.../scripts/cleanDatabase.ts
.../scripts/createChatsPrices.ts
.../scripts/migrateSubscriptionsToUsageBased.ts
Added scripts for database cleanup, creating chat prices, and migrating subscriptions to usage-based billing.

Tips

Chat with CodeRabbit Bot (@coderabbitai)

  • Mention @coderabbitai in any review comment for bot assistance.
  • Note: Review comments are made on code diffs or files, not on the PR overview.

Pause Incremental Reviews

  • Insert @coderabbitai: ignore in the PR description to halt the bot's ongoing reviews. Remove the line to resume.
  • Additionally, enter @coderabbitai pause to halt the bot's ongoing reviews as any issue or review comment. To
    resume reviews, enter @coderabbitai resume as any issue or review comment.

@vercel
Copy link

vercel bot commented Oct 13, 2023

The latest updates on your projects. Learn more about Vercel for Git ↗︎

4 Ignored Deployments
Name Status Preview Comments Updated (UTC)
builder-v2 ⬜️ Ignored (Inspect) Visit Preview Oct 17, 2023 5:35am
docs ⬜️ Ignored (Inspect) Visit Preview Oct 17, 2023 5:35am
landing-page-v2 ⬜️ Ignored (Inspect) Visit Preview Oct 17, 2023 5:35am
viewer-v2 ⬜️ Ignored (Inspect) Visit Preview Oct 17, 2023 5:35am

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Status

Actionable comments generated: 18

Commits Files that changed from the base of the PR and between d4041c7 and 3a8d66d.
Files ignored due to filter (5)
  • apps/docs/openapi/builder/spec.json
  • apps/docs/openapi/chat/spec.json
  • packages/lib/package.json
  • packages/scripts/package.json
  • pnpm-lock.yaml
Files selected for processing (45)
  • apps/builder/src/features/billing/api/createCheckoutSession.ts (6 hunks)
  • apps/builder/src/features/billing/api/getSubscription.ts (2 hunks)
  • apps/builder/src/features/billing/api/getUsage.ts (3 hunks)
  • apps/builder/src/features/billing/api/listInvoices.ts (1 hunks)
  • apps/builder/src/features/billing/api/updateSubscription.ts (7 hunks)
  • apps/builder/src/features/billing/billing.spec.ts (4 hunks)
  • apps/builder/src/features/billing/components/ChangePlanForm.tsx (5 hunks)
  • apps/builder/src/features/billing/components/ChatsProTiersModal.tsx (1 hunks)
  • apps/builder/src/features/billing/components/FeaturesList.tsx (1 hunks)
  • apps/builder/src/features/billing/components/PreCheckoutModal.tsx (1 hunks)
  • apps/builder/src/features/billing/components/ProPlanPricingCard.tsx (1 hunks)
  • apps/builder/src/features/billing/components/StarterPlanPricingCard.tsx (1 hunks)
  • apps/builder/src/features/billing/components/UsageProgressBars.tsx (3 hunks)
  • apps/builder/src/features/dashboard/components/DashboardPage.tsx (3 hunks)
  • apps/builder/src/features/results/components/ResultsPage.tsx (2 hunks)
  • apps/builder/src/features/workspace/components/MembersList.tsx (3 hunks)
  • apps/builder/src/pages/api/stripe/webhook.ts (5 hunks)
  • apps/builder/src/pages/api/workspaces/[workspaceId]/invitations.ts (2 hunks)
  • apps/builder/src/test/utils/databaseActions.ts (1 hunks)
  • apps/docs/docs/self-hosting/configuration.md (1 hunks)
  • apps/landing-page/components/PricingPage/ChatsProTiersModal.tsx (1 hunks)
  • apps/landing-page/components/PricingPage/Faq.tsx (1 hunks)
  • apps/landing-page/components/PricingPage/FreePlanCard.tsx (2 hunks)
  • apps/landing-page/components/PricingPage/PlanComparisonTables.tsx (4 hunks)
  • apps/landing-page/components/PricingPage/PricingCard/index.tsx (1 hunks)
  • apps/landing-page/components/PricingPage/ProPlanCard.tsx (1 hunks)
  • apps/landing-page/components/PricingPage/StarterPlanCard.tsx (2 hunks)
  • apps/landing-page/pages/pricing.tsx (3 hunks)
  • packages/bot-engine/queries/findPublicTypebot.ts (1 hunks)
  • packages/emails/src/emails/AlmostReachedChatsLimitEmail.tsx (3 hunks)
  • packages/env/env.ts (1 hunks)
  • packages/lib/billing/constants.ts (1 hunks)
  • packages/lib/billing/formatPrice.ts (1 hunks)
  • packages/lib/billing/getChatsLimit.ts (1 hunks)
  • packages/lib/billing/getSeatsLimit.ts (1 hunks)
  • packages/lib/billing/guessIfUserIsEuropean.ts (1 hunks)
  • packages/schemas/features/billing/subscription.ts (1 hunks)
  • packages/schemas/features/telemetry.ts (1 hunks)
  • packages/scripts/cleanDatabase.ts (1 hunks)
  • packages/scripts/createChatsPrices.ts (1 hunks)
  • packages/scripts/inspectUser.ts (2 hunks)
  • packages/scripts/migrateSubscriptionsToUsageBased.ts (1 hunks)
  • packages/scripts/playground.ts (2 hunks)
  • packages/scripts/sendAlertEmails.ts (4 hunks)
  • packages/scripts/sendTotalResultsDigest.ts (6 hunks)
Files skipped from review due to trivial changes (17)
  • apps/builder/src/features/billing/api/createCheckoutSession.ts
  • apps/builder/src/features/billing/api/getSubscription.ts
  • apps/builder/src/features/billing/api/listInvoices.ts
  • apps/builder/src/features/billing/components/ChangePlanForm.tsx
  • apps/builder/src/features/billing/components/FeaturesList.tsx
  • apps/builder/src/features/billing/components/PreCheckoutModal.tsx
  • apps/builder/src/features/billing/components/ProPlanPricingCard.tsx
  • apps/builder/src/features/billing/components/UsageProgressBars.tsx
  • apps/builder/src/features/results/components/ResultsPage.tsx
  • apps/builder/src/features/workspace/components/MembersList.tsx
  • apps/landing-page/components/PricingPage/Faq.tsx
  • apps/landing-page/components/PricingPage/PricingCard/index.tsx
  • apps/landing-page/components/PricingPage/StarterPlanCard.tsx
  • packages/bot-engine/queries/findPublicTypebot.ts
  • packages/schemas/features/telemetry.ts
  • packages/scripts/cleanDatabase.ts
  • packages/scripts/inspectUser.ts
Additional comments (Suppressed): 78
apps/builder/src/test/utils/databaseActions.ts (1)
  • 18-22: The function signature has changed, removing the additionalChatsIndex from the metadata. Ensure that all calls to this function throughout the codebase have been updated to match the new signature.
apps/landing-page/components/PricingPage/FreePlanCard.tsx (2)
  • 3-6: The import statement for chatsLimits has been updated to reflect the new location of the constant. Ensure that the new path is correct and that the chatsLimits constant is correctly exported from the new location.

  • 14-19: The chatsLimits.FREE constant is now being used instead of chatsLimit.FREE.totalIncluded. Ensure that chatsLimits.FREE correctly represents the number of chats included in the free plan.

apps/docs/docs/self-hosting/configuration.md (1)
  • 228-241: The new hunk simplifies the Stripe configuration by removing the yearly pricing and additional storage pricing options. This aligns with the PR summary stating that the billing system is transitioning to a usage-based model and removing yearly billing options. However, ensure that these changes are reflected in the rest of the codebase and that the removal of these options does not affect existing users who may be on yearly plans or have additional storage.
apps/builder/src/features/dashboard/components/DashboardPage.tsx (3)
  • 18-18: The import path for guessIfUserIsEuropean has been changed from @typebot.io/lib/pricing to @typebot.io/lib/billing/guessIfUserIsEuropean. Ensure that the new path is correct and the function is available at this location.

  • 36-40: The isYearly and chats query parameters have been removed from the router query destructuring. If these parameters are no longer used, ensure that they have been removed from all places in the code where they were previously set.

  • 51-55: The additionalChats and isYearly properties have been removed from the setPreCheckoutPlan function call. If these properties are no longer used, ensure that they have been removed from the function definition and any other places in the code where they were previously used.

apps/builder/src/pages/api/workspaces/[workspaceId]/invitations.ts (2)
  • 8-14: The import statement for isSeatsLimitReached has been replaced with getSeatsLimit. Ensure that the new function getSeatsLimit is correctly implemented and returns the expected results.

  • 37-43: The isSeatsLimitReached function has been replaced with getSeatsLimit. The new logic checks if the seats limit is less than or equal to the sum of existing members and invitations. This change seems to be in line with the new billing model. However, ensure that the getSeatsLimit function correctly calculates the seats limit for the workspace.

apps/landing-page/components/PricingPage/ProPlanCard.tsx (4)
  • 20-78: The new hunk has removed the state management for selectedChatsLimitIndex and the dropdown menu for selecting the chats limit. The pricing is now fixed at 10,000 chats/mo for the "Pro" plan. Ensure that this change aligns with the new usage-based billing model and that the fixed pricing does not negatively impact the user experience or the business model.

  • 23-23: The prices.PRO constant is used directly to set the price of the "Pro" plan. Ensure that this constant is correctly defined and updated in the @typebot.io/lib/billing/constants module.

  • 47-54: A new button has been added for viewing the pricing tiers for extra chats. Ensure that the onChatsTiersClick function passed as a prop to the ProPlanCard component correctly handles this action.

  • 66-73: The link for the "Subscribe now" button has been simplified, removing the query parameters for the chats limit and the subscription type. Ensure that this change aligns with the new usage-based billing model and that the registration page correctly handles the simplified link.

apps/builder/src/features/billing/billing.spec.ts (4)
  • 116-138: The new hunk has removed the ability to select different options for quotas and instead directly upgrades to a fixed plan. Ensure that this change aligns with the new usage-based billing model and that the UI has been updated to reflect this change. Also, the additionalChatsIndex property has been removed from the addSubscriptionToWorkspace function call. Verify that this property is no longer used in the new billing model.

  • 143-151: The new hunk has removed the ability to select different options for quotas and instead directly upgrades to a fixed plan. Ensure that this change aligns with the new usage-based billing model and that the UI has been updated to reflect this change.

  • 156-166: The new hunk has removed the checks for the number of quotas in the billing portal. Ensure that this change aligns with the new usage-based billing model and that the UI has been updated to reflect this change. Also, the test now checks for a '$50.00' price. Verify that this price is expected in the new billing model.

  • 188-193: The new hunk has removed the check for the '$174.00' price in the invoices. Verify that this price is no longer expected in the new billing model.

apps/builder/src/features/billing/api/getUsage.ts (2)
  • 3-9: The new hunk introduces Stripe and environment variables. Ensure that the Stripe secret key is securely stored and not exposed in any logs or error messages. Also, verify that the Stripe library version is up-to-date and doesn't have any known security vulnerabilities.

  • 21-35: The output schema of the function has been updated to include a resetsAt date. Ensure that all calls to this function throughout the codebase have been updated to handle this new property. Also, the workspace query now selects stripeId and plan fields. Make sure these fields are used appropriately and do not expose sensitive information.

apps/landing-page/components/PricingPage/PlanComparisonTables.tsx (4)
  • 19-37: The import statements have been updated to reflect the new billing constants and functions. The PlanComparisonTables component now accepts a onChatsTiersClick prop, which is used to handle click events on the "See tiers" button. Ensure that this prop is being correctly passed from the parent component.

  • 54-75: The table rows for "Chats" and "Additional Chats" have been updated to reflect the new billing model. The "Chats" row now displays the chat limits for each plan, and the "Additional Chats" row displays the price for additional chats. The "See tiers" button has been added to the "Additional Chats" row for the Pro plan, which triggers the onChatsTiersClick function when clicked. Ensure that this function is correctly implemented and that it displays the chat tier pricing as expected.

  • 82-87: The "Members" row has been updated to use the new seatsLimits constant. Ensure that this constant correctly reflects the number of seats for each plan.

  • 271-284: A new row for "WhatsApp integration" has been added to the table. This feature is only available for the Pro plan. Ensure that this accurately reflects the available features for each plan.

apps/landing-page/pages/pricing.tsx (3)
  • 6-33: The Switch and useState imports have been removed, and useDisclosure has been added. This suggests a change in the state management of the component. The useState hook was previously used to toggle between monthly and yearly pricing options. The new code seems to be using the useDisclosure hook from Chakra UI for managing the visibility of a modal. Ensure that this change aligns with the new billing model and that the removal of the pricing toggle does not affect other parts of the application.

  • 87-102: The ProPlanCard component now takes a onChatsTiersClick prop, which is linked to the onOpen function from the useDisclosure hook. This suggests that clicking on a certain element within the ProPlanCard component will open a modal. The StarterPlanCard and ProPlanCard components no longer take the isYearly prop, which aligns with the removal of the pricing toggle in the new code. Ensure that the onChatsTiersClick prop is properly handled within the ProPlanCard component and that the removal of the isYearly prop does not cause any issues.

  • 105-111: The PlanComparisonTables component now takes a onChatsTiersClick prop, similar to the ProPlanCard component. This suggests that clicking on a certain element within the PlanComparisonTables component will also open the same modal. Ensure that the onChatsTiersClick prop is properly handled within the PlanComparisonTables component.

apps/builder/src/features/billing/components/StarterPlanPricingCard.tsx (3)
  • 23-97: The new hunk simplifies the StarterPlanPricingCard component by removing the dropdown menu for selecting the number of chats per month and the logic associated with it. The price is now fixed at $39 per month, and the number of chats is set to 2,000 per month with extra chats costing $10 per 500. The button label is determined based on the currentPlan prop. The component now only receives the currentPlan, isLoading, currency, and onPayClick props. The workspace and currentSubscription props and the isYearly state are no longer used. The onPayClick function is now called directly when the button is clicked, without passing any arguments. The component still renders a list of features included in the starter plan and a button for upgrading or downgrading the plan. The button is disabled if the current plan is the starter plan.

  • 89-91: The onPayClick function is now called directly when the button is clicked, without passing any arguments. This simplifies the logic for handling the button click. However, ensure that the onPayClick function can handle this change.

  • 32-36: The getButtonLabel function now only checks the currentPlan prop to determine the button label. It no longer checks the selectedChatsLimitIndex state or the isYearly prop. This simplifies the logic for determining the button label. However, ensure that the currentPlan prop is always provided and that it can handle the new values.

apps/builder/src/pages/api/stripe/webhook.ts (5)
  • 46-67: The additionalChats field has been removed from the metadata object. Ensure that this change is reflected in all places where this metadata is being created or used. Also, verify that the removal of this field does not break any existing functionality or expectations in the codebase.

  • 83-88: The additionalChatsIndex field has been removed from the telemetry event data. Ensure that this change is reflected in all places where this telemetry event is being created or used. Also, verify that the removal of this field does not break any existing functionality or expectations in the codebase.

  • 117-122: The additionalChatsIndex field has been removed from the telemetry event data. Ensure that this change is reflected in all places where this telemetry event is being created or used. Also, verify that the removal of this field does not break any existing functionality or expectations in the codebase.

  • 145-150: The additionalChatsIndex field has been removed from the workspace update data. Ensure that this change is reflected in all places where this update data is being created or used. Also, verify that the removal of this field does not break any existing functionality or expectations in the codebase.

  • 168-173: The additionalChatsIndex field has been removed from the telemetry event data. Ensure that this change is reflected in all places where this telemetry event is being created or used. Also, verify that the removal of this field does not break any existing functionality or expectations in the codebase.

apps/builder/src/features/billing/api/updateSubscription.ts (7)
  • 5-14: The import of getChatsLimit and chatPriceIds from @typebot.io/lib/pricing and @typebot.io/lib/api/getUsage has been removed. Ensure that these are no longer needed in the context of the new billing model.

  • 25-29: The additionalChats and isYearly parameters have been removed from the input schema. Ensure that all calls to this function throughout the codebase have been updated to match the new signature.

  • 38-41: The additionalChats and isYearly parameters have been removed from the input object. Ensure that these are no longer needed in the context of the new billing model.

  • 78-107: The logic for determining the current plan and usage items in the subscription has been updated. The code now checks for the presence of env.STRIPE_STARTER_PRICE_ID and env.STRIPE_PRO_PRICE_ID in the item price IDs. The logic for constructing the items array to update in the subscription has also been updated. Ensure that these changes align with the new billing model.

  • 109-133: The logic for updating the subscription has been updated. The code now checks if the total chats used is greater than or equal to 4000 before allowing a downgrade to the Starter plan. The proration behavior is also set differently depending on the plan. Ensure that these changes align with the new billing model.

  • 146-152: The logic for updating the workspace in the database has been simplified. The additionalChatsIndex property has been removed, and the isQuarantined property is now always set to false. Ensure that these changes align with the new billing model.

  • 158-162: The additionalChatsIndex property has been removed from the telemetry event data. Ensure that this property is no longer needed in the context of the new billing model.

packages/emails/src/emails/AlmostReachedChatsLimitEmail.tsx (3)
  • 1-15: The workspaceName prop has been added to AlmostReachedChatsLimitEmailProps and the url prop has been removed. Ensure that all calls to AlmostReachedChatsLimitEmail have been updated to match the new prop requirements. Also, verify that the removal of the url prop does not affect the functionality of the email, as it was previously used for the upgrade button.

  • 21-29: The url prop is no longer passed to AlmostReachedChatsLimitEmail. Ensure that this change does not affect the functionality of the email, as the url was previously used for the upgrade button.

  • 40-61: The email content has been updated to reflect the new usage-based billing model. The upgrade button has been removed, and a link to the pricing page has been added. Ensure that these changes align with the new billing model and provide the necessary information to the user.

packages/lib/billing/getSeatsLimit.ts (1)
  • 5-12: The function getSeatsLimit has been introduced to determine the seats limit based on the plan and custom seats limit. It handles three cases: UNLIMITED, CUSTOM, and other plans. For UNLIMITED and CUSTOM without a custom seats limit, it returns -1 indicating no limit. For CUSTOM with a custom seats limit, it returns the custom seats limit. For other plans, it returns the limit from the seatsLimits constant. This function seems to handle all possible scenarios correctly.
packages/lib/billing/guessIfUserIsEuropean.ts (1)
  • 1-29: The list of European Union country codes seems to be missing a few countries. Please verify if this is intentional or an oversight. For example, 'PT' (Portugal) is missing.
packages/env/env.ts (2)
  • 140-147: The Stripe environment variables have been simplified, removing the yearly pricing options and additional storage pricing options. Ensure that these changes are reflected in the Stripe dashboard and that the new price IDs are correctly set in the environment variables. Also, verify that the billing logic in the application has been updated to handle these changes.

  • 148-149: The Stripe public key is still being exposed to the client. This is expected and is necessary for Stripe to function correctly on the client side. However, ensure that no sensitive Stripe keys (like the secret key) are exposed to the client.

packages/schemas/features/billing/subscription.ts (1)
  • 1-10: The new currentBillingPeriod object in the subscription schema is a good addition for the new usage-based billing model. However, the removal of isYearly property might have implications on the existing codebase where it is being used. Please verify if all instances of isYearly have been properly handled or replaced.
packages/lib/billing/getChatsLimit.ts (1)
  • 1-19: The function getChatsLimit has been introduced to determine the chat limit based on the plan type. It handles different plan types including UNLIMITED, LIFETIME, OFFERED, and CUSTOM. For CUSTOM plan, it uses the customChatsLimit if provided, otherwise defaults to -1 (unlimited). For other plans, it fetches the limit from chatsLimits constant. The function seems to handle all the cases correctly and is well-structured.
packages/scripts/sendAlertEmails.ts (6)
  • 8-8: The import path for getChatsLimit has been changed from @typebot.io/lib/pricing to @typebot.io/lib/billing/getChatsLimit. Ensure that the function is correctly exported from the new location and that all other references to this function in the codebase have been updated.

  • 17-17: The LIMIT_EMAIL_TRIGGER_PERCENT has been reduced from 0.8 to 0.75. This means that the "almost reached chats limit" email will be sent earlier than before. Ensure that this change is intentional and that it aligns with the business requirements.

  • 23-27: The WorkspaceForDigest type has been updated. The additionalChatsIndex property has been removed and the name and isQuarantined properties have been added. Ensure that all uses of this type in the codebase have been updated to reflect these changes.

  • 72-78: The name property has been added to the select clause for workspaces. Ensure that this property is used in the updated code and that it is correctly populated in the database.

  • 144-147: The url property has been removed from the sendAlmostReachedChatsLimitEmail function call. If this property was used in the email template, ensure that the template has been updated to remove any references to it.

  • 157-160: The condition for automatically quarantining a workspace has been changed. Previously, a workspace was quarantined if the total chats used exceeded three times the limit for free plan workspaces. Now, a workspace is quarantined if the total chats used exceeds 1.5 times the limit. Ensure that this change is intentional and that it aligns with the business requirements.

packages/scripts/migrateSubscriptionsToUsageBased.ts (16)
  • 1-4: The script imports necessary modules and functions. It imports PrismaClient from @typebot.io/prisma to interact with the database, promptAndSetEnvironment from ./utils to set the environment, Stripe from stripe to interact with the Stripe API, and createId from @paralleldrive/cuid2 to create unique IDs.

  • 6-8: The script defines an asynchronous function migrateSubscriptionsToUsageBased and initializes a new Prisma client.

  • 10-16: The script checks if the environment variables STRIPE_STARTER_CHATS_PRICE_ID and STRIPE_PRO_CHATS_PRICE_ID are set. If not, it throws an error. This is a good practice to ensure that necessary environment variables are set before the script runs.

  • 18-39: The script fetches all workspaces with a paid plan (either 'PRO' or 'STARTER') that are not suspended. It selects the plan, name, id, stripeId, isQuarantined, and members of each workspace.

  • 41-43: The script initializes a new Stripe client with the secret key from the environment variables and sets the API version to '2022-11-15'.

  • 45-47: The script creates a new Date object todayMidnight and sets the time to midnight. This will be used later to filter results based on their creation date.

  • 48-214: The script iterates over each workspace with a paid plan. For each workspace, it logs the workspace details, checks if the workspace has a Stripe ID, fetches the current subscription of the workspace from Stripe, checks if the current subscription includes a 'STARTER' or 'PRO' plan, updates the subscription to include the new usage-based pricing, counts the total results for the workspace, checks if the workspace should be upgraded to the 'PRO' plan, creates a usage record for the subscription item, and updates the workspace if it is quarantined.

  • 56-58: The script checks if the workspace has a Stripe ID. If not, it logs a message and skips to the next workspace. This is a good practice to ensure that only workspaces with a Stripe ID are processed.

  • 60-72: The script fetches the list of subscriptions for the workspace from Stripe and filters out the current subscription. It checks if a current subscription exists. If not, it logs a message and skips to the next workspace.

  • 74-86: The script checks if the current subscription includes a 'STARTER' or 'PRO' plan. If not, it logs a message and skips to the next workspace. This is a good practice to ensure that only subscriptions with a 'STARTER' or 'PRO' plan are processed.

  • 88-138: The script updates the current subscription to include the new usage-based pricing. It removes yearly plans and adds a new item with the usage-based price ID. This is a key part of the migration to the new usage-based model.

  • 140-149: The script counts the total results for the workspace. It filters results based on their creation date and whether they have started.

  • 151-179: The script checks if the workspace should be upgraded to the 'PRO' plan. If the workspace is on the 'STARTER' plan and has more than 4000 chats, it is automatically upgraded to the 'PRO' plan. This is a good practice to ensure that workspaces are on the appropriate plan based on their usage.

  • 181-204: The script creates a usage record for the subscription item. It uses the total results as the quantity and sets the timestamp to 'now'. It also generates a unique idempotency key to ensure that the request is not duplicated.

  • 206-213: The script checks if the workspace is quarantined. If so, it updates the workspace to remove the quarantine. This is a good practice to ensure that workspaces are not quarantined after the migration.

  • 217-217: The script calls the migrateSubscriptionsToUsageBased function to start the migration process.

packages/scripts/sendTotalResultsDigest.ts (5)
  • 1-17: The import paths for Plan and getChatsLimit have been changed. Ensure that these changes are reflected throughout the codebase and that the new paths are correct.
- import { getChatsLimit } from '@typebot.io/lib/pricing'
- import { Plan } from '@typebot.io/prisma'
+ import { getChatsLimit } from '@typebot.io/lib/billing/getChatsLimit'
+ import { Plan } from '@typebot.io/schemas'
  • 21-26: The additionalChatsIndex property has been removed from the WorkspaceForDigest type. Ensure that this change is reflected throughout the codebase and that it does not break any existing functionality.
- | 'additionalChatsIndex'
  • 99-109: The additionalChatsIndex property has been removed from the database query. Ensure that this change is reflected throughout the codebase and that it does not break any existing functionality.
- additionalChatsIndex: true,
  • 124-134: The reportUsageToStripe function has been added to report usage to Stripe. Ensure that this function is correctly implemented and that it does not introduce any new bugs.
+ console.log('Reporting usage to Stripe...')
+ await reportUsageToStripe(resultsWithWorkspaces)
  • 198-262: The reportUsageToStripe function has been added. This function reports usage to Stripe for each workspace with a 'STARTER' or 'PRO' plan. It throws an error if a paid workspace does not have a stripeId, a subscription, or a subscription item. It uses the createId function to create an idempotency key for each usage record. Ensure that this function is correctly implemented and that it does not introduce any new bugs.
+ const reportUsageToStripe = async (
+   resultsWithWorkspaces: (Pick<ResultWithWorkspace, 'totalResultsYesterday'> & {
+     workspace: Pick<
+       ResultWithWorkspace['workspace'],
+       'id' | 'plan' | 'stripeId'
+     >
+   })[]
+ ) => {
+   if (isEmpty(process.env.STRIPE_SECRET_KEY))
+     throw new Error('Missing STRIPE_SECRET_KEY env variable')
+ 
+   const stripe = new Stripe(process.env.STRIPE_SECRET_KEY, {
+     apiVersion: '2022-11-15',
+   })
+ 
+   for (const result of resultsWithWorkspaces.filter(
+     (result) =>
+       result.workspace.plan === 'STARTER' || result.workspace.plan === 'PRO'
+   )) {
+     if (!result.workspace.stripeId)
+       throw new Error(
+         `Found paid workspace without a stripeId: ${result.workspace.stripeId}`
+       )
+     const subscriptions = await stripe.subscriptions.list({
+       customer: result.workspace.stripeId,
+     })
+ 
+     const currentSubscription = subscriptions.data
+       .filter((sub) => ['past_due', 'active'].includes(sub.status))
+       .sort((a, b) => a.created - b.created)
+       .shift()
+ 
+     if (!currentSubscription)
+       throw new Error(
+         `Found paid workspace without a subscription: ${result.workspace.stripeId}`
+       )
+ 
+     const subscriptionItem = currentSubscription.items.data.find(
+       (item) =>
+         item.price.id === process.env.STRIPE_STARTER_CHATS_PRICE_ID ||
+         item.price.id === process.env.STRIPE_PRO_CHATS_PRICE_ID
+     )
+ 
+     if (!subscriptionItem)
+       throw new Error(
+         `Could not find subscription item for workspace ${result.workspace.id}`
+       )
+ 
+     const idempotencyKey = createId()
+ 
+     await stripe.subscriptionItems.createUsageRecord(
+       subscriptionItem.id,
+       {
+         quantity: result.totalResultsYesterday,
+         timestamp: 'now',
+       },
+       {
+         idempotencyKey,
+       }
+     )
+   }
+ }
packages/scripts/playground.ts (1)
  • 1-33: The new hunk introduces a new type ResultWithWorkspace and imports additional modules. The ResultWithWorkspace type seems to be used for handling workspace-related data, including user, workspace, and typebot details. However, this type is not used in the provided new hunks. Ensure that it is used appropriately in the rest of the code.

BREAKING CHANGE: Stripe environment variables simplified. Check out the new configs to adapt your existing system.

Closes #906
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Status

Actionable comments generated: 17

Commits Files that changed from the base of the PR and between d4041c7 and ec7aed3.
Files ignored due to filter (5)
  • apps/docs/openapi/builder/spec.json
  • apps/docs/openapi/chat/spec.json
  • packages/lib/package.json
  • packages/scripts/package.json
  • pnpm-lock.yaml
Files selected for processing (44)
  • .github/workflows/check-and-report-chats-usage.yml (2 hunks)
  • apps/builder/src/features/billing/api/createCheckoutSession.ts (6 hunks)
  • apps/builder/src/features/billing/api/getSubscription.ts (2 hunks)
  • apps/builder/src/features/billing/api/getUsage.ts (3 hunks)
  • apps/builder/src/features/billing/api/listInvoices.ts (1 hunks)
  • apps/builder/src/features/billing/api/updateSubscription.ts (7 hunks)
  • apps/builder/src/features/billing/billing.spec.ts (4 hunks)
  • apps/builder/src/features/billing/components/ChangePlanForm.tsx (5 hunks)
  • apps/builder/src/features/billing/components/ChatsProTiersModal.tsx (1 hunks)
  • apps/builder/src/features/billing/components/FeaturesList.tsx (1 hunks)
  • apps/builder/src/features/billing/components/PreCheckoutModal.tsx (1 hunks)
  • apps/builder/src/features/billing/components/ProPlanPricingCard.tsx (1 hunks)
  • apps/builder/src/features/billing/components/StarterPlanPricingCard.tsx (1 hunks)
  • apps/builder/src/features/billing/components/UsageProgressBars.tsx (3 hunks)
  • apps/builder/src/features/dashboard/components/DashboardPage.tsx (3 hunks)
  • apps/builder/src/features/results/components/ResultsPage.tsx (2 hunks)
  • apps/builder/src/features/workspace/components/MembersList.tsx (3 hunks)
  • apps/builder/src/pages/api/stripe/webhook.ts (5 hunks)
  • apps/builder/src/pages/api/workspaces/[workspaceId]/invitations.ts (2 hunks)
  • apps/builder/src/test/utils/databaseActions.ts (1 hunks)
  • apps/docs/docs/self-hosting/configuration.md (1 hunks)
  • apps/landing-page/components/PricingPage/ChatsProTiersModal.tsx (1 hunks)
  • apps/landing-page/components/PricingPage/Faq.tsx (1 hunks)
  • apps/landing-page/components/PricingPage/FreePlanCard.tsx (2 hunks)
  • apps/landing-page/components/PricingPage/PlanComparisonTables.tsx (4 hunks)
  • apps/landing-page/components/PricingPage/PricingCard/index.tsx (1 hunks)
  • apps/landing-page/components/PricingPage/ProPlanCard.tsx (1 hunks)
  • apps/landing-page/components/PricingPage/StarterPlanCard.tsx (2 hunks)
  • apps/landing-page/pages/pricing.tsx (3 hunks)
  • packages/bot-engine/queries/findPublicTypebot.ts (1 hunks)
  • packages/emails/src/emails/AlmostReachedChatsLimitEmail.tsx (3 hunks)
  • packages/env/env.ts (1 hunks)
  • packages/lib/billing/constants.ts (1 hunks)
  • packages/lib/billing/formatPrice.ts (1 hunks)
  • packages/lib/billing/getChatsLimit.ts (1 hunks)
  • packages/lib/billing/getSeatsLimit.ts (1 hunks)
  • packages/lib/billing/guessIfUserIsEuropean.ts (1 hunks)
  • packages/schemas/features/billing/subscription.ts (1 hunks)
  • packages/schemas/features/telemetry.ts (1 hunks)
  • packages/scripts/checkAndReportChatsUsage.ts (8 hunks)
  • packages/scripts/cleanDatabase.ts (1 hunks)
  • packages/scripts/createChatsPrices.ts (1 hunks)
  • packages/scripts/inspectUser.ts (2 hunks)
  • packages/scripts/migrateSubscriptionsToUsageBased.ts (1 hunks)
Files skipped from review due to trivial changes (16)
  • .github/workflows/check-and-report-chats-usage.yml
  • apps/builder/src/features/billing/api/createCheckoutSession.ts
  • apps/builder/src/features/billing/api/getSubscription.ts
  • apps/builder/src/features/billing/api/listInvoices.ts
  • apps/builder/src/features/billing/components/FeaturesList.tsx
  • apps/builder/src/features/billing/components/UsageProgressBars.tsx
  • apps/builder/src/features/results/components/ResultsPage.tsx
  • apps/builder/src/features/workspace/components/MembersList.tsx
  • apps/landing-page/components/PricingPage/Faq.tsx
  • apps/landing-page/components/PricingPage/FreePlanCard.tsx
  • apps/landing-page/components/PricingPage/PricingCard/index.tsx
  • apps/landing-page/components/PricingPage/StarterPlanCard.tsx
  • packages/bot-engine/queries/findPublicTypebot.ts
  • packages/schemas/features/telemetry.ts
  • packages/scripts/cleanDatabase.ts
  • packages/scripts/inspectUser.ts
Additional comments (Suppressed): 90
apps/builder/src/features/billing/components/PreCheckoutModal.tsx (1)
  • 25-30: The additionalChats and isYearly fields have been removed from the selectedSubscription prop. Ensure that these changes are reflected in all parts of the codebase where this component is used. Also, verify that the removal of these fields aligns with the new billing model.
apps/builder/src/features/billing/components/ProPlanPricingCard.tsx (3)
  • 28-33: The function signature has changed. Ensure that all calls to this function throughout the codebase have been updated to match the new signature.

  • 38-41: The logic for determining the button label has been simplified. Ensure that this change does not affect the user experience or functionality.

  • 139-145: The isDisabled condition has been simplified. Ensure that this change does not affect the user experience or functionality.

apps/landing-page/components/PricingPage/PlanComparisonTables.tsx (4)
  • 19-37: The new hunk introduces a new prop onChatsTiersClick to the PlanComparisonTables component. Ensure that all instances of this component in the codebase are updated to pass this prop. Also, the import paths for chatsLimits, prices, seatsLimits, and formatPrice have been updated. Verify that these paths are correct and the imported modules exist at these locations.

  • 54-76: The way additional chats are displayed for the Starter and Pro plans has been changed. Previously, the price per additional chat was calculated based on the graduated price. Now, it's hardcoded to "$10 per 500 chats" for the Starter plan, and a "See tiers" button is displayed for the Pro plan. Ensure that this change aligns with the new billing model.

  • 82-87: The seatsLimits for the Starter and Pro plans are now directly used from the imported seatsLimits constant. Previously, the totalIncluded property of the seatsLimit for each plan was used. Ensure that the seatsLimits constant has the correct values for each plan.

  • 271-284: A new feature "WhatsApp integration" has been added to the Pro plan. Ensure that this feature is indeed available for the Pro plan and not for the other plans.

apps/builder/src/features/billing/billing.spec.ts (4)
  • 116-138: The test case has been updated to reflect the new billing model. The test now simulates a plan upgrade to the STARTER plan with a fixed price of $39. The addSubscriptionToWorkspace function is called with the Stripe price IDs for the STARTER plan and chats. Ensure that the Stripe price IDs are correctly set in the environment variables. Also, verify that the addSubscriptionToWorkspace function correctly handles the new billing model and Stripe integration.

  • 143-151: The test case simulates an upgrade to the PRO plan. The expected price of the PRO plan is $89. The test verifies the successful update of the workspace plan. Ensure that the price of the PRO plan is correctly set in the application and matches the expected price in the test.

  • 156-166: The test case navigates to the billing portal and verifies the displayed prices. The expected prices are $39.00 and $50.00. Ensure that these prices match the prices set in the application and Stripe. The test also cancels the subscription using the cancelSubscription function. Verify that this function correctly cancels the subscription in the new billing model and Stripe integration.

  • 188-193: The test case verifies the visibility and content of the invoices section. The expected prices in the invoices are $39.00 and $50.00. Ensure that these prices match the prices set in the application and Stripe. Also, verify that the invoices are correctly generated and displayed in the new billing model and Stripe integration.

apps/builder/src/pages/api/stripe/webhook.ts (7)
  • 46-67: The additionalChats field has been removed from the metadata object. Ensure that this change is reflected in all places where this metadata object is created or used. Also, verify that the removal of this field does not affect the functionality of the application.

  • 55-58: The check for valid metadata has been simplified by removing the check for additionalChats. If additionalChats was a required field before, its removal might cause issues. Please verify.

  • 60-67: The additionalChatsIndex field has been removed from the workspace update data. Ensure that this change is reflected in all places where this data object is used. Also, verify that the removal of this field does not affect the functionality of the application.

  • 83-88: The additionalChatsIndex field has been removed from the telemetry event data. Ensure that this change is reflected in all places where this data object is used. Also, verify that the removal of this field does not affect the functionality of the application.

  • 117-122: The additionalChatsIndex field has been removed from the telemetry event data. Ensure that this change is reflected in all places where this data object is used. Also, verify that the removal of this field does not affect the functionality of the application.

  • 145-150: The additionalChatsIndex field has been removed from the workspace update data. Ensure that this change is reflected in all places where this data object is used. Also, verify that the removal of this field does not affect the functionality of the application.

  • 168-173: The additionalChatsIndex field has been removed from the telemetry event data. Ensure that this change is reflected in all places where this data object is used. Also, verify that the removal of this field does not affect the functionality of the application.

apps/builder/src/features/billing/components/StarterPlanPricingCard.tsx (4)
  • 23-97: The new hunk simplifies the StarterPlanPricingCard component by removing the dropdown menu for selecting the number of chats per month and the logic for calculating the price based on the selected chats limit, plan type, and currency. Instead, it now displays a fixed price and a fixed number of chats per month. The onPayClick callback no longer receives the selected chats limit index as an argument. Ensure that these changes align with the new usage-based billing model and that all calls to this component have been updated accordingly.

  • 32-36: The getButtonLabel function now only checks if the current plan is Plan.PRO or Plan.STARTER to determine the button label. It no longer checks if the current plan matches the selected options or if the selected chats limit index is undefined. Make sure that this simplified logic is sufficient for all use cases.

  • 90-91: The payment button is now disabled if the current plan is Plan.STARTER. Previously, it was also disabled if the current plan matched the selected options. Confirm that this change in behavior is intentional and appropriate.

  • 65-83: The FeaturesList component now receives a fixed list of features. Previously, it included a menu for selecting the number of chats per month. Ensure that this change is consistent with the new billing model.

apps/builder/src/features/billing/api/getUsage.ts (4)
  • 3-9: The new hunk introduces Stripe and environment variables. Ensure that the Stripe secret key is securely stored and not exposed in any logs or error messages. Also, make sure that the Stripe library is properly installed and imported.

  • 21-35: The new hunk modifies the output schema of the getUsage function to include a resetsAt date. Ensure that all calls to this function throughout the codebase have been updated to handle the new output schema.

  • 46-73: The new hunk introduces a check for the existence of the STRIPE_SECRET_KEY environment variable and the stripeId property of the workspace. If either is missing, or if the workspace plan is not 'STARTER' or 'PRO', the function falls back to the old behavior of calculating the total chats used for the current month. This is a good approach to maintain backward compatibility and handle edge cases.

  • 75-107: The new hunk introduces Stripe integration to fetch the current subscription for the workspace and calculate the total chats used based on the subscription's current period start date. This is a significant change that should be thoroughly tested to ensure it works as expected and handles all possible edge cases. For example, what happens if the Stripe API call fails? What if the workspace has multiple subscriptions? What if the subscription status is not 'past_due' or 'active'? These scenarios should be handled gracefully to prevent unexpected behavior or errors.

packages/env/env.ts (2)
  • 140-147: The new environment variables for Stripe pricing IDs have been simplified, removing the distinction between monthly and yearly prices, as well as storage prices. This aligns with the transition to a usage-based billing model. Ensure that these new environment variables are correctly set in your deployment environments and that any code referencing the old environment variables has been updated.

  • 148-149: The client-side Stripe public key remains unchanged. Ensure that it is correctly set in your deployment environments.

packages/lib/billing/getSeatsLimit.ts (1)
  • 5-12: The function getSeatsLimit has been updated to handle different plans including UNLIMITED and CUSTOM. It seems to return -1 for unlimited seats. Ensure that the rest of the codebase correctly interprets -1 as unlimited seats. Also, verify that customSeatsLimit is always a positive integer when plan is CUSTOM.
packages/lib/billing/constants.ts (2)
  • 164-164: The use of 'inf' as a string to represent infinity might cause issues if not handled properly in the code that uses these tiers. Ensure that the code that uses these tiers can handle 'inf' correctly.

  • 166-166: The unit_amount_decimal is a string. Ensure that this is the correct type and that it is handled correctly in the code that uses this value.

apps/builder/src/test/utils/databaseActions.ts (1)
  • 18-22: The function signature for addSubscriptionToWorkspace has changed. The metadata parameter no longer includes additionalChatsIndex. Ensure that all calls to this function throughout the codebase have been updated to match the new signature.
apps/landing-page/pages/pricing.tsx (3)
  • 6-33: The new hunk introduces the ChatsProTiersModal component and the useDisclosure hook from Chakra UI. The isOpen, onOpen, and onClose states are used to control the visibility of the ChatsProTiersModal. Ensure that the ChatsProTiersModal component is implemented correctly and that it properly handles the isOpen, onOpen, and onClose props. Also, verify that the useDisclosure hook is used correctly and that it properly manages the state of the modal.

  • 87-102: The new hunk removes the switch for toggling between monthly and yearly pricing. It also introduces a new prop onChatsTiersClick for the ProPlanCard component. Ensure that the removal of the pricing toggle switch aligns with the new billing model and that all instances of the ProPlanCard component have been updated to include the onChatsTiersClick prop. Also, verify that the onChatsTiersClick prop is implemented correctly in the ProPlanCard component and that it properly handles the click event.

  • 105-111: The new hunk introduces a new prop onChatsTiersClick for the PlanComparisonTables component. Ensure that all instances of the PlanComparisonTables component have been updated to include the onChatsTiersClick prop. Also, verify that the onChatsTiersClick prop is implemented correctly in the PlanComparisonTables component and that it properly handles the click event.

apps/builder/src/features/billing/components/ChangePlanForm.tsx (5)
  • 1-8: The import of Switch and Tag from @chakra-ui/react has been removed, indicating that these components are no longer used in the new code. The guessIfUserIsEuropean function has been moved to a different location in the codebase. Ensure that the new location is correctly referenced across the codebase.

  • 12-18: The guessIfUserIsEuropean function has been moved to a different location in the codebase. Ensure that the new location is correctly referenced across the codebase.

  • 26-37: The useState hook for isYearly has been removed, indicating that the option to switch between monthly and yearly billing has been removed. The onSuccess callback in the useQuery hook has also been removed, which previously set the isYearly state based on the subscription data. Ensure that the removal of these features aligns with the intended changes to the billing model.

  • 56-70: The handlePayClick function no longer accepts a selectedChatsLimitIndex parameter, and the additionalChats and isYearly fields have been removed from the newSubscription object. This aligns with the removal of additional chats and yearly billing options. However, ensure that the removal of these fields does not affect other parts of the code that may rely on them.

  • 105-124: The Switch component for switching between monthly and yearly billing has been removed. The StarterPlanPricingCard and ProPlanPricingCard components no longer receive the workspace and currentSubscription props, and the onPayClick prop now only passes the plan type. The isYearly prop has also been removed. These changes align with the removal of additional chats and yearly billing options. However, ensure that the removal of these props does not affect other parts of the code that may rely on them.

packages/schemas/features/billing/subscription.ts (1)
  • 1-10: The isYearly field has been removed and replaced with currentBillingPeriod which is an object containing start and end dates. Ensure that all references to isYearly in the codebase have been updated or removed, and that the new currentBillingPeriod field is properly utilized where necessary.
-  isYearly: z.boolean(),
+  currentBillingPeriod: z.object({
+    start: z.date(),
+    end: z.date(),
+  }),
apps/builder/src/features/dashboard/components/DashboardPage.tsx (3)
  • 18-18: The import path for guessIfUserIsEuropean has been changed from @typebot.io/lib/pricing to @typebot.io/lib/billing/guessIfUserIsEuropean. Ensure that the new path is correct and the function is available at this location.

  • 33-43: The isYearly and chats query parameters are no longer being destructured from router.query. If these parameters are no longer used, ensure that they have been removed from all places where they were previously set. If they are still required, they should be included in the destructuring assignment.

  • 51-55: The additionalChats and isYearly properties have been removed from the setPreCheckoutPlan function call. If these properties are no longer used, ensure that they have been removed from the setPreCheckoutPlan function definition and all places where they were previously used. If they are still required, they should be included in the function call.

apps/builder/src/pages/api/workspaces/[workspaceId]/invitations.ts (2)
  • 8-14: The import statement for getSeatsLimit has been added and isSeatsLimitReached has been removed. Ensure that the getSeatsLimit function is correctly implemented and returns the expected values. Also, verify that all instances of isSeatsLimitReached have been replaced with getSeatsLimit throughout the codebase.

  • 37-43: The getSeatsLimit function is now being used to check if the seats limit has been reached. This is a change from the previous implementation where isSeatsLimitReached was used. Ensure that getSeatsLimit is correctly checking the seats limit and that the logic for checking if the seats limit has been reached is correct.

packages/lib/billing/getChatsLimit.ts (1)
  • 6-19: The function getChatsLimit has been updated to handle different types of plans. It returns -1 for unlimited, lifetime, and offered plans, and for custom plans, it returns the customChatsLimit if it exists, otherwise -1. For all other plans, it returns the limit from the chatsLimits constant. Ensure that the chatsLimits constant includes all the necessary plans and their corresponding limits.
packages/emails/src/emails/AlmostReachedChatsLimitEmail.tsx (2)
  • 9-13: The AlmostReachedChatsLimitEmailProps type has been updated to include workspaceName and remove url. Ensure that all instances where AlmostReachedChatsLimitEmail is used have been updated to reflect this change.

  • 24-27: The AlmostReachedChatsLimitEmail function now takes workspaceName as a prop. Ensure that this prop is being correctly passed wherever this function is used.

packages/scripts/checkAndReportChatsUsage.ts (10)
  • 16-16: The createId function from @paralleldrive/cuid2 is used for generating idempotency keys for Stripe API calls. Ensure that this function generates sufficiently unique keys to prevent accidental reuse.

  • 33-33: The WorkspaceForDigest type has been modified. Ensure that all uses of this type throughout the codebase have been updated to match the new structure.

  • 36-60: The ResultWithWorkspace type has been modified. Ensure that all uses of this type throughout the codebase have been updated to match the new structure.

  • 62-62: The function checkAndReportChatsUsage has been introduced. Ensure that it is being called appropriately in the codebase.

  • 76-82: The where clause in the prisma.result.groupBy query has been updated to include a lt condition for createdAt. Ensure that this change does not unintentionally exclude any results that should be included.

  • 99-115: The select clause in the prisma.workspace.findUnique query has been updated. Ensure that all uses of the returned data match the new structure.

  • 144-144: The function reportUsageToStripe is being called. Ensure that it is being called with the correct arguments and that the Stripe API keys are correctly configured.

  • 193-197: The sendAlmostReachedChatsLimitEmail function is being called with a new set of arguments. Ensure that the function has been updated to handle these new arguments.

  • 207-210: The condition for automatically quarantining a workspace has been changed. Ensure that this new condition is appropriate and does not unintentionally quarantine workspaces.

  • 234-295: The reportUsageToStripe function has been introduced. Ensure that it is correctly reporting usage to Stripe and handling any errors that may occur during this process.

apps/docs/docs/self-hosting/configuration.md (1)
  • 228-242: The new hunk simplifies the Stripe configuration by removing the yearly pricing options and additional storage pricing options. This aligns with the PR summary's mention of removing additional chats and yearly billing options. However, ensure that these changes are reflected in all parts of the application that interact with Stripe, including front-end components, back-end services, and any scripts or utilities that use these environment variables. Also, make sure to update any documentation or user guides that reference these options.
apps/landing-page/components/PricingPage/ProPlanCard.tsx (5)
  • 16-18: The onChatsTiersClick prop is introduced but it's not clear what it does from the context. Ensure that it's properly documented and used correctly in the parent component.

  • 21-77: The new hunk simplifies the ProPlanCard component by removing the state and the dropdown menu for selecting the chats limit. The price and the number of chats per month are now hardcoded. This change simplifies the component but reduces its flexibility. If the pricing or the number of chats per month changes in the future, the code will need to be updated.

  • 33-43: The tooltip explaining what a chat is remains unchanged. This is good for maintaining consistency in the user experience.

  • 47-54: The "See tiers" button is introduced. It's not clear from the context what it does. Ensure that it's linked to the correct functionality and that it provides a good user experience.

  • 65-75: The subscription link is updated to remove the query parameters for the chats limit and the subscription type. This change simplifies the subscription process but reduces its flexibility. If the subscription process requires these parameters in the future, the code will need to be updated.

packages/lib/billing/formatPrice.ts (1)
  • 1-21: The formatPrice function assumes that the user is either European or American based on the guessIfUserIsEuropean function. This might not be accurate for users from other regions. Consider adding more options or a more sophisticated way to guess the user's locale. Also, the function defaults to 'usd' if the currency is not provided. This might not be the desired behavior in all cases. Consider making the currency a required parameter or defaulting to a more neutral currency.
-  currency?: 'eur' | 'usd'
+  currency: 'eur' | 'usd'
apps/builder/src/features/billing/api/updateSubscription.ts (7)
  • 5-14: The import of getChatsLimit and chatPriceIds from @typebot.io/lib/pricing and ./getSubscription respectively, and getUsage from @typebot.io/lib/api/getUsage have been removed. Ensure that these functions are not required anymore in the new implementation.

  • 25-29: The additionalChats and isYearly parameters have been removed from the input schema. Ensure that all calls to this function throughout the codebase have been updated to match the new signature.

  • 38-41: The additionalChats and isYearly parameters have been removed from the input object. Ensure that these parameters are not required anymore in the new implementation.

  • 78-106: The logic for determining the current plan and usage items in the subscription has been updated. The code now checks if the item price ID matches the environment variables STRIPE_STARTER_PRICE_ID or STRIPE_PRO_PRICE_ID for the plan item, and STRIPE_STARTER_CHATS_PRICE_ID or STRIPE_PRO_CHATS_PRICE_ID for the usage item. This is a change from the previous implementation where the code checked if the item price product ID was included in the env.STRIPE_STARTER_PRODUCT_ID or env.STRIPE_PRO_PRODUCT_ID for the plan item, and if the item price ID was included in chatPriceIds for the additional chats item. Ensure that the new logic correctly identifies the plan and usage items in the subscription.

  • 109-133: The logic for updating the subscription has been updated. The code now checks if the plan is 'STARTER' and if so, counts the total chats used during the current billing period. If the total chats used is greater than or equal to 4000, it throws an error. This is a change from the previous implementation where the code updated the subscription items and set the proration behavior to 'always_invoice' without any checks. Ensure that the new logic correctly updates the subscription and handles the case where the total chats used is greater than or equal to 4000.

  • 146-152: The logic for updating the workspace in the database has been simplified. The code now sets the plan and isQuarantined flag without any checks. This is a change from the previous implementation where the code checked if the workspace was quarantined and updated the isQuarantined flag based on the new chats limit and usage. Ensure that the new logic correctly updates the workspace and that the removal of the checks does not introduce any issues.

  • 158-162: The additionalChatsIndex parameter has been removed from the telemetry event data. Ensure that this parameter is not required anymore in the new implementation.

packages/scripts/migrateSubscriptionsToUsageBased.ts (16)
  • 1-4: The script imports necessary modules and functions. It imports PrismaClient from @typebot.io/prisma for database operations, promptAndSetEnvironment from ./utils for setting up the environment, Stripe from stripe for Stripe operations, and createId from @paralleldrive/cuid2 for generating unique IDs.

  • 6-8: The script defines an asynchronous function migrateSubscriptionsToUsageBased and initializes a new Prisma client. The function is immediately invoked at the end of the script.

  • 10-16: The script checks if the environment variables STRIPE_STARTER_CHATS_PRICE_ID and STRIPE_PRO_CHATS_PRICE_ID are set. If not, it throws an error. This is a good practice to ensure that necessary environment variables are set before the script proceeds.

  • 18-39: The script fetches all workspaces with a paid plan (either 'PRO' or 'STARTER') that are not suspended. It selects the plan, name, id, stripeId, isQuarantined, and members of each workspace. This is a good practice to fetch only the necessary data.

  • 41-43: The script initializes a new Stripe client with the secret key from the environment variables and sets the API version to '2022-11-15'.

  • 45-47: The script sets todayMidnight to the current date at midnight UTC. This is used later to filter results based on their creation date.

  • 48-214: The script iterates over each workspace with a paid plan. For each workspace, it logs the workspace details, checks if the workspace has a Stripe ID, fetches the workspace's subscriptions from Stripe, finds the current subscription, checks if the current subscription includes a 'STARTER' or 'PRO' plan, updates the subscription to include a 'STARTER_CHATS' or 'PRO_CHATS' price based on the workspace's plan, counts the total results for the workspace, checks if the workspace has more than 4000 chats and upgrades it to the 'PRO' plan if necessary, finds the subscription item for the 'STARTER_CHATS' or 'PRO_CHATS' price, creates a usage record for the subscription item with the total results as the quantity, and updates the workspace to not be quarantined if it was quarantined.

  • 48-72: The script logs the workspace details and checks if the workspace has a Stripe ID. If not, it skips the workspace. This is a good practice to ensure that only workspaces with a Stripe ID are processed.

  • 60-72: The script fetches the workspace's subscriptions from Stripe and finds the current subscription. If there is no current subscription, it logs a message and skips the workspace. This is a good practice to ensure that only workspaces with a current subscription are processed.

  • 74-86: The script checks if the current subscription includes a 'STARTER' or 'PRO' plan. If not, it logs a message and skips the workspace. This is a good practice to ensure that only workspaces with a 'STARTER' or 'PRO' plan are processed.

  • 88-138: The script updates the subscription to include a 'STARTER_CHATS' or 'PRO_CHATS' price based on the workspace's plan. It also removes any 'STARTER_YEARLY' or 'PRO_YEARLY' prices and replaces them with a 'STARTER' or 'PRO' price based on the workspace's plan. This is a good practice to ensure that the subscription includes the correct prices.

  • 140-149: The script counts the total results for the workspace. It filters the results based on their typebot's workspace ID, whether they have started, and their creation date. This is a good practice to count only the relevant results.

  • 151-179: The script checks if the workspace has more than 4000 chats and upgrades it to the 'PRO' plan if necessary. It updates the subscription to include the 'PRO' price and updates the workspace's plan in the database. This is a good practice to ensure that workspaces with more than 4000 chats are upgraded to the 'PRO' plan.

  • 181-204: The script finds the subscription item for the 'STARTER_CHATS' or 'PRO_CHATS' price, creates a usage record for the subscription item with the total results as the quantity, and sets an idempotency key to ensure that the operation is idempotent. This is a good practice to ensure that the usage record is created correctly and that the operation is idempotent.

  • 206-213: The script updates the workspace to not be quarantined if it was quarantined. This is a good practice to ensure that workspaces are not quarantined after the migration.

  • 217-217: The script immediately invokes the migrateSubscriptionsToUsageBased function. This is a good practice to ensure that the function is invoked when the script is run

packages/lib/billing/constants.ts Show resolved Hide resolved
packages/scripts/createChatsPrices.ts Show resolved Hide resolved
usagePercent: number
chatsLimit: number
url: string
}

const now = new Date()
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The now constant is defined but not used in this hunk. If it's not used elsewhere in the code, consider removing it to avoid unnecessary variable declarations.

packages/scripts/checkAndReportChatsUsage.ts Show resolved Hide resolved
packages/lib/billing/formatPrice.ts Show resolved Hide resolved
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Status

Actionable comments generated: 25

Commits Files that changed from the base of the PR and between d4041c7 and eb38440.
Files ignored due to filter (5)
  • apps/docs/openapi/builder/spec.json
  • apps/docs/openapi/chat/spec.json
  • packages/lib/package.json
  • packages/scripts/package.json
  • pnpm-lock.yaml
Files selected for processing (44)
  • .github/workflows/check-and-report-chats-usage.yml (2 hunks)
  • apps/builder/src/features/billing/api/createCheckoutSession.ts (6 hunks)
  • apps/builder/src/features/billing/api/getSubscription.ts (2 hunks)
  • apps/builder/src/features/billing/api/getUsage.ts (3 hunks)
  • apps/builder/src/features/billing/api/listInvoices.ts (1 hunks)
  • apps/builder/src/features/billing/api/updateSubscription.ts (7 hunks)
  • apps/builder/src/features/billing/billing.spec.ts (4 hunks)
  • apps/builder/src/features/billing/components/ChangePlanForm.tsx (5 hunks)
  • apps/builder/src/features/billing/components/ChatsProTiersModal.tsx (1 hunks)
  • apps/builder/src/features/billing/components/FeaturesList.tsx (1 hunks)
  • apps/builder/src/features/billing/components/PreCheckoutModal.tsx (1 hunks)
  • apps/builder/src/features/billing/components/ProPlanPricingCard.tsx (1 hunks)
  • apps/builder/src/features/billing/components/StarterPlanPricingCard.tsx (1 hunks)
  • apps/builder/src/features/billing/components/UsageProgressBars.tsx (3 hunks)
  • apps/builder/src/features/dashboard/components/DashboardPage.tsx (3 hunks)
  • apps/builder/src/features/results/components/ResultsPage.tsx (2 hunks)
  • apps/builder/src/features/workspace/components/MembersList.tsx (3 hunks)
  • apps/builder/src/pages/api/stripe/webhook.ts (5 hunks)
  • apps/builder/src/pages/api/workspaces/[workspaceId]/invitations.ts (2 hunks)
  • apps/builder/src/test/utils/databaseActions.ts (1 hunks)
  • apps/docs/docs/self-hosting/configuration.md (1 hunks)
  • apps/landing-page/components/PricingPage/ChatsProTiersModal.tsx (1 hunks)
  • apps/landing-page/components/PricingPage/Faq.tsx (1 hunks)
  • apps/landing-page/components/PricingPage/FreePlanCard.tsx (2 hunks)
  • apps/landing-page/components/PricingPage/PlanComparisonTables.tsx (4 hunks)
  • apps/landing-page/components/PricingPage/PricingCard/index.tsx (1 hunks)
  • apps/landing-page/components/PricingPage/ProPlanCard.tsx (1 hunks)
  • apps/landing-page/components/PricingPage/StarterPlanCard.tsx (2 hunks)
  • apps/landing-page/pages/pricing.tsx (3 hunks)
  • packages/bot-engine/queries/findPublicTypebot.ts (1 hunks)
  • packages/emails/src/emails/AlmostReachedChatsLimitEmail.tsx (3 hunks)
  • packages/env/env.ts (1 hunks)
  • packages/lib/billing/constants.ts (1 hunks)
  • packages/lib/billing/formatPrice.ts (1 hunks)
  • packages/lib/billing/getChatsLimit.ts (1 hunks)
  • packages/lib/billing/getSeatsLimit.ts (1 hunks)
  • packages/lib/billing/guessIfUserIsEuropean.ts (1 hunks)
  • packages/schemas/features/billing/subscription.ts (1 hunks)
  • packages/schemas/features/telemetry.ts (1 hunks)
  • packages/scripts/checkAndReportChatsUsage.ts (8 hunks)
  • packages/scripts/cleanDatabase.ts (1 hunks)
  • packages/scripts/createChatsPrices.ts (1 hunks)
  • packages/scripts/inspectUser.ts (2 hunks)
  • packages/scripts/migrateSubscriptionsToUsageBased.ts (1 hunks)
Files skipped from review due to trivial changes (20)
  • .github/workflows/check-and-report-chats-usage.yml
  • apps/builder/src/features/billing/api/createCheckoutSession.ts
  • apps/builder/src/features/billing/api/getSubscription.ts
  • apps/builder/src/features/billing/api/listInvoices.ts
  • apps/builder/src/features/billing/components/ChangePlanForm.tsx
  • apps/builder/src/features/billing/components/FeaturesList.tsx
  • apps/builder/src/features/billing/components/PreCheckoutModal.tsx
  • apps/builder/src/features/billing/components/ProPlanPricingCard.tsx
  • apps/builder/src/features/billing/components/StarterPlanPricingCard.tsx
  • apps/builder/src/features/billing/components/UsageProgressBars.tsx
  • apps/builder/src/features/results/components/ResultsPage.tsx
  • apps/builder/src/features/workspace/components/MembersList.tsx
  • apps/landing-page/components/PricingPage/Faq.tsx
  • apps/landing-page/components/PricingPage/FreePlanCard.tsx
  • apps/landing-page/components/PricingPage/StarterPlanCard.tsx
  • packages/bot-engine/queries/findPublicTypebot.ts
  • packages/lib/billing/getSeatsLimit.ts
  • packages/schemas/features/telemetry.ts
  • packages/scripts/cleanDatabase.ts
  • packages/scripts/inspectUser.ts
Additional comments (Suppressed): 58
apps/landing-page/components/PricingPage/PricingCard/index.tsx (1)
  • 14-14: The formatPrice function has been moved from @typebot.io/lib/pricing to @typebot.io/lib/billing/formatPrice. Ensure that this change does not break any other parts of the code that rely on the old import path.
apps/builder/src/test/utils/databaseActions.ts (1)
  • 18-22: The function signature for addSubscriptionToWorkspace has changed. The metadata parameter no longer includes additionalChatsIndex. Ensure that all calls to this function throughout the codebase have been updated to match the new signature. Also, verify that the removal of additionalChatsIndex does not affect the functionality of the system, especially the billing logic.
apps/landing-page/components/PricingPage/PlanComparisonTables.tsx (4)
  • 19-37: The import paths for chatsLimits, prices, seatsLimits, and formatPrice have been updated. Ensure that these paths are correct and the imported modules exist at these locations.

  • 54-76: The way additional chats are calculated and displayed has been changed. Previously, the number of additional chats was calculated based on the difference between the total included chats in the current and next tier. Now, it's a fixed value of 500 chats for $10. Ensure this change aligns with the new billing model.

  • 82-88: The way the number of seats for the Starter and Pro plans are displayed has been changed. Previously, the number of seats was fetched from seatsLimit.STARTER.totalIncluded and seatsLimit.PRO.totalIncluded. Now, it's fetched from seatsLimits.STARTER and seatsLimits.PRO. Ensure these new properties exist and contain the correct values.

  • 271-284: A new feature, "WhatsApp integration", has been added to the Pro plan. Ensure that this feature is implemented and available for Pro users.

apps/builder/src/features/dashboard/components/DashboardPage.tsx (3)
  • 18-18: The function guessIfUserIsEuropean has been moved from @typebot.io/lib/pricing to @typebot.io/lib/billing/guessIfUserIsEuropean. Ensure that this change does not break any other parts of the code that might be using this function.

  • 33-43: The isYearly and chats query parameters have been removed from the router query destructuring. This change aligns with the PR summary stating that the billing system has been simplified and the yearly billing options and additional chats have been removed. However, ensure that these changes do not affect other parts of the code that might be using these query parameters.

  • 51-55: The additionalChats and isYearly properties have been removed from the setPreCheckoutPlan function call. This change aligns with the PR summary stating that the billing system has been simplified and the yearly billing options and additional chats have been removed. However, ensure that these changes do not affect other parts of the code that might be using these properties.

apps/builder/src/features/billing/billing.spec.ts (4)
  • 116-138: The new hunk has simplified the plan upgrade process by removing the selection of additional chats and yearly billing options. The new code directly upgrades to the starter plan with a fixed price. Ensure that the new billing model aligns with the business requirements and that the STRIPE_STARTER_PRICE_ID and STRIPE_STARTER_CHATS_PRICE_ID are correctly configured in the environment variables.

  • 143-151: The upgrade process to the PRO plan has been simplified. The new code directly upgrades to the PRO plan without selecting additional options. Ensure that the new billing model aligns with the business requirements.

  • 156-166: The new hunk has updated the expected invoice amounts to reflect the new billing model. Ensure that the new invoice amounts align with the updated pricing structure. Also, the new code expects the text "$50.00" to be visible, which was not present in the old hunk. Verify that this is the expected behavior.

  • 188-193: The new hunk has updated the expected invoice amounts to reflect the new billing model. Ensure that the new invoice amounts align with the updated pricing structure. Also, the new code expects the text "$50.00" to be visible, which was not present in the old hunk. Verify that this is the expected behavior.

apps/landing-page/components/PricingPage/ProPlanCard.tsx (2)
  • 16-18: The onChatsTiersClick prop is introduced but it's not clear what it does from the context. Ensure that it's properly documented and used correctly.

  • 66-73: The subscription link is hardcoded. If the subscription URL changes, this could lead to broken links. Consider fetching this URL from a centralized configuration or an API.

Overall, the changes in this hunk simplify the ProPlanCard component by removing the dynamic chats limit selection and the yearly/monthly subscription option. The component now displays a fixed price and a fixed number of chats per month for the "Pro" plan. The subscription link no longer includes the chats and isYearly parameters. The onChatsTiersClick prop is introduced, which is used to handle clicks on the "See tiers" button. The changes align with the PR's goal of transitioning to a usage-based billing model.

apps/landing-page/pages/pricing.tsx (3)
  • 26-33: The useDisclosure hook is used to control the visibility of the ChatsProTiersModal component. This is a change from the previous version where a useState hook was used to toggle between monthly and yearly pricing options. Ensure that the ChatsProTiersModal component is correctly implemented and that the isOpen, onOpen, and onClose functions are used appropriately.

  • 90-102: The ProPlanCard component now takes a onChatsTiersClick prop which is set to the onOpen function from the useDisclosure hook. This is a change from the previous version where the ProPlanCard and StarterPlanCard components took an isYearly prop. Ensure that the onChatsTiersClick prop is correctly used in the ProPlanCard component to open the ChatsProTiersModal when required.

  • 107-108: The PlanComparisonTables component now takes a onChatsTiersClick prop which is set to the onOpen function from the useDisclosure hook. This is a change from the previous version where the PlanComparisonTables component took no props. Ensure that the onChatsTiersClick prop is correctly used in the PlanComparisonTables component to open the ChatsProTiersModal when required.

packages/emails/src/emails/AlmostReachedChatsLimitEmail.tsx (3)
  • 1-15: The import of ComponentProps from 'react' has been removed. If this is not used elsewhere in the code, this change is fine. However, if it's used, this could cause a breaking change. Also, the url prop has been removed from AlmostReachedChatsLimitEmailProps and workspaceName has been added. Ensure that all calls to AlmostReachedChatsLimitEmail have been updated to match the new props.

  • 21-29: The url prop is no longer passed to AlmostReachedChatsLimitEmail. If this prop is not used elsewhere in the component, this change is fine. However, if it's used, this could cause a breaking change. Ensure that all calls to AlmostReachedChatsLimitEmail have been updated to match the new props.

  • 40-61: The email content has been updated to reflect the new usage-based billing system. The "Upgrade workspace" button has been removed. Ensure that this change aligns with the desired user experience. Also, the sentence "reach out to ." at the end of line 57 seems to be incomplete. It should probably include a contact method or person.

-               annual commitment, reach out to .
+               annual commitment, reach out to [contact method/person].
apps/builder/src/pages/api/stripe/webhook.ts (5)
  • 46-67: The new hunk removes the additionalChats field from the metadata and the additionalChatsIndex field from the workspace data. Ensure that this change is intentional and that it doesn't break any existing functionality that relies on these fields. Also, verify that all calls to this function have been updated to match the new structure.

  • 83-88: The new hunk removes the additionalChatsIndex field from the user data. Ensure that this change is intentional and that it doesn't break any existing functionality that relies on this field. Also, verify that all calls to this function have been updated to match the new structure.

  • 117-122: The new hunk removes the additionalChatsIndex field from the user data. Ensure that this change is intentional and that it doesn't break any existing functionality that relies on this field. Also, verify that all calls to this function have been updated to match the new structure.

  • 145-150: The new hunk removes the additionalChatsIndex field from the workspace data. Ensure that this change is intentional and that it doesn't break any existing functionality that relies on this field. Also, verify that all calls to this function have been updated to match the new structure.

  • 168-173: The new hunk removes the additionalChatsIndex field from the user data. Ensure that this change is intentional and that it doesn't break any existing functionality that relies on this field. Also, verify that all calls to this function have been updated to match the new structure.

apps/builder/src/pages/api/workspaces/[workspaceId]/invitations.ts (2)
  • 8-14: The import statement for getSeatsLimit has been added and isSeatsLimitReached has been removed. Ensure that the getSeatsLimit function is correctly implemented and returns the expected results. Also, verify that all instances of isSeatsLimitReached have been replaced with getSeatsLimit throughout the codebase.

  • 37-43: The check for the seats limit has been simplified. The getSeatsLimit function is now directly compared with the sum of existingMembersCount and existingInvitationsCount. This change simplifies the code and improves readability. However, ensure that the getSeatsLimit function correctly handles all edge cases that were previously handled by the isSeatsLimitReached function.

apps/docs/docs/self-hosting/configuration.md (2)
  • 228-241: The new hunk simplifies the Stripe configuration by removing the yearly pricing options and additional storage pricing options. This aligns with the PR summary stating that the billing system is transitioning to a usage-based model and removing yearly billing options. However, ensure that these changes are reflected in the application's billing logic and UI components. Also, verify that the removed parameters are not used elsewhere in the codebase.

  • 235-238: The new parameters STRIPE_STARTER_PRICE_ID and STRIPE_PRO_PRICE_ID replace the old parameters STRIPE_STARTER_PRODUCT_ID, STRIPE_STARTER_MONTHLY_PRICE_ID, STRIPE_PRO_PRODUCT_ID, and STRIPE_PRO_MONTHLY_PRICE_ID. Make sure to update all references to these parameters in the codebase. Also, ensure that the new parameters are correctly set in the environment configuration.

apps/builder/src/features/billing/api/getUsage.ts (8)
  • 3-10: The new hunk introduces the import of env from @typebot.io/env and Stripe from stripe. Ensure that these packages are installed and the environment variables are correctly set up for Stripe.

  • 21-35: The new hunk modifies the workspace query to select additional fields stripeId and plan. Ensure that these fields exist in the workspace schema and are populated correctly.

  • 50-73: The new hunk introduces a conditional check for the existence of STRIPE_SECRET_KEY, workspace.stripeId, and the workspace plan. If any of these conditions are not met, the function falls back to the previous logic of counting chats from the start of the current month. This is a good fallback mechanism.

  • 75-77: The new hunk initializes a new Stripe instance with the STRIPE_SECRET_KEY and a specific API version. Ensure that the API version is compatible with the Stripe functions used in the code.

  • 79-86: The new hunk retrieves a list of subscriptions for the workspace's Stripe customer ID and filters for the most recent active or past due subscription. Ensure that the Stripe customer ID is correctly linked to the workspace and that the subscription statuses are handled correctly.

  • 88-92: The new hunk throws an error if no current subscription is found for the workspace. This is a good error handling mechanism.

  • 94-102: The new hunk modifies the totalChatsUsed calculation to count chats from the start of the current subscription period. This aligns with the new usage-based billing model.

  • 104-107: The new hunk returns an additional resetsAt field indicating the end of the current subscription period. This is a useful addition for the UI to display when the usage count will reset.

packages/env/env.ts (1)
  • 140-147: The environment variables related to Stripe pricing have been simplified, removing the yearly and storage-related variables. This aligns with the PR summary stating that the billing system has been simplified and the yearly billing options have been removed. Ensure that the new environment variables are correctly set in the deployment environment and that the removed variables are no longer in use anywhere in the codebase.
packages/schemas/features/billing/subscription.ts (1)
  • 1-10: The isYearly property has been replaced with a currentBillingPeriod object that contains start and end dates. This change aligns with the transition to a usage-based billing model. Ensure that all references to the isYearly property in the codebase have been updated to use the new currentBillingPeriod object.
packages/lib/billing/constants.ts (1)
  • 165-165: The unit_amount and unit_amount_decimal fields are used to define the price per unit for the final tier. Ensure that the logic for calculating the total price correctly handles these fields.
apps/builder/src/features/billing/api/updateSubscription.ts (7)
  • 5-14: The import of isDefined, getChatsLimit, chatPriceIds, getUsage, and priceIds has been removed. Ensure that these are no longer needed in the codebase or have been replaced with equivalent functionality.

  • 25-29: The additionalChats and isYearly parameters have been removed from the input schema. Ensure that all calls to this function throughout the codebase have been updated to match the new signature.

  • 38-41: The additionalChats and isYearly parameters have been removed from the input object. Ensure that these are no longer needed in the codebase or have been replaced with equivalent functionality.

  • 78-106: The logic for determining the current plan and usage items in the subscription has been updated. The code now uses env.STRIPE_STARTER_PRICE_ID and env.STRIPE_PRO_PRICE_ID to find the current plan item, and env.STRIPE_STARTER_CHATS_PRICE_ID and env.STRIPE_PRO_CHATS_PRICE_ID to find the current usage item. Ensure that these changes are consistent with the updated billing model.

  • 109-133: The logic for updating the subscription has been updated. The code now checks if the total chats used is greater than or equal to 4000 before allowing a downgrade to the Starter plan. The proration behavior is also now dependent on the plan. Ensure that these changes are consistent with the updated billing model.

  • 146-152: The additionalChatsIndex and isYearly properties have been removed from the workspace update data. Ensure that these are no longer needed in the codebase or have been replaced with equivalent functionality.

  • 158-162: The additionalChatsIndex property has been removed from the telemetry event data. Ensure that this is no longer needed in the codebase or has been replaced with equivalent functionality.

packages/scripts/migrateSubscriptionsToUsageBased.ts (4)
  • 1-17: Ensure that all necessary environment variables are set before running this script. The script will fail if any of the Stripe-related environment variables are missing.

  • 173-203: The script automatically upgrades a workspace to the PRO plan if it has more than 4000 chats. This could potentially lead to unexpected billing for the customer. Ensure that this is communicated clearly to the customer.

  • 205-228: The script creates a usage record for the subscription item. Ensure that the createId() function generates a unique idempotency key for each call to prevent duplicate records.

  • 230-237: The script sets isQuarantined to false for the workspace. Ensure that this is the intended behavior and that it doesn't have unintended side effects.

packages/scripts/checkAndReportChatsUsage.ts (6)
  • 4-29: The import statements have been updated to reflect the changes in the billing system. The getChatsLimit function has been moved from @typebot.io/lib/pricing to @typebot.io/lib/billing/getChatsLimit. The isDefined function has been replaced with isDefined, isEmpty from @typebot.io/lib. The additionalChatsIndex property has been removed from the WorkspaceForDigest type. Ensure that these changes are consistent across the codebase.

  • 33-72: > Note: This review was outside of the patch, so it was mapped to the patch with the greatest overlap. Original lines [33-82]

The checkAndReportChatsUsage function has been introduced to replace the sendTotalResultsDigest function. This function checks and reports chat usage to Stripe. It first sets the environment to 'production', then retrieves results from the last hour. The hourAgo variable has been replaced with zeroedMinutesHour and hourAgo, which are set to the start of the previous hour. The where clause in the prisma.result.groupBy call has been updated to include a lt: zeroedMinutesHour condition. Ensure that this change in time calculation does not affect the accuracy of the results.

  • 99-117: The select clause in the prisma.workspace.findUnique call has been updated. The name and stripeId properties have been added, and the additionalChatsIndex property has been removed. Ensure that these changes are consistent with the updated database schema.

  • 141-165: The reportUsageToStripe function has been called with resultsWithWorkspaces as an argument. The newResultsCollectedEvents array has been created to store telemetry events for new results. The sendTelemetryEvents function has been called with events.concat(newResultsCollectedEvents) as an argument. Ensure that these changes are consistent with the updated telemetry system.

  • 193-199: > Note: This review was outside of the patch, so it was mapped to the patch with the greatest overlap. Original lines [193-210]

The sendAlertIfLimitReached function has been updated. The url property has been removed from the sendAlmostReachedChatsLimitEmail call. The condition for automatically quarantining a workspace has been changed from totalChatsUsed > chatsLimit * 3 to totalChatsUsed > chatsLimit * 1.5. Ensure that these changes are consistent with the updated billing system and the new usage-based billing model.

  • 234-295: The reportUsageToStripe function has been introduced to report chat usage to Stripe. It first checks if the STRIPE_SECRET_KEY environment variable is set, then initializes a new Stripe client. It then iterates over the resultsWithWorkspaces array, filtering out workspaces with the 'STARTER' or 'PRO' plan. For each result, it checks if the workspace has a stripeId, retrieves the workspace's subscriptions from Stripe, and finds the current subscription. It then finds the subscription item with the STRIPE_STARTER_CHATS_PRICE_ID or STRIPE_PRO_CHATS_PRICE_ID price ID. Finally, it creates a usage record for the subscription item with the totalResultsYesterday quantity. Ensure that these changes are consistent with the updated billing system and the new usage-based billing model.

Comment on lines +27 to +91
export const ChatsProTiersModal = ({ isOpen, onClose }: Props) => {
return (
<Modal isOpen={isOpen} onClose={onClose} size="xl">
<ModalOverlay />
<ModalContent>
<ModalHeader>
<Heading size="lg">Chats pricing table</Heading>
</ModalHeader>
<ModalCloseButton />
<ModalBody as={Stack} spacing="6">
<TableContainer>
<Table variant="simple">
<Thead>
<Tr>
<Th isNumeric>Max chats</Th>
<Th isNumeric>Price per month</Th>
<Th isNumeric>Price per 1k chats</Th>
</Tr>
</Thead>
<Tbody>
{proChatTiers.map((tier, index) => {
const pricePerMonth =
proChatTiers
.slice(0, index + 1)
.reduce(
(acc, slicedTier) =>
acc + (slicedTier.flat_amount ?? 0),
0
) / 100
return (
<Tr key={tier.up_to}>
<Td isNumeric>
{tier.up_to === 'inf'
? '2,000,000+'
: tier.up_to.toLocaleString()}
</Td>
<Td isNumeric>
{index === 0 ? 'included' : formatPrice(pricePerMonth)}
</Td>
<Td isNumeric>
{index === proChatTiers.length - 1
? formatPrice(4.42, { maxFractionDigits: 2 })
: index === 0
? 'included'
: formatPrice(
(((pricePerMonth * 100) /
((tier.up_to as number) -
(proChatTiers.at(0)?.up_to as number))) *
1000) /
100,
{ maxFractionDigits: 2 }
)}
</Td>
</Tr>
)
})}
</Tbody>
</Table>
</TableContainer>
</ModalBody>
<ModalFooter />
</ModalContent>
</Modal>
)
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ChatsProTiersModal component is well-structured and follows best practices for React functional components. However, the calculation logic inside the component's render method (lines 48-55 and 67-78) could be moved to a separate utility function to improve readability and maintainability. This would also allow for easier testing of the calculation logic separately from the component rendering.

-                  const pricePerMonth =
-                    proChatTiers
-                      .slice(0, index + 1)
-                      .reduce(
-                        (acc, slicedTier) =>
-                          acc + (slicedTier.flat_amount ?? 0),
-                        0
-                      ) / 100
+                  const pricePerMonth = calculatePricePerMonth(proChatTiers, index)

-                      {index === proChatTiers.length - 1
-                        ? formatPrice(4.42, { maxFractionDigits: 2 })
-                        : index === 0
-                        ? 'included'
-                        : formatPrice(
-                            (((pricePerMonth * 100) /
-                              ((tier.up_to as number) -
-                                (proChatTiers.at(0)?.up_to as number))) *
-                              1000) /
-                              100,
-                            { maxFractionDigits: 2 }
-                          )}
+                      {calculatePricePerKChats(proChatTiers, index, pricePerMonth)}

packages/lib/billing/formatPrice.ts Show resolved Hide resolved
packages/lib/billing/getChatsLimit.ts Show resolved Hide resolved
packages/lib/billing/constants.ts Show resolved Hide resolved
packages/scripts/createChatsPrices.ts Show resolved Hide resolved
packages/scripts/migrateSubscriptionsToUsageBased.ts Outdated Show resolved Hide resolved
Comment on lines 78 to 101
if (!currentSubscription) {
console.log('No current subscription in workspace:', workspace.id)
continue
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If there is no current subscription for a workspace, the script simply logs a message and continues. Consider adding more robust error handling here.

Comment on lines 95 to 130
if (
!currentSubscription.items.data.find(
(item) =>
item.price.id === process.env.STRIPE_STARTER_PRICE_ID ||
item.price.id === process.env.STRIPE_PRO_PRICE_ID
)
) {
console.log(
'Could not find STARTER or PRO plan in items for workspace:',
workspace.id
)
failedWorkspaces.push(workspace)
continue
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The script checks if the workspace has already been migrated to usage-based billing and if it can find the STARTER or PRO plan in the subscription items. If either of these checks fail, the workspace is added to the failedWorkspaces array, but this array is never used. Consider logging or handling these failed workspaces in some way.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Status

Actionable comments generated: 29

Commits Files that changed from the base of the PR and between d4041c7 and 8a23fe9.
Files ignored due to filter (5)
  • apps/docs/openapi/builder/spec.json
  • apps/docs/openapi/chat/spec.json
  • packages/lib/package.json
  • packages/scripts/package.json
  • pnpm-lock.yaml
Files selected for processing (44)
  • .github/workflows/check-and-report-chats-usage.yml (2 hunks)
  • apps/builder/src/features/billing/api/createCheckoutSession.ts (6 hunks)
  • apps/builder/src/features/billing/api/getSubscription.ts (2 hunks)
  • apps/builder/src/features/billing/api/getUsage.ts (3 hunks)
  • apps/builder/src/features/billing/api/listInvoices.ts (1 hunks)
  • apps/builder/src/features/billing/api/updateSubscription.ts (7 hunks)
  • apps/builder/src/features/billing/billing.spec.ts (4 hunks)
  • apps/builder/src/features/billing/components/ChangePlanForm.tsx (5 hunks)
  • apps/builder/src/features/billing/components/ChatsProTiersModal.tsx (1 hunks)
  • apps/builder/src/features/billing/components/FeaturesList.tsx (1 hunks)
  • apps/builder/src/features/billing/components/PreCheckoutModal.tsx (1 hunks)
  • apps/builder/src/features/billing/components/ProPlanPricingCard.tsx (1 hunks)
  • apps/builder/src/features/billing/components/StarterPlanPricingCard.tsx (1 hunks)
  • apps/builder/src/features/billing/components/UsageProgressBars.tsx (3 hunks)
  • apps/builder/src/features/dashboard/components/DashboardPage.tsx (3 hunks)
  • apps/builder/src/features/results/components/ResultsPage.tsx (2 hunks)
  • apps/builder/src/features/workspace/components/MembersList.tsx (3 hunks)
  • apps/builder/src/pages/api/stripe/webhook.ts (5 hunks)
  • apps/builder/src/pages/api/workspaces/[workspaceId]/invitations.ts (2 hunks)
  • apps/builder/src/test/utils/databaseActions.ts (1 hunks)
  • apps/docs/docs/self-hosting/configuration.md (1 hunks)
  • apps/landing-page/components/PricingPage/ChatsProTiersModal.tsx (1 hunks)
  • apps/landing-page/components/PricingPage/Faq.tsx (1 hunks)
  • apps/landing-page/components/PricingPage/FreePlanCard.tsx (2 hunks)
  • apps/landing-page/components/PricingPage/PlanComparisonTables.tsx (4 hunks)
  • apps/landing-page/components/PricingPage/PricingCard/index.tsx (1 hunks)
  • apps/landing-page/components/PricingPage/ProPlanCard.tsx (1 hunks)
  • apps/landing-page/components/PricingPage/StarterPlanCard.tsx (2 hunks)
  • apps/landing-page/pages/pricing.tsx (3 hunks)
  • packages/bot-engine/queries/findPublicTypebot.ts (1 hunks)
  • packages/emails/src/emails/AlmostReachedChatsLimitEmail.tsx (3 hunks)
  • packages/env/env.ts (1 hunks)
  • packages/lib/billing/constants.ts (1 hunks)
  • packages/lib/billing/formatPrice.ts (1 hunks)
  • packages/lib/billing/getChatsLimit.ts (1 hunks)
  • packages/lib/billing/getSeatsLimit.ts (1 hunks)
  • packages/lib/billing/guessIfUserIsEuropean.ts (1 hunks)
  • packages/schemas/features/billing/subscription.ts (1 hunks)
  • packages/schemas/features/telemetry.ts (1 hunks)
  • packages/scripts/checkAndReportChatsUsage.ts (8 hunks)
  • packages/scripts/cleanDatabase.ts (1 hunks)
  • packages/scripts/createChatsPrices.ts (1 hunks)
  • packages/scripts/inspectUser.ts (2 hunks)
  • packages/scripts/migrateSubscriptionsToUsageBased.ts (1 hunks)
Files skipped from review due to trivial changes (21)
  • .github/workflows/check-and-report-chats-usage.yml
  • apps/builder/src/features/billing/api/createCheckoutSession.ts
  • apps/builder/src/features/billing/api/getSubscription.ts
  • apps/builder/src/features/billing/api/listInvoices.ts
  • apps/builder/src/features/billing/components/ChangePlanForm.tsx
  • apps/builder/src/features/billing/components/FeaturesList.tsx
  • apps/builder/src/features/billing/components/PreCheckoutModal.tsx
  • apps/builder/src/features/billing/components/ProPlanPricingCard.tsx
  • apps/builder/src/features/billing/components/StarterPlanPricingCard.tsx
  • apps/builder/src/features/billing/components/UsageProgressBars.tsx
  • apps/builder/src/features/results/components/ResultsPage.tsx
  • apps/builder/src/features/workspace/components/MembersList.tsx
  • apps/builder/src/pages/api/stripe/webhook.ts
  • apps/landing-page/components/PricingPage/Faq.tsx
  • apps/landing-page/components/PricingPage/FreePlanCard.tsx
  • apps/landing-page/components/PricingPage/PricingCard/index.tsx
  • packages/bot-engine/queries/findPublicTypebot.ts
  • packages/lib/billing/getSeatsLimit.ts
  • packages/schemas/features/telemetry.ts
  • packages/scripts/cleanDatabase.ts
  • packages/scripts/inspectUser.ts
Additional comments (Suppressed): 48
apps/builder/src/test/utils/databaseActions.ts (1)
  • 18-22: The function signature for addSubscriptionToWorkspace has changed. The metadata parameter no longer includes additionalChatsIndex. Ensure that all calls to this function throughout the codebase have been updated to match the new signature.
apps/landing-page/components/PricingPage/StarterPlanCard.tsx (2)
  • 1-43: The new hunk simplifies the StarterPlanCard component by removing the dropdown menu for selecting the chats limit and the state for managing the selected limit. The price is now a constant defined in the @typebot.io/lib/billing/constants module, and the chats limit is hardcoded as "2,000 chats/mo". The seats limit is also hardcoded as {seatsLimits.STARTER} seats. This change simplifies the component but reduces its flexibility. If the pricing or limits change in the future, the code will need to be updated. Consider whether this loss of flexibility is acceptable.

  • 48-54: The subscription link no longer includes query parameters for the chats limit and the subscription type. This change simplifies the link but may affect the subscription process if these parameters are used on the server side. Ensure that the server can handle the new link format and that the subscription process works correctly without these parameters.

apps/landing-page/pages/pricing.tsx (3)
  • 6-33: The Switch component and the useState hook have been removed from the imports and the Pricing component. This suggests that the option to switch between monthly and yearly pricing has been removed. Ensure that this change aligns with the new usage-based billing model and that it doesn't negatively impact the user experience.

  • 87-102: The ProPlanCard and PlanComparisonTables components now have an onChatsTiersClick prop. This suggests that a new feature has been added to these components to handle click events related to chat tiers. Ensure that the event handlers are properly implemented and tested.

  • 105-111: The PlanComparisonTables component now has an onChatsTiersClick prop. This suggests that a new feature has been added to handle click events related to chat tiers. This is a duplicate of the previous comment and should be disregarded.

apps/landing-page/components/PricingPage/ProPlanCard.tsx (3)
  • 22-26: The data prop of PricingCard has been simplified. The price is now directly fetched from prices.PRO instead of being computed. Ensure that the prices.PRO value is correctly set and updated when necessary.

  • 27-56: The features list has been simplified. The chat limit selection has been removed and replaced with a static value of "10,000 chats/mo". A button has been added to see extra chat tiers. Ensure that the onChatsTiersClick function is correctly implemented and that the chat tiers are correctly displayed when the button is clicked.

  • 65-75: The href prop of the Button component has been simplified. The subscribePlan query parameter is now always set to Plan.PRO, and the chats and isYearly query parameters have been removed. Ensure that this change does not affect the subscription process and that the Plan.PRO value is correctly handled on the subscription page.

apps/builder/src/features/dashboard/components/DashboardPage.tsx (3)
  • 18-18: The import path for guessIfUserIsEuropean has been changed from @typebot.io/lib/pricing to @typebot.io/lib/billing/guessIfUserIsEuropean. Ensure that the function is correctly moved to the new location and the import path is updated everywhere it's used.

  • 33-43: The chats and isYearly query parameters are removed from the router query destructuring. This aligns with the PR summary stating that additional chats and yearly billing options have been removed. Ensure that these parameters are no longer used anywhere in the codebase.

  • 51-55: The additionalChats and isYearly properties are removed from the setPreCheckoutPlan function call. This aligns with the PR summary stating that additional chats and yearly billing options have been removed. Ensure that these properties are no longer used anywhere in the codebase.

packages/emails/src/emails/AlmostReachedChatsLimitEmail.tsx (3)
  • 1-15: The url prop has been removed from AlmostReachedChatsLimitEmailProps and workspaceName has been added. Ensure that all calls to AlmostReachedChatsLimitEmail have been updated to match the new props. Also, the Button component and MjmlSpacer are no longer imported, ensure that they are not used elsewhere in the code.

  • 21-29: The url prop is no longer used in AlmostReachedChatsLimitEmail. Ensure that the removal of this prop does not affect the functionality of the email.

  • 40-61: The email content has been updated to reflect the new usage-based billing model. The "Upgrade workspace" button has been removed and replaced with a link to the pricing page. Ensure that this change aligns with the new billing model and does not affect the user experience.

apps/landing-page/components/PricingPage/PlanComparisonTables.tsx (4)
  • 19-37: The import paths for chatsLimits, prices, seatsLimits, and formatPrice have been updated. Ensure that these paths are correct and the imported modules exist at these locations. Also, the function signature of PlanComparisonTables has been updated to accept a Props object with onChatsTiersClick function. Make sure all instances of PlanComparisonTables in the codebase are updated to pass this prop.

  • 54-75: The way chat limits and additional chat prices are displayed has been simplified. Ensure that the new chatsLimits and formatPrice values accurately represent the intended chat limits and pricing for each plan. Also, the onChatsTiersClick function is now being used when the "See tiers" button is clicked. Verify that this function performs the expected action.

  • 82-87: The way member seat limits are displayed has been simplified. Ensure that the new seatsLimits values accurately represent the intended seat limits for each plan.

  • 271-281: A new feature, "WhatsApp integration", has been added to the Pro plan. Ensure that this feature is correctly implemented and available only for the Pro plan.

apps/builder/src/pages/api/workspaces/[workspaceId]/invitations.ts (2)
  • 8-14: The import statement for getSeatsLimit has been added and isSeatsLimitReached has been removed. Ensure that the getSeatsLimit function is correctly implemented and returns the expected values. Also, verify that all instances of isSeatsLimitReached have been replaced with getSeatsLimit throughout the codebase.

  • 37-43: The check for the seats limit has been simplified. Instead of passing an object to isSeatsLimitReached, now getSeatsLimit is directly compared with the sum of existingMembersCount and existingInvitationsCount. Ensure that this change does not affect the logic of the seats limit check.

packages/env/env.ts (2)
  • 140-147: The environment variables related to Stripe have been simplified, removing the yearly and storage price IDs and adding new price IDs for the Starter and Pro plans. Ensure that these changes are reflected in the Stripe dashboard and that the new price IDs are correctly set in the environment variables. Also, verify that the billing logic has been updated to use these new price IDs.

  • 148-149: The Stripe public key is now optional. Ensure that the client-side code can handle the absence of this key and that it falls back to a safe default behavior if the key is not provided.

apps/builder/src/features/billing/api/getUsage.ts (7)
  • 3-9: The new hunk introduces Stripe and environment variable imports. Ensure that the Stripe secret key is securely stored and not exposed in any logs or error messages. Also, verify that the Stripe library version is compatible with the rest of the system.

  • 24-24: The output schema of the function has been updated to include a resetsAt field. This field represents the date when the total chats used count resets. Ensure that all parts of the system that use this function are updated to handle this new field.

  • 50-53: The function now checks if the Stripe secret key is present and if the workspace has a Stripe ID and is on either the 'STARTER' or 'PRO' plan. If any of these conditions are not met, it falls back to the old logic of counting chats from the start of the month. This is a good way to maintain backward compatibility while transitioning to the new billing system.

  • 79-86: The function retrieves the list of subscriptions for the workspace's Stripe customer ID and filters for 'past_due' and 'active' subscriptions. It then sorts them by creation date and selects the most recent one. This logic assumes that a workspace will only have one active or past due subscription at a time. If this assumption is not correct, it could lead to incorrect usage calculations.

  • 88-92: The function throws an error if no active or past due subscription is found for the workspace. This is a good practice as it prevents the function from proceeding with potentially incorrect data.

  • 94-102: The function now counts chats from the start of the current subscription period instead of the start of the month. This aligns with the new usage-based billing model.

  • 105-107: The function now returns the date when the current subscription period ends as the resetsAt field. This provides more accurate information to the user about when their chat count will reset.

apps/docs/docs/self-hosting/configuration.md (1)
  • 228-241: The new hunk simplifies the Stripe configuration by removing the yearly pricing and additional storage pricing options. Ensure that these changes align with the new usage-based billing model and that all references to the removed parameters have been updated in the codebase. Also, verify that the new price IDs correspond to the correct prices in the Stripe dashboard.
packages/lib/billing/guessIfUserIsEuropean.ts (1)
  • 1-29: The list of European Union country codes seems to be missing a few countries. Please verify if this is intentional or an oversight. For example, 'GB' for the United Kingdom is not included.
packages/schemas/features/billing/subscription.ts (1)
  • 1-10: The isYearly field has been removed and replaced with currentBillingPeriod which is an object containing start and end dates. This change aligns with the transition to a usage-based billing model. Ensure that all references to the isYearly field in the codebase have been updated or removed, and that the new currentBillingPeriod field is properly utilized where necessary.
apps/builder/src/features/billing/api/updateSubscription.ts (7)
  • 5-14: The import of isDefined and getChatsLimit from @typebot.io/lib and chatPriceIds from ./getSubscription have been removed. Ensure these are no longer needed in the codebase.

  • 25-29: The additionalChats and isYearly parameters have been removed from the input schema. Ensure that all calls to this function throughout the codebase have been updated to match the new signature.

  • 38-41: The additionalChats and isYearly parameters have been removed from the input object. Ensure that all calls to this function throughout the codebase have been updated to match the new signature.

  • 78-106: The logic for determining the current plan and usage items in the subscription has been updated. The code now uses environment variables for the price IDs instead of product IDs and chat price IDs. The logic for constructing the items to update in the subscription has also been simplified, removing the handling for additional chats and yearly billing options. Ensure that these changes align with the new usage-based billing model and that the environment variables are correctly configured.

  • 109-133: The logic for updating the subscription has been updated. The code now checks if the total chats used exceeds a certain limit before downgrading to the Starter plan. The proration behavior for the subscription update has also been updated based on the new plan. Ensure that these changes align with the new usage-based billing model.

  • 146-152: The logic for updating the workspace in the database has been simplified, removing the handling for additional chats and the quarantine check based on the new chats limit. Ensure that these changes align with the new usage-based billing model.

  • 158-162: The telemetry event for the subscription update has been simplified, removing the additional chats index from the data. Ensure that these changes align with the new usage-based billing model.

packages/scripts/checkAndReportChatsUsage.ts (8)
  • 7-8: The import isEmpty is new and not used in the old hunk. Ensure that it is necessary and used appropriately in the code.

  • 19-29: The WorkspaceForDigest type has been modified. The name and chatsLimitSecondEmailSentAt fields have been added, and the additionalChatsIndex field has been removed. Ensure that these changes are reflected in all parts of the code that use this type.

  • 67-69: The zeroedMinutesHour variable is a new addition. It's used to get the time an hour ago with minutes and seconds set to zero. This might change the logic of the code if the previous logic was intended to get results from exactly an hour ago.

  • 76-82: The where clause in the prisma.result.groupBy function has been updated. Previously, it was only checking if createdAt was greater than or equal to hourAgo. Now, it checks if createdAt is less than zeroedMinutesHour and greater than or equal to hourAgo. This change might affect the results returned by the function.

  • 99-115: The select clause in the prisma.workspace.findUnique function has been updated. The name and stripeId fields have been added, and the additionalChatsIndex field has been removed. Ensure that these changes are reflected in all parts of the code that use the results of this function.

  • 144-164: The reportUsageToStripe function is being called with resultsWithWorkspaces as an argument. This is a new addition and it's not clear what this function does from the provided hunks. Ensure that it is implemented correctly and that it doesn't introduce any breaking changes.

  • 207-210: The condition for quarantining a workspace has been changed. Previously, a workspace was quarantined if the total chats used was more than three times the chat limit and the workspace plan was free. Now, a workspace is quarantined if the total chats used is more than 1.5 times the chat limit and the workspace plan is free. This is a significant change and might affect the user experience.

  • 234-295: The reportUsageToStripe function is a new addition. It seems to report the chat usage to Stripe for billing purposes. Ensure that it is implemented correctly and that it doesn't introduce any breaking changes.

packages/lib/billing/constants.ts Show resolved Hide resolved
packages/lib/billing/constants.ts Show resolved Hide resolved
flat_amount: 89400,
},
{
up_to: 'inf',
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The use of 'inf' as a string to represent infinity might cause confusion or errors. Consider using Number.POSITIVE_INFINITY for clarity and to avoid potential type issues.

-    up_to: 'inf',
+    up_to: Number.POSITIVE_INFINITY,

{
up_to: 'inf',
unit_amount_decimal: '0.442',
},
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The unit_amount_decimal is a string. If this value is used in calculations, it should be a number to avoid potential type issues.

-    unit_amount_decimal: '0.442',
+    unit_amount_decimal: 0.442,

starterChatTiers,
} from '@typebot.io/lib/billing/constants'

const chatsProductId = 'prod_MVXtq5sATQzIcM'
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The chatsProductId is hardcoded in the script. If this ID changes in the future, the script will need to be manually updated. Consider moving this to a configuration file or environment variable.

Comment on lines +33 to +47
await stripe.prices.create({
currency: 'usd',
billing_scheme: 'tiered',
recurring: { interval: 'month', usage_type: 'metered' },
tiers: proChatTiers,
tiers_mode: 'volume',
tax_behavior: 'exclusive',
product: chatsProductId,
currency_options: {
eur: {
tax_behavior: 'exclusive',
tiers: proChatTiers,
},
},
})
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The two blocks of code for creating prices for 'starter' and 'pro' chat tiers are almost identical, differing only in the tiers and currency_options.tiers values. This is a violation of the DRY (Don't Repeat Yourself) principle. Consider refactoring this into a separate function that takes the tiers as a parameter.

Comment on lines 76 to 257
{
price:
workspace.plan === 'STARTER'
? starterPriceId
: proPriceId,
quantity: 1,
},
]
return {
id: item.id,
price: item.price.id,
quantity: item.quantity,
deleted: true,
}
}
),
{
price:
workspace.plan === 'STARTER'
? starterChatsPriceId
: proChatsPriceId,
},
],
}
)

const totalResults = await prisma.result.count({
where: {
typebot: { workspaceId: workspace.id },
hasStarted: true,
createdAt: {
gte: new Date(newSubscription.current_period_start * 1000),
lt: todayMidnight,
},
},
})

if (workspace.plan === 'STARTER' && totalResults >= 4000) {
console.log(
'Workspace has more than 4000 chats, automatically upgrading to PRO plan'
)
const currentPlanItemId = newSubscription?.items.data.find((item) =>
[starterPriceId, proPriceId].includes(item.price.id)
)?.id

if (!currentPlanItemId)
throw new Error(`Could not find current plan item ID for workspace`)

await stripe.subscriptions.update(newSubscription.id, {
items: [
{
id: currentPlanItemId,
price: proPriceId,
quantity: 1,
},
],
})

await prisma.workspace.update({
where: { id: workspace.id },
data: {
plan: 'PRO',
},
})
}

const subscriptionItem = newSubscription.items.data.find(
(item) =>
item.price.id === starterChatsPriceId ||
item.price.id === proChatsPriceId
)

if (!subscriptionItem)
throw new Error(
`Could not find subscription item for workspace ${workspace.id}`
)

const idempotencyKey = createId()

console.log('Reporting total results:', totalResults)
await stripe.subscriptionItems.createUsageRecord(
subscriptionItem.id,
{
quantity: totalResults,
timestamp: 'now',
},
{
idempotencyKey,
}
)

if (workspace.isQuarantined) {
await prisma.workspace.update({
where: { id: workspace.id },
data: {
isQuarantined: false,
},
})
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The script uses a for loop to process each workspace. This could be slow if there are many workspaces. Consider using Promise.all to process the workspaces in parallel. This would improve performance.

Comment on lines 98 to 100
console.log('No current subscription in workspace:', workspace.id)
workspacesWithoutSubscription.push(workspace)
continue
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The script pushes workspaces without a current subscription to an array. It would be better to handle these cases immediately instead of storing them for later processing. This would make the script more efficient and easier to understand.

Comment on lines 259 to 267
writeFileSync(
'./failedWorkspaces.json',
JSON.stringify(failedWorkspaces, null, 2)
)

writeFileSync(
'./workspacesWithoutSubscription.json',
JSON.stringify(workspacesWithoutSubscription, null, 2)
)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The script writes the failed workspaces and workspaces without a subscription to files. This could potentially expose sensitive information if the files are not properly secured. Consider encrypting the files or storing the data in a secure database.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Status

Actionable comments generated: 9

Commits Files that changed from the base of the PR and between 8a23fe9 and 8ac438e.
Files selected for processing (1)
  • packages/scripts/migrateSubscriptionsToUsageBased.ts (1 hunks)
Additional comments (Suppressed): 3
packages/scripts/migrateSubscriptionsToUsageBased.ts (3)
  • 213-240: The script automatically upgrades the workspace to the PRO plan if the total results exceed a certain threshold. This could lead to unexpected billing charges for the user. Consider adding a confirmation step or notifying the user before making the upgrade.

  • 253-265: The script creates a usage record in Stripe using an idempotency key. This is a good practice as it ensures that the operation is idempotent and can be safely retried in case of failure.

  • 267-274: The script updates the workspace in the database to remove the quarantine status. This could lead to unexpected behavior if the workspace was quarantined for a reason. Consider adding a confirmation step or notifying the user before making the change.

Comment on lines +1 to +17
import { PrismaClient } from '@typebot.io/prisma'
import { promptAndSetEnvironment } from './utils'
import { Stripe } from 'stripe'
import { createId } from '@paralleldrive/cuid2'
import { writeFileSync } from 'fs'

const migrateSubscriptionsToUsageBased = async () => {
await promptAndSetEnvironment()
const prisma = new PrismaClient()

if (
!process.env.STRIPE_STARTER_CHATS_PRICE_ID ||
!process.env.STRIPE_PRO_CHATS_PRICE_ID ||
!process.env.STRIPE_SECRET_KEY ||
!process.env.STRIPE_STARTER_PRICE_ID ||
!process.env.STRIPE_PRO_PRICE_ID
)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The script relies heavily on environment variables. It would be better to use a configuration file or a centralized configuration service to manage these variables. This would make it easier to manage and update the configuration, and it would also improve security by reducing the risk of accidentally exposing sensitive data.

!process.env.STRIPE_STARTER_PRICE_ID ||
!process.env.STRIPE_PRO_PRICE_ID
)
throw new Error('Missing some env variables')
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error message 'Missing some env variables' is not very informative. It would be better to specify which environment variables are missing to make it easier to diagnose and fix the problem.

- throw new Error('Missing some env variables')
+ throw new Error('Missing one or more of the following environment variables: STRIPE_STARTER_CHATS_PRICE_ID, STRIPE_PRO_CHATS_PRICE_ID, STRIPE_SECRET_KEY, STRIPE_STARTER_PRICE_ID, STRIPE_PRO_PRICE_ID')

Comment on lines +38 to +59
const workspacesWithPaidPlan = await prisma.workspace.findMany({
where: {
plan: {
in: ['PRO', 'STARTER'],
},
isSuspended: false,
},
select: {
plan: true,
name: true,
id: true,
stripeId: true,
isQuarantined: true,
members: {
select: {
user: {
select: { email: true },
},
},
},
},
})
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The database query is quite complex and could be simplified by using a view or stored procedure in the database. This would improve performance and make the code easier to read and maintain.

Comment on lines +61 to +64
writeFileSync(
'./workspacesWithPaidPlan.json',
JSON.stringify(workspacesWithPaidPlan, null, 2)
)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The script writes intermediate results to a file. This could be a potential performance bottleneck if the number of workspaces is large. Consider using a more efficient data storage method, such as a database or an in-memory data structure.

Comment on lines +66 to +68
const stripe = new Stripe(secretKey, {
apiVersion: '2022-11-15',
})
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Stripe API version is hardcoded. It would be better to move this to a configuration file or environment variable to make it easier to update in the future.

Comment on lines +77 to +275
{
price:
workspace.plan === 'STARTER'
? starterPriceId
: proPriceId,
quantity: 1,
},
]
return {
id: item.id,
price: item.price.id,
quantity: item.quantity,
deleted: true,
}
}
),
{
price:
workspace.plan === 'STARTER'
? starterChatsPriceId
: proChatsPriceId,
},
],
}
)

const totalResults = await prisma.result.count({
where: {
typebot: { workspaceId: workspace.id },
hasStarted: true,
createdAt: {
gte: new Date(newSubscription.current_period_start * 1000),
lt: todayMidnight,
},
},
})

if (workspace.plan === 'STARTER' && totalResults >= 4000) {
console.log(
'Workspace has more than 4000 chats, automatically upgrading to PRO plan'
)
const currentPlanItemId = newSubscription?.items.data.find((item) =>
[starterPriceId, proPriceId].includes(item.price.id)
)?.id

if (!currentPlanItemId)
throw new Error(`Could not find current plan item ID for workspace`)

await stripe.subscriptions.update(newSubscription.id, {
items: [
{
id: currentPlanItemId,
price: proPriceId,
quantity: 1,
},
],
})

await prisma.workspace.update({
where: { id: workspace.id },
data: {
plan: 'PRO',
},
})
}

const subscriptionItem = newSubscription.items.data.find(
(item) =>
item.price.id === starterChatsPriceId ||
item.price.id === proChatsPriceId
)

if (!subscriptionItem)
throw new Error(
`Could not find subscription item for workspace ${workspace.id}`
)

const idempotencyKey = createId()

console.log('Reporting total results:', totalResults)
await stripe.subscriptionItems.createUsageRecord(
subscriptionItem.id,
{
quantity: totalResults,
timestamp: 'now',
},
{
idempotencyKey,
}
)

if (workspace.isQuarantined) {
await prisma.workspace.update({
where: { id: workspace.id },
data: {
isQuarantined: false,
},
})
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The script uses a for loop to process each workspace. If the number of workspaces is large, this could be a performance bottleneck. Consider using a more efficient processing method, such as parallel processing or batch processing.

Comment on lines +98 to +106
const subscriptions = await stripe.subscriptions.list({
customer: workspace.stripeId,
})

const currentSubscription = subscriptions.data
.filter((sub) => ['past_due', 'active'].includes(sub.status))
.sort((a, b) => a.created - b.created)
.shift()

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The script retrieves all subscriptions for a workspace and then filters and sorts them in memory. This could be a performance bottleneck if the number of subscriptions is large. Consider using a more efficient method, such as filtering and sorting the subscriptions in the database query.

Comment on lines +150 to +200
const newSubscription = await stripe.subscriptions.update(
currentSubscription.id,
{
items: [
...currentSubscription.items.data.flatMap<Stripe.SubscriptionUpdateParams.Item>(
(item) => {
if (
item.price.id === starterPriceId ||
item.price.id === proPriceId
)
return {
id: item.id,
price: item.price.id,
quantity: item.quantity,
}
if (
item.price.id === starterYearlyPriceId ||
item.price.id === proYearlyPriceId
)
return [
{
id: item.id,
price: item.price.id,
quantity: item.quantity,
deleted: true,
},
{
price:
workspace.plan === 'STARTER'
? starterPriceId
: proPriceId,
quantity: 1,
},
]
return {
id: item.id,
price: item.price.id,
quantity: item.quantity,
deleted: true,
}
}
),
{
price:
workspace.plan === 'STARTER'
? starterChatsPriceId
: proChatsPriceId,
},
],
}
)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The script updates the subscription in Stripe and then immediately queries the database for results. This could lead to race conditions if the Stripe update has not completed before the database query is executed. Consider adding error handling or retry logic to handle this situation.

Comment on lines +202 to +211
const totalResults = await prisma.result.count({
where: {
typebot: { workspaceId: workspace.id },
hasStarted: true,
createdAt: {
gte: new Date(newSubscription.current_period_start * 1000),
lt: todayMidnight,
},
},
})
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The script counts the total results in the database for each workspace. This could be a performance bottleneck if the number of results is large. Consider using a more efficient method, such as aggregating the results in the database.

@baptisteArno baptisteArno merged commit 797751b into main Oct 17, 2023
6 checks passed
@baptisteArno baptisteArno deleted the feat/new-usage-based-billing branch October 17, 2023 06:03
Repository owner deleted a comment from sentry-io bot Oct 25, 2023
kweripx added a commit to toolzzlabzz/toolzzchat-builder that referenced this pull request Dec 11, 2023
* 🚸 (typebotLink) Make sure variables from child bots are merged if necessary

* ⚡ (customDomain) Add configuration modal for domain verification

Closes baptisteArno#742

* 🐛 Fix bubble icon file upload

* 🚸 (results) Use header id as table accessor to allow duplicate names

* 🐛 (payment) Fix postalCode camel case issue

Closes baptisteArno#822

* 🐛 (results) Fix result modal content display

* ⚡ (whatsapp) Improve WhatsApp preview management

Closes baptisteArno#800

* 🛂 (fileUpload) Improve file upload size limit enforcement

Closes baptisteArno#799, closes baptisteArno#797

* 📝 Change googleSheets date system var name

* ♻️ Export bot-engine code into its own package

* ♻️ Remove storage limit related code

* 🚑 Fix file upload expiration issue

* 🚑 (upload) Fix upload in embed

* 📝 Add custom domain troobleshoot section

* 👷 (vercel) Increase max execution duration for webhooks

* ⚡ (whatsapp) Improve whatsApp management and media collection

Closes baptisteArno#796

* 💚 Rename back viewer

* 👷 Only build docker images on tag push

* ✨ (whatsapp) Add custom session expiration (baptisteArno#842)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
### Summary by CodeRabbit

- New Feature: Introduced session expiry timeout for WhatsApp
integration, allowing users to set the duration after which a session
expires.
- New Feature: Added an option to enable/disable the start bot condition
in WhatsApp integration settings.
- Refactor: Enhanced error handling by throwing specific errors when
necessary conditions are not met.
- Refactor: Improved UI components like `NumberInput` and
`SwitchWithLabel` for better usability.
- Bug Fix: Fixed issues related to session resumption and message
sending in expired sessions. Now, if a session is expired, a new one
will be started instead of attempting to resume the old one.
- Chore: Updated various schemas to reflect changes in session
management and WhatsApp settings.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

* 🚑 (billing) Fix disabled upgrade buttons

* ♿ (embed) Add aria-label to bubble button

* ⚡ (wordpress) Add query params exclusion support

* 🐛 (bot) Fix reactivity issue when filtering single choices

Closes baptisteArno#803

* ⚡ Auto continue bot on whatsApp if starting block is input  (baptisteArno#849)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
### Summary by CodeRabbit

**New Features:**
- Added WhatsApp integration feature to the Pro plan.

**Refactor:**
- Introduced the ability to exclude specific plans from being displayed
in the Change Plan Modal.
- Renamed the function `isProPlan` to `hasProPerks`, enhancing code
readability and maintainability.
- Updated the `EmbedButton` component to handle a new `lockTagPlan`
property and use the `modal` function instead of the `Modal` component.

**Chore:**
- Removed the `whatsAppPhoneNumberId` field from the `Typebot` model
across various files, simplifying the data structure of the model.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

* 🚑 (fileUpload) Fix file upload in linked typebots

* ⚡ (setVariable) Add "Environment name" value in Set variable block (baptisteArno#850)

Closes baptisteArno#848
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
### Summary by CodeRabbit

- New Feature: Added "Environment name" as a new value type in the
SetVariable function, allowing users to distinguish between 'web' and
'whatsapp' environments.
- Refactor: Simplified session state handling in `resumeWhatsAppFlow.ts`
for improved code clarity.
- Refactor: Updated `startWhatsAppSession.ts` to include an initial
session state with WhatsApp contact and expiry timeout, enhancing
session management.
- Bug Fix: Improved null handling in `executeSetVariable.ts` for
'Contact name' and 'Phone number', preventing potential issues with
falsy values.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

* 🛂 Improve editor authorization feedback (baptisteArno#856)

Closes baptisteArno#844, closes baptisteArno#839

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
### Summary by CodeRabbit

- New Feature: Added a `logOut` function to the user context for
improved logout handling.
- Refactor: Updated the redirect path in the `SignInForm` component for
better user redirection after authentication.
- New Feature: Enhanced the "Add" button and "Connect new" menu item in
`CredentialsDropdown` with role-based access control.
- Refactor: Replaced the `signOut` function with the `logOut` function
from the `useUser` hook in `DashboardHeader`.
- Bug Fix: Prevented execution of certain code blocks in
`TypebotProvider` when `typebotData` is read-only.
- Refactor: Optimized the `handleObserver` function in `ResultsTable`
with a `useCallback` hook.
- Bug Fix: Improved router readiness check in `WorkspaceProvider` to
prevent premature execution of certain operations.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

* 🚸 Better random IDs generation in setVariable

* 🐛 (pixel) Fix multiple Meta pixels tracking

Closes baptisteArno#846

* 📝 (whatsapp) Add a "Create WhatsApp app" guide

* 🚸 (whatsapp) Improve upgrade plan for whatsapp notice

* 🐛 (preview) Fix always displayed start props toast

* 🐛 (whatsapp) Fix preview failing to start and wait timeo…

* 🚸 (pictureChoice) Improve single picture choice with same titles

Closes baptisteArno#859

* 🚸 (pictureChoice) Allow dynamic picture choice with… (baptisteArno#865)

… string variables
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
### Summary by CodeRabbit

- Refactor: Updated `GoogleSheetsNodeContent` component to use the
`options` prop instead of `action`, and integrated the `useTypebot` hook
for better functionality.
- Style: Improved UI text and layout in `GoogleSheetsSettings.tsx`,
enhancing user experience when selecting rows.
- Refactor: Simplified rendering logic in `BlockNodeContent.tsx` by
directly calling `GoogleSheetsNodeContent` component, improving code
readability.
- Bug Fix: Enhanced `injectVariableValuesInPictureChoiceBlock` function
to handle different types of values for titles, descriptions, and
picture sources, fixing issues with variable value injection.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

* 🐛 (whatsapp) Fix auto start input where it didn't display next bu… (baptisteArno#869)

…bbles
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
### Summary by CodeRabbit

**Release Notes**

- New Feature: Enhanced WhatsApp integration with improved phone number
formatting and session ID generation.
- Refactor: Updated the `startWhatsAppPreview` and
`receiveMessagePreview` functions for better consistency and
readability.
- Bug Fix: Added a check for `phoneNumberId` in the `receiveMessage`
function to prevent errors when it's undefined.
- Documentation: Expanded the WhatsApp integration guide and FAQs in the
docs, providing more detailed instructions and addressing common
queries.
- Chore: Introduced a new `metadata` field in the
`whatsAppWebhookRequestBodySchema` to store the `phone_number_id`.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

* 🐛 (typebotLink) Fix nested typebot link pop

* 📝 (typebotLink) Add instructions about shared variables and merge answers

* 🛂 (whatsapp) Remove feature flag

Closes baptisteArno#401

* 🚑 (js) Fix dependency issue preventing user to install @typebot.io/js

Closes baptisteArno#871

* 🚸 (whatsapp) Improve how the whatsapp preview behaves (baptisteArno#873)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
### Summary by CodeRabbit

- New Feature: Updated WhatsApp logo with a new design and color scheme.
- New Feature: Added a help button in the UI linking to documentation,
enhancing user guidance.
- New Feature: Introduced an alert message indicating that the WhatsApp
integration is in beta testing.
- New Feature: Implemented a button to open WhatsApp Web directly from
the application, improving user convenience.
- Refactor: Adjusted the retrieval of `contactPhoneNumber` in
`receiveMessagePreview` function for better data structure
compatibility.
- Refactor: Optimized the initialization and management of the WhatsApp
session in `startWhatsAppPreview`.
- Refactor: Improved the `parseButtonsReply` function by refining
condition checks.
- Refactor: Enhanced the readability of serialized rich text in
`convertRichTextToWhatsAppText` by introducing newline characters.
- Bug Fix: Ensured preservation of `contact` information when resuming
the WhatsApp flow in `resumeWhatsAppFlow`.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

* 📝 Update About page content

Closes baptisteArno#757

* 🐛 (builder) Fix system color mode not syncing properly

* 🔖 Release v2.18.0

* 🚸 (sendEmail) Rename username SMTP creds label to avoid confusion

* 🐳 Bump Postgres version in official docker compose file

* 📝 (whatsapp) Re-organize whatsapp overview doc

* 📝 (vercel) Add a note on function maxDuration for Hobby plans

* 📝 (docker) Update postgres image name

* 🚑 (whatsapp) Fix start whatsapp session when user has multiple whatsapp enabled

* 🛂 (whatsapp) Disable whatsapp by default on duplication

* 🛂 (whatsapp) Set default whatsapp expiry to 4 hours

* 🚸 (videoBubble) Reparse variable video URL to correctly detect provider

* 🐛 (whatsapp) Fix force create session when flow is completed at first round

* 🧑‍💻 Improve invalid environment variable insight on build fail

* 🐳 Remove wait-for-it script to avoid edge cases issues

* 🚑 (results) Fix broken infinite scroll

* ♻️ (api) Auto start bot if starting with input

Closes baptisteArno#877, closes baptisteArno#884

* ✨ Automatically parse markdown from variables in text bubbles

Closes baptisteArno#539

* 📝 (whatsapp) Remove private beta mention

* 🚑 Fix text styling parsing on variables

* 🐛 New sendMessage version for the new parser

Make sure old client still communicate with old parser

* 🔖 (wordpress) Deploy v3.4.0

* ⬆️ (openai) Replace openai-edge with openai and upgrade next

* 🐛 Enable stream again by migrating endpoint to route handler

https://vercel.com/docs/functions/edge-functions/streaming#streaming-data-with-edge-functions

* 🚸 (openai) Improve streamed message lists CSS

* 🔥 Remove streamer Pages API endpoint

* 🐛 Add no cache instructions to streamer

Attempt to fix buffering issue when Cloudflare proxy is enabled

* 💄 Better parsing of lists and code in streaming bubbles

* ♻️ Remove sentry client monitoring in viewer

* 🚸 (condition) Don't show value in node content if operator is "set" or "empty"

* 📝 (embed) Add note about non-embeddable websites

* ⬆️ Upgrade sentry and improve its reliability

* 🐛 (editor) Fix default branding settings on cre…

* 🛂 Sanitize custom CSS and head code to avoid modification of lite badge

* 📝 (s3) Add s3 configuration detailed instructions

* 🚑 Fix custom CSS sanitization

* 🚸 (openai) Improve streaming bubble sequence and visual

* 💚 Fix docker build when Sentry not enabled

* 🔖 Release v2.18.1

* 🚑 Fix empty bubble issue when plate element does not have a type attribute

* 🐛 (openai) Fix 2 openai streaming back to back

* 📝 (openai) Add "Multiple OpenAI blocks" video section

* ⚡ (video) Allow changing video height when resolved to an iframe

* 🐛 Fix link parsing when using variables

Closes baptisteArno#764

* 🐛 (textBubble) Fix overflow with long links

Closes baptisteArno#764

* 🐛 (videoBubble) Fix youtube parsing for IDs containing a "-"

* 🐳 Force Next.js apps local hostname

Closes baptisteArno#911

* 🔖 Release v2.18.2

* 🐛 (webhook) Fix webhook response data key number parsing

* 📝 Add bounties info in README

* ⚡ (billing) Automatic usage-based billing (baptisteArno#924)

BREAKING CHANGE: Stripe environment variables simplified. Check out the
new configs to adapt your existing system.

Closes baptisteArno#906





<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
### Summary by CodeRabbit

**New Features:**
- Introduced a usage-based billing system, providing more flexibility
and options for users.
- Integrated with Stripe for a smoother and more secure payment process.
- Enhanced the user interface with improvements to the billing,
workspace, and pricing pages for a more intuitive experience.

**Improvements:**
- Simplified the billing logic, removing additional chats and yearly
billing for a more streamlined user experience.
- Updated email notifications to keep users informed about their usage
and limits.
- Improved pricing and currency formatting for better clarity and
understanding.

**Testing:**
- Updated tests and specifications to ensure the reliability of new
features and improvements.

**Note:** These changes aim to provide a more flexible and user-friendly
billing system, with clearer pricing and improved notifications. Users
should find the new system more intuitive and easier to navigate.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

* 🚑 (billing) Fix chats pricing tiers incremental flat amou…

* 👷 Improve getUsage accuracy in check cron job

* 🐛 (results) Lower the max limit in getResults endpoint to avoid payload size error

Closes baptisteArno#908

* 💚 Fix send email in CI "React is not defined"

* 🐛 Freeze body overflow when opening a Popup embed (baptisteArno#937)

fix baptisteArno#763
/claim baptisteArno#763

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
### Summary by CodeRabbit

- **Enhancement**: Improved the visibility management of the bot in the
popup. This update ensures a smoother and more intuitive user experience
when interacting with the bot.
- **Bug Fix**: Resolved an issue where certain styles could interfere
with the bot's visibility in the popup. The update prioritizes the
necessary style settings, ensuring the bot's visibility is maintained as
expected, regardless of other conflicting styles.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

* 🐛 Fixed pinch zooming mouse issue (with ctrl key) (baptisteArno#940)

**Fixed the drastic zoom increase decrease on ctrl + mouse scroll.**

The issue was occuring due to the "scale" property in the pinch gesture.
The scale was getting bigger values, leading to more zooming.
What I did was, made sure that maximum scale difference between current
and last value cannot be more than the scaling factor used in
zoomin/zoomout buttons. That is. 0.2
Also, the pinch zoom would work as expected.

/claim baptisteArno#567
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
### Summary by CodeRabbit

- Improvement: Enhanced zoom precision in the Graph component. This
update allows for more accurate scaling when adjusting the view in the
graph builder. The change ensures that the zoom level adjusts more
precisely, providing a smoother and more controlled user experience when
interacting with graphs.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

* Fix audio content overflow in windows. (baptisteArno#944)

/claim baptisteArno#667

The volume slider in audio element in windows chrome overflows.
Possible fixes:
1. Change the width of audio blocks. (Not suggested, as the width of all
blocks should be consistent)
2. Adjust the audio sub elements, so it doesn't overflow.(IMPLEMENTED)

It's not so straightforward to apply customization to audio tag element.
The best possible way I could find, to make the app look good, is by
hiding the timeline track in the audio. It was showing up very small
anyway(due to block width), so there shouldn't be an issue.
Please look at the before & after videos below.


https://github.com/baptisteArno/typebot.io/assets/29385192/f61c5b58-834d-470f-b684-bd82181e07f4



https://github.com/baptisteArno/typebot.io/assets/29385192/88f932eb-dc7e-4346-bf64-6405a015013e


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
### Summary by CodeRabbit

- Style: Improved the visual layout of the audio player on Windows. The
update includes a cleaner interface by hiding the track timeline and
adjusting the media controls panel. This change enhances the user
experience by providing a more streamlined and intuitive audio player
design.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

* ♻️ Update import contact to brevo script

* 👷 Add convenient script for migrating Stripe prices

* 🩹 Surround logs saving in a try catch block

It seems that in some particular set up the logs saving is failing.

* 🚸 (buttons) Trim items content when parsing reply for better consistency

Closes baptisteArno#948

* 🔖 Release v2.18.3

* ✏️ Fix popup blocked toast typo

* 🧑‍💻 (whatsapp) Improve whatsapp start log

* 🐛 (numberInput) Fix input clearing out on dot or comma press

* 🚑 Fix can invite new members in workspace bool

Closes baptisteArno#964

* 🔖 Release v2.18.4

* 🐛 Fix graph flickering on high res displays (baptisteArno#959)

This PR fixes the flickering and improves the performance so panning
around the graph is much smoother than before.



https://github.com/baptisteArno/typebot.io/assets/62795688/56b91e20-1eb0-44b5-9a4a-c07525c2ba48


/claim baptisteArno#575
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

### Summary by CodeRabbit

- Refactor: Improved the Graph component's scaling calculation for
enhanced readability and maintenance.
- Style: Updated the Graph component's style properties to ensure better
compatibility and visual performance on webkit browsers.

These changes aim to enhance the user experience by ensuring the Graph
component displays consistently across different web browsers. The
refactoring of the scaling calculation also makes the code easier to
understand and maintain, potentially leading to quicker updates and bug
fixes in the future.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Baptiste Arnaud <contact@baptiste-arnaud.fr>

* ✏️ Fix manual deployment doc start script typo

Closes baptisteArno#969

* 💚 Fix checkAndReportChatsUsage script sending multiple emails at once

* 🧑‍💻 Fix type resolution for @typebot.io/react and nextjs

Closes baptisteArno#968

* 🧑‍💻 Migrate to Tolgee (baptisteArno#976)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

### Summary by CodeRabbit

- Refactor: Transitioned to a new translation library (`@tolgee/react`)
across the application, enhancing the localization capabilities and
consistency.
- New Feature: Introduced a JSON configuration file for application
settings, improving customization and flexibility.
- Refactor: Updated SVG attribute naming convention in the
`WhatsAppLogo` component to align with React standards.
- Chore: Adjusted the `.gitignore` file and added a new line at the end.
- Documentation: Added instructions for setting up environment variables
for the Tolgee i18n contribution dev tool, improving the self-hosting
configuration guide.
- Style: Updated the `CollaborationMenuButton` to hide the
`PopoverContent` component by scaling it down to zero.
- Refactor: Simplified error handling logic for fetching and updating
typebots in `TypebotProvider.tsx`, improving code readability and
maintenance.
- Refactor: Removed the dependency on the `parseGroupTitle` function,
simplifying the code in several components.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

* 🐛 Fix group duplicate new title bug

* 📝 Add webhook configuration tuto video

* 🐛 (number) Fix number input validation with variables

* 📝 Add text link section in text bubble doc

* ✏️ Fix CORSRules content typo for S3 config

* 🐛 Fix formatted message in input block when input is retried

* ⚡ Add cache-control header on newly uploaded files

* ✏️ (billing) Fix plan name typo

* 🚑 Move cache control header into the post policy

* 🔖 Release v2.19.0

* 📝 Add UTM params forwarding video tutorial

* 📦 Add strict package versioning to avoid incompatibility in workspace

* ⬆️ Upgrade Sentry to mitigate security issue

https://github.com/getsentry/sentry-javascript/security?mkt_tok=Nzc2LU1KTi01MDEAAAGPNi0ooiOxT0sphdzXd6xHU63d5z5Sc75FNR8cH-6EK-zlvUsUuUqP1YsmnxivxEyXnGZS2cN8XkpuNNGi3NIfoDnwoHci-31tbyJQB8y0Cg

* ⚡ (chatwoot) Unmount Typebot embed bubble when opening chatwoot

Closes baptisteArno#1007

* 🚑 Fix weird env parsing on Firefox making it crash

* 🛂 Update Cache-Control header in generatePresignedPostPolicy

* fix: whole page overflowing on the x axis and displaying a horizontal scrollbar (baptisteArno#1011)

this PR fixes issue
baptisteArno#1008 by making the
position of `HandDrawnArrow`'s parent relative, which confines the
absolute position of its children to be within the bounds of the parent,
causing layout not shift due to `right -30px`

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **New Features**
- Updated the layout behavior of the `RealTimeResults` component on the
landing page for better user experience.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

* ♻️ Introduce typebot v6 with events (baptisteArno#1013)

Closes baptisteArno#885

* 🚑 Fix parsing issue with new events field on ongoing session states

* 🐛 (import) Fix import typebot files that does not have name field

* 🚸 (typebotLink) Make "current" option work like typebot links instead of jump

* 🐛 Fix typebot publishing endpoint events parsing

* 🐛 Fix default initial items in TableList

* 🚑 (editor) Fix move block with outgoing edge

* 🚑 (zapier) Fix execute webhook endpoint too strict on block type check

* 🐛 (typebotLink) Fix link to first group with start event

* 🚑 (webhook) Fix webhook execution with default method

* 🐛 (editor) Fix edge delete with undefined groupIndex

* 🐛 Sort variables to parse to fix text bubble parsing issue

* 🐛 Fix theme background and font default selection

* 💄 Fix multi choice checkbox UI on small screens

* 📝 Add breaking changes and OpenAI block improvements docs

* ⚡ Add more video supports (baptisteArno#1023)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **New Features**
	- Introduced a new layout option for the TextInput component.
- Added support for GUMLET and TIKTOK video content types in
VideoBubbleContent.
- Enhanced VideoUploadContent to handle new properties like aspectRatio
and maxWidth.
- Updated VideoBubble to include aspect-ratio and max-width styles based
on content properties.
- **Refactor**
- Changed the extension used for internationalization (i18n) in the VS
Code environment.
	- Modified how environment variables are accessed in tolgee.tsx.
- Updated parseVideoUrl function to include a new property
videoSizeSuggestion.
- **Chores**
- Updated the tolgeeEnv object in env.ts and added a new optional
parameter to the getRuntimeVariable function.
- Expanded video handling capabilities by introducing new video content
types and associated regular expressions.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Closes baptisteArno#978 baptisteArno#936 baptisteArno#926

* 🛂 Reduce sendMessage serverless function max memory

* 🐛 (webhook) Fix legacy webhook {{state}} body parsing

* 🧑‍💻 (chat) Introduce startChat and continueChat endpoints

Closes baptisteArno#1030

* ⏪ Revert new authentication method for preview bot

* 📝 Add OpenAI Dialogue option in breaking change doc

* ⚡ Add maxWidth and maxHeight bubble them props

Closes baptisteArno#458

* 📝 Change community URLs, introduce Discord server

Closes baptisteArno#866

* 🐛 (textBubble) Fix variable parsing when starting or finishing by spaces

* ⏪ (wordpress) Revert to specific non breaking version for self-hosters

* 🐛 (billing) Set invoicing behavior to "always invoice" to fix double payment issue

* 🐛 (js) Fix default theme values css variables

Closes baptisteArno#1031

* 🐛 (fileUpload) Fix results file display if name contains comma

Closes baptisteArno#955

* ⬆️ (date) Upgrade date parser package

* 📝 Update Discord invite link

* 🚸 Auto scroll once picture choice images are fully loaded

* ♿ Show scrollbar on searchable items

* 🐛 Fix typebot parsing for legacy columnsWidth setting

* 🐛 (wordpress) Fix version mismatch for self-hosters for Standard embed as well

Closes baptisteArno#1038

* 🐛 (typebotLink) Fix variables merging with new values

* 🐛 (editor) Fix AB test items not connectable

* 🔊 Add response debug log for failing requests without errors

* 💚 Fix docker build missing ts target in schemas

* 🔖 Release v2.19.1

* ✏️ Fix typebot v7 breaking changes doc typo

* 🌐 Add es and ro support

* ✨ (openai) Add create speech OpenAI action

Closes baptisteArno#1025

* 🐛 (chatwoot) Fix email prefill when Chatwoot contact already exist

* 🛂 (billing) Add isPastDue field in workspace (baptisteArno#1046)

Closes baptisteArno#1039

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **New Features**
  - Workspaces now include additional status indicator: `isPastDue`.
- New pages for handling workspaces that are past due. Meaning, an
invoice is unpaid.

- **Bug Fixes**
- Fixed issues with workspace status checks and redirections for
suspended workspaces.

- **Refactor**
- Refactored workspace-related API functions to accommodate new status
fields.
- Improved permission checks for reading and writing typebots based on
workspace status.

- **Chores**
  - Database schema updated to include `isPastDue` field for workspaces.
- Implemented new webhook event handling for subscription and invoice
updates.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

* 🚑 (editor) Fix typebot update permission

* ✨ Allow user to share a flow publicly and make it duplicatable

Closes baptisteArno#360

* 🐛 (pictureChoice) Fix choice parsing too unrestrictive

* 🔥 Remove VIEWER_URL_INTERNAL variable

BREAKING CHANGE: NEXT_PUBLIC_VIEWER_INTERNAL does not exist anymore as typebot.io now directly points to the viewer project

* 🚑 (billing) Fix stripe webhook "invoice.paid" typo

* 🐛 Fix processTelemetry endpoint not reachable

* ⚡ (billing) Improve past_due workspace checking webhook

* 📝 Add new start and continue endpoints in the API runtime instructions

* 🚸 (redirect) Make sure the redirection is always done on top frame

* 🔧 Increase builder request max size to 4MB

* 💚 Update broken action-autotag package

* 🚑 (pictureChoice) Fix pic choice multi select parsing

* 📝 Improve WP prefilled var explanation

* 🐛 Fix default webhook body with multi inputs groups

* 🛂 Allow app admin to read a typebot

* 🐛 (share) Fix duplicate folderId issue

* 🚸 (fileUpload) Properly encode commas from uploaded file urls

Closes baptisteArno#955

* ⚡ (wordpress) Add lib_version prop in shortcode

Closes baptisteArno#1035

* 📝 Add flow share docs

* 🔖 Release v2.20.0

* ⚗️ (docs) Replace Algolia search with Community Search

* Updated vercel deployment guide. (baptisteArno#1075)

Adding explanation text about builder and viewer.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **Documentation**
- Enhanced the self-hosting guide with additional explanatory notes on
deploying both the Builder and Viewer components for Typebot, clarifying
their distinct roles in service flow creation and user interaction.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

* 🐛 (editor) Fix old typebot flash when changing the typebot

* 🐛 Fix multiple item dragged issue

* 🐛 Fix right click in bubble text editor selects the group

Closes baptisteArno#920

* ✏️ Fix invalid ending comma in API instructions

Closes baptisteArno#1022

* 🧑‍💻 Automatically guess env URLs for Vercel preview deploy… (baptisteArno#1076)

…ments

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **New Features**
- Introduced new URL processing logic to enhance compatibility with
Vercel preview environments.
- Improved handling of environment-specific URLs for authentication and
viewer services.

- **Enhancements**
- Streamlined environment variable management for more reliable
deployment configurations.

- **Documentation**
- Updated documentation to reflect new environment variable processing
functions.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

* 📝 Add node prerequisite in Contributing guide

* 🛂 (billing) Past due status only for unpaid invoices with additional usage

* 🚸 (docs) Open community search docs results in same tab

* ♻️ Remove references to old s3 URLs

* 🔧 Update vercel.json to reflect new api path

* 🛂 Hide workspace members list from guest

* update typebot

* update typebot

* 🔧 Update main viewer domain to typebot.co

* Delete apps/landing-page/public/favicon.png

* Add files via upload

* Delete apps/builder/public/favicon.png

* Add files via upload

* Delete apps/viewer/public/favicon.png

* Add files via upload

* Add files via upload

* Delete apps/builder/public/favicon.png

* Add files via upload

* Add files via upload

* translate

* app.chatwoot.com

* options.baseUrl

* ⚡ Add dynamic timeout to bot engine api

* 🐛 (sheets) Init OAuth client inside a function to avoid potential conflict

* 🐛 Fix change language not working in the editor

* ✨ Introducing Radar, fraud detection

* Update publishTypebot.ts

* 📝 (docs): fix typo in Unsplash description (baptisteArno#1094)

Documentation

This PR updates the documentation to fix an incorrect description for
the Unsplash configuration.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **Documentation**
- Corrected a hyperlink and associated text in the self-hosting
configuration guide, changing "Giphy" to "Unsplash" for image search
references.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Signed-off-by: Victor Santos <vsantos.py@gmail.com>

* 📈 Only send suspicious bot alert if risk level is below 100

* 🛂 Auto ban IP on suspected bot publishing (baptisteArno#1095)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **New Features**
- Enhanced sign-in error handling with specific messages for different
error types.
- Implemented IP-based restrictions for authentication and publishing
actions.

- **Bug Fixes**
- Updated the retrieval of user session information to improve
reliability.

- **Documentation**
- Updated usage instructions for `getServerSession` to reflect the new
authentication options.

- **Refactor**
- Replaced direct usage of `authOptions` with a new function
`getAuthOptions` to dynamically generate authentication options.
- Improved IP address extraction logic to handle various header formats.

- **Chores**
- Added a new `BannedIp` model to the database schema for managing
IP-based restrictions.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

* feat: toolzz logo on sigin and header

---------

Signed-off-by: Victor Santos <vsantos.py@gmail.com>
Co-authored-by: Baptiste Arnaud <contact@baptiste-arnaud.fr>
Co-authored-by: Rishi Raj Jain <rishi18304@iiitd.ac.in>
Co-authored-by: Prateek Kalra <prateekkalra1997@gmail.com>
Co-authored-by: neo773 <62795688+neo773@users.noreply.github.com>
Co-authored-by: onFire(Abhi) <40654066+AbhiShake1@users.noreply.github.com>
Co-authored-by: Thiago Mendonça <thiagomendonca09@gmail.com>
Co-authored-by: lucasbuges <123869629+lucasbuges@users.noreply.github.com>
Co-authored-by: Baptiste Arnaud <baptiste.arnaud95@gmail.com>
Co-authored-by: Victor Santos <vsantos.py@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Implement new usage-based monitored pricing
1 participant