diff --git a/.changeset/hot-jars-smell.md b/.changeset/hot-jars-smell.md new file mode 100644 index 00000000000..e193a730ace --- /dev/null +++ b/.changeset/hot-jars-smell.md @@ -0,0 +1,6 @@ +--- +'@clerk/clerk-js': patch +'@clerk/backend': patch +--- + +Update the supported API version to `2025-10-01`. diff --git a/packages/backend/src/constants.ts b/packages/backend/src/constants.ts index ac7b9fa1d30..0e5d7acb7f3 100644 --- a/packages/backend/src/constants.ts +++ b/packages/backend/src/constants.ts @@ -3,7 +3,7 @@ export const API_VERSION = 'v1'; export const USER_AGENT = `${PACKAGE_NAME}@${PACKAGE_VERSION}`; export const MAX_CACHE_LAST_UPDATED_AT_SECONDS = 5 * 60; -export const SUPPORTED_BAPI_VERSION = '2025-04-10'; +export const SUPPORTED_BAPI_VERSION = '2025-10-01'; const Attributes = { AuthToken: '__clerkAuthToken', diff --git a/packages/backend/src/tokens/__tests__/handshake.test.ts b/packages/backend/src/tokens/__tests__/handshake.test.ts index 29eed7d2838..16444a964db 100644 --- a/packages/backend/src/tokens/__tests__/handshake.test.ts +++ b/packages/backend/src/tokens/__tests__/handshake.test.ts @@ -427,7 +427,7 @@ describe('HandshakeService', () => { // Verify all required parameters are present expect(url.searchParams.get('redirect_url')).toBeDefined(); - expect(url.searchParams.get('__clerk_api_version')).toBe('2025-04-10'); + expect(url.searchParams.get('__clerk_api_version')).toBe('2025-10-01'); expect(url.searchParams.get(constants.QueryParameters.SuffixedCookies)).toMatch(/^(true|false)$/); expect(url.searchParams.get(constants.QueryParameters.HandshakeReason)).toBe('test-reason'); }); diff --git a/packages/clerk-js/src/core/constants.ts b/packages/clerk-js/src/core/constants.ts index dac4f71a494..674b184a351 100644 --- a/packages/clerk-js/src/core/constants.ts +++ b/packages/clerk-js/src/core/constants.ts @@ -54,4 +54,4 @@ export const SIGN_UP_MODES = { } satisfies Record; // This is the currently supported version of the Frontend API -export const SUPPORTED_FAPI_VERSION = '2025-04-10'; +export const SUPPORTED_FAPI_VERSION = '2025-10-01'; diff --git a/packages/clerk-js/src/core/resources/BillingPlan.ts b/packages/clerk-js/src/core/resources/BillingPlan.ts index dfd5f96da9d..c8383c54eef 100644 --- a/packages/clerk-js/src/core/resources/BillingPlan.ts +++ b/packages/clerk-js/src/core/resources/BillingPlan.ts @@ -8,8 +8,8 @@ export class BillingPlan extends BaseResource implements BillingPlanResource { id!: string; name!: string; fee!: BillingMoneyAmount; - annualFee!: BillingMoneyAmount; - annualMonthlyFee!: BillingMoneyAmount; + annualFee: BillingMoneyAmount | null = null; + annualMonthlyFee: BillingMoneyAmount | null = null; description!: string; isDefault!: boolean; isRecurring!: boolean; @@ -32,11 +32,17 @@ export class BillingPlan extends BaseResource implements BillingPlanResource { return this; } + console.log('data', { + fee: data.fee, + annual_fee: data.annual_fee, + annual_monthly_fee: data.annual_monthly_fee, + }); + this.id = data.id; this.name = data.name; this.fee = billingMoneyAmountFromJSON(data.fee); - this.annualFee = billingMoneyAmountFromJSON(data.annual_fee); - this.annualMonthlyFee = billingMoneyAmountFromJSON(data.annual_monthly_fee); + this.annualFee = data.annual_fee ? billingMoneyAmountFromJSON(data.annual_fee) : null; + this.annualMonthlyFee = data.annual_monthly_fee ? billingMoneyAmountFromJSON(data.annual_monthly_fee) : null; this.description = data.description; this.isDefault = data.is_default; this.isRecurring = data.is_recurring; diff --git a/packages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsx b/packages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsx index 1fd038d7578..460c7e74d54 100644 --- a/packages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsx +++ b/packages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsx @@ -39,7 +39,11 @@ export const CheckoutForm = withCardStateProvider(() => { const showPastDue = !!totals.pastDue?.amount && totals.pastDue.amount > 0; const showDowngradeInfo = !isImmediatePlanChange; - const fee = planPeriod === 'month' ? plan.fee : plan.annualMonthlyFee; + const fee = + planPeriod === 'month' + ? plan.fee + : // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + plan.annualMonthlyFee!; return ( @@ -166,10 +170,10 @@ const useCheckoutMutations = () => { e.preventDefault(); const data = new FormData(e.currentTarget); - const paymentSourceId = data.get(HIDDEN_INPUT_NAME) as string; + const paymentMethodId = data.get(HIDDEN_INPUT_NAME) as string; return confirmCheckout({ - paymentSourceId, + paymentMethodId, }); }; diff --git a/packages/clerk-js/src/ui/components/PaymentAttempts/PaymentAttemptPage.tsx b/packages/clerk-js/src/ui/components/PaymentAttempts/PaymentAttemptPage.tsx index 1721aae96f7..24d5f795b80 100644 --- a/packages/clerk-js/src/ui/components/PaymentAttempts/PaymentAttemptPage.tsx +++ b/packages/clerk-js/src/ui/components/PaymentAttempts/PaymentAttemptPage.tsx @@ -215,7 +215,10 @@ function PaymentAttemptBody({ subscriptionItem }: { subscriptionItem: BillingSub } const fee = - subscriptionItem.planPeriod === 'month' ? subscriptionItem.plan.fee : subscriptionItem.plan.annualMonthlyFee; + subscriptionItem.planPeriod === 'month' + ? subscriptionItem.plan.fee + : // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + subscriptionItem.plan.annualMonthlyFee!; return ( ((props, ref) => { const { plan, closeSlot, planPeriod, setPlanPeriod } = props; const fee = useMemo(() => { - if (plan.annualMonthlyFee.amount <= 0) { + if (!plan.annualMonthlyFee) { return plan.fee; } return planPeriod === 'annual' ? plan.annualMonthlyFee : plan.fee; @@ -333,7 +333,7 @@ const Header = React.forwardRef((props, ref) => { - {plan.annualMonthlyFee.amount > 0 ? ( + {plan.annualMonthlyFee ? ( ({ diff --git a/packages/clerk-js/src/ui/components/Plans/__tests__/PlanDetails.test.tsx b/packages/clerk-js/src/ui/components/Plans/__tests__/PlanDetails.test.tsx index 1c9faf04ab4..fab81348ba5 100644 --- a/packages/clerk-js/src/ui/components/Plans/__tests__/PlanDetails.test.tsx +++ b/packages/clerk-js/src/ui/components/Plans/__tests__/PlanDetails.test.tsx @@ -221,18 +221,8 @@ describe('PlanDetails', () => { currencySymbol: '$', currency: 'USD', }, - annualFee: { - amount: 0, - amountFormatted: '0.00', - currencySymbol: '$', - currency: 'USD', - }, - annualMonthlyFee: { - amount: 0, - amountFormatted: '0.00', - currencySymbol: '$', - currency: 'USD', - }, + annualFee: null, + annualMonthlyFee: null, }; const { wrapper } = await createFixtures(f => { @@ -265,18 +255,8 @@ describe('PlanDetails', () => { currencySymbol: '$', currency: 'USD', }, - annualFee: { - amount: 0, - amountFormatted: '0.00', - currencySymbol: '$', - currency: 'USD', - }, - annualMonthlyFee: { - amount: 0, - amountFormatted: '0.00', - currencySymbol: '$', - currency: 'USD', - }, + annualFee: null, + annualMonthlyFee: null, isDefault: true, }; diff --git a/packages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.tsx b/packages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.tsx index 84317a3ca3d..2a5d19b85ec 100644 --- a/packages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.tsx +++ b/packages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.tsx @@ -282,13 +282,17 @@ const CardHeader = React.forwardRef((props, ref const { plan, isCompact, planPeriod, setPlanPeriod, badge } = props; const { name, annualMonthlyFee } = plan; - const planSupportsAnnual = annualMonthlyFee.amount > 0; + const planSupportsAnnual = Boolean(annualMonthlyFee); const fee = React.useMemo(() => { if (!planSupportsAnnual) { return plan.fee; } - return planPeriod === 'annual' ? plan.annualMonthlyFee : plan.fee; + + return planPeriod === 'annual' + ? // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + plan.annualMonthlyFee! + : plan.fee; }, [planSupportsAnnual, planPeriod, plan.fee, plan.annualMonthlyFee]); const feeFormatted = React.useMemo(() => { diff --git a/packages/clerk-js/src/ui/components/PricingTable/PricingTableMatrix.tsx b/packages/clerk-js/src/ui/components/PricingTable/PricingTableMatrix.tsx index 0ec8b725f05..b9629bc3fd2 100644 --- a/packages/clerk-js/src/ui/components/PricingTable/PricingTableMatrix.tsx +++ b/packages/clerk-js/src/ui/components/PricingTable/PricingTableMatrix.tsx @@ -60,7 +60,7 @@ export function PricingTableMatrix({ const gridTemplateColumns = React.useMemo(() => `repeat(${plans.length + 1}, minmax(9.375rem,1fr))`, [plans.length]); - const renderBillingCycleControls = React.useMemo(() => plans.some(plan => plan.annualMonthlyFee.amount > 0), [plans]); + const renderBillingCycleControls = React.useMemo(() => plans.some(plan => Boolean(plan.annualMonthlyFee)), [plans]); const getAllFeatures = React.useMemo(() => { const featuresSet = new Set(); @@ -156,12 +156,11 @@ export function PricingTableMatrix({ {plans.map(plan => { const highlight = plan.slug === highlightedPlan; - const planFee = - plan.annualMonthlyFee.amount <= 0 - ? plan.fee - : planPeriod === 'annual' - ? plan.annualMonthlyFee - : plan.fee; + const planFee = !plan.annualMonthlyFee + ? plan.fee + : planPeriod === 'annual' + ? plan.annualMonthlyFee + : plan.fee; return ( - {plan.annualMonthlyFee.amount > 0 ? ( + {plan.annualMonthlyFee ? ( 0; + const isSwitchingPaidPeriod = planPeriod !== subscription.planPeriod && Boolean(plan.annualMonthlyFee); const isActiveFreeTrial = plan.freeTrialEnabled && subscription.isFreeTrial; if (isCanceled || isSwitchingPaidPeriod) { diff --git a/packages/clerk-js/src/ui/components/SubscriptionDetails/__tests__/SubscriptionDetails.test.tsx b/packages/clerk-js/src/ui/components/SubscriptionDetails/__tests__/SubscriptionDetails.test.tsx index bb0dbffce26..038f9117152 100644 --- a/packages/clerk-js/src/ui/components/SubscriptionDetails/__tests__/SubscriptionDetails.test.tsx +++ b/packages/clerk-js/src/ui/components/SubscriptionDetails/__tests__/SubscriptionDetails.test.tsx @@ -262,18 +262,8 @@ describe('SubscriptionDetails', () => { currencySymbol: '$', currency: 'USD', }, - annualFee: { - amount: 0, - amountFormatted: '0.00', - currencySymbol: '$', - currency: 'USD', - }, - annualMonthlyFee: { - amount: 0, - amountFormatted: '0.00', - currencySymbol: '$', - currency: 'USD', - }, + annualFee: null, + annualMonthlyFee: null, description: 'Free Plan description', hasBaseFee: false, isRecurring: true, diff --git a/packages/clerk-js/src/ui/components/SubscriptionDetails/index.tsx b/packages/clerk-js/src/ui/components/SubscriptionDetails/index.tsx index a8828713205..f9292278433 100644 --- a/packages/clerk-js/src/ui/components/SubscriptionDetails/index.tsx +++ b/packages/clerk-js/src/ui/components/SubscriptionDetails/index.tsx @@ -374,7 +374,7 @@ const SubscriptionCardActions = ({ subscription }: { subscription: BillingSubscr const canManageBilling = subscriberType === 'user' || canOrgManageBilling; const isSwitchable = - ((subscription.planPeriod === 'month' && subscription.plan.annualMonthlyFee.amount > 0) || + ((subscription.planPeriod === 'month' && Boolean(subscription.plan.annualMonthlyFee)) || subscription.planPeriod === 'annual') && subscription.status !== 'past_due'; const isFree = isFreePlan(subscription.plan); @@ -409,8 +409,10 @@ const SubscriptionCardActions = ({ subscription }: { subscription: BillingSubscr label: subscription.planPeriod === 'month' ? localizationKeys('billing.switchToAnnualWithAnnualPrice', { - price: normalizeFormatted(subscription.plan.annualFee.amountFormatted), - currency: subscription.plan.annualFee.currencySymbol, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + price: normalizeFormatted(subscription.plan.annualFee!.amountFormatted), + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + currency: subscription.plan.annualFee!.currencySymbol, }) : localizationKeys('billing.switchToMonthlyWithPrice', { price: normalizeFormatted(subscription.plan.fee.amountFormatted), @@ -480,7 +482,11 @@ const SubscriptionCardActions = ({ subscription }: { subscription: BillingSubscr const SubscriptionCard = ({ subscription }: { subscription: BillingSubscriptionItemResource }) => { const { t } = useLocalizations(); - const fee = subscription.planPeriod === 'month' ? subscription.plan.fee : subscription.plan.annualFee; + const fee = + subscription.planPeriod === 'month' + ? subscription.plan.fee + : // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + subscription.plan.annualFee!; return ( { diff --git a/packages/clerk-js/src/ui/contexts/components/Plans.tsx b/packages/clerk-js/src/ui/contexts/components/Plans.tsx index f76d0d7e0fb..5136cf42c83 100644 --- a/packages/clerk-js/src/ui/contexts/components/Plans.tsx +++ b/packages/clerk-js/src/ui/contexts/components/Plans.tsx @@ -213,7 +213,7 @@ export const usePlansContext = () => { const subscription = sub ?? (plan ? activeOrUpcomingSubscriptionWithPlanPeriod(plan, selectedPlanPeriod) : undefined); let _selectedPlanPeriod = selectedPlanPeriod; - const isEligibleForSwitchToAnnual = (plan?.annualMonthlyFee.amount ?? 0) > 0; + const isEligibleForSwitchToAnnual = Boolean(plan?.annualMonthlyFee); if (_selectedPlanPeriod === 'annual' && !isEligibleForSwitchToAnnual) { _selectedPlanPeriod = 'month'; @@ -326,7 +326,7 @@ export const usePlansContext = () => { clerk.__internal_openCheckout({ planId: plan.id, // if the plan doesn't support annual, use monthly - planPeriod: planPeriod === 'annual' && plan.annualMonthlyFee.amount === 0 ? 'month' : planPeriod, + planPeriod: planPeriod === 'annual' && !plan.annualMonthlyFee ? 'month' : planPeriod, for: subscriberType, onSubscriptionComplete: () => { revalidateAll(); diff --git a/packages/clerk-js/src/utils/billing.ts b/packages/clerk-js/src/utils/billing.ts index 496dd10520b..edc373a6968 100644 --- a/packages/clerk-js/src/utils/billing.ts +++ b/packages/clerk-js/src/utils/billing.ts @@ -25,15 +25,17 @@ export const billingTotalsFromJSON = ; /** - * The `confirm()` method accepts the following parameters. **Only one of `paymentSourceId`, `paymentToken`, or `useTestCard` should be provided.** + * The `confirm()` method accepts the following parameters. **Only one of `paymentMethodId`, `paymentToken`, or `useTestCard` should be provided.** * * @unionReturnHeadings - * ["paymentSourceId", "paymentToken", "useTestCard"] + * ["paymentMethodId", "paymentToken", "useTestCard"] * * @experimental This is an experimental API for the Billing feature that is available under a public beta, and the API is subject to change. It is advised to [pin](https://clerk.com/docs/pinning) the SDK version and the clerk-js version to avoid breaking changes. */ @@ -700,7 +700,7 @@ export type ConfirmCheckoutParams = /** * The ID of a saved payment method to use for this checkout. */ - paymentSourceId?: string; + paymentMethodId?: string; } | { /** diff --git a/packages/types/src/json.ts b/packages/types/src/json.ts index 2da7732ac6a..9f63617cf7d 100644 --- a/packages/types/src/json.ts +++ b/packages/types/src/json.ts @@ -634,8 +634,8 @@ export interface BillingPlanJSON extends ClerkResourceJSON { id: string; name: string; fee: BillingMoneyAmountJSON; - annual_fee: BillingMoneyAmountJSON; - annual_monthly_fee: BillingMoneyAmountJSON; + annual_fee: BillingMoneyAmountJSON | null; + annual_monthly_fee: BillingMoneyAmountJSON | null; amount: number; amount_formatted: string; annual_amount: number;