Skip to content
Merged
11 changes: 11 additions & 0 deletions .changeset/metal-zebras-jog.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
'@clerk/tanstack-react-start': patch
'@clerk/react-router': patch
'@clerk/clerk-js': patch
'@clerk/nextjs': patch
'@clerk/clerk-react': patch
Copy link
Member

Choose a reason for hiding this comment

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

I think we are missing Astro

Copy link
Member Author

Choose a reason for hiding this comment

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

Its not merged #5774

'@clerk/remix': patch
'@clerk/types': patch
---

Introduce `checkoutContinueUrl` option.
2 changes: 1 addition & 1 deletion packages/clerk-js/bundlewatch.config.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
{ "path": "./dist/clerk.browser.js", "maxSize": "68KB" },
{ "path": "./dist/clerk.legacy.browser.js", "maxSize": "110KB" },
{ "path": "./dist/clerk.headless*.js", "maxSize": "52KB" },
{ "path": "./dist/ui-common*.js", "maxSize": "102KB" },
{ "path": "./dist/ui-common*.js", "maxSize": "102.5KB" },
{ "path": "./dist/vendors*.js", "maxSize": "39KB" },
{ "path": "./dist/coinbase*.js", "maxSize": "38KB" },
{ "path": "./dist/createorganization*.js", "maxSize": "5KB" },
Expand Down
9 changes: 9 additions & 0 deletions packages/clerk-js/src/core/clerk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ const defaultOptions: ClerkOptions = {
signInForceRedirectUrl: undefined,
signUpForceRedirectUrl: undefined,
treatPendingAsSignedOut: true,
__experimental_checkoutContinueUrl: undefined,
};

export class Clerk implements ClerkInterface {
Expand Down Expand Up @@ -1377,6 +1378,14 @@ export class Clerk implements ClerkInterface {
return this.buildUrlWithAuth(this.#options.afterSignOutUrl);
}

public __experimental_buildCheckoutContinueUrl(): string {
if (!this.#options.__experimental_checkoutContinueUrl) {
return this.buildAfterSignInUrl();
}

return this.#options.__experimental_checkoutContinueUrl;
}

public buildWaitlistUrl(options?: { initialValues?: Record<string, string> }): string {
if (!this.environment || !this.environment.displayConfig) {
return '';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import type { __experimental_CommerceCheckoutResource } from '@clerk/types';

import { useCheckoutContext } from '../../contexts';
import { Box, Button, descriptors, Heading, localizationKeys, Span, Text } from '../../customizables';
import { Drawer, LineItems, useDrawerContext } from '../../elements';
import { transitionDurationValues, transitionTiming } from '../../foundations/transitions';
import { useRouter } from '../../router';
import { animations } from '../../styledSystem';
import { formatDate } from '../../utils';

const capitalize = (name: string) => name[0].toUpperCase() + name.slice(1);

export const CheckoutComplete = ({
Expand All @@ -14,9 +17,14 @@ export const CheckoutComplete = ({
checkout: __experimental_CommerceCheckoutResource;
isMotionSafe: boolean;
}) => {
const router = useRouter();
const { setIsOpen } = useDrawerContext();
const { __experimental_checkoutContinueUrl } = useCheckoutContext();

const handleClose = () => {
if (__experimental_checkoutContinueUrl) {
void router.navigate(__experimental_checkoutContinueUrl);
}
if (setIsOpen) {
setIsOpen(false);
}
Expand Down
20 changes: 18 additions & 2 deletions packages/clerk-js/src/ui/contexts/components/Checkout.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,36 @@
import { createContext, useContext } from 'react';
import { useClerk } from '@clerk/shared/react';
import { createContext, useContext, useMemo } from 'react';

import type { __experimental_CheckoutCtx } from '../../types';

export const __experimental_CheckoutContext = createContext<__experimental_CheckoutCtx | null>(null);

export const useCheckoutContext = () => {
const context = useContext(__experimental_CheckoutContext);
const clerk = useClerk();

if (!context || context.componentName !== 'Checkout') {
throw new Error('Clerk: useCheckoutContext called outside Checkout.');
}

const checkoutContinueUrl = useMemo(() => {
// When we're rendered via the PricingTable with mode = 'modal' we provide a `portalRoot` value
// we want to keep users within the context of the modal, so we do this to prevent navigating away
if (context.portalRoot) {
return undefined;
}

if (context.__experimental_checkoutContinueUrl) {
return context.__experimental_checkoutContinueUrl;
}

return clerk.__experimental_buildCheckoutContinueUrl?.();
}, [context.portalRoot, context.__experimental_checkoutContinueUrl, clerk]);

const { componentName, ...ctx } = context;

return {
...ctx,
componentName,
__experimental_checkoutContinueUrl: checkoutContinueUrl,
};
};
4 changes: 2 additions & 2 deletions packages/clerk-js/src/ui/hooks/useCheckout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { useFetch } from './useFetch';

export const useCheckout = (props: __experimental_CheckoutProps) => {
const { planId, planPeriod, subscriberType = 'user' } = props;
const { __experimental_commerce } = useClerk();
const clerk = useClerk();
const { organization } = useOrganization();
const [currentCheckout, setCurrentCheckout] = useState<__experimental_CommerceCheckoutResource | null>(null);

Expand All @@ -19,7 +19,7 @@ export const useCheckout = (props: __experimental_CheckoutProps) => {
revalidate,
error: _error,
} = useFetch(
__experimental_commerce?.__experimental_billing.startCheckout,
clerk.__experimental_commerce?.__experimental_billing.startCheckout,
{
planId,
planPeriod,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export function MountedCheckoutDrawer({
planPeriod={checkoutDrawer.props.planPeriod}
subscriberType={checkoutDrawer.props.subscriberType}
onSubscriptionComplete={checkoutDrawer.props.onSubscriptionComplete}
portalRoot={checkoutDrawer.props.portalRoot}
/>
)}
</LazyDrawerRenderer>
Expand Down
3 changes: 2 additions & 1 deletion packages/clerk-js/src/ui/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type {
__experimental_CheckoutContinueUrl,
__experimental_CheckoutProps,
__experimental_CommerceInvoiceResource,
__experimental_CommercePlanResource,
Expand Down Expand Up @@ -118,7 +119,7 @@ export type __experimental_PricingTableCtx = __experimental_PricingTableProps &

export type __experimental_CheckoutCtx = __experimental_CheckoutProps & {
componentName: 'Checkout';
};
} & __experimental_CheckoutContinueUrl;

export type __experimental_PaymentSourcesCtx = {
componentName: 'PaymentSources';
Expand Down
2 changes: 2 additions & 0 deletions packages/nextjs/src/utils/mergeNextClerkPropsWithEnv.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ export const mergeNextClerkPropsWithEnv = (props: Omit<NextClerkProviderProps, '
props.signUpFallbackRedirectUrl || process.env.NEXT_PUBLIC_CLERK_SIGN_UP_FALLBACK_REDIRECT_URL || '',
afterSignInUrl: props.afterSignInUrl || process.env.NEXT_PUBLIC_CLERK_AFTER_SIGN_IN_URL || '',
afterSignUpUrl: props.afterSignUpUrl || process.env.NEXT_PUBLIC_CLERK_AFTER_SIGN_UP_URL || '',
__experimental_checkoutContinueUrl:
props.__experimental_checkoutContinueUrl || process.env.NEXT_PUBLIC_CLERK_CHECKOUT_CONTINUE_URL || '',
telemetry: props.telemetry ?? {
disabled: isTruthy(process.env.NEXT_PUBLIC_CLERK_TELEMETRY_DISABLED),
debug: isTruthy(process.env.NEXT_PUBLIC_CLERK_TELEMETRY_DEBUG),
Expand Down
1 change: 1 addition & 0 deletions packages/react-router/src/utils/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,6 @@ export const getPublicEnvVariables = (context: AppLoadContext | undefined) => {
signUpFallbackRedirectUrl: getValue('CLERK_SIGN_UP_FALLBACK_REDIRECT_URL'),
afterSignInUrl: getValue('CLERK_AFTER_SIGN_IN_URL'),
afterSignUpUrl: getValue('CLERK_AFTER_SIGN_UP_URL'),
__experimental_checkoutContinueUrl: getValue('CLERK_CHECKOUT_CONTINUE_URL'),
};
};
9 changes: 9 additions & 0 deletions packages/react/src/isomorphicClerk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,15 @@ export class IsomorphicClerk implements IsomorphicLoadedClerk {
}
};

__experimental_buildCheckoutContinueUrl = (): string | void => {
const callback = () => this.clerkjs?.__experimental_buildCheckoutContinueUrl() || '';
if (this.clerkjs && this.loaded) {
return callback();
} else {
this.premountMethodCalls.set('__experimental_buildCheckoutContinueUrl', callback);
}
};

buildAfterMultiSessionSingleSignOutUrl = (): string | void => {
const callback = () => this.clerkjs?.buildAfterMultiSessionSingleSignOutUrl() || '';
if (this.clerkjs && this.loaded) {
Expand Down
3 changes: 3 additions & 0 deletions packages/remix/src/ssr/loadOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ export const loadOptions = (args: LoaderFunctionArgs, overrides: RootAuthLoaderO
overrides.signUpFallbackRedirectUrl || getEnvVariable('CLERK_SIGN_UP_FALLBACK_REDIRECT_URL', context) || '';
const afterSignInUrl = overrides.afterSignInUrl || getEnvVariable('CLERK_AFTER_SIGN_IN_URL', context) || '';
const afterSignUpUrl = overrides.afterSignUpUrl || getEnvVariable('CLERK_AFTER_SIGN_UP_URL', context) || '';
const __experimental_checkoutContinueUrl =
overrides.__experimental_checkoutContinueUrl || getEnvVariable('CLERK_CHECKOUT_CONTINUE_URL', context) || '';

let proxyUrl;
if (!!relativeOrAbsoluteProxyUrl && isProxyUrlRelative(relativeOrAbsoluteProxyUrl)) {
Expand Down Expand Up @@ -81,5 +83,6 @@ export const loadOptions = (args: LoaderFunctionArgs, overrides: RootAuthLoaderO
signUpForceRedirectUrl,
signInFallbackRedirectUrl,
signUpFallbackRedirectUrl,
__experimental_checkoutContinueUrl,
};
};
3 changes: 3 additions & 0 deletions packages/remix/src/ssr/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { AuthObject, Organization, Session, User, VerifyTokenOptions } from '@clerk/backend';
import type { RequestState } from '@clerk/backend/internal';
import type {
__experimental_CheckoutContinueUrl,
LegacyRedirectProps,
MultiDomainAndOrProxy,
SignInFallbackRedirectUrl,
Expand Down Expand Up @@ -36,13 +37,15 @@ export type RootAuthLoaderOptions = {
SignInFallbackRedirectUrl &
SignUpForceRedirectUrl &
SignUpFallbackRedirectUrl &
__experimental_CheckoutContinueUrl &
LegacyRedirectProps;

export type RequestStateWithRedirectUrls = RequestState &
SignInForceRedirectUrl &
SignInFallbackRedirectUrl &
SignUpForceRedirectUrl &
SignUpFallbackRedirectUrl &
__experimental_CheckoutContinueUrl &
LegacyRedirectProps;

export type RootAuthLoaderCallback<Options extends RootAuthLoaderOptions> = (
Expand Down
1 change: 1 addition & 0 deletions packages/remix/src/ssr/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ export function getResponseClerkState(requestState: RequestStateWithRedirectUrls
__signUpForceRedirectUrl: requestState.signUpForceRedirectUrl,
__signInFallbackRedirectUrl: requestState.signInFallbackRedirectUrl,
__signUpFallbackRedirectUrl: requestState.signUpFallbackRedirectUrl,
__experimental_checkoutContinueUrl: requestState.__experimental_checkoutContinueUrl,
__clerk_debug: debugRequestState(requestState),
__clerkJSUrl: getEnvVariable('CLERK_JS', context),
__clerkJSVersion: getEnvVariable('CLERK_JS_VERSION', context),
Expand Down
1 change: 1 addition & 0 deletions packages/tanstack-react-start/src/utils/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,6 @@ export const getPublicEnvVariables = (context?: H3EventContext) => {
telemetryDebug: isTruthy(getValue('CLERK_TELEMETRY_DEBUG')),
afterSignInUrl: getValue('CLERK_AFTER_SIGN_IN_URL'),
afterSignUpUrl: getValue('CLERK_AFTER_SIGN_UP_URL'),
__experimental_checkoutContinueUrl: getValue('CLERK_CHECKOUT_CONTINUE_URL'),
} as const;
};
13 changes: 13 additions & 0 deletions packages/types/src/clerk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import type { OAuthProvider, OAuthScope } from './oauth';
import type { OrganizationResource } from './organization';
import type { OrganizationCustomRoleKey } from './organizationMembership';
import type {
__experimental_CheckoutContinueUrl,
AfterMultiSessionSingleSignOutUrl,
AfterSignOutUrl,
LegacyRedirectProps,
Expand Down Expand Up @@ -565,6 +566,11 @@ export interface Clerk {
*/
buildAfterSignOutUrl(): string;

/**
* Returns the configured checkoutContinueUrl of the instance.
*/
__experimental_buildCheckoutContinueUrl(): string;

/**
* Returns the configured afterMultiSessionSingleSignOutUrl of the instance.
*/
Expand Down Expand Up @@ -814,6 +820,7 @@ export type ClerkOptions = PendingSessionOptions &
SignInFallbackRedirectUrl &
SignUpForceRedirectUrl &
SignUpFallbackRedirectUrl &
__experimental_CheckoutContinueUrl &
LegacyRedirectProps &
AfterSignOutUrl &
AfterMultiSessionSingleSignOutUrl & {
Expand Down Expand Up @@ -1563,6 +1570,7 @@ export type WaitlistModalProps = WaitlistProps;
type __experimental_PricingTableDefaultProps = {
ctaPosition?: 'top' | 'bottom';
collapseFeatures?: boolean;
__experimental_checkoutContinueUrl?: string;
};

type __experimental_PricingTableBaseProps = {
Expand All @@ -1584,6 +1592,11 @@ export type __experimental_CheckoutProps = {
onSubscriptionComplete?: () => void;
portalId?: string;
portalRoot?: PortalRoot;
/**
* Full URL or path to navigate to after checkout is complete and the user clicks the "Continue" button.
* @default undefined
*/
__experimental_checkoutContinueUrl?: string;
};

export type __experimental_PlanDetailsProps = {
Expand Down
7 changes: 7 additions & 0 deletions packages/types/src/redirects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,3 +121,10 @@ export type SignInForceRedirectUrl = {
*/
signInForceRedirectUrl?: string | null;
};

export type __experimental_CheckoutContinueUrl = {
/**
* The URL to navigate to after the user completes the checkout and clicks the "Continue" button.
*/
__experimental_checkoutContinueUrl?: string | null;
};
1 change: 1 addition & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.