feat(billing) - facilitate top up in ai chat (#21645)
Today, when a trialing user hits their AI usage cap inside the Ask AI chat, ending the trial bounces them to the Stripe billing portal (and, for card-less users, loses their place in the conversation). This PR makes activating a paid plan / topping up credits feel seamless from within the chat: Trial users with a card on file activate their subscription in place, without leaving the app. Trial users without a card are sent to the Stripe payment-method portal and, on return, the trial is ended automatically and they're dropped back into the exact Ask AI thread they came from. Credit-exhaustion and trial banners now reflect whether a payment method exists (Add Credit Card vs Subscribe Now / End Trial Period) and upgrade inline via a confirmation modal instead of redirecting to Settings. Uploading Screen Recording 2026-06-16 at 07.51.12.mov… https://github.com/user-attachments/assets/4ea77273-da63-4b32-b6f1-5ac9e9560651 <!-- This is an auto-generated description by cubic. --> <a href="https://cubic.dev/pr/twentyhq/twenty/pull/21645?utm_source=github" target="_blank" rel="noopener noreferrer" data-no-image-dialog="true"><picture><source media="(prefers-color-scheme: dark)" srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img alt="Review in cubic" src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a> <!-- End of auto-generated description by cubic. -->
This commit is contained in:
@@ -20,10 +20,12 @@ import { agentChatMessagesComponentFamilyState } from '@/ai/states/agentChatMess
|
||||
import { agentChatUsageComponentFamilyState } from '@/ai/states/agentChatUsageComponentFamilyState';
|
||||
import { currentAiChatThreadTitleComponentFamilyState } from '@/ai/states/currentAiChatThreadTitleComponentFamilyState';
|
||||
import { AiChatErrorCode } from '@/ai/utils/aiChatErrorCode';
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { dispatchBrowserEvent } from '@/browser-event/utils/dispatchBrowserEvent';
|
||||
import { sseClientState } from '@/sse-db-event/states/sseClientState';
|
||||
import { useAtomComponentFamilyStateCallbackState } from '@/ui/utilities/state/jotai/hooks/useAtomComponentFamilyStateCallbackState';
|
||||
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
|
||||
import { BillingProductKey } from '~/generated-metadata/graphql';
|
||||
|
||||
const THROTTLE_MS = 100;
|
||||
|
||||
@@ -324,6 +326,35 @@ export const useAgentChatSubscription = (threadId: string | null) => {
|
||||
}
|
||||
|
||||
case 'credits-exhausted': {
|
||||
//TODO : add real time on currentUser
|
||||
store.set(currentWorkspaceState.atom, (currentWorkspace) => {
|
||||
const currentBillingSubscription =
|
||||
currentWorkspace?.currentBillingSubscription;
|
||||
const billingSubscriptionItems =
|
||||
currentBillingSubscription?.billingSubscriptionItems;
|
||||
|
||||
if (
|
||||
!isDefined(currentWorkspace) ||
|
||||
!isDefined(currentBillingSubscription) ||
|
||||
!isDefined(billingSubscriptionItems)
|
||||
) {
|
||||
return currentWorkspace;
|
||||
}
|
||||
|
||||
return {
|
||||
...currentWorkspace,
|
||||
currentBillingSubscription: {
|
||||
...currentBillingSubscription,
|
||||
billingSubscriptionItems: billingSubscriptionItems.map((item) =>
|
||||
item.billingProduct.metadata?.['productKey'] ===
|
||||
BillingProductKey.RESOURCE_CREDIT
|
||||
? { ...item, hasReachedCurrentPeriodCap: true }
|
||||
: item,
|
||||
),
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
const noMoreCreditsError = new Error(
|
||||
'Chat stopped: no more available credits.',
|
||||
) as Error & { code?: string };
|
||||
|
||||
Reference in New Issue
Block a user