Refresh billing state after ending trial period (#23534)

Fixes #23530

After adding a credit card in the billing prompt, the credits section
and subscription details stayed stale until a full page refresh.

The `endSubscriptionTrialPeriod` mutation only returned `status` and
`hasPaymentMethod`, and the frontend hook only patched the subscription
status into the workspace state. The credits query was never refetched,
so granted credits kept showing trial values, and `currentPeriodEnd`
(renewal date) and `billingCustomer.hasPaymentMethod` stayed outdated.
The backend already syncs everything to the database synchronously
before the mutation returns, so fresh data was available, just never
fetched.

Changes:
- `BillingEndTrialPeriodDTO` now includes nullable
`currentBillingSubscription` and `billingSubscriptions`, returned by the
resolver on success, mirroring the other billing update mutations
(`switchSubscriptionInterval`, etc.)
- `useEndSubscriptionTrialPeriod` applies the full billing update via
`useApplyCurrentWorkspaceBillingUpdate` (falling back to the previous
status-only patch), marks the billing customer as having a payment
method, and refetches `GetResourceCreditUsage` so the credits section
updates for any active observer

This covers all entry points that end the trial: the billing page card
modal, the trial banner, the AI chat banner, and the return from the
Stripe portal.

---
_Generated by [Claude
Code](https://claude.ai/code/session_01W1J7cW2MhaqXGfdjvHeFoX)_

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/23534?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:
Félix Malfait
2026-07-30 09:53:26 +02:00
committed by GitHub
parent 8b707c5131
commit 0b335d15b3
8 changed files with 91 additions and 2 deletions
@@ -6,6 +6,12 @@ export const END_SUBSCRIPTION_TRIAL_PERIOD = gql`
status
hasPaymentMethod
billingPortalUrl
currentBillingSubscription {
...CurrentBillingSubscriptionFragment
}
billingSubscriptions {
...BillingSubscriptionFragment
}
}
}
`;
@@ -1,6 +1,7 @@
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
import { useRedirect } from '@/domain-manager/hooks/useRedirect';
import { START_SUBSCRIPTION_AFTER_PAYMENT_METHOD_QUERY_PARAM } from '@/settings/billing/constants/StartSubscriptionAfterPaymentMethodQueryParam';
import { useApplyCurrentWorkspaceBillingUpdate } from '@/settings/billing/hooks/useApplyCurrentWorkspaceBillingUpdate';
import { useAtomState } from '@/ui/utilities/state/jotai/hooks/useAtomState';
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
import { t } from '@lingui/core/macro';
@@ -11,6 +12,7 @@ import { useLazyQuery, useMutation } from '@apollo/client/react';
import {
BillingPortalSessionDocument,
EndSubscriptionTrialPeriodDocument,
GetResourceCreditUsageDocument,
} from '~/generated-metadata/graphql';
export const useEndSubscriptionTrialPeriod = () => {
@@ -18,11 +20,16 @@ export const useEndSubscriptionTrialPeriod = () => {
useSnackBar();
const [endSubscriptionTrialPeriod] = useMutation(
EndSubscriptionTrialPeriodDocument,
{
refetchQueries: [GetResourceCreditUsageDocument],
},
);
const [getBillingPortalSession] = useLazyQuery(BillingPortalSessionDocument);
const [currentWorkspace, setCurrentWorkspace] = useAtomState(
currentWorkspaceState,
);
const { applyCurrentWorkspaceBillingUpdate } =
useApplyCurrentWorkspaceBillingUpdate();
const [isLoading, setIsLoading] = useState(false);
const { redirect } = useRedirect();
const location = useLocation();
@@ -96,8 +103,23 @@ export const useEndSubscriptionTrialPeriod = () => {
return { success: false, hasPaymentMethod: false };
}
const updatedCurrentBillingSubscription =
endTrialPeriodOutput?.currentBillingSubscription;
const updatedBillingSubscriptions =
endTrialPeriodOutput?.billingSubscriptions;
const hasAppliedFullBillingUpdate =
isDefined(updatedCurrentBillingSubscription) &&
isDefined(updatedBillingSubscriptions)
? applyCurrentWorkspaceBillingUpdate({
currentBillingSubscription: updatedCurrentBillingSubscription,
billingSubscriptions: updatedBillingSubscriptions,
})
: false;
const updatedSubscriptionStatus = endTrialPeriodOutput?.status;
if (
!hasAppliedFullBillingUpdate &&
isDefined(updatedSubscriptionStatus) &&
isDefined(currentWorkspace?.currentBillingSubscription)
) {
@@ -117,6 +139,19 @@ export const useEndSubscriptionTrialPeriod = () => {
});
}
setCurrentWorkspace((previousWorkspace) =>
isDefined(previousWorkspace) &&
isDefined(previousWorkspace.billingCustomer)
? {
...previousWorkspace,
billingCustomer: {
...previousWorkspace.billingCustomer,
hasPaymentMethod: true,
},
}
: previousWorkspace,
);
enqueueSuccessSnackBar({
message: t`Subscription activated.`,
});