mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-02-04 23:20:07 +00:00
FirebaseUID gating pending purchases
This commit is contained in:
@@ -10,7 +10,8 @@ const {
|
||||
mockShowSubscriptionRequiredDialog,
|
||||
mockGetAuthHeader,
|
||||
mockPushDataLayerEvent,
|
||||
mockTelemetry
|
||||
mockTelemetry,
|
||||
mockUserId
|
||||
} = vi.hoisted(() => ({
|
||||
mockIsLoggedIn: { value: false },
|
||||
mockReportError: vi.fn(),
|
||||
@@ -23,7 +24,8 @@ const {
|
||||
mockTelemetry: {
|
||||
trackSubscription: vi.fn(),
|
||||
trackMonthlySubscriptionCancelled: vi.fn()
|
||||
}
|
||||
},
|
||||
mockUserId: { value: 'user-123' }
|
||||
}))
|
||||
|
||||
let scope: ReturnType<typeof effectScope> | undefined
|
||||
@@ -89,7 +91,8 @@ vi.mock('@/services/dialogService', () => ({
|
||||
|
||||
vi.mock('@/stores/firebaseAuthStore', () => ({
|
||||
useFirebaseAuthStore: vi.fn(() => ({
|
||||
getFirebaseAuthHeader: mockGetAuthHeader
|
||||
getFirebaseAuthHeader: mockGetAuthHeader,
|
||||
userId: mockUserId.value
|
||||
})),
|
||||
FirebaseAuthStoreError: class extends Error {}
|
||||
}))
|
||||
@@ -112,6 +115,7 @@ describe('useSubscription', () => {
|
||||
mockTelemetry.trackSubscription.mockReset()
|
||||
mockTelemetry.trackMonthlySubscriptionCancelled.mockReset()
|
||||
mockPushDataLayerEvent.mockReset()
|
||||
mockUserId.value = 'user-123'
|
||||
mockPushDataLayerEvent.mockImplementation((event) => {
|
||||
const dataLayer = window.dataLayer ?? (window.dataLayer = [])
|
||||
dataLayer.push(event)
|
||||
@@ -249,6 +253,7 @@ describe('useSubscription', () => {
|
||||
localStorage.setItem(
|
||||
'pending_subscription_purchase',
|
||||
JSON.stringify({
|
||||
firebaseUid: 'user-123',
|
||||
tierKey: 'creator',
|
||||
billingCycle: 'monthly',
|
||||
timestamp: Date.now()
|
||||
@@ -287,6 +292,38 @@ describe('useSubscription', () => {
|
||||
expect(localStorage.getItem('pending_subscription_purchase')).toBeNull()
|
||||
})
|
||||
|
||||
it('ignores pending purchase when user does not match', async () => {
|
||||
window.dataLayer = []
|
||||
localStorage.setItem(
|
||||
'pending_subscription_purchase',
|
||||
JSON.stringify({
|
||||
firebaseUid: 'user-123',
|
||||
tierKey: 'creator',
|
||||
billingCycle: 'monthly',
|
||||
timestamp: Date.now()
|
||||
})
|
||||
)
|
||||
|
||||
mockUserId.value = 'user-456'
|
||||
vi.mocked(global.fetch).mockResolvedValue({
|
||||
ok: true,
|
||||
json: async () => ({
|
||||
is_active: true,
|
||||
subscription_id: 'sub_123',
|
||||
subscription_tier: 'CREATOR',
|
||||
subscription_duration: 'MONTHLY'
|
||||
})
|
||||
} as Response)
|
||||
|
||||
mockIsLoggedIn.value = true
|
||||
const { fetchStatus } = useSubscriptionWithScope()
|
||||
|
||||
await fetchStatus()
|
||||
|
||||
expect(window.dataLayer).toHaveLength(0)
|
||||
expect(localStorage.getItem('pending_subscription_purchase')).toBeNull()
|
||||
})
|
||||
|
||||
it('should handle fetch errors gracefully', async () => {
|
||||
vi.mocked(global.fetch).mockResolvedValue({
|
||||
ok: false,
|
||||
|
||||
@@ -45,7 +45,7 @@ function useSubscriptionInternal() {
|
||||
const { reportError, accessBillingPortal } = useFirebaseAuthActions()
|
||||
const { showSubscriptionRequiredDialog } = useDialogService()
|
||||
|
||||
const { getFirebaseAuthHeader } = useFirebaseAuthStore()
|
||||
const { getFirebaseAuthHeader, userId } = useFirebaseAuthStore()
|
||||
const { wrapWithErrorHandlingAsync } = useErrorHandling()
|
||||
|
||||
const { isLoggedIn } = useCurrentUser()
|
||||
@@ -109,7 +109,9 @@ function useSubscriptionInternal() {
|
||||
): void {
|
||||
if (!status?.is_active || !status.subscription_id) return
|
||||
|
||||
const pendingPurchase = getPendingSubscriptionPurchase()
|
||||
if (!userId) return
|
||||
|
||||
const pendingPurchase = getPendingSubscriptionPurchase(userId)
|
||||
if (!pendingPurchase) return
|
||||
|
||||
const { tierKey, billingCycle } = pendingPurchase
|
||||
|
||||
@@ -36,7 +36,7 @@ export async function performSubscriptionCheckout(
|
||||
): Promise<void> {
|
||||
if (!isCloud) return
|
||||
|
||||
const { getFirebaseAuthHeader } = useFirebaseAuthStore()
|
||||
const { getFirebaseAuthHeader, userId } = useFirebaseAuthStore()
|
||||
const authHeader = await getFirebaseAuthHeader()
|
||||
|
||||
if (!authHeader) {
|
||||
@@ -79,7 +79,9 @@ export async function performSubscriptionCheckout(
|
||||
const data = await response.json()
|
||||
|
||||
if (data.checkout_url) {
|
||||
startSubscriptionPurchaseTracking(tierKey, currentBillingCycle)
|
||||
if (userId) {
|
||||
startSubscriptionPurchaseTracking(tierKey, currentBillingCycle, userId)
|
||||
}
|
||||
if (openInNewTab) {
|
||||
window.open(data.checkout_url, '_blank')
|
||||
} else {
|
||||
|
||||
@@ -2,6 +2,7 @@ import type { TierKey } from '@/platform/cloud/subscription/constants/tierPricin
|
||||
import type { BillingCycle } from './subscriptionTierRank'
|
||||
|
||||
type PendingSubscriptionPurchase = {
|
||||
firebaseUid: string
|
||||
tierKey: TierKey
|
||||
billingCycle: BillingCycle
|
||||
timestamp: number
|
||||
@@ -22,11 +23,14 @@ const safeRemove = (): void => {
|
||||
|
||||
export function startSubscriptionPurchaseTracking(
|
||||
tierKey: TierKey,
|
||||
billingCycle: BillingCycle
|
||||
billingCycle: BillingCycle,
|
||||
firebaseUid: string
|
||||
): void {
|
||||
if (typeof window === 'undefined') return
|
||||
if (!firebaseUid) return
|
||||
try {
|
||||
const payload: PendingSubscriptionPurchase = {
|
||||
firebaseUid,
|
||||
tierKey,
|
||||
billingCycle,
|
||||
timestamp: Date.now()
|
||||
@@ -37,8 +41,11 @@ export function startSubscriptionPurchaseTracking(
|
||||
}
|
||||
}
|
||||
|
||||
export function getPendingSubscriptionPurchase(): PendingSubscriptionPurchase | null {
|
||||
export function getPendingSubscriptionPurchase(
|
||||
firebaseUid: string
|
||||
): PendingSubscriptionPurchase | null {
|
||||
if (typeof window === 'undefined') return null
|
||||
if (!firebaseUid) return null
|
||||
|
||||
try {
|
||||
const raw = localStorage.getItem(STORAGE_KEY)
|
||||
@@ -50,8 +57,9 @@ export function getPendingSubscriptionPurchase(): PendingSubscriptionPurchase |
|
||||
return null
|
||||
}
|
||||
|
||||
const { tierKey, billingCycle, timestamp } = parsed
|
||||
const { firebaseUid: storedUid, tierKey, billingCycle, timestamp } = parsed
|
||||
if (
|
||||
storedUid !== firebaseUid ||
|
||||
!VALID_TIERS.includes(tierKey) ||
|
||||
!VALID_CYCLES.includes(billingCycle) ||
|
||||
typeof timestamp !== 'number'
|
||||
|
||||
Reference in New Issue
Block a user