Files
ComfyUI_frontend/src/platform/cloud/subscription/components/SubscribeButton.vue
Simula_r c5431de123 Feat/workspaces 6 billing (#8508)
## Summary

Implements billing infrastructure for team workspaces, separate from
legacy personal billing.

## Changes

- **Billing abstraction**: New `useBillingContext` composable that
switches between legacy (personal) and workspace billing based on
context
- **Workspace subscription flows**: Pricing tables, plan transitions,
cancellation dialogs, and payment preview components for workspace
billing
- **Top-up credits**: Workspace-specific top-up dialog with polling for
payment confirmation
- **Workspace API**: Extended with billing endpoints (subscriptions,
invoices, payment methods, credits top-up)
- **Workspace switcher**: Now displays tier badges for each workspace
- **Subscribe polling**: Added polling mechanisms
(`useSubscribePolling`, `useTopupPolling`) for async payment flows

## Review Focus

- Billing flow correctness for workspace vs legacy contexts
- Polling timeout and error handling in payment flows

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8508-Feat-workspaces-6-billing-2f96d73d365081f69f65c1ddf369010d)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 20:52:53 -08:00

73 lines
1.7 KiB
Vue

<template>
<Button
:size
:disabled="disabled"
variant="primary"
:style="
variant === 'gradient'
? {
background: 'var(--color-subscription-button-gradient)',
color: 'var(--color-white)'
}
: undefined
"
:class="cn('font-bold', fluid && 'w-full')"
@click="handleSubscribe"
>
{{ label || $t('subscription.required.subscribe') }}
</Button>
</template>
<script setup lang="ts">
import { onBeforeUnmount, ref, watch } from 'vue'
import Button from '@/components/ui/button/Button.vue'
import { useBillingContext } from '@/composables/billing/useBillingContext'
import { isCloud } from '@/platform/distribution/types'
import { useTelemetry } from '@/platform/telemetry'
import { cn } from '@/utils/tailwindUtil'
const {
size = 'lg',
fluid = true,
variant = 'default',
label,
disabled = false
} = defineProps<{
label?: string
size?: 'sm' | 'lg'
variant?: 'default' | 'gradient'
fluid?: boolean
disabled?: boolean
}>()
const emit = defineEmits<{
subscribed: []
}>()
const { isActiveSubscription, showSubscriptionDialog } = useBillingContext()
const isAwaitingStripeSubscription = ref(false)
watch(
[isAwaitingStripeSubscription, isActiveSubscription],
([awaiting, isActive]) => {
if (isCloud && awaiting && isActive) {
emit('subscribed')
isAwaitingStripeSubscription.value = false
}
}
)
const handleSubscribe = () => {
if (isCloud) {
useTelemetry()?.trackSubscription('subscribe_clicked')
}
isAwaitingStripeSubscription.value = true
showSubscriptionDialog()
}
onBeforeUnmount(() => {
isAwaitingStripeSubscription.value = false
})
</script>