Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 22 additions & 17 deletions src/locales/en/main.json
Original file line number Diff line number Diff line change
Expand Up @@ -1924,25 +1924,29 @@
},
"tiers": {
"founder": {
"name": "Founder's Edition Standard",
"name": "Founder's Edition",
"price": "20.00",
"benefits": {
"monthlyCredits": "5,460 monthly credits",
"maxDuration": "30 min max duration of each workflow run",
"rtx6000": "RTX 6000 Pro (96GB VRAM)",
"addCredits": "Add more credits whenever"
"monthlyCredits": "5,460",
"monthlyCreditsLabel": "monthly credits",
"maxDuration": "30 min",
"maxDurationLabel": "max duration of each workflow run",
"gpuLabel": "RTX 6000 Pro (96GB VRAM)",
"addCreditsLabel": "Add more credits whenever",
"customLoRAsLabel": "Import your own LoRAs"
}
},
"standard": {
"name": "Standard",
"price": "20.00",
"benefits": {
"monthlyCredits": "4,200 monthly credits",
"maxDuration": "30 min max duration of each workflow run",
"rtx6000": "RTX 6000 Pro (96GB VRAM)",
"addCredits": "Add more credits whenever",
"customLoRAs": "Import your own LoRAs",
"videoEstimate": "164"
"monthlyCredits": "4,200",
"monthlyCreditsLabel": "monthly credits",
"maxDuration": "30 min",
"maxDurationLabel": "max duration of each workflow run",
"gpuLabel": "RTX 6000 Pro (96GB VRAM)",
"addCreditsLabel": "Add more credits whenever",
"customLoRAsLabel": "Import your own LoRAs"
}
},
"creator": {
Expand All @@ -1962,12 +1966,13 @@
"name": "Pro",
"price": "100.00",
"benefits": {
"monthlyCredits": "21,100 monthly credits",
"maxDuration": "1 hr max duration of each workflow run",
"rtx6000": "RTX 6000 Pro (96GB VRAM)",
"addCredits": "Add more credits whenever",
"customLoRAs": "Import your own LoRAs",
"videoEstimate": "821"
"monthlyCredits": "21,100",
"monthlyCreditsLabel": "monthly credits",
"maxDuration": "1 hr",
"maxDurationLabel": "max duration of each workflow run",
"gpuLabel": "RTX 6000 Pro (96GB VRAM)",
"addCreditsLabel": "Add more credits whenever",
"customLoRAsLabel": "Import your own LoRAs"
}
}
},
Expand Down
40 changes: 30 additions & 10 deletions src/platform/cloud/subscription/components/SubscriptionPanel.vue
Original file line number Diff line number Diff line change
Expand Up @@ -353,8 +353,21 @@ import { useSubscription } from '@/platform/cloud/subscription/composables/useSu
import { useSubscriptionActions } from '@/platform/cloud/subscription/composables/useSubscriptionActions'
import { useSubscriptionCredits } from '@/platform/cloud/subscription/composables/useSubscriptionCredits'
import { useSubscriptionDialog } from '@/platform/cloud/subscription/composables/useSubscriptionDialog'
import type { components } from '@/types/comfyRegistryTypes'
import { cn } from '@/utils/tailwindUtil'

type SubscriptionTier = components['schemas']['SubscriptionTier']

/** Maps API subscription tier values to i18n translation keys */
const TIER_TO_I18N_KEY: Record<SubscriptionTier, string> = {
STANDARD: 'standard',
CREATOR: 'creator',
PRO: 'pro',
FOUNDERS_EDITION: 'founder'
}

const DEFAULT_TIER_KEY = 'standard'

const { buildDocsUrl } = useExternalLink()
const { t } = useI18n()

Expand All @@ -363,14 +376,20 @@ const {
isCancelled,
formattedRenewalDate,
formattedEndDate,
subscriptionTier,
handleInvoiceHistory
} = useSubscription()

const { show: showSubscriptionDialog } = useSubscriptionDialog()

// Tier data - hardcoded for Creator tier as requested
const tierName = computed(() => t('subscription.tiers.creator.name'))
const tierPrice = computed(() => t('subscription.tiers.creator.price'))
const tierKey = computed(() => {
const tier = subscriptionTier.value
if (!tier) return DEFAULT_TIER_KEY
return TIER_TO_I18N_KEY[tier] ?? DEFAULT_TIER_KEY
})

const tierName = computed(() => t(`subscription.tiers.${tierKey.value}.name`))
const tierPrice = computed(() => t(`subscription.tiers.${tierKey.value}.price`))

// Tier benefits for v-for loop
type BenefitType = 'metric' | 'feature'
Expand All @@ -383,33 +402,34 @@ interface Benefit {
}

const tierBenefits = computed(() => {
const key = tierKey.value
const baseBenefits: Benefit[] = [
{
key: 'monthlyCredits',
type: 'metric',
value: t('subscription.tiers.creator.benefits.monthlyCredits'),
label: t('subscription.tiers.creator.benefits.monthlyCreditsLabel')
value: t(`subscription.tiers.${key}.benefits.monthlyCredits`),
label: t(`subscription.tiers.${key}.benefits.monthlyCreditsLabel`)
},
{
key: 'maxDuration',
type: 'metric',
value: t('subscription.tiers.creator.benefits.maxDuration'),
label: t('subscription.tiers.creator.benefits.maxDurationLabel')
value: t(`subscription.tiers.${key}.benefits.maxDuration`),
label: t(`subscription.tiers.${key}.benefits.maxDurationLabel`)
},
{
key: 'gpu',
type: 'feature',
label: t('subscription.tiers.creator.benefits.gpuLabel')
label: t(`subscription.tiers.${key}.benefits.gpuLabel`)
},
{
key: 'addCredits',
type: 'feature',
label: t('subscription.tiers.creator.benefits.addCreditsLabel')
label: t(`subscription.tiers.${key}.benefits.addCreditsLabel`)
},
{
key: 'customLoRAs',
type: 'feature',
label: t('subscription.tiers.creator.benefits.customLoRAsLabel')
label: t(`subscription.tiers.${key}.benefits.customLoRAsLabel`)
}
]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ import { computed, onBeforeUnmount, watch } from 'vue'

import CloudBadge from '@/components/topbar/CloudBadge.vue'
import { useFeatureFlags } from '@/composables/useFeatureFlags'
import { MONTHLY_SUBSCRIPTION_PRICE } from '@/config/subscriptionPricesConfig'
import StripePricingTable from '@/platform/cloud/subscription/components/StripePricingTable.vue'
import SubscribeButton from '@/platform/cloud/subscription/components/SubscribeButton.vue'
import SubscriptionBenefits from '@/platform/cloud/subscription/components/SubscriptionBenefits.vue'
Expand All @@ -155,8 +156,18 @@ const emit = defineEmits<{
close: [subscribed: boolean]
}>()

const { formattedMonthlyPrice, fetchStatus, isActiveSubscription } =
useSubscription()
const { fetchStatus, isActiveSubscription } = useSubscription()

// Legacy price for non-tier flow with locale-aware formatting
const formattedMonthlyPrice = new Intl.NumberFormat(
navigator.language || 'en-US',
{
style: 'currency',
currency: 'USD',
minimumFractionDigits: 0,
maximumFractionDigits: 0
}
).format(MONTHLY_SUBSCRIPTION_PRICE)
const { featureFlag } = useFeatureFlags()
const subscriptionTiersEnabled = featureFlag(
'subscription_tiers_enabled',
Expand Down
17 changes: 7 additions & 10 deletions src/platform/cloud/subscription/composables/useSubscription.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { useCurrentUser } from '@/composables/auth/useCurrentUser'
import { useFirebaseAuthActions } from '@/composables/auth/useFirebaseAuthActions'
import { useErrorHandling } from '@/composables/useErrorHandling'
import { getComfyApiBaseUrl, getComfyPlatformBaseUrl } from '@/config/comfyApi'
import { MONTHLY_SUBSCRIPTION_PRICE } from '@/config/subscriptionPricesConfig'
import { t } from '@/i18n'
import { isCloud } from '@/platform/distribution/types'
import { useTelemetry } from '@/platform/telemetry'
Expand All @@ -14,18 +13,16 @@ import {
useFirebaseAuthStore
} from '@/stores/firebaseAuthStore'
import { useDialogService } from '@/services/dialogService'
import type { operations } from '@/types/comfyRegistryTypes'
import { useSubscriptionCancellationWatcher } from './useSubscriptionCancellationWatcher'

type CloudSubscriptionCheckoutResponse = {
checkout_url: string
}

export type CloudSubscriptionStatusResponse = {
is_active: boolean
subscription_id: string
renewal_date: string | null
end_date?: string | null
}
export type CloudSubscriptionStatusResponse = NonNullable<
operations['GetCloudSubscriptionStatus']['responses']['200']['content']['application/json']
>

function useSubscriptionInternal() {
const subscriptionStatus = ref<CloudSubscriptionStatusResponse | null>(null)
Expand Down Expand Up @@ -72,8 +69,8 @@ function useSubscriptionInternal() {
})
})

const formattedMonthlyPrice = computed(
() => `$${MONTHLY_SUBSCRIPTION_PRICE.toFixed(0)}`
const subscriptionTier = computed(
() => subscriptionStatus.value?.subscription_tier ?? null
)

const buildApiUrl = (path: string) => `${getComfyApiBaseUrl()}${path}`
Expand Down Expand Up @@ -227,7 +224,7 @@ function useSubscriptionInternal() {
isCancelled,
formattedRenewalDate,
formattedEndDate,
formattedMonthlyPrice,
subscriptionTier,

// Actions
subscribe,
Expand Down
Loading
Loading