From b22807496ff05d6c05fffc0d647cfb708bd75cb2 Mon Sep 17 00:00:00 2001 From: panteliselef Date: Wed, 6 Aug 2025 12:26:13 +0300 Subject: [PATCH 01/20] chore(clerk-js,types): Update PricingTable with trial info --- .changeset/sour-lemons-talk.md | 6 +++ .changeset/tender-planets-win.md | 7 +++ .../src/core/resources/CommercePlan.ts | 4 ++ .../core/resources/CommerceSubscription.ts | 5 ++ .../PricingTable/PricingTableDefault.tsx | 13 +++-- .../src/ui/contexts/components/Plans.tsx | 17 +++++-- packages/localizations/src/en-US.ts | 2 + packages/types/src/commerce.ts | 47 +++++++++++++++++++ packages/types/src/json.ts | 6 +++ packages/types/src/localization.ts | 2 + 10 files changed, 102 insertions(+), 7 deletions(-) create mode 100644 .changeset/sour-lemons-talk.md create mode 100644 .changeset/tender-planets-win.md diff --git a/.changeset/sour-lemons-talk.md b/.changeset/sour-lemons-talk.md new file mode 100644 index 00000000000..5bdb616dbdb --- /dev/null +++ b/.changeset/sour-lemons-talk.md @@ -0,0 +1,6 @@ +--- +'@clerk/clerk-js': minor +'@clerk/types': minor +--- + +Update billing resources with trial properties. diff --git a/.changeset/tender-planets-win.md b/.changeset/tender-planets-win.md new file mode 100644 index 00000000000..4054fffaa80 --- /dev/null +++ b/.changeset/tender-planets-win.md @@ -0,0 +1,7 @@ +--- +'@clerk/localizations': minor +'@clerk/clerk-js': minor +'@clerk/types': minor +--- + +Update PricingTable with trial info. diff --git a/packages/clerk-js/src/core/resources/CommercePlan.ts b/packages/clerk-js/src/core/resources/CommercePlan.ts index 77b7477cd26..163e459f0a7 100644 --- a/packages/clerk-js/src/core/resources/CommercePlan.ts +++ b/packages/clerk-js/src/core/resources/CommercePlan.ts @@ -27,6 +27,8 @@ export class CommercePlan extends BaseResource implements CommercePlanResource { slug!: string; avatarUrl!: string; features!: CommerceFeature[]; + freeTrialDays!: number | null; + freeTrialEnabled!: boolean; constructor(data: CommercePlanJSON) { super(); @@ -56,6 +58,8 @@ export class CommercePlan extends BaseResource implements CommercePlanResource { this.publiclyVisible = data.publicly_visible; this.slug = data.slug; this.avatarUrl = data.avatar_url; + this.freeTrialDays = this.withDefault(data.free_trial_days, null); + this.freeTrialEnabled = this.withDefault(data.free_trial_enabled, false); this.features = (data.features || []).map(feature => new CommerceFeature(feature)); return this; diff --git a/packages/clerk-js/src/core/resources/CommerceSubscription.ts b/packages/clerk-js/src/core/resources/CommerceSubscription.ts index 4c852f1d353..89587104c10 100644 --- a/packages/clerk-js/src/core/resources/CommerceSubscription.ts +++ b/packages/clerk-js/src/core/resources/CommerceSubscription.ts @@ -27,6 +27,7 @@ export class CommerceSubscription extends BaseResource implements CommerceSubscr date: Date; } | null = null; subscriptionItems!: CommerceSubscriptionItemResource[]; + eligibleForFreeTrial?: boolean; constructor(data: CommerceSubscriptionJSON) { super(); @@ -51,6 +52,7 @@ export class CommerceSubscription extends BaseResource implements CommerceSubscr } : null; this.subscriptionItems = (data.subscription_items || []).map(item => new CommerceSubscriptionItem(item)); + this.eligibleForFreeTrial = data.eligible_for_free_trial; return this; } } @@ -74,6 +76,7 @@ export class CommerceSubscriptionItem extends BaseResource implements CommerceSu credit?: { amount: CommerceMoney; }; + freeTrialEndsAt!: Date | null; constructor(data: CommerceSubscriptionItemJSON) { super(); @@ -103,6 +106,8 @@ export class CommerceSubscriptionItem extends BaseResource implements CommerceSu this.amount = data.amount ? commerceMoneyFromJSON(data.amount) : undefined; this.credit = data.credit && data.credit.amount ? { amount: commerceMoneyFromJSON(data.credit.amount) } : undefined; + + this.freeTrialEndsAt = data.free_trial_ends_at ? unixEpochToDate(data.free_trial_ends_at) : null; return this; } diff --git a/packages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.tsx b/packages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.tsx index 9d2ea35b194..f34925722cb 100644 --- a/packages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.tsx +++ b/packages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.tsx @@ -147,6 +147,9 @@ function Card(props: CardProps) { } else if (planPeriod !== subscription.planPeriod && plan.annualMonthlyAmount > 0) { shouldShowFooter = true; shouldShowFooterNotice = false; + } else if (plan.freeTrialEnabled && subscription.freeTrialEndsAt !== null) { + shouldShowFooter = true; + shouldShowFooterNotice = true; } else { shouldShowFooter = false; shouldShowFooterNotice = false; @@ -232,9 +235,13 @@ function Card(props: CardProps) { ({ paddingBlock: t.space.$1x5, diff --git a/packages/clerk-js/src/ui/contexts/components/Plans.tsx b/packages/clerk-js/src/ui/contexts/components/Plans.tsx index a18b7995877..93af966cf83 100644 --- a/packages/clerk-js/src/ui/contexts/components/Plans.tsx +++ b/packages/clerk-js/src/ui/contexts/components/Plans.tsx @@ -108,7 +108,7 @@ export const usePlansContext = () => { return false; }, [clerk, subscriberType]); - const { subscriptionItems, revalidate: revalidateSubscriptions } = useSubscription(); + const { subscriptionItems, revalidate: revalidateSubscriptions, data: topLevelSubscription } = useSubscription(); // Invalidates cache but does not fetch immediately const { data: plans, revalidate: revalidatePlans } = usePlans({ mode: 'cache' }); @@ -187,6 +187,7 @@ export const usePlansContext = () => { const buttonPropsForPlan = useCallback( ({ plan, + // TODO(@COMMERCE): This needs to be removed. subscription: sub, isCompact = false, selectedPlanPeriod = 'annual', @@ -211,6 +212,13 @@ export const usePlansContext = () => { const isEligibleForSwitchToAnnual = (plan?.annualMonthlyAmount ?? 0) > 0; + const freeTrialOr = (localizationKey: LocalizationKey): LocalizationKey => { + if (plan?.freeTrialEnabled && topLevelSubscription?.eligibleForFreeTrial) { + return localizationKeys('commerce.startFreeTrial', { days: plan.freeTrialDays ?? 0 }); + } + return localizationKey; + }; + const getLocalizationKey = () => { // Handle subscription cases if (subscription) { @@ -246,20 +254,21 @@ export const usePlansContext = () => { // Handle non-subscription cases const hasNonDefaultSubscriptions = subscriptionItems.filter(subscription => !subscription.plan.isDefault).length > 0; + return hasNonDefaultSubscriptions ? localizationKeys('commerce.switchPlan') - : localizationKeys('commerce.subscribe'); + : freeTrialOr(localizationKeys('commerce.subscribe')); }; return { - localizationKey: getLocalizationKey(), + localizationKey: freeTrialOr(getLocalizationKey()), variant: isCompact ? 'bordered' : 'solid', colorScheme: isCompact ? 'secondary' : 'primary', isDisabled: !canManageBilling, disabled: !canManageBilling, }; }, - [activeOrUpcomingSubscriptionWithPlanPeriod, canManageBilling, subscriptionItems], + [activeOrUpcomingSubscriptionWithPlanPeriod, canManageBilling, subscriptionItems, topLevelSubscription], ); const captionForSubscription = useCallback((subscription: CommerceSubscriptionItemResource) => { diff --git a/packages/localizations/src/en-US.ts b/packages/localizations/src/en-US.ts index 4f216d3a58e..46b3e0f5500 100644 --- a/packages/localizations/src/en-US.ts +++ b/packages/localizations/src/en-US.ts @@ -43,6 +43,7 @@ export const enUS: LocalizationResource = { badge__default: 'Default', badge__endsAt: "Ends {{ date | shortDate('en-US') }}", badge__expired: 'Expired', + badge__trialEndsAt: "Trial ends {{ date | shortDate('en-US') }}", badge__otherImpersonatorDevice: 'Other impersonator device', badge__pastDueAt: "Past due {{ date | shortDate('en-US') }}", badge__pastDuePlan: 'Past due', @@ -140,6 +141,7 @@ export const enUS: LocalizationResource = { }, subtotal: 'Subtotal', switchPlan: 'Switch to this plan', + startFreeTrial: 'Start {{days}}-day free trial', switchToAnnual: 'Switch to annual', switchToAnnualWithAnnualPrice: 'Switch to annual {{currency}}{{price}} / year', switchToMonthly: 'Switch to monthly', diff --git a/packages/types/src/commerce.ts b/packages/types/src/commerce.ts index 3ae559e3c23..082095b6d14 100644 --- a/packages/types/src/commerce.ts +++ b/packages/types/src/commerce.ts @@ -437,6 +437,24 @@ export interface CommercePlanResource extends ClerkResource { * ``` */ features: CommerceFeatureResource[]; + /** + * @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. + * @example + * ```tsx + * + * ``` + */ + freeTrialDays: number | 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. + * @example + * ```tsx + * + * ``` + */ + freeTrialEnabled: boolean; __internal_toSnapshot: () => CommercePlanJSONSnapshot; } @@ -1106,6 +1124,25 @@ export interface CommerceSubscriptionItemResource extends ClerkResource { * ``` */ cancel: (params: CancelSubscriptionParams) => Promise; + // /** + // * @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. + // * @example + // * ```tsx + // * + // * ``` + // */ + // isFreeTrial: boolean; + + /** + * @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. + * @example + * ```tsx + * + * ``` + */ + freeTrialEndsAt: Date | null; } /** @@ -1215,6 +1252,16 @@ export interface CommerceSubscriptionResource extends ClerkResource { * ``` */ updatedAt: 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. + * @example + * ```tsx + * + * ``` + */ + eligibleForFreeTrial?: boolean; } /** diff --git a/packages/types/src/json.ts b/packages/types/src/json.ts index 11dd4760797..937df067f70 100644 --- a/packages/types/src/json.ts +++ b/packages/types/src/json.ts @@ -649,6 +649,8 @@ export interface CommercePlanJSON extends ClerkResourceJSON { slug: string; avatar_url: string; features: CommerceFeatureJSON[]; + free_trial_days?: number | null; + free_trial_enabled?: boolean; } /** @@ -780,6 +782,9 @@ export interface CommerceSubscriptionItemJSON extends ClerkResourceJSON { period_end: number; canceled_at: number | null; past_due_at: number | null; + // is_free_trial: boolean; + // TODO(@COMMERCE): Remove optional after GA. + free_trial_ends_at?: number | null; } /** @@ -809,6 +814,7 @@ export interface CommerceSubscriptionJSON extends ClerkResourceJSON { updated_at: number | null; past_due_at: number | null; subscription_items: CommerceSubscriptionItemJSON[] | null; + eligible_for_free_trial?: boolean; } /** diff --git a/packages/types/src/localization.ts b/packages/types/src/localization.ts index d16cc640aa0..a08d10af953 100644 --- a/packages/types/src/localization.ts +++ b/packages/types/src/localization.ts @@ -149,6 +149,7 @@ export type __internal_LocalizationResource = { badge__pastDuePlan: LocalizationValue; badge__startsAt: LocalizationValue<'date'>; badge__pastDueAt: LocalizationValue<'date'>; + badge__trialEndsAt: LocalizationValue<'date'>; badge__endsAt: LocalizationValue; badge__expired: LocalizationValue; badge__canceledEndsAt: LocalizationValue<'date'>; @@ -174,6 +175,7 @@ export type __internal_LocalizationResource = { keepSubscription: LocalizationValue; reSubscribe: LocalizationValue; subscribe: LocalizationValue; + startFreeTrial: LocalizationValue<'days'>; switchPlan: LocalizationValue; switchToMonthly: LocalizationValue; switchToAnnual: LocalizationValue; From 9eda6faa08b2c15bd27917a1354fe27a04f41abc Mon Sep 17 00:00:00 2001 From: panteliselef Date: Fri, 8 Aug 2025 20:31:24 +0300 Subject: [PATCH 02/20] chore(clerk-js,types): Update checkout to handle trials --- .../src/core/resources/CommerceCheckout.ts | 4 ++++ .../ui/components/Checkout/CheckoutForm.tsx | 22 ++++++++++++++++++- .../src/ui/components/Subscriptions/badge.tsx | 6 +++-- .../clerk-js/src/ui/elements/LineItems.tsx | 4 +++- packages/localizations/src/en-US.ts | 2 ++ .../shared/src/react/hooks/useCheckout.ts | 1 + packages/types/src/commerce.ts | 9 ++++++++ packages/types/src/json.ts | 2 ++ packages/types/src/localization.ts | 2 ++ 9 files changed, 48 insertions(+), 4 deletions(-) diff --git a/packages/clerk-js/src/core/resources/CommerceCheckout.ts b/packages/clerk-js/src/core/resources/CommerceCheckout.ts index 04fb7e0465c..f8da65639a8 100644 --- a/packages/clerk-js/src/core/resources/CommerceCheckout.ts +++ b/packages/clerk-js/src/core/resources/CommerceCheckout.ts @@ -7,6 +7,8 @@ import type { ConfirmCheckoutParams, } from '@clerk/types'; +import { unixEpochToDate } from '@/utils/date'; + import { commerceTotalsFromJSON } from '../../utils'; import { BaseResource, CommercePaymentSource, CommercePlan, isClerkAPIResponseError } from './internal'; @@ -21,6 +23,7 @@ export class CommerceCheckout extends BaseResource implements CommerceCheckoutRe status!: 'needs_confirmation' | 'completed'; totals!: CommerceCheckoutTotals; isImmediatePlanChange!: boolean; + freeTrialEndsAt!: Date | null; constructor(data: CommerceCheckoutJSON, orgId?: string) { super(); @@ -43,6 +46,7 @@ export class CommerceCheckout extends BaseResource implements CommerceCheckoutRe this.status = data.status; this.totals = commerceTotalsFromJSON(data.totals); this.isImmediatePlanChange = data.is_immediate_plan_change; + this.freeTrialEndsAt = data.free_trial_ends_at ? unixEpochToDate(data.free_trial_ends_at) : null; return this; } diff --git a/packages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsx b/packages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsx index 9fb0cbb07ef..abba2c2e33a 100644 --- a/packages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsx +++ b/packages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsx @@ -17,6 +17,7 @@ import { Box, Button, Col, descriptors, Flex, Form, localizationKeys, Text } fro import { ChevronUpDown, InformationCircle } from '../../icons'; import * as AddPaymentSource from '../PaymentSources/AddPaymentSource'; import { PaymentSourceRow } from '../PaymentSources/PaymentSourceRow'; +import { SubscriptionBadge } from '../Subscriptions/badge'; type PaymentMethodSource = 'existing' | 'new'; @@ -25,7 +26,7 @@ const capitalize = (name: string) => name[0].toUpperCase() + name.slice(1); export const CheckoutForm = withCardStateProvider(() => { const { checkout } = useCheckout(); - const { id, plan, totals, isImmediatePlanChange, planPeriod } = checkout; + const { id, plan, totals, isImmediatePlanChange, planPeriod, freeTrialEndsAt } = checkout; if (!id) { return null; @@ -51,6 +52,11 @@ export const CheckoutForm = withCardStateProvider(() => { + ) : null + } /> { )} + + {freeTrialEndsAt && plan.freeTrialDays && ( + + + + + )} + diff --git a/packages/clerk-js/src/ui/components/Subscriptions/badge.tsx b/packages/clerk-js/src/ui/components/Subscriptions/badge.tsx index 64524cc7f7a..eecba6b9abd 100644 --- a/packages/clerk-js/src/ui/components/Subscriptions/badge.tsx +++ b/packages/clerk-js/src/ui/components/Subscriptions/badge.tsx @@ -7,19 +7,21 @@ const keys = { active: 'badge__activePlan', upcoming: 'badge__upcomingPlan', past_due: 'badge__pastDuePlan', + free_trial: 'badge__freeTrial', }; const colors = { active: 'secondary', upcoming: 'primary', past_due: 'warning', + free_trial: 'secondary', }; -export const SubscriptionBadge = ({ +export const SubscriptionBadge = ({ subscription, elementDescriptor, }: { - subscription: CommerceSubscriptionItemResource; + subscription: T | { status: 'free_trial' }; elementDescriptor?: ElementDescriptor; }) => { return ( diff --git a/packages/clerk-js/src/ui/elements/LineItems.tsx b/packages/clerk-js/src/ui/elements/LineItems.tsx index f7cbe03eddb..119d93e0a52 100644 --- a/packages/clerk-js/src/ui/elements/LineItems.tsx +++ b/packages/clerk-js/src/ui/elements/LineItems.tsx @@ -84,9 +84,10 @@ interface TitleProps { title?: string | LocalizationKey; description?: string | LocalizationKey; icon?: React.ComponentType; + badge?: React.ReactNode; } -const Title = React.forwardRef(({ title, description, icon }, ref) => { +const Title = React.forwardRef(({ title, description, icon, badge = null }, ref) => { const context = React.useContext(GroupContext); if (!context) { throw new Error('LineItems.Title must be used within LineItems.Group'); @@ -120,6 +121,7 @@ const Title = React.forwardRef(({ title, descr /> ) : null} + {badge} ) : null} {description ? ( diff --git a/packages/localizations/src/en-US.ts b/packages/localizations/src/en-US.ts index 46b3e0f5500..0e1cbe5db20 100644 --- a/packages/localizations/src/en-US.ts +++ b/packages/localizations/src/en-US.ts @@ -40,6 +40,7 @@ export const enUS: LocalizationResource = { badge__activePlan: 'Active', badge__canceledEndsAt: "Canceled • Ends {{ date | shortDate('en-US') }}", badge__currentPlan: 'Current plan', + badge__freeTrial: 'Free trial', badge__default: 'Default', badge__endsAt: "Ends {{ date | shortDate('en-US') }}", badge__expired: 'Expired', @@ -87,6 +88,7 @@ export const enUS: LocalizationResource = { title__subscriptionBegins: 'Subscription begins', title__totalPaid: 'Total paid', }, + totalDueAfterTrial: 'Total Due after trial ends in {{days}} days', pastDueNotice: 'Your previous subscription was past due, with no payment.', perMonth: 'per month', title: 'Checkout', diff --git a/packages/shared/src/react/hooks/useCheckout.ts b/packages/shared/src/react/hooks/useCheckout.ts index de717c53069..0aee53f26d4 100644 --- a/packages/shared/src/react/hooks/useCheckout.ts +++ b/packages/shared/src/react/hooks/useCheckout.ts @@ -109,6 +109,7 @@ export const useCheckout = (options?: Params): __experimental_UseCheckoutReturn planPeriod: null, plan: null, paymentSource: null, + freeTrialEndsAt: null, }; } const { diff --git a/packages/types/src/commerce.ts b/packages/types/src/commerce.ts index 082095b6d14..f28dd401b6a 100644 --- a/packages/types/src/commerce.ts +++ b/packages/types/src/commerce.ts @@ -1578,4 +1578,13 @@ export interface CommerceCheckoutResource extends ClerkResource { * ``` */ isImmediatePlanChange: boolean; + /** + * @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. + * @example + * ```tsx + * + * ``` + */ + freeTrialEndsAt: Date | null; } diff --git a/packages/types/src/json.ts b/packages/types/src/json.ts index 937df067f70..682f625d041 100644 --- a/packages/types/src/json.ts +++ b/packages/types/src/json.ts @@ -879,6 +879,8 @@ export interface CommerceCheckoutJSON extends ClerkResourceJSON { status: 'needs_confirmation' | 'completed'; totals: CommerceCheckoutTotalsJSON; is_immediate_plan_change: boolean; + // TODO(@COMMERCE): Remove optional after GA. + free_trial_ends_at?: number | null; } export interface ApiKeyJSON extends ClerkResourceJSON { diff --git a/packages/types/src/localization.ts b/packages/types/src/localization.ts index a08d10af953..f09fde99364 100644 --- a/packages/types/src/localization.ts +++ b/packages/types/src/localization.ts @@ -143,6 +143,7 @@ export type __internal_LocalizationResource = { badge__unverified: LocalizationValue; badge__requiresAction: LocalizationValue; badge__you: LocalizationValue; + badge__freeTrial: LocalizationValue; badge__currentPlan: LocalizationValue; badge__upcomingPlan: LocalizationValue; badge__activePlan: LocalizationValue; @@ -254,6 +255,7 @@ export type __internal_LocalizationResource = { }; downgradeNotice: LocalizationValue; pastDueNotice: LocalizationValue; + totalDueAfterTrial: LocalizationValue<'days'>; perMonth: LocalizationValue; }; }; From 1c6b47c3f861b47e28b5fada47ecdef20b345e41 Mon Sep 17 00:00:00 2001 From: panteliselef Date: Fri, 8 Aug 2025 21:03:44 +0300 Subject: [PATCH 03/20] chore(clerk-js,types): Update checkout to handle trials --- .../ui/components/Checkout/CheckoutForm.tsx | 57 ++++++++++--------- .../src/ui/contexts/components/Plans.tsx | 2 +- packages/localizations/src/en-US.ts | 7 ++- packages/types/src/localization.ts | 3 +- 4 files changed, 38 insertions(+), 31 deletions(-) diff --git a/packages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsx b/packages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsx index abba2c2e33a..5cc7576edec 100644 --- a/packages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsx +++ b/packages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsx @@ -53,7 +53,7 @@ export const CheckoutForm = withCardStateProvider(() => { title={plan.name} description={planPeriod === 'annual' ? localizationKeys('commerce.billedAnnually') : undefined} badge={ - plan.freeTrialEnabled && plan.freeTrialDays ? ( + plan.freeTrialEnabled && freeTrialEndsAt ? ( ) : null } @@ -296,15 +296,32 @@ export const PayWithTestPaymentSource = () => { ); }; -const AddPaymentSourceForCheckout = withCardStateProvider(() => { - const { addPaymentSourceAndPay } = useCheckoutMutations(); +const useSubmitLabel = () => { const { checkout } = useCheckout(); - const { status, totals } = checkout; + const { status, freeTrialEndsAt, totals } = checkout; if (status === 'needs_initialization') { - return null; + throw new Error('Clerk: Invalid state'); } + if (freeTrialEndsAt) { + return localizationKeys('commerce.startFreeTrial'); + } + + if (totals.totalDueNow.amount > 0) { + return localizationKeys('commerce.pay', { + amount: `${totals.totalDueNow.currencySymbol}${totals.totalDueNow.amountFormatted}`, + }); + } + + return localizationKeys('commerce.subscribe'); +}; + +const AddPaymentSourceForCheckout = withCardStateProvider(() => { + const { addPaymentSourceAndPay } = useCheckoutMutations(); + const submitLabel = useSubmitLabel(); + const { checkout } = useCheckout(); + return ( { - {totals.totalDueNow.amount > 0 ? ( - - ) : ( - - )} + ); }); @@ -335,8 +344,9 @@ const ExistingPaymentSourceForm = withCardStateProvider( totalDueNow: CommerceMoney; paymentSources: CommercePaymentSourceResource[]; }) => { + const submitLabel = useSubmitLabel(); const { checkout } = useCheckout(); - const { paymentSource } = checkout; + const { paymentSource, freeTrialEndsAt } = checkout; const { payWithExistingPaymentSource } = useCheckoutMutations(); const card = useCardState(); @@ -358,6 +368,8 @@ const ExistingPaymentSourceForm = withCardStateProvider( }); }, [paymentSources]); + const isSchedulePayment = totalDueNow.amount > 0 && !freeTrialEndsAt; + return (
- {totalDueNow.amount > 0 ? ( + {isSchedulePayment ? (