mirror of
https://github.com/simstudioai/sim.git
synced 2026-01-31 09:48:06 -05:00
Compare commits
3 Commits
fix/invite
...
fix/billin
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a660907c22 | ||
|
|
1eacfcb864 | ||
|
|
92403e0594 |
@@ -150,7 +150,9 @@ export function Editor() {
|
||||
blockSubBlockValues,
|
||||
canonicalIndex
|
||||
)
|
||||
const displayAdvancedOptions = advancedMode || advancedValuesPresent
|
||||
const displayAdvancedOptions = userPermissions.canEdit
|
||||
? advancedMode
|
||||
: advancedMode || advancedValuesPresent
|
||||
|
||||
const hasAdvancedOnlyFields = useMemo(() => {
|
||||
for (const subBlock of subBlocksForCanonical) {
|
||||
|
||||
@@ -30,7 +30,7 @@ import {
|
||||
ensureOrganizationForTeamSubscription,
|
||||
syncSubscriptionUsageLimits,
|
||||
} from '@/lib/billing/organization'
|
||||
import { getPlans } from '@/lib/billing/plans'
|
||||
import { getPlans, resolvePlanFromStripeSubscription } from '@/lib/billing/plans'
|
||||
import { syncSeatsFromStripeQuantity } from '@/lib/billing/validation/seat-management'
|
||||
import { handleChargeDispute, handleDisputeClosed } from '@/lib/billing/webhooks/disputes'
|
||||
import { handleManualEnterpriseSubscription } from '@/lib/billing/webhooks/enterprise'
|
||||
@@ -2641,29 +2641,42 @@ export const auth = betterAuth({
|
||||
}
|
||||
},
|
||||
onSubscriptionComplete: async ({
|
||||
stripeSubscription,
|
||||
subscription,
|
||||
}: {
|
||||
event: Stripe.Event
|
||||
stripeSubscription: Stripe.Subscription
|
||||
subscription: any
|
||||
}) => {
|
||||
const { priceId, planFromStripe, isTeamPlan } =
|
||||
resolvePlanFromStripeSubscription(stripeSubscription)
|
||||
|
||||
logger.info('[onSubscriptionComplete] Subscription created', {
|
||||
subscriptionId: subscription.id,
|
||||
referenceId: subscription.referenceId,
|
||||
plan: subscription.plan,
|
||||
dbPlan: subscription.plan,
|
||||
planFromStripe,
|
||||
priceId,
|
||||
status: subscription.status,
|
||||
})
|
||||
|
||||
const subscriptionForOrgCreation = isTeamPlan
|
||||
? { ...subscription, plan: 'team' }
|
||||
: subscription
|
||||
|
||||
let resolvedSubscription = subscription
|
||||
try {
|
||||
resolvedSubscription = await ensureOrganizationForTeamSubscription(subscription)
|
||||
resolvedSubscription = await ensureOrganizationForTeamSubscription(
|
||||
subscriptionForOrgCreation
|
||||
)
|
||||
} catch (orgError) {
|
||||
logger.error(
|
||||
'[onSubscriptionComplete] Failed to ensure organization for team subscription',
|
||||
{
|
||||
subscriptionId: subscription.id,
|
||||
referenceId: subscription.referenceId,
|
||||
plan: subscription.plan,
|
||||
dbPlan: subscription.plan,
|
||||
planFromStripe,
|
||||
error: orgError instanceof Error ? orgError.message : String(orgError),
|
||||
stack: orgError instanceof Error ? orgError.stack : undefined,
|
||||
}
|
||||
@@ -2684,22 +2697,67 @@ export const auth = betterAuth({
|
||||
event: Stripe.Event
|
||||
subscription: any
|
||||
}) => {
|
||||
const stripeSubscription = event.data.object as Stripe.Subscription
|
||||
const { priceId, planFromStripe, isTeamPlan } =
|
||||
resolvePlanFromStripeSubscription(stripeSubscription)
|
||||
|
||||
if (priceId && !planFromStripe) {
|
||||
logger.warn(
|
||||
'[onSubscriptionUpdate] Could not determine plan from Stripe price ID',
|
||||
{
|
||||
subscriptionId: subscription.id,
|
||||
priceId,
|
||||
dbPlan: subscription.plan,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
const isUpgradeToTeam =
|
||||
isTeamPlan &&
|
||||
subscription.plan !== 'team' &&
|
||||
!subscription.referenceId.startsWith('org_')
|
||||
|
||||
const effectivePlanForTeamFeatures = planFromStripe ?? subscription.plan
|
||||
|
||||
logger.info('[onSubscriptionUpdate] Subscription updated', {
|
||||
subscriptionId: subscription.id,
|
||||
status: subscription.status,
|
||||
plan: subscription.plan,
|
||||
dbPlan: subscription.plan,
|
||||
planFromStripe,
|
||||
isUpgradeToTeam,
|
||||
referenceId: subscription.referenceId,
|
||||
})
|
||||
|
||||
const subscriptionForOrgCreation = isUpgradeToTeam
|
||||
? { ...subscription, plan: 'team' }
|
||||
: subscription
|
||||
|
||||
let resolvedSubscription = subscription
|
||||
try {
|
||||
resolvedSubscription = await ensureOrganizationForTeamSubscription(subscription)
|
||||
resolvedSubscription = await ensureOrganizationForTeamSubscription(
|
||||
subscriptionForOrgCreation
|
||||
)
|
||||
|
||||
if (isUpgradeToTeam) {
|
||||
logger.info(
|
||||
'[onSubscriptionUpdate] Detected Pro -> Team upgrade, ensured organization creation',
|
||||
{
|
||||
subscriptionId: subscription.id,
|
||||
originalPlan: subscription.plan,
|
||||
newPlan: planFromStripe,
|
||||
resolvedReferenceId: resolvedSubscription.referenceId,
|
||||
}
|
||||
)
|
||||
}
|
||||
} catch (orgError) {
|
||||
logger.error(
|
||||
'[onSubscriptionUpdate] Failed to ensure organization for team subscription',
|
||||
{
|
||||
subscriptionId: subscription.id,
|
||||
referenceId: subscription.referenceId,
|
||||
plan: subscription.plan,
|
||||
dbPlan: subscription.plan,
|
||||
planFromStripe,
|
||||
isUpgradeToTeam,
|
||||
error: orgError instanceof Error ? orgError.message : String(orgError),
|
||||
stack: orgError instanceof Error ? orgError.stack : undefined,
|
||||
}
|
||||
@@ -2717,9 +2775,8 @@ export const auth = betterAuth({
|
||||
})
|
||||
}
|
||||
|
||||
if (resolvedSubscription.plan === 'team') {
|
||||
if (effectivePlanForTeamFeatures === 'team') {
|
||||
try {
|
||||
const stripeSubscription = event.data.object as Stripe.Subscription
|
||||
const quantity = stripeSubscription.items?.data?.[0]?.quantity || 1
|
||||
|
||||
const result = await syncSeatsFromStripeQuantity(
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type Stripe from 'stripe'
|
||||
import {
|
||||
getFreeTierLimit,
|
||||
getProTierLimit,
|
||||
@@ -56,6 +57,13 @@ export function getPlanByName(planName: string): BillingPlan | undefined {
|
||||
return getPlans().find((plan) => plan.name === planName)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a specific plan by Stripe price ID
|
||||
*/
|
||||
export function getPlanByPriceId(priceId: string): BillingPlan | undefined {
|
||||
return getPlans().find((plan) => plan.priceId === priceId)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get plan limits for a given plan name
|
||||
*/
|
||||
@@ -63,3 +71,26 @@ export function getPlanLimits(planName: string): number {
|
||||
const plan = getPlanByName(planName)
|
||||
return plan?.limits.cost ?? getFreeTierLimit()
|
||||
}
|
||||
|
||||
export interface StripePlanResolution {
|
||||
priceId: string | undefined
|
||||
planFromStripe: string | null
|
||||
isTeamPlan: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve plan information from a Stripe subscription object.
|
||||
* Used to get the authoritative plan from Stripe rather than relying on DB state.
|
||||
*/
|
||||
export function resolvePlanFromStripeSubscription(
|
||||
stripeSubscription: Stripe.Subscription
|
||||
): StripePlanResolution {
|
||||
const priceId = stripeSubscription?.items?.data?.[0]?.price?.id
|
||||
const plan = priceId ? getPlanByPriceId(priceId) : undefined
|
||||
|
||||
return {
|
||||
priceId,
|
||||
planFromStripe: plan?.name ?? null,
|
||||
isTeamPlan: plan?.name === 'team',
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user