Skip to content

Commit

Permalink
Fix race condition on stripe subscription (#9629)
Browse files Browse the repository at this point in the history
Fixes twentyhq/core-team-issues#191
- remove automatic redirection on payment success page when subscription
status is undefined
- add an effect component to refresh the subscription status on payment
success page

Observation: Locally, I had to delay the stripe webhook subscription
created endpoint by 7s to see race condition issue


https://github.com/user-attachments/assets/463e1816-34fd-4c4f-b590-3994a3a3e91a
  • Loading branch information
martmull authored Jan 15, 2025
1 parent d63aec4 commit c01e3af
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ const testCases = [
{ loc: AppPath.PlanRequired, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam },
{ loc: AppPath.PlanRequired, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: defaultHomePagePath },

{ loc: AppPath.PlanRequiredSuccess, isLoggedIn: true, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired },
{ loc: AppPath.PlanRequiredSuccess, isLoggedIn: true, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: undefined },
{ loc: AppPath.PlanRequiredSuccess, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' },
{ loc: AppPath.PlanRequiredSuccess, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' },
{ loc: AppPath.PlanRequiredSuccess, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: defaultHomePagePath },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ export const usePageChangeEffectNavigateLocation = () => {

if (
onboardingStatus === OnboardingStatus.PlanRequired &&
!isMatchingLocation(AppPath.PlanRequired)
!isMatchingLocation(AppPath.PlanRequired) &&
!isMatchingLocation(AppPath.PlanRequiredSuccess)
) {
return AppPath.PlanRequired;
}
Expand Down
52 changes: 44 additions & 8 deletions packages/twenty-front/src/pages/onboarding/PaymentSuccess.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,26 @@
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { useRecoilValue } from 'recoil';
import { useSetRecoilState } from 'recoil';
import {
AnimatedEaseIn,
IconCheck,
isDefined,
MainButton,
RGBA,
UndecoratedLink,
} from 'twenty-ui';

import { SubTitle } from '@/auth/components/SubTitle';
import { Title } from '@/auth/components/Title';
import { currentUserState } from '@/auth/states/currentUserState';
import {
OnboardingStatus,
useGetCurrentUserLazyQuery,
} from '~/generated/graphql';
import { useSubscriptionStatus } from '@/workspace/hooks/useSubscriptionStatus';
import { useOnboardingStatus } from '@/onboarding/hooks/useOnboardingStatus';
import { AppPath } from '@/types/AppPath';
import { OnboardingStatus } from '~/generated/graphql';
import React from 'react';
import { useNavigate } from 'react-router-dom';

const StyledCheckContainer = styled.div`
align-items: center;
Expand All @@ -34,11 +41,38 @@ const StyledButtonContainer = styled.div`

export const PaymentSuccess = () => {
const theme = useTheme();
const currentUser = useRecoilValue(currentUserState);
const navigate = useNavigate();
const subscriptionStatus = useSubscriptionStatus();
const onboardingStatus = useOnboardingStatus();
const [getCurrentUser] = useGetCurrentUserLazyQuery();
const setCurrentUser = useSetRecoilState(currentUserState);
const color =
theme.name === 'light' ? theme.grayScale.gray90 : theme.grayScale.gray10;

if (currentUser?.onboardingStatus === OnboardingStatus.Completed) {
const navigateWithSubscriptionCheck = async () => {
if (isDefined(subscriptionStatus)) {
navigate(AppPath.CreateWorkspace);
return;
}

const result = await getCurrentUser({ fetchPolicy: 'network-only' });
const currentUser = result.data?.currentUser;
const refreshedSubscriptionStatus =
currentUser?.currentWorkspace?.currentBillingSubscription?.status;

if (isDefined(currentUser) && isDefined(refreshedSubscriptionStatus)) {
setCurrentUser(currentUser);
navigate(AppPath.CreateWorkspace);
return;
}

throw new Error(
"We're waiting for a confirmation from our payment provider (Stripe).\n" +
'Please try again in a few seconds, sorry.',
);
};

if (onboardingStatus === OnboardingStatus.Completed) {
return <></>;
}

Expand All @@ -52,9 +86,11 @@ export const PaymentSuccess = () => {
<Title>All set!</Title>
<SubTitle>Your account has been activated.</SubTitle>
<StyledButtonContainer>
<UndecoratedLink to={AppPath.CreateWorkspace}>
<MainButton title="Start" width={200} />
</UndecoratedLink>
<MainButton
title="Start"
width={200}
onClick={navigateWithSubscriptionCheck}
/>
</StyledButtonContainer>
</>
);
Expand Down

0 comments on commit c01e3af

Please sign in to comment.