mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-03-02 11:40:00 +00:00
feat: handling subscription tier button link parameter (#7553)
## Summary Discussion here: https://comfy-organization.slack.com/archives/C0A0XANFJRE/p1764899027465379 Implement: Subscription tier query parameter for direct checkout flow Example button link: `/cloud/subscribe?tier=standard` `tier` could be `standard`, `creator` or `pro` `cycle` could be `monthly` or `yearly`. it is optional, and `monthly` by default. <!-- One sentence describing what changed and why. --> ## Changes - **What**: <!-- Core functionality added/modified --> - Add a landing page called `CloudSubscriptionRedirectView.vue` to handling the subscription tier button link parameter - Extract subscription handling logic from `PriceTable.vue` - **Breaking**: <!-- Any breaking changes (if none, remove this line) --> - Code change touched `PriceTable.vue` - **Dependencies**: <!-- New dependencies (if none, remove this line) --> ## Review Focus - link will redirect to login url, when cloud app not login - after login, the cloud app will redirect to CloudSubscriptionRedirect page - wait for several seconds, the cloud app will be redirected to checkout page <!-- Critical design decisions or edge cases that need attention --> <!-- If this PR fixes an issue, uncomment and update the line below --> <!-- Fixes #ISSUE_NUMBER --> ## Screenshots (if applicable)  <!-- Add screenshots or video recording to help explain your changes --> ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-7553-feat-handling-subscription-tier-button-link-parameter-2cb6d73d365081ee9580e89090248300) by [Unito](https://www.unito.io) --------- Co-authored-by: GitHub Action <action@github.com>
This commit is contained in:
@@ -252,7 +252,6 @@ import { useI18n } from 'vue-i18n'
|
||||
import Button from '@/components/ui/button/Button.vue'
|
||||
import { useFirebaseAuthActions } from '@/composables/auth/useFirebaseAuthActions'
|
||||
import { useErrorHandling } from '@/composables/useErrorHandling'
|
||||
import { getComfyApiBaseUrl } from '@/config/comfyApi'
|
||||
import { t } from '@/i18n'
|
||||
import { useSubscription } from '@/platform/cloud/subscription/composables/useSubscription'
|
||||
import {
|
||||
@@ -263,13 +262,10 @@ import type {
|
||||
TierKey,
|
||||
TierPricing
|
||||
} from '@/platform/cloud/subscription/constants/tierPricing'
|
||||
import { performSubscriptionCheckout } from '@/platform/cloud/subscription/utils/subscriptionCheckoutUtil'
|
||||
import { isPlanDowngrade } from '@/platform/cloud/subscription/utils/subscriptionTierRank'
|
||||
import type { BillingCycle } from '@/platform/cloud/subscription/utils/subscriptionTierRank'
|
||||
import { isCloud } from '@/platform/distribution/types'
|
||||
import {
|
||||
FirebaseAuthStoreError,
|
||||
useFirebaseAuthStore
|
||||
} from '@/stores/firebaseAuthStore'
|
||||
import type { components } from '@/types/comfyRegistryTypes'
|
||||
|
||||
type SubscriptionTier = components['schemas']['SubscriptionTier']
|
||||
@@ -332,7 +328,6 @@ const tiers: PricingTierConfig[] = [
|
||||
]
|
||||
|
||||
const { n } = useI18n()
|
||||
const { getAuthHeader } = useFirebaseAuthStore()
|
||||
const { isActiveSubscription, subscriptionTier, isYearlySubscription } =
|
||||
useSubscription()
|
||||
const { accessBillingPortal, reportError } = useFirebaseAuthActions()
|
||||
@@ -384,12 +379,13 @@ const getButtonLabel = (tier: PricingTierConfig): string => {
|
||||
: t('subscription.subscribeTo', { plan: planName })
|
||||
}
|
||||
|
||||
const getButtonSeverity = (tier: PricingTierConfig): 'primary' | 'secondary' =>
|
||||
isCurrentPlan(tier.key)
|
||||
? 'secondary'
|
||||
: tier.key === 'creator'
|
||||
? 'primary'
|
||||
: 'secondary'
|
||||
const getButtonSeverity = (
|
||||
tier: PricingTierConfig
|
||||
): 'primary' | 'secondary' => {
|
||||
if (isCurrentPlan(tier.key)) return 'secondary'
|
||||
if (tier.key === 'creator') return 'primary'
|
||||
return 'secondary'
|
||||
}
|
||||
|
||||
const getButtonTextClass = (tier: PricingTierConfig): string =>
|
||||
tier.key === 'creator'
|
||||
@@ -405,47 +401,6 @@ const getAnnualTotal = (tier: PricingTierConfig): number =>
|
||||
const getCreditsDisplay = (tier: PricingTierConfig): number =>
|
||||
tier.pricing.credits * (currentBillingCycle.value === 'yearly' ? 12 : 1)
|
||||
|
||||
const initiateCheckout = async (tierKey: CheckoutTierKey) => {
|
||||
const authHeader = await getAuthHeader()
|
||||
if (!authHeader) {
|
||||
throw new FirebaseAuthStoreError(t('toastMessages.userNotAuthenticated'))
|
||||
}
|
||||
|
||||
const checkoutTier = getCheckoutTier(tierKey, currentBillingCycle.value)
|
||||
const response = await fetch(
|
||||
`${getComfyApiBaseUrl()}/customers/cloud-subscription-checkout/${checkoutTier}`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: { ...authHeader, 'Content-Type': 'application/json' }
|
||||
}
|
||||
)
|
||||
|
||||
if (!response.ok) {
|
||||
let errorMessage = 'Failed to initiate checkout'
|
||||
try {
|
||||
const errorData = await response.json()
|
||||
errorMessage = errorData.message || errorMessage
|
||||
} catch {
|
||||
// If JSON parsing fails, try to get text response or use HTTP status
|
||||
try {
|
||||
const errorText = await response.text()
|
||||
errorMessage =
|
||||
errorText || `HTTP ${response.status} ${response.statusText}`
|
||||
} catch {
|
||||
errorMessage = `HTTP ${response.status} ${response.statusText}`
|
||||
}
|
||||
}
|
||||
|
||||
throw new FirebaseAuthStoreError(
|
||||
t('toastMessages.failedToInitiateSubscription', {
|
||||
error: errorMessage
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
return await response.json()
|
||||
}
|
||||
|
||||
const handleSubscribe = wrapWithErrorHandlingAsync(
|
||||
async (tierKey: CheckoutTierKey) => {
|
||||
if (!isCloud || isLoading.value || isCurrentPlan(tierKey)) return
|
||||
@@ -475,10 +430,7 @@ const handleSubscribe = wrapWithErrorHandlingAsync(
|
||||
await accessBillingPortal(checkoutTier)
|
||||
}
|
||||
} else {
|
||||
const response = await initiateCheckout(tierKey)
|
||||
if (response.checkout_url) {
|
||||
window.open(response.checkout_url, '_blank')
|
||||
}
|
||||
await performSubscriptionCheckout(tierKey, currentBillingCycle.value)
|
||||
}
|
||||
} finally {
|
||||
isLoading.value = false
|
||||
|
||||
Reference in New Issue
Block a user