Pass Billing Checkout var in url to bypass credit card (#9283)
This commit is contained in:
@ -43,16 +43,17 @@ import { getTimeFormatFromWorkspaceTimeFormat } from '@/localization/utils/getTi
|
||||
import { currentUserState } from '../states/currentUserState';
|
||||
import { tokenPairState } from '../states/tokenPairState';
|
||||
|
||||
import { BillingCheckoutSession } from '@/auth/types/billingCheckoutSession.type';
|
||||
import { isMultiWorkspaceEnabledState } from '@/client-config/states/isMultiWorkspaceEnabledState';
|
||||
import { useIsCurrentLocationOnAWorkspaceSubdomain } from '@/domain-manager/hooks/useIsCurrentLocationOnAWorkspaceSubdomain';
|
||||
import { useLastAuthenticatedWorkspaceDomain } from '@/domain-manager/hooks/useLastAuthenticatedWorkspaceDomain';
|
||||
import { useReadWorkspaceSubdomainFromCurrentLocation } from '@/domain-manager/hooks/useReadWorkspaceSubdomainFromCurrentLocation';
|
||||
import { useRedirect } from '@/domain-manager/hooks/useRedirect';
|
||||
import { useRedirectToWorkspaceDomain } from '@/domain-manager/hooks/useRedirectToWorkspaceDomain';
|
||||
import { domainConfigurationState } from '@/domain-manager/states/domainConfigurationState';
|
||||
import { isAppWaitingForFreshObjectMetadataState } from '@/object-metadata/states/isAppWaitingForFreshObjectMetadataState';
|
||||
import { workspaceAuthProvidersState } from '@/workspace/states/workspaceAuthProvidersState';
|
||||
import { AppPath } from '@/types/AppPath';
|
||||
import { useRedirectToWorkspaceDomain } from '@/domain-manager/hooks/useRedirectToWorkspaceDomain';
|
||||
import { useRedirect } from '@/domain-manager/hooks/useRedirect';
|
||||
import { workspaceAuthProvidersState } from '@/workspace/states/workspaceAuthProvidersState';
|
||||
|
||||
export const useAuth = () => {
|
||||
const setTokenPair = useSetRecoilState(tokenPairState);
|
||||
@ -382,6 +383,7 @@ export const useAuth = () => {
|
||||
params: {
|
||||
workspacePersonalInviteToken?: string;
|
||||
workspaceInviteHash?: string;
|
||||
billingCheckoutSession?: BillingCheckoutSession;
|
||||
},
|
||||
) => {
|
||||
const url = new URL(`${REACT_APP_SERVER_BASE_URL}${path}`);
|
||||
@ -394,6 +396,12 @@ export const useAuth = () => {
|
||||
params.workspacePersonalInviteToken,
|
||||
);
|
||||
}
|
||||
if (isDefined(params.billingCheckoutSession)) {
|
||||
url.searchParams.set(
|
||||
'billingCheckoutSessionState',
|
||||
JSON.stringify(params.billingCheckoutSession),
|
||||
);
|
||||
}
|
||||
|
||||
if (isDefined(workspaceSubdomain)) {
|
||||
url.searchParams.set('workspaceSubdomain', workspaceSubdomain);
|
||||
@ -408,6 +416,7 @@ export const useAuth = () => {
|
||||
(params: {
|
||||
workspacePersonalInviteToken?: string;
|
||||
workspaceInviteHash?: string;
|
||||
billingCheckoutSession?: BillingCheckoutSession;
|
||||
}) => {
|
||||
redirect(buildRedirectUrl('/auth/google', params));
|
||||
},
|
||||
@ -418,6 +427,7 @@ export const useAuth = () => {
|
||||
(params: {
|
||||
workspacePersonalInviteToken?: string;
|
||||
workspaceInviteHash?: string;
|
||||
billingCheckoutSession?: BillingCheckoutSession;
|
||||
}) => {
|
||||
redirect(buildRedirectUrl('/auth/microsoft', params));
|
||||
},
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { useParams, useSearchParams } from 'react-router-dom';
|
||||
import { useAuth } from '@/auth/hooks/useAuth';
|
||||
import { useSignInWithGoogle } from '@/auth/sign-in-up/hooks/useSignInWithGoogle';
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { useParams, useSearchParams } from 'react-router-dom';
|
||||
import { BillingPlanKey, SubscriptionInterval } from '~/generated/graphql';
|
||||
import { getJestMetadataAndApolloMocksWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksWrapper';
|
||||
|
||||
jest.mock('react-router-dom', () => ({
|
||||
useParams: jest.fn(),
|
||||
@ -13,10 +15,24 @@ jest.mock('@/auth/hooks/useAuth', () => ({
|
||||
}));
|
||||
|
||||
describe('useSignInWithGoogle', () => {
|
||||
const mockBillingCheckoutSession = {
|
||||
plan: BillingPlanKey.Pro,
|
||||
interval: SubscriptionInterval.Month,
|
||||
requirePaymentMethod: true,
|
||||
skipPlanPage: false,
|
||||
};
|
||||
|
||||
const Wrapper = getJestMetadataAndApolloMocksWrapper({
|
||||
apolloMocks: [],
|
||||
});
|
||||
|
||||
it('should call signInWithGoogle with correct params', () => {
|
||||
const signInWithGoogleMock = jest.fn();
|
||||
const mockUseParams = { workspaceInviteHash: 'testHash' };
|
||||
const mockSearchParams = new URLSearchParams('inviteToken=testToken');
|
||||
|
||||
const mockSearchParams = new URLSearchParams(
|
||||
'inviteToken=testToken&billingCheckoutSessionState={"plan":"Pro","interval":"Month","requirePaymentMethod":true,"skipPlanPage":false}',
|
||||
);
|
||||
|
||||
(useParams as jest.Mock).mockReturnValue(mockUseParams);
|
||||
(useSearchParams as jest.Mock).mockReturnValue([mockSearchParams]);
|
||||
@ -24,12 +40,15 @@ describe('useSignInWithGoogle', () => {
|
||||
signInWithGoogle: signInWithGoogleMock,
|
||||
});
|
||||
|
||||
const { result } = renderHook(() => useSignInWithGoogle());
|
||||
const { result } = renderHook(() => useSignInWithGoogle(), {
|
||||
wrapper: Wrapper,
|
||||
});
|
||||
result.current.signInWithGoogle();
|
||||
|
||||
expect(signInWithGoogleMock).toHaveBeenCalledWith({
|
||||
workspaceInviteHash: 'testHash',
|
||||
workspacePersonalInviteToken: 'testToken',
|
||||
billingCheckoutSession: mockBillingCheckoutSession,
|
||||
});
|
||||
});
|
||||
|
||||
@ -44,12 +63,15 @@ describe('useSignInWithGoogle', () => {
|
||||
signInWithGoogle: signInWithGoogleMock,
|
||||
});
|
||||
|
||||
const { result } = renderHook(() => useSignInWithGoogle());
|
||||
const { result } = renderHook(() => useSignInWithGoogle(), {
|
||||
wrapper: Wrapper,
|
||||
});
|
||||
result.current.signInWithGoogle();
|
||||
|
||||
expect(signInWithGoogleMock).toHaveBeenCalledWith({
|
||||
workspaceInviteHash: 'testHash',
|
||||
workspacePersonalInviteToken: undefined,
|
||||
billingCheckoutSession: mockBillingCheckoutSession,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { useParams, useSearchParams } from 'react-router-dom';
|
||||
import { useAuth } from '@/auth/hooks/useAuth';
|
||||
import { useSignInWithMicrosoft } from '@/auth/sign-in-up/hooks/useSignInWithMicrosoft';
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { useParams, useSearchParams } from 'react-router-dom';
|
||||
import { getJestMetadataAndApolloMocksWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksWrapper';
|
||||
|
||||
jest.mock('react-router-dom', () => ({
|
||||
useParams: jest.fn(),
|
||||
@ -13,6 +14,17 @@ jest.mock('@/auth/hooks/useAuth', () => ({
|
||||
}));
|
||||
|
||||
describe('useSignInWithMicrosoft', () => {
|
||||
const Wrapper = getJestMetadataAndApolloMocksWrapper({
|
||||
apolloMocks: [],
|
||||
});
|
||||
|
||||
const mockBillingCheckoutSession = {
|
||||
plan: 'PRO',
|
||||
interval: 'Month',
|
||||
requirePaymentMethod: true,
|
||||
skipPlanPage: false,
|
||||
};
|
||||
|
||||
it('should call signInWithMicrosoft with the correct parameters', () => {
|
||||
const workspaceInviteHashMock = 'testHash';
|
||||
const inviteTokenMock = 'testToken';
|
||||
@ -28,12 +40,15 @@ describe('useSignInWithMicrosoft', () => {
|
||||
signInWithMicrosoft: signInWithMicrosoftMock,
|
||||
});
|
||||
|
||||
const { result } = renderHook(() => useSignInWithMicrosoft());
|
||||
const { result } = renderHook(() => useSignInWithMicrosoft(), {
|
||||
wrapper: Wrapper,
|
||||
});
|
||||
result.current.signInWithMicrosoft();
|
||||
|
||||
expect(signInWithMicrosoftMock).toHaveBeenCalledWith({
|
||||
workspaceInviteHash: workspaceInviteHashMock,
|
||||
workspacePersonalInviteToken: inviteTokenMock,
|
||||
billingCheckoutSession: mockBillingCheckoutSession,
|
||||
});
|
||||
});
|
||||
|
||||
@ -49,10 +64,13 @@ describe('useSignInWithMicrosoft', () => {
|
||||
signInWithMicrosoft: signInWithMicrosoftMock,
|
||||
});
|
||||
|
||||
const { result } = renderHook(() => useSignInWithMicrosoft());
|
||||
const { result } = renderHook(() => useSignInWithMicrosoft(), {
|
||||
wrapper: Wrapper,
|
||||
});
|
||||
result.current.signInWithMicrosoft();
|
||||
|
||||
expect(signInWithMicrosoftMock).toHaveBeenCalledWith({
|
||||
billingCheckoutSession: mockBillingCheckoutSession,
|
||||
workspaceInviteHash: workspaceInviteHashMock,
|
||||
workspacePersonalInviteToken: undefined,
|
||||
});
|
||||
|
||||
@ -1,15 +1,27 @@
|
||||
import { useParams, useSearchParams } from 'react-router-dom';
|
||||
|
||||
import { useAuth } from '@/auth/hooks/useAuth';
|
||||
import { BillingCheckoutSession } from '@/auth/types/billingCheckoutSession.type';
|
||||
|
||||
export const useSignInWithGoogle = () => {
|
||||
const workspaceInviteHash = useParams().workspaceInviteHash;
|
||||
const [searchParams] = useSearchParams();
|
||||
const workspacePersonalInviteToken =
|
||||
searchParams.get('inviteToken') ?? undefined;
|
||||
const billingCheckoutSession = {
|
||||
plan: 'PRO',
|
||||
interval: 'Month',
|
||||
requirePaymentMethod: true,
|
||||
skipPlanPage: false,
|
||||
} as BillingCheckoutSession;
|
||||
|
||||
const { signInWithGoogle } = useAuth();
|
||||
return {
|
||||
signInWithGoogle: () =>
|
||||
signInWithGoogle({ workspaceInviteHash, workspacePersonalInviteToken }),
|
||||
signInWithGoogle({
|
||||
workspaceInviteHash,
|
||||
workspacePersonalInviteToken,
|
||||
billingCheckoutSession,
|
||||
}),
|
||||
};
|
||||
};
|
||||
|
||||
@ -1,18 +1,23 @@
|
||||
import { useParams, useSearchParams } from 'react-router-dom';
|
||||
|
||||
import { useAuth } from '@/auth/hooks/useAuth';
|
||||
import { billingCheckoutSessionState } from '@/auth/states/billingCheckoutSessionState';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
export const useSignInWithMicrosoft = () => {
|
||||
const workspaceInviteHash = useParams().workspaceInviteHash;
|
||||
const [searchParams] = useSearchParams();
|
||||
const workspacePersonalInviteToken =
|
||||
searchParams.get('inviteToken') ?? undefined;
|
||||
const billingCheckoutSession = useRecoilValue(billingCheckoutSessionState);
|
||||
|
||||
const { signInWithMicrosoft } = useAuth();
|
||||
return {
|
||||
signInWithMicrosoft: () =>
|
||||
signInWithMicrosoft({
|
||||
workspaceInviteHash,
|
||||
workspacePersonalInviteToken,
|
||||
billingCheckoutSession,
|
||||
}),
|
||||
};
|
||||
};
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { createState } from 'twenty-ui';
|
||||
import { createState } from '@ui/utilities/state/utils/createState';
|
||||
import { UserExists } from '~/generated/graphql';
|
||||
|
||||
export const availableSSOIdentityProvidersForAuthState = createState<
|
||||
|
||||
@ -0,0 +1,39 @@
|
||||
import { BillingCheckoutSession } from '@/auth/types/billingCheckoutSession.type';
|
||||
import { createState } from '@ui/utilities/state/utils/createState';
|
||||
import { syncEffect } from 'recoil-sync';
|
||||
import { BillingPlanKey, SubscriptionInterval } from '~/generated/graphql';
|
||||
|
||||
export const billingCheckoutSessionState = createState<BillingCheckoutSession>({
|
||||
key: 'billingCheckoutSessionState',
|
||||
defaultValue: {
|
||||
plan: BillingPlanKey.Pro,
|
||||
interval: SubscriptionInterval.Month,
|
||||
requirePaymentMethod: true,
|
||||
skipPlanPage: false,
|
||||
},
|
||||
effects: [
|
||||
syncEffect({
|
||||
refine: (value: unknown) => {
|
||||
if (
|
||||
typeof value === 'object' &&
|
||||
value !== null &&
|
||||
'plan' in value &&
|
||||
'interval' in value &&
|
||||
'requirePaymentMethod' in value &&
|
||||
'skipPlanPage' in value
|
||||
) {
|
||||
return {
|
||||
type: 'success',
|
||||
value: value as BillingCheckoutSession,
|
||||
warnings: [],
|
||||
} as const;
|
||||
}
|
||||
return {
|
||||
type: 'failure',
|
||||
message: 'Invalid BillingCheckoutSessionState',
|
||||
path: [] as any,
|
||||
} as const;
|
||||
},
|
||||
}),
|
||||
],
|
||||
});
|
||||
@ -1,4 +1,4 @@
|
||||
import { createState } from 'twenty-ui';
|
||||
import { createState } from '@ui/utilities/state/utils/createState';
|
||||
|
||||
import { User } from '~/generated/graphql';
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { createState } from 'twenty-ui';
|
||||
import { createState } from '@ui/utilities/state/utils/createState';
|
||||
|
||||
import { WorkspaceMember } from '@/workspace-member/types/WorkspaceMember';
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { CurrentWorkspaceMember } from '@/auth/states/currentWorkspaceMemberState';
|
||||
import { createState } from 'twenty-ui';
|
||||
import { createState } from '@ui/utilities/state/utils/createState';
|
||||
|
||||
export const currentWorkspaceMembersState = createState<
|
||||
CurrentWorkspaceMember[]
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { createState } from 'twenty-ui';
|
||||
import { createState } from '@ui/utilities/state/utils/createState';
|
||||
|
||||
import { Workspace } from '~/generated/graphql';
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { createState } from 'twenty-ui';
|
||||
import { createState } from '@ui/utilities/state/utils/createState';
|
||||
|
||||
export const isCurrentUserLoadedState = createState<boolean>({
|
||||
key: 'isCurrentUserLoadedState',
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { createState } from 'twenty-ui';
|
||||
import { createState } from '@ui/utilities/state/utils/createState';
|
||||
|
||||
export const isVerifyPendingState = createState<boolean>({
|
||||
key: 'isVerifyPendingState',
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { createState } from 'twenty-ui';
|
||||
import { createState } from '@ui/utilities/state/utils/createState';
|
||||
|
||||
export const previousUrlState = createState<string>({
|
||||
key: 'previousUrlState',
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { createState } from 'twenty-ui';
|
||||
import { SignInUpMode } from '@/auth/types/signInUpMode';
|
||||
import { createState } from '@ui/utilities/state/utils/createState';
|
||||
|
||||
export const signInUpModeState = createState<SignInUpMode>({
|
||||
key: 'signInUpModeState',
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { createState } from 'twenty-ui';
|
||||
import { createState } from '@ui/utilities/state/utils/createState';
|
||||
|
||||
export enum SignInUpStep {
|
||||
Init = 'init',
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { createState } from 'twenty-ui';
|
||||
import { createState } from '@ui/utilities/state/utils/createState';
|
||||
|
||||
import { AuthTokenPair } from '~/generated/graphql';
|
||||
import { cookieStorageEffect } from '~/utils/recoil-effects';
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { createState } from 'twenty-ui';
|
||||
import { createState } from '@ui/utilities/state/utils/createState';
|
||||
import { PublicWorkspaceDataOutput } from '~/generated/graphql';
|
||||
|
||||
export const workspacePublicDataState =
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { createState } from 'twenty-ui';
|
||||
import { createState } from '@ui/utilities/state/utils/createState';
|
||||
|
||||
import { Workspace } from '~/generated/graphql';
|
||||
|
||||
|
||||
@ -0,0 +1,9 @@
|
||||
import { SubscriptionInterval } from '~/generated-metadata/graphql';
|
||||
import { BillingPlanKey } from '~/generated/graphql';
|
||||
|
||||
export type BillingCheckoutSession = {
|
||||
plan: BillingPlanKey;
|
||||
interval: SubscriptionInterval;
|
||||
requirePaymentMethod: boolean;
|
||||
skipPlanPage: boolean;
|
||||
};
|
||||
Reference in New Issue
Block a user