From 95d3d99d843b7404c325d183bff5fcb99f1e4914 Mon Sep 17 00:00:00 2001 From: Vikhyath Mondreti Date: Fri, 9 Jan 2026 14:26:47 -0800 Subject: [PATCH 1/8] improvement(billng): team upgrade + session management --- apps/sim/lib/auth/auth.ts | 41 +++++++++-- apps/sim/lib/billing/organization.ts | 104 ++++++++++++++++----------- 2 files changed, 98 insertions(+), 47 deletions(-) diff --git a/apps/sim/lib/auth/auth.ts b/apps/sim/lib/auth/auth.ts index 43e4c919ad..aa45b25336 100644 --- a/apps/sim/lib/auth/auth.ts +++ b/apps/sim/lib/auth/auth.ts @@ -2184,8 +2184,26 @@ export const auth = betterAuth({ status: subscription.status, }) - const resolvedSubscription = - await ensureOrganizationForTeamSubscription(subscription) + let resolvedSubscription = subscription + try { + resolvedSubscription = await ensureOrganizationForTeamSubscription(subscription) + } catch (orgError) { + // Critical: Log detailed error for manual investigation + // This can happen if user joined another org between checkout start and completion + logger.error( + '[onSubscriptionComplete] Failed to ensure organization for team subscription', + { + subscriptionId: subscription.id, + referenceId: subscription.referenceId, + plan: subscription.plan, + error: orgError instanceof Error ? orgError.message : String(orgError), + stack: orgError instanceof Error ? orgError.stack : undefined, + } + ) + // Re-throw to signal webhook failure - Stripe will retry + // This ensures we don't leave subscriptions in broken state silently + throw orgError + } await handleSubscriptionCreated(resolvedSubscription) @@ -2206,8 +2224,23 @@ export const auth = betterAuth({ plan: subscription.plan, }) - const resolvedSubscription = - await ensureOrganizationForTeamSubscription(subscription) + let resolvedSubscription = subscription + try { + resolvedSubscription = await ensureOrganizationForTeamSubscription(subscription) + } catch (orgError) { + // Log but don't throw - subscription updates should still process other logic + // The subscription may have been created with user ID if org creation failed initially + logger.error( + '[onSubscriptionUpdate] Failed to ensure organization for team subscription', + { + subscriptionId: subscription.id, + referenceId: subscription.referenceId, + plan: subscription.plan, + error: orgError instanceof Error ? orgError.message : String(orgError), + } + ) + // Continue with original subscription - don't block other updates + } try { await syncSubscriptionUsageLimits(resolvedSubscription) diff --git a/apps/sim/lib/billing/organization.ts b/apps/sim/lib/billing/organization.ts index 579dfbd886..ffffc21d39 100644 --- a/apps/sim/lib/billing/organization.ts +++ b/apps/sim/lib/billing/organization.ts @@ -1,5 +1,11 @@ import { db } from '@sim/db' -import * as schema from '@sim/db/schema' +import { + member, + organization, + session, + subscription as subscriptionTable, + user, +} from '@sim/db/schema' import { createLogger } from '@sim/logger' import { and, eq } from 'drizzle-orm' import { getPlanPricing } from '@/lib/billing/core/billing' @@ -20,16 +26,16 @@ type SubscriptionData = { */ async function getUserOwnedOrganization(userId: string): Promise { const existingMemberships = await db - .select({ organizationId: schema.member.organizationId }) - .from(schema.member) - .where(and(eq(schema.member.userId, userId), eq(schema.member.role, 'owner'))) + .select({ organizationId: member.organizationId }) + .from(member) + .where(and(eq(member.userId, userId), eq(member.role, 'owner'))) .limit(1) if (existingMemberships.length > 0) { const [existingOrg] = await db - .select({ id: schema.organization.id }) - .from(schema.organization) - .where(eq(schema.organization.id, existingMemberships[0].organizationId)) + .select({ id: organization.id }) + .from(organization) + .where(eq(organization.id, existingMemberships[0].organizationId)) .limit(1) return existingOrg?.id || null @@ -40,6 +46,8 @@ async function getUserOwnedOrganization(userId: string): Promise /** * Create a new organization and add user as owner + * Uses transaction to ensure org + member are created atomically + * Also updates user's active sessions to set the new org as active */ async function createOrganizationWithOwner( userId: string, @@ -49,31 +57,36 @@ async function createOrganizationWithOwner( ): Promise { const orgId = `org_${crypto.randomUUID()}` - const [newOrg] = await db - .insert(schema.organization) - .values({ + await db.transaction(async (tx) => { + await tx.insert(organization).values({ id: orgId, name: organizationName, slug: organizationSlug, metadata, }) - .returning({ id: schema.organization.id }) - - // Add user as owner/admin of the organization - await db.insert(schema.member).values({ - id: crypto.randomUUID(), - userId: userId, - organizationId: newOrg.id, - role: 'owner', + + await tx.insert(member).values({ + id: crypto.randomUUID(), + userId: userId, + organizationId: orgId, + role: 'owner', + }) }) + const updatedSessions = await db + .update(session) + .set({ activeOrganizationId: orgId }) + .where(eq(session.userId, userId)) + .returning({ id: session.id }) + logger.info('Created organization with owner', { userId, - organizationId: newOrg.id, + organizationId: orgId, organizationName, + sessionsUpdated: updatedSessions.length, }) - return newOrg.id + return orgId } export async function createOrganizationForTeamPlan( @@ -132,12 +145,12 @@ export async function ensureOrganizationForTeamSubscription( const existingMembership = await db .select({ - id: schema.member.id, - organizationId: schema.member.organizationId, - role: schema.member.role, + id: member.id, + organizationId: member.organizationId, + role: member.role, }) - .from(schema.member) - .where(eq(schema.member.userId, userId)) + .from(member) + .where(eq(member.userId, userId)) .limit(1) if (existingMembership.length > 0) { @@ -149,9 +162,14 @@ export async function ensureOrganizationForTeamSubscription( }) await db - .update(schema.subscription) + .update(subscriptionTable) .set({ referenceId: membership.organizationId }) - .where(eq(schema.subscription.id, subscription.id)) + .where(eq(subscriptionTable.id, subscription.id)) + + await db + .update(session) + .set({ activeOrganizationId: membership.organizationId }) + .where(eq(session.userId, userId)) return { ...subscription, referenceId: membership.organizationId } } @@ -165,9 +183,9 @@ export async function ensureOrganizationForTeamSubscription( } const [userData] = await db - .select({ name: schema.user.name, email: schema.user.email }) - .from(schema.user) - .where(eq(schema.user.id, userId)) + .select({ name: user.name, email: user.email }) + .from(user) + .where(eq(user.id, userId)) .limit(1) const orgId = await createOrganizationForTeamPlan( @@ -177,9 +195,9 @@ export async function ensureOrganizationForTeamSubscription( ) await db - .update(schema.subscription) + .update(subscriptionTable) .set({ referenceId: orgId }) - .where(eq(schema.subscription.id, subscription.id)) + .where(eq(subscriptionTable.id, subscription.id)) logger.info('Created organization and updated subscription referenceId', { subscriptionId: subscription.id, @@ -204,9 +222,9 @@ export async function syncSubscriptionUsageLimits(subscription: SubscriptionData // Check if this is a user or organization subscription const users = await db - .select({ id: schema.user.id }) - .from(schema.user) - .where(eq(schema.user.id, subscription.referenceId)) + .select({ id: user.id }) + .from(user) + .where(eq(user.id, subscription.referenceId)) .limit(1) if (users.length > 0) { @@ -230,9 +248,9 @@ export async function syncSubscriptionUsageLimits(subscription: SubscriptionData // Only set if not already set or if updating to a higher value based on seats const orgData = await db - .select({ orgUsageLimit: schema.organization.orgUsageLimit }) - .from(schema.organization) - .where(eq(schema.organization.id, organizationId)) + .select({ orgUsageLimit: organization.orgUsageLimit }) + .from(organization) + .where(eq(organization.id, organizationId)) .limit(1) const currentLimit = @@ -243,12 +261,12 @@ export async function syncSubscriptionUsageLimits(subscription: SubscriptionData // Update if no limit set, or if new seat-based minimum is higher if (currentLimit < orgLimit) { await db - .update(schema.organization) + .update(organization) .set({ orgUsageLimit: orgLimit.toFixed(2), updatedAt: new Date(), }) - .where(eq(schema.organization.id, organizationId)) + .where(eq(organization.id, organizationId)) logger.info('Set organization usage limit for team plan', { organizationId, @@ -262,9 +280,9 @@ export async function syncSubscriptionUsageLimits(subscription: SubscriptionData // Sync usage limits for all members const members = await db - .select({ userId: schema.member.userId }) - .from(schema.member) - .where(eq(schema.member.organizationId, organizationId)) + .select({ userId: member.userId }) + .from(member) + .where(eq(member.organizationId, organizationId)) if (members.length > 0) { for (const member of members) { From ed1c841c6bd253687ada5cbdb6ddebccd258e427 Mon Sep 17 00:00:00 2001 From: Vikhyath Mondreti Date: Fri, 9 Jan 2026 14:32:54 -0800 Subject: [PATCH 2/8] remove comments --- apps/sim/lib/auth/auth.ts | 7 ------- 1 file changed, 7 deletions(-) diff --git a/apps/sim/lib/auth/auth.ts b/apps/sim/lib/auth/auth.ts index aa45b25336..989187d7ce 100644 --- a/apps/sim/lib/auth/auth.ts +++ b/apps/sim/lib/auth/auth.ts @@ -2188,8 +2188,6 @@ export const auth = betterAuth({ try { resolvedSubscription = await ensureOrganizationForTeamSubscription(subscription) } catch (orgError) { - // Critical: Log detailed error for manual investigation - // This can happen if user joined another org between checkout start and completion logger.error( '[onSubscriptionComplete] Failed to ensure organization for team subscription', { @@ -2200,8 +2198,6 @@ export const auth = betterAuth({ stack: orgError instanceof Error ? orgError.stack : undefined, } ) - // Re-throw to signal webhook failure - Stripe will retry - // This ensures we don't leave subscriptions in broken state silently throw orgError } @@ -2228,8 +2224,6 @@ export const auth = betterAuth({ try { resolvedSubscription = await ensureOrganizationForTeamSubscription(subscription) } catch (orgError) { - // Log but don't throw - subscription updates should still process other logic - // The subscription may have been created with user ID if org creation failed initially logger.error( '[onSubscriptionUpdate] Failed to ensure organization for team subscription', { @@ -2239,7 +2233,6 @@ export const auth = betterAuth({ error: orgError instanceof Error ? orgError.message : String(orgError), } ) - // Continue with original subscription - don't block other updates } try { From 85a09eab39b897df2b92cac06cc610a27c51bb98 Mon Sep 17 00:00:00 2001 From: Vikhyath Mondreti Date: Fri, 9 Jan 2026 14:37:24 -0800 Subject: [PATCH 3/8] session updates should be atomic --- apps/sim/lib/billing/organization.ts | 35 ++++++++++++++++------------ 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/apps/sim/lib/billing/organization.ts b/apps/sim/lib/billing/organization.ts index ffffc21d39..dde9db0c2c 100644 --- a/apps/sim/lib/billing/organization.ts +++ b/apps/sim/lib/billing/organization.ts @@ -56,6 +56,7 @@ async function createOrganizationWithOwner( metadata: Record = {} ): Promise { const orgId = `org_${crypto.randomUUID()}` + let sessionsUpdated = 0 await db.transaction(async (tx) => { await tx.insert(organization).values({ @@ -71,19 +72,21 @@ async function createOrganizationWithOwner( organizationId: orgId, role: 'owner', }) - }) - const updatedSessions = await db - .update(session) - .set({ activeOrganizationId: orgId }) - .where(eq(session.userId, userId)) - .returning({ id: session.id }) + const updatedSessions = await tx + .update(session) + .set({ activeOrganizationId: orgId }) + .where(eq(session.userId, userId)) + .returning({ id: session.id }) + + sessionsUpdated = updatedSessions.length + }) logger.info('Created organization with owner', { userId, organizationId: orgId, organizationName, - sessionsUpdated: updatedSessions.length, + sessionsUpdated, }) return orgId @@ -161,15 +164,17 @@ export async function ensureOrganizationForTeamSubscription( organizationId: membership.organizationId, }) - await db - .update(subscriptionTable) - .set({ referenceId: membership.organizationId }) - .where(eq(subscriptionTable.id, subscription.id)) + await db.transaction(async (tx) => { + await tx + .update(subscriptionTable) + .set({ referenceId: membership.organizationId }) + .where(eq(subscriptionTable.id, subscription.id)) - await db - .update(session) - .set({ activeOrganizationId: membership.organizationId }) - .where(eq(session.userId, userId)) + await tx + .update(session) + .set({ activeOrganizationId: membership.organizationId }) + .where(eq(session.userId, userId)) + }) return { ...subscription, referenceId: membership.organizationId } } From ed197d8395d87c73c8b62ea91d6d64b0867a3c38 Mon Sep 17 00:00:00 2001 From: Vikhyath Mondreti Date: Fri, 9 Jan 2026 14:42:11 -0800 Subject: [PATCH 4/8] make consistent for onSubscritionUpdate --- apps/sim/lib/auth/auth.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/sim/lib/auth/auth.ts b/apps/sim/lib/auth/auth.ts index 989187d7ce..38d9e80754 100644 --- a/apps/sim/lib/auth/auth.ts +++ b/apps/sim/lib/auth/auth.ts @@ -2231,8 +2231,10 @@ export const auth = betterAuth({ referenceId: subscription.referenceId, plan: subscription.plan, error: orgError instanceof Error ? orgError.message : String(orgError), + stack: orgError instanceof Error ? orgError.stack : undefined, } ) + throw orgError } try { From 61a7b3736790b4d57870fd8d4afc471efd5895ba Mon Sep 17 00:00:00 2001 From: Vikhyath Mondreti Date: Fri, 9 Jan 2026 16:20:19 -0800 Subject: [PATCH 5/8] plan upgrade to refresh session --- .../app/_shell/providers/session-provider.tsx | 29 ++++++++++-- .../components/subscription/subscription.tsx | 45 ++++++++++++------- .../settings-modal/settings-modal.tsx | 1 + apps/sim/hooks/queries/organization.ts | 16 +++++++ apps/sim/lib/billing/client/upgrade.ts | 3 +- 5 files changed, 73 insertions(+), 21 deletions(-) diff --git a/apps/sim/app/_shell/providers/session-provider.tsx b/apps/sim/app/_shell/providers/session-provider.tsx index 70fe344bd0..29ab636e74 100644 --- a/apps/sim/app/_shell/providers/session-provider.tsx +++ b/apps/sim/app/_shell/providers/session-provider.tsx @@ -2,6 +2,7 @@ import type React from 'react' import { createContext, useCallback, useEffect, useMemo, useState } from 'react' +import { useQueryClient } from '@tanstack/react-query' import posthog from 'posthog-js' import { client } from '@/lib/auth/auth-client' @@ -35,12 +36,15 @@ export function SessionProvider({ children }: { children: React.ReactNode }) { const [data, setData] = useState(null) const [isPending, setIsPending] = useState(true) const [error, setError] = useState(null) + const queryClient = useQueryClient() - const loadSession = useCallback(async () => { + const loadSession = useCallback(async (bypassCache = false) => { try { setIsPending(true) setError(null) - const res = await client.getSession() + const res = bypassCache + ? await client.getSession({ query: { disableCookieCache: true } }) + : await client.getSession() setData(res?.data ?? null) } catch (e) { setError(e instanceof Error ? e : new Error('Failed to fetch session')) @@ -50,8 +54,25 @@ export function SessionProvider({ children }: { children: React.ReactNode }) { }, []) useEffect(() => { - loadSession() - }, [loadSession]) + // Check if user was redirected after plan upgrade + const params = new URLSearchParams(window.location.search) + const wasUpgraded = params.get('upgraded') === 'true' + + if (wasUpgraded) { + params.delete('upgraded') + const newUrl = params.toString() + ? `${window.location.pathname}?${params.toString()}` + : window.location.pathname + window.history.replaceState({}, '', newUrl) + } + + loadSession(wasUpgraded).then(() => { + if (wasUpgraded) { + queryClient.invalidateQueries({ queryKey: ['organizations'] }) + queryClient.invalidateQueries({ queryKey: ['subscription'] }) + } + }) + }, [loadSession, queryClient]) useEffect(() => { if (isPending || typeof posthog.identify !== 'function') { diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/subscription/subscription.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/subscription/subscription.tsx index 5eafe5b90c..c9c7995259 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/subscription/subscription.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/subscription/subscription.tsx @@ -8,6 +8,7 @@ import { Skeleton } from '@/components/ui' import { useSession } from '@/lib/auth/auth-client' import { useSubscriptionUpgrade } from '@/lib/billing/client/upgrade' import { USAGE_THRESHOLDS } from '@/lib/billing/client/usage-visualization' +import { getEffectiveSeats } from '@/lib/billing/subscriptions/utils' import { cn } from '@/lib/core/utils/cn' import { getBaseUrl } from '@/lib/core/utils/urls' import { getUserRole } from '@/lib/workspaces/organization/utils' @@ -191,7 +192,13 @@ export function Subscription() { const [upgradeError, setUpgradeError] = useState<'pro' | 'team' | null>(null) const usageLimitRef = useRef(null) - const isLoading = isSubscriptionLoading || isUsageLimitLoading || isWorkspaceLoading + const isOrgPlan = + subscriptionData?.data?.plan === 'team' || subscriptionData?.data?.plan === 'enterprise' + const isLoading = + isSubscriptionLoading || + isUsageLimitLoading || + isWorkspaceLoading || + (isOrgPlan && isOrgBillingLoading) const subscription = { isFree: subscriptionData?.data?.plan === 'free' || !subscriptionData?.data?.plan, @@ -204,7 +211,7 @@ export function Subscription() { subscriptionData?.data?.status === 'active', plan: subscriptionData?.data?.plan || 'free', status: subscriptionData?.data?.status || 'inactive', - seats: organizationBillingData?.totalSeats ?? 0, + seats: getEffectiveSeats(subscriptionData?.data), } const usage = { @@ -445,16 +452,10 @@ export function Subscription() { ? `${subscription.seats} seats` : undefined } - current={ - subscription.isEnterprise || subscription.isTeam - ? (organizationBillingData?.totalCurrentUsage ?? usage.current) - : usage.current - } + current={usage.current} limit={ subscription.isEnterprise || subscription.isTeam - ? organizationBillingData?.totalUsageLimit || - organizationBillingData?.minimumBillingAmount || - usage.limit + ? organizationBillingData?.data?.totalUsageLimit : !subscription.isFree && (permissions.canEditUsageLimit || permissions.showTeamMemberView) ? usage.current // placeholder; rightContent will render UsageLimit @@ -468,19 +469,31 @@ export function Subscription() { { logger.info('Usage limit updated') }} diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/settings-modal.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/settings-modal.tsx index 63c6748519..811f60c811 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/settings-modal.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/settings-modal.tsx @@ -174,6 +174,7 @@ export function SettingsModal({ open, onOpenChange }: SettingsModalProps) { const userEmail = session?.user?.email const userId = session?.user?.id + const userRole = getUserRole(activeOrganization, userEmail) const isOwner = userRole === 'owner' const isAdmin = userRole === 'admin' diff --git a/apps/sim/hooks/queries/organization.ts b/apps/sim/hooks/queries/organization.ts index 7f4fbe7ee4..f26ccf8147 100644 --- a/apps/sim/hooks/queries/organization.ts +++ b/apps/sim/hooks/queries/organization.ts @@ -47,6 +47,22 @@ export function useOrganizations() { }) } +/** + * Fetch organizations where user is owner/admin directly from DB + * This bypasses session's activeOrganizationId for reliable role checking + */ +export function useUserOwnedOrganizations() { + return useQuery({ + queryKey: ['user-owned-organizations'], + queryFn: async () => { + const res = await fetch('/api/organizations') + if (!res.ok) return { organizations: [] } + return res.json() + }, + staleTime: 0, // Always refetch to ensure fresh org membership data + }) +} + /** * Fetch a specific organization by ID */ diff --git a/apps/sim/lib/billing/client/upgrade.ts b/apps/sim/lib/billing/client/upgrade.ts index 953f585a94..ab29bd7c25 100644 --- a/apps/sim/lib/billing/client/upgrade.ts +++ b/apps/sim/lib/billing/client/upgrade.ts @@ -81,12 +81,13 @@ export function useSubscriptionUpgrade() { } const currentUrl = `${window.location.origin}${window.location.pathname}` + const successUrl = `${currentUrl}?upgraded=true` try { const upgradeParams = { plan: targetPlan, referenceId, - successUrl: currentUrl, + successUrl, cancelUrl: currentUrl, ...(targetPlan === 'team' && { seats: CONSTANTS.INITIAL_TEAM_SEATS }), } as const From 3bd56ec1d6e34543ea841a19780b7ac3ef17cede Mon Sep 17 00:00:00 2001 From: Vikhyath Mondreti Date: Fri, 9 Jan 2026 16:24:12 -0800 Subject: [PATCH 6/8] fix var name --- apps/sim/lib/billing/organization.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/sim/lib/billing/organization.ts b/apps/sim/lib/billing/organization.ts index dde9db0c2c..eff6a03c0c 100644 --- a/apps/sim/lib/billing/organization.ts +++ b/apps/sim/lib/billing/organization.ts @@ -290,12 +290,12 @@ export async function syncSubscriptionUsageLimits(subscription: SubscriptionData .where(eq(member.organizationId, organizationId)) if (members.length > 0) { - for (const member of members) { + for (const m of members) { try { - await syncUsageLimitsFromSubscription(member.userId) + await syncUsageLimitsFromSubscription(m.userId) } catch (memberError) { logger.error('Failed to sync usage limits for organization member', { - userId: member.userId, + userId: m.userId, organizationId, subscriptionId: subscription.id, error: memberError, From 16e39f4de6a1e330a15644f13db2057946b97939 Mon Sep 17 00:00:00 2001 From: Vikhyath Mondreti Date: Fri, 9 Jan 2026 16:26:00 -0800 Subject: [PATCH 7/8] remove dead code --- apps/sim/hooks/queries/organization.ts | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/apps/sim/hooks/queries/organization.ts b/apps/sim/hooks/queries/organization.ts index f26ccf8147..7f4fbe7ee4 100644 --- a/apps/sim/hooks/queries/organization.ts +++ b/apps/sim/hooks/queries/organization.ts @@ -47,22 +47,6 @@ export function useOrganizations() { }) } -/** - * Fetch organizations where user is owner/admin directly from DB - * This bypasses session's activeOrganizationId for reliable role checking - */ -export function useUserOwnedOrganizations() { - return useQuery({ - queryKey: ['user-owned-organizations'], - queryFn: async () => { - const res = await fetch('/api/organizations') - if (!res.ok) return { organizations: [] } - return res.json() - }, - staleTime: 0, // Always refetch to ensure fresh org membership data - }) -} - /** * Fetch a specific organization by ID */ From ee4dc4be7072ae7d396f9ef5cb414ea2b95dac41 Mon Sep 17 00:00:00 2001 From: Vikhyath Mondreti Date: Fri, 9 Jan 2026 16:36:31 -0800 Subject: [PATCH 8/8] preserve params --- apps/sim/lib/billing/client/upgrade.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/sim/lib/billing/client/upgrade.ts b/apps/sim/lib/billing/client/upgrade.ts index ab29bd7c25..acd7e651ce 100644 --- a/apps/sim/lib/billing/client/upgrade.ts +++ b/apps/sim/lib/billing/client/upgrade.ts @@ -81,7 +81,9 @@ export function useSubscriptionUpgrade() { } const currentUrl = `${window.location.origin}${window.location.pathname}` - const successUrl = `${currentUrl}?upgraded=true` + const successUrlObj = new URL(window.location.href) + successUrlObj.searchParams.set('upgraded', 'true') + const successUrl = successUrlObj.toString() try { const upgradeParams = {