Compare commits

...

1 Commits

Author SHA1 Message Date
Hunter Senft-Grupp
e8423f64ca feat: route all billing through cloud server
- useBillingContext always returns 'workspace' type when team workspaces
  enabled (no personal workspace special case)
- getComfyApiBaseUrl returns '' in cloud mode to proxy through cloud
- comfyRegistryService uses getComfyApiBaseUrl instead of hardcoded URL
- Remove createCustomer from all auth actions (no legacy Stripe customers)
- useSubscriptionDialog simplified to just teamWorkspacesEnabled flag
2026-02-07 22:31:03 -08:00
6 changed files with 44 additions and 44 deletions

View File

@@ -3,6 +3,14 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'
import { useBillingContext } from './useBillingContext'
vi.mock('@/composables/useFeatureFlags', () => ({
useFeatureFlags: () => ({
flags: {
teamWorkspacesEnabled: true
}
})
}))
vi.mock('@/platform/workspace/stores/teamWorkspaceStore', () => {
const isInPersonalWorkspace = { value: true }
const activeWorkspace = { value: { id: 'personal-123', type: 'personal' } }
@@ -10,6 +18,7 @@ vi.mock('@/platform/workspace/stores/teamWorkspaceStore', () => {
useTeamWorkspaceStore: () => ({
isInPersonalWorkspace: isInPersonalWorkspace.value,
activeWorkspace: activeWorkspace.value,
updateActiveWorkspace: vi.fn(),
_setPersonalWorkspace: (value: boolean) => {
isInPersonalWorkspace.value = value
activeWorkspace.value = value
@@ -80,7 +89,10 @@ vi.mock('@/platform/workspace/api/workspaceApi', () => ({
currency: 'usd'
}),
subscribe: vi.fn().mockResolvedValue({ status: 'subscribed' }),
previewSubscribe: vi.fn().mockResolvedValue({ allowed: true })
previewSubscribe: vi.fn().mockResolvedValue({ allowed: true }),
getPaymentPortalUrl: vi
.fn()
.mockResolvedValue({ url: 'https://example.com/billing' })
}
}))
@@ -90,35 +102,37 @@ describe('useBillingContext', () => {
vi.clearAllMocks()
})
it('returns legacy type for personal workspace', () => {
it('returns workspace type for personal workspace when feature flag enabled', () => {
const { type } = useBillingContext()
expect(type.value).toBe('legacy')
expect(type.value).toBe('workspace')
})
it('provides subscription info from legacy billing', () => {
const { subscription } = useBillingContext()
it('provides subscription info from workspace billing', async () => {
const { subscription, initialize } = useBillingContext()
await initialize()
expect(subscription.value).toEqual({
isActive: true,
tier: 'PRO',
duration: 'MONTHLY',
planSlug: null,
renewalDate: 'Jan 1, 2025',
renewalDate: null,
endDate: null,
isCancelled: false,
hasFunds: true
})
})
it('provides balance info from legacy billing', () => {
const { balance } = useBillingContext()
it('provides balance info from workspace billing', async () => {
const { balance, initialize } = useBillingContext()
await initialize()
expect(balance.value).toEqual({
amountMicros: 5000000,
amountMicros: 10000000,
currency: 'usd',
effectiveBalanceMicros: 5000000,
prepaidBalanceMicros: 0,
cloudCreditBalanceMicros: 0
effectiveBalanceMicros: undefined,
prepaidBalanceMicros: undefined,
cloudCreditBalanceMicros: undefined
})
})
@@ -139,7 +153,7 @@ describe('useBillingContext', () => {
it('exposes subscribe action', async () => {
const { subscribe } = useBillingContext()
await expect(subscribe('pro-monthly')).resolves.toBeUndefined()
await expect(subscribe('pro-monthly')).resolves.toBeDefined()
})
it('exposes manageSubscription action', async () => {

View File

@@ -83,13 +83,11 @@ function useBillingContextInternal(): BillingContext {
/**
* Determines which billing type to use:
* - If team workspaces feature is disabled: always use legacy (/customers)
* - If team workspaces feature is enabled:
* - Personal workspace: use legacy (/customers)
* - Team workspace: use workspace (/billing)
* - If team workspaces feature is enabled: always use workspace (/billing)
*/
const type = computed<BillingType>(() => {
if (!flags.teamWorkspacesEnabled) return 'legacy'
return store.isInPersonalWorkspace ? 'legacy' : 'workspace'
return 'workspace'
})
const activeContext = computed(() =>
@@ -121,7 +119,7 @@ function useBillingContextInternal(): BillingContext {
watch(
subscription,
(sub) => {
if (!sub || store.isInPersonalWorkspace) return
if (!sub) return
store.updateActiveWorkspace({
isSubscribed: sub.isActive && !sub.isCancelled,

View File

@@ -23,11 +23,9 @@ export function getComfyApiBaseUrl(): string {
return BUILD_TIME_API_BASE_URL
}
return configValueOrDefault(
remoteConfig.value,
'comfy_api_base_url',
BUILD_TIME_API_BASE_URL
)
// In cloud mode, proxy all comfy-api requests through the cloud server
// instead of calling api.comfy.org directly
return ''
}
export function getComfyPlatformBaseUrl(): string {

View File

@@ -2,7 +2,6 @@ import { defineAsyncComponent } from 'vue'
import { useDialogService } from '@/services/dialogService'
import { useDialogStore } from '@/stores/dialogStore'
import { useFeatureFlags } from '@/composables/useFeatureFlags'
import { useTeamWorkspaceStore } from '@/platform/workspace/stores/teamWorkspaceStore'
const DIALOG_KEY = 'subscription-required'
@@ -10,15 +9,13 @@ export const useSubscriptionDialog = () => {
const { flags } = useFeatureFlags()
const dialogService = useDialogService()
const dialogStore = useDialogStore()
const workspaceStore = useTeamWorkspaceStore()
function hide() {
dialogStore.closeDialog({ key: DIALOG_KEY })
}
function show() {
const useWorkspaceVariant =
flags.teamWorkspacesEnabled && !workspaceStore.isInPersonalWorkspace
const useWorkspaceVariant = flags.teamWorkspacesEnabled
const component = useWorkspaceVariant
? defineAsyncComponent(

View File

@@ -4,11 +4,10 @@ import { ref } from 'vue'
import type { components, operations } from '@/types/comfyRegistryTypes'
import { isAbortError } from '@/utils/typeGuardUtil'
const API_BASE_URL = 'https://api.comfy.org'
import { getComfyApiBaseUrl } from '@/config/comfyApi'
const registryApiClient = axios.create({
baseURL: API_BASE_URL,
baseURL: getComfyApiBaseUrl(),
headers: {
'Content-Type': 'application/json'
},

View File

@@ -340,10 +340,8 @@ export const useFirebaseAuthStore = defineStore('firebaseAuth', () => {
email: string,
password: string
): Promise<UserCredential> => {
const result = await executeAuthAction(
(authInstance) =>
signInWithEmailAndPassword(authInstance, email, password),
{ createCustomer: true }
const result = await executeAuthAction((authInstance) =>
signInWithEmailAndPassword(authInstance, email, password)
)
if (isCloud) {
@@ -361,10 +359,8 @@ export const useFirebaseAuthStore = defineStore('firebaseAuth', () => {
email: string,
password: string
): Promise<UserCredential> => {
const result = await executeAuthAction(
(authInstance) =>
createUserWithEmailAndPassword(authInstance, email, password),
{ createCustomer: true }
const result = await executeAuthAction((authInstance) =>
createUserWithEmailAndPassword(authInstance, email, password)
)
if (isCloud) {
@@ -379,9 +375,8 @@ export const useFirebaseAuthStore = defineStore('firebaseAuth', () => {
}
const loginWithGoogle = async (): Promise<UserCredential> => {
const result = await executeAuthAction(
(authInstance) => signInWithPopup(authInstance, googleProvider),
{ createCustomer: true }
const result = await executeAuthAction((authInstance) =>
signInWithPopup(authInstance, googleProvider)
)
if (isCloud) {
@@ -398,9 +393,8 @@ export const useFirebaseAuthStore = defineStore('firebaseAuth', () => {
}
const loginWithGithub = async (): Promise<UserCredential> => {
const result = await executeAuthAction(
(authInstance) => signInWithPopup(authInstance, githubProvider),
{ createCustomer: true }
const result = await executeAuthAction((authInstance) =>
signInWithPopup(authInstance, githubProvider)
)
if (isCloud) {