From dad8cfc9e8d4c8ea873089490ebcc181868926f5 Mon Sep 17 00:00:00 2001 From: panteliselef Date: Mon, 11 Aug 2025 12:01:58 +0300 Subject: [PATCH 1/3] chore(clerk-js, types): Cleanup naming inconsistencies in billing dates --- .changeset/brown-news-unite.md | 6 ++++++ .../src/core/resources/CommerceSubscription.ts | 18 ++++++------------ .../components/PricingTable/PricingTable.tsx | 2 +- .../PricingTable/PricingTableDefault.tsx | 4 ++-- .../components/SubscriptionDetails/index.tsx | 14 +++++++------- .../Subscriptions/SubscriptionsList.tsx | 2 +- .../src/ui/contexts/components/Plans.tsx | 18 +++++++++--------- packages/types/src/commerce.ts | 18 +++--------------- packages/types/src/json.ts | 5 ++++- 9 files changed, 39 insertions(+), 48 deletions(-) create mode 100644 .changeset/brown-news-unite.md diff --git a/.changeset/brown-news-unite.md b/.changeset/brown-news-unite.md new file mode 100644 index 00000000000..55a7d1922cd --- /dev/null +++ b/.changeset/brown-news-unite.md @@ -0,0 +1,6 @@ +--- +'@clerk/clerk-js': minor +'@clerk/types': minor +--- + +[Billing Beta] Cleanup naming inconsistencies in billing dates. diff --git a/packages/clerk-js/src/core/resources/CommerceSubscription.ts b/packages/clerk-js/src/core/resources/CommerceSubscription.ts index 4c852f1d353..341b515f968 100644 --- a/packages/clerk-js/src/core/resources/CommerceSubscription.ts +++ b/packages/clerk-js/src/core/resources/CommerceSubscription.ts @@ -62,13 +62,10 @@ export class CommerceSubscriptionItem extends BaseResource implements CommerceSu planPeriod!: CommerceSubscriptionPlanPeriod; status!: CommerceSubscriptionStatus; createdAt!: Date; + periodStart!: Date; + periodEnd!: Date | null; + canceledAt!: Date | null; pastDueAt!: Date | null; - periodStartDate!: Date; - periodEndDate!: Date | null; - canceledAtDate!: Date | null; - periodStart!: number; - periodEnd!: number; - canceledAt!: number | null; //TODO(@COMMERCE): Why can this be undefined ? amount?: CommerceMoney; credit?: { @@ -90,16 +87,13 @@ export class CommerceSubscriptionItem extends BaseResource implements CommerceSu this.plan = new CommercePlan(data.plan); this.planPeriod = data.plan_period; this.status = data.status; - this.periodStart = data.period_start; - this.periodEnd = data.period_end; - this.canceledAt = data.canceled_at; this.createdAt = unixEpochToDate(data.created_at); this.pastDueAt = data.past_due_at ? unixEpochToDate(data.past_due_at) : null; - this.periodStartDate = unixEpochToDate(data.period_start); - this.periodEndDate = data.period_end ? unixEpochToDate(data.period_end) : null; - this.canceledAtDate = data.canceled_at ? unixEpochToDate(data.canceled_at) : null; + this.periodStart = unixEpochToDate(data.period_start); + this.periodEnd = data.period_end ? unixEpochToDate(data.period_end) : null; + this.canceledAt = data.canceled_at ? unixEpochToDate(data.canceled_at) : null; this.amount = data.amount ? commerceMoneyFromJSON(data.amount) : undefined; this.credit = data.credit && data.credit.amount ? { amount: commerceMoneyFromJSON(data.credit.amount) } : undefined; diff --git a/packages/clerk-js/src/ui/components/PricingTable/PricingTable.tsx b/packages/clerk-js/src/ui/components/PricingTable/PricingTable.tsx index eae1fcd381c..f7c69c58611 100644 --- a/packages/clerk-js/src/ui/components/PricingTable/PricingTable.tsx +++ b/packages/clerk-js/src/ui/components/PricingTable/PricingTable.tsx @@ -25,7 +25,7 @@ const PricingTableRoot = (props: PricingTableProps) => { // don't pay attention to the default plan const activeSubscription = subscriptionItems?.find( - sub => !sub.canceledAtDate && sub.status === 'active' && !sub.plan.isDefault, + sub => !sub.canceledAt && sub.status === 'active' && !sub.plan.isDefault, ); if (activeSubscription) { return activeSubscription.planPeriod; diff --git a/packages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.tsx b/packages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.tsx index 9d2ea35b194..b664a3c4b04 100644 --- a/packages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.tsx +++ b/packages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.tsx @@ -141,7 +141,7 @@ function Card(props: CardProps) { shouldShowFooter = true; shouldShowFooterNotice = true; } else if (subscription.status === 'active') { - if (subscription.canceledAtDate) { + if (subscription.canceledAt) { shouldShowFooter = true; shouldShowFooterNotice = false; } else if (planPeriod !== subscription.planPeriod && plan.annualMonthlyAmount > 0) { @@ -233,7 +233,7 @@ function Card(props: CardProps) { elementDescriptor={descriptors.pricingTableCardFooterNotice} variant={isCompact ? 'buttonSmall' : 'buttonLarge'} localizationKey={localizationKeys('badge__startsAt', { - date: subscription?.periodStartDate, + date: subscription?.periodStart, })} colorScheme='secondary' sx={t => ({ diff --git a/packages/clerk-js/src/ui/components/SubscriptionDetails/index.tsx b/packages/clerk-js/src/ui/components/SubscriptionDetails/index.tsx index 70a947b2b43..125e80c462c 100644 --- a/packages/clerk-js/src/ui/components/SubscriptionDetails/index.tsx +++ b/packages/clerk-js/src/ui/components/SubscriptionDetails/index.tsx @@ -271,7 +271,7 @@ const SubscriptionDetailsFooter = withCardStateProvider(() => { : localizationKeys('commerce.cancelSubscriptionAccessUntil', { plan: selectedSubscription.plan.name, // this will always be defined in this state - date: selectedSubscription.periodEndDate as Date, + date: selectedSubscription.periodEnd as Date, }) } /> @@ -339,8 +339,8 @@ const SubscriptionCardActions = ({ subscription }: { subscription: CommerceSubsc subscription.planPeriod === 'annual') && subscription.status !== 'past_due'; const isFree = isFreePlan(subscription.plan); - const isCancellable = subscription.canceledAtDate === null && !isFree && subscription.status !== 'past_due'; - const isReSubscribable = subscription.canceledAtDate !== null && !isFree; + const isCancellable = subscription.canceledAt === null && !isFree && subscription.status !== 'past_due'; + const isReSubscribable = subscription.canceledAt !== null && !isFree; const openCheckout = useCallback( (params?: __internal_CheckoutProps) => { @@ -523,14 +523,14 @@ const SubscriptionCard = ({ subscription }: { subscription: CommerceSubscription value={formatDate(subscription.createdAt)} /> {/* The free plan does not have a period end date */} - {subscription.periodEndDate && ( + {subscription.periodEnd && ( )} @@ -539,7 +539,7 @@ const SubscriptionCard = ({ subscription }: { subscription: CommerceSubscription {subscription.status === 'upcoming' ? ( ) : null} diff --git a/packages/clerk-js/src/ui/components/Subscriptions/SubscriptionsList.tsx b/packages/clerk-js/src/ui/components/Subscriptions/SubscriptionsList.tsx index fbb3e77a6f4..7db9432768d 100644 --- a/packages/clerk-js/src/ui/components/Subscriptions/SubscriptionsList.tsx +++ b/packages/clerk-js/src/ui/components/Subscriptions/SubscriptionsList.tsx @@ -120,7 +120,7 @@ export function SubscriptionsList({ > {subscription.plan.name} - {sortedSubscriptions.length > 1 || !!subscription.canceledAtDate ? ( + {sortedSubscriptions.length > 1 || !!subscription.canceledAt ? ( ) : null} diff --git a/packages/clerk-js/src/ui/contexts/components/Plans.tsx b/packages/clerk-js/src/ui/contexts/components/Plans.tsx index a18b7995877..f9117a90dfd 100644 --- a/packages/clerk-js/src/ui/contexts/components/Plans.tsx +++ b/packages/clerk-js/src/ui/contexts/components/Plans.tsx @@ -129,7 +129,7 @@ export const usePlansContext = () => { // should the default plan be shown as active const isDefaultPlanImplicitlyActiveOrUpcoming = useMemo(() => { // are there no subscriptions or are all subscriptions canceled - return subscriptionItems.length === 0 || !subscriptionItems.some(subscription => !subscription.canceledAtDate); + return subscriptionItems.length === 0 || !subscriptionItems.some(subscription => !subscription.canceledAt); }, [subscriptionItems]); // return the active or upcoming subscription for a plan if it exists @@ -178,7 +178,7 @@ export const usePlansContext = () => { ({ plan, subscription: sub }: { plan?: CommercePlanResource; subscription?: CommerceSubscriptionItemResource }) => { const subscription = sub ?? (plan ? activeOrUpcomingSubscription(plan) : undefined); - return !subscription || !subscription.canceledAtDate; + return !subscription || !subscription.canceledAt; }, [activeOrUpcomingSubscription], ); @@ -214,7 +214,7 @@ export const usePlansContext = () => { const getLocalizationKey = () => { // Handle subscription cases if (subscription) { - if (_selectedPlanPeriod !== subscription.planPeriod && subscription.canceledAtDate) { + if (_selectedPlanPeriod !== subscription.planPeriod && subscription.canceledAt) { if (_selectedPlanPeriod === 'month') { return localizationKeys('commerce.switchToMonthly'); } @@ -224,7 +224,7 @@ export const usePlansContext = () => { } } - if (subscription.canceledAtDate) { + if (subscription.canceledAt) { return localizationKeys('commerce.reSubscribe'); } @@ -268,14 +268,14 @@ export const usePlansContext = () => { } if (subscription.status === 'upcoming') { - return localizationKeys('badge__startsAt', { date: subscription.periodStartDate }); + return localizationKeys('badge__startsAt', { date: subscription.periodStart }); } - if (subscription.canceledAtDate) { + if (subscription.canceledAt) { // @ts-expect-error `periodEndDate` is always defined when `canceledAtDate` exists - return localizationKeys('badge__canceledEndsAt', { date: subscription.periodEndDate }); + return localizationKeys('badge__canceledEndsAt', { date: subscription.periodEnd }); } - if (subscription.periodEndDate) { - return localizationKeys('badge__renewsAt', { date: subscription.periodEndDate }); + if (subscription.periodEnd) { + return localizationKeys('badge__renewsAt', { date: subscription.periodEnd }); } return; }, []); diff --git a/packages/types/src/commerce.ts b/packages/types/src/commerce.ts index 3ae559e3c23..1b51dd1077c 100644 --- a/packages/types/src/commerce.ts +++ b/packages/types/src/commerce.ts @@ -1038,7 +1038,7 @@ export interface CommerceSubscriptionItemResource extends ClerkResource { * * ``` */ - periodStartDate: Date; + periodStart: Date; /** * @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 the SDK version and the clerk-js version to a specific version to avoid breaking changes. @@ -1047,7 +1047,7 @@ export interface CommerceSubscriptionItemResource extends ClerkResource { * * ``` */ - periodEndDate: Date | null; + periodEnd: Date | null; /** * @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 the SDK version and the clerk-js version to a specific version to avoid breaking changes. @@ -1056,19 +1056,7 @@ export interface CommerceSubscriptionItemResource extends ClerkResource { * * ``` */ - canceledAtDate: Date | null; - /** - * @deprecated Use `periodStartDate` instead - */ - periodStart: number; - /** - * @deprecated Use `periodEndDate` instead - */ - periodEnd: number; - /** - * @deprecated Use `canceledAtDate` instead - */ - canceledAt: number | null; + canceledAt: Date | null; /** * @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 the SDK version and the clerk-js version to a specific version to avoid breaking changes. diff --git a/packages/types/src/json.ts b/packages/types/src/json.ts index 11dd4760797..f7b4fa685fd 100644 --- a/packages/types/src/json.ts +++ b/packages/types/src/json.ts @@ -777,7 +777,10 @@ export interface CommerceSubscriptionItemJSON extends ClerkResourceJSON { status: CommerceSubscriptionStatus; created_at: number; period_start: number; - period_end: number; + /** + * Period end is `null` for subscription items that are on the free plan. + */ + period_end: number | null; canceled_at: number | null; past_due_at: number | null; } From 615be0a41fcadfb8a165d56b7ca430990510ae7d Mon Sep 17 00:00:00 2001 From: panteliselef Date: Mon, 11 Aug 2025 12:15:24 +0300 Subject: [PATCH 2/3] update tests --- .../__tests__/SubscriptionDetails.test.tsx | 64 +++++++++---------- 1 file changed, 32 insertions(+), 32 deletions(-) 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 4ce9e8732ba..143ef4e22e4 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 @@ -68,9 +68,9 @@ describe('SubscriptionDetails', () => { isDefault: false, }, createdAt: new Date('2021-01-01'), - periodStartDate: new Date('2021-01-01'), - periodEndDate: new Date('2021-02-01'), - canceledAtDate: null, + periodStart: new Date('2021-01-01'), + periodEnd: new Date('2021-02-01'), + canceledAt: null, paymentSourceId: 'src_123', planPeriod: 'month', status: 'active', @@ -161,9 +161,9 @@ describe('SubscriptionDetails', () => { isDefault: false, }, createdAt: new Date('2021-01-01'), - periodStartDate: new Date('2021-01-01'), - periodEndDate: new Date('2022-01-01'), - canceledAtDate: null, + periodStart: new Date('2021-01-01'), + periodEnd: new Date('2022-01-01'), + canceledAt: null, paymentSourceId: 'src_123', planPeriod: 'annual' as const, status: 'active' as const, @@ -246,9 +246,9 @@ describe('SubscriptionDetails', () => { isDefault: true, }, createdAt: new Date('2021-01-01'), - periodStartDate: new Date('2021-01-01'), - periodEndDate: new Date('2021-02-01'), - canceledAtDate: null, + periodStart: new Date('2021-01-01'), + periodEnd: new Date('2021-02-01'), + canceledAt: null, paymentSourceId: 'src_123', planPeriod: 'month' as const, status: 'active' as const, @@ -354,9 +354,9 @@ describe('SubscriptionDetails', () => { id: 'sub_annual', plan: planAnnual, createdAt: new Date('2021-01-01'), - periodStartDate: new Date('2021-01-01'), - periodEndDate: new Date('2022-01-01'), - canceledAtDate: new Date('2021-04-01'), + periodStart: new Date('2021-01-01'), + periodEnd: new Date('2022-01-01'), + canceledAt: new Date('2021-04-01'), paymentSourceId: 'src_annual', planPeriod: 'annual' as const, status: 'active' as const, @@ -365,9 +365,9 @@ describe('SubscriptionDetails', () => { id: 'sub_monthly', plan: planMonthly, createdAt: new Date('2022-01-01'), - periodStartDate: new Date('2022-02-01'), - periodEndDate: new Date('2022-03-01'), - canceledAtDate: null, + periodStart: new Date('2022-02-01'), + periodEnd: new Date('2022-03-01'), + canceledAt: null, paymentSourceId: 'src_monthly', planPeriod: 'month' as const, status: 'upcoming' as const, @@ -486,9 +486,9 @@ describe('SubscriptionDetails', () => { id: 'test_active', plan: planMonthly, createdAt: new Date('2021-01-01'), - periodStartDate: new Date('2021-01-01'), - periodEndDate: new Date('2021-02-01'), - canceledAtDate: new Date('2021-01-03'), + periodStart: new Date('2021-01-01'), + periodEnd: new Date('2021-02-01'), + canceledAt: new Date('2021-01-03'), paymentSourceId: 'src_free_active', planPeriod: 'month' as const, status: 'active' as const, @@ -497,8 +497,8 @@ describe('SubscriptionDetails', () => { id: 'sub_free_upcoming', plan: planFreeUpcoming, createdAt: new Date('2021-01-03'), - periodStartDate: new Date('2021-02-01'), - canceledAtDate: null, + periodStart: new Date('2021-02-01'), + canceledAt: null, paymentSourceId: 'src_free_upcoming', planPeriod: 'month' as const, status: 'upcoming' as const, @@ -582,9 +582,9 @@ describe('SubscriptionDetails', () => { isDefault: false, }, createdAt: new Date('2021-01-01'), - periodStartDate: new Date('2021-01-01'), - periodEndDate: new Date('2021-02-01'), - canceledAtDate: null, + periodStart: new Date('2021-01-01'), + periodEnd: new Date('2021-02-01'), + canceledAt: null, paymentSourceId: 'src_123', planPeriod: 'month' as const, status: 'active' as const, @@ -668,9 +668,9 @@ describe('SubscriptionDetails', () => { id: 'sub_annual', plan, createdAt: new Date('2021-01-01'), - periodStartDate: new Date('2021-01-01'), - periodEndDate: new Date('2022-01-01'), - canceledAtDate: new Date('2021-04-01'), + periodStart: new Date('2021-01-01'), + periodEnd: new Date('2022-01-01'), + canceledAt: new Date('2021-04-01'), paymentSourceId: 'src_annual', planPeriod: 'annual' as const, status: 'active' as const, @@ -755,9 +755,9 @@ describe('SubscriptionDetails', () => { id: 'sub_annual', plan, createdAt: new Date('2021-01-01'), - periodStartDate: new Date('2021-01-01'), - periodEndDate: new Date('2022-01-01'), - canceledAtDate: null, + periodStart: new Date('2021-01-01'), + periodEnd: new Date('2022-01-01'), + canceledAt: null, paymentSourceId: 'src_annual', planPeriod: 'annual' as const, status: 'active' as const, @@ -862,9 +862,9 @@ describe('SubscriptionDetails', () => { id: 'sub_past_due', plan, createdAt: new Date('2021-01-01'), - periodStartDate: new Date('2021-01-01'), - periodEndDate: new Date('2021-02-01'), - canceledAtDate: null, + periodStart: new Date('2021-01-01'), + periodEnd: new Date('2021-02-01'), + canceledAt: null, paymentSourceId: 'src_123', planPeriod: 'month' as const, status: 'past_due' as const, From c07fbc6ef033f9c88234a10356146029dc595671 Mon Sep 17 00:00:00 2001 From: panteliselef Date: Mon, 11 Aug 2025 13:19:13 +0300 Subject: [PATCH 3/3] update changeset --- .changeset/brown-news-unite.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.changeset/brown-news-unite.md b/.changeset/brown-news-unite.md index 55a7d1922cd..dc19b9a7f38 100644 --- a/.changeset/brown-news-unite.md +++ b/.changeset/brown-news-unite.md @@ -3,4 +3,9 @@ '@clerk/types': minor --- -[Billing Beta] Cleanup naming inconsistencies in billing dates. + [Billing Beta] Cleanup naming inconsistencies in billing dates. + +## Migration +- subscriptionItem.periodStartDate → subscriptionItem.periodStart +- subscriptionItem.periodEndDate → subscriptionItem.periodEnd +- subscriptionItem.canceledAtDate → subscriptionItem.canceledAt