Skip to content

Commit 3bb85f2

Browse files
committed
fix(admin): use existing credit functions and handle enterprise seats
1 parent 1e89d14 commit 3bb85f2

File tree

1 file changed

+63
-95
lines changed
  • apps/sim/app/api/v1/admin/credits

1 file changed

+63
-95
lines changed

apps/sim/app/api/v1/admin/credits/route.ts

Lines changed: 63 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,12 @@
2626
import { db } from '@sim/db'
2727
import { organization, subscription, user, userStats } from '@sim/db/schema'
2828
import { createLogger } from '@sim/logger'
29-
import { and, eq, sql } from 'drizzle-orm'
29+
import { and, eq } from 'drizzle-orm'
3030
import { nanoid } from 'nanoid'
31-
import { getPlanPricing } from '@/lib/billing/core/billing'
3231
import { getHighestPrioritySubscription } from '@/lib/billing/core/subscription'
32+
import { addCredits } from '@/lib/billing/credits/balance'
33+
import { setUsageLimitForCredits } from '@/lib/billing/credits/purchase'
34+
import { getEffectiveSeats } from '@/lib/billing/subscriptions/utils'
3335
import { withAdminAuth } from '@/app/api/v1/admin/middleware'
3436
import {
3537
badRequestResponse,
@@ -85,15 +87,20 @@ export const POST = withAdminAuth(async (request) => {
8587

8688
const userSubscription = await getHighestPrioritySubscription(resolvedUserId)
8789

90+
if (!userSubscription || !['pro', 'team', 'enterprise'].includes(userSubscription.plan)) {
91+
return badRequestResponse(
92+
'User must have an active Pro, Team, or Enterprise subscription to receive credits'
93+
)
94+
}
95+
8896
let entityType: 'user' | 'organization'
8997
let entityId: string
90-
let plan: string
98+
const plan = userSubscription.plan
9199
let seats: number | null = null
92100

93-
if (userSubscription?.plan === 'team' || userSubscription?.plan === 'enterprise') {
101+
if (plan === 'team' || plan === 'enterprise') {
94102
entityType = 'organization'
95103
entityId = userSubscription.referenceId
96-
plan = userSubscription.plan
97104

98105
const [orgExists] = await db
99106
.select({ id: organization.id })
@@ -106,106 +113,67 @@ export const POST = withAdminAuth(async (request) => {
106113
}
107114

108115
const [subData] = await db
109-
.select({ seats: subscription.seats })
116+
.select()
110117
.from(subscription)
111118
.where(and(eq(subscription.referenceId, entityId), eq(subscription.status, 'active')))
112119
.limit(1)
113120

114-
seats = subData?.seats ?? null
115-
} else if (userSubscription?.plan === 'pro') {
121+
seats = getEffectiveSeats(subData)
122+
} else {
116123
entityType = 'user'
117124
entityId = resolvedUserId
118-
plan = 'pro'
119-
} else {
120-
return badRequestResponse(
121-
'User must have an active Pro or Team subscription to receive credits'
122-
)
123-
}
124125

125-
const { basePrice } = getPlanPricing(plan)
126-
127-
const result = await db.transaction(async (tx) => {
128-
let newCreditBalance: number
129-
let newUsageLimit: number
130-
131-
if (entityType === 'organization') {
132-
await tx
133-
.update(organization)
134-
.set({ creditBalance: sql`${organization.creditBalance} + ${amount}` })
135-
.where(eq(organization.id, entityId))
136-
137-
const [orgData] = await tx
138-
.select({
139-
creditBalance: organization.creditBalance,
140-
orgUsageLimit: organization.orgUsageLimit,
141-
})
142-
.from(organization)
143-
.where(eq(organization.id, entityId))
144-
.limit(1)
145-
146-
newCreditBalance = Number.parseFloat(orgData?.creditBalance || '0')
147-
const currentLimit = Number.parseFloat(orgData?.orgUsageLimit || '0')
148-
const planBase = Number(basePrice) * (seats || 1)
149-
const calculatedLimit = planBase + newCreditBalance
150-
151-
if (calculatedLimit > currentLimit) {
152-
await tx
153-
.update(organization)
154-
.set({ orgUsageLimit: calculatedLimit.toString() })
155-
.where(eq(organization.id, entityId))
156-
newUsageLimit = calculatedLimit
157-
} else {
158-
newUsageLimit = currentLimit
159-
}
160-
} else {
161-
const [existingStats] = await tx
162-
.select({ id: userStats.id })
163-
.from(userStats)
164-
.where(eq(userStats.userId, entityId))
165-
.limit(1)
166-
167-
if (!existingStats) {
168-
await tx.insert(userStats).values({
169-
id: nanoid(),
170-
userId: entityId,
171-
creditBalance: amount.toString(),
172-
})
173-
} else {
174-
await tx
175-
.update(userStats)
176-
.set({ creditBalance: sql`${userStats.creditBalance} + ${amount}` })
177-
.where(eq(userStats.userId, entityId))
178-
}
179-
180-
const [stats] = await tx
181-
.select({
182-
creditBalance: userStats.creditBalance,
183-
currentUsageLimit: userStats.currentUsageLimit,
184-
})
185-
.from(userStats)
186-
.where(eq(userStats.userId, entityId))
187-
.limit(1)
188-
189-
newCreditBalance = Number.parseFloat(stats?.creditBalance || '0')
190-
const currentLimit = Number.parseFloat(stats?.currentUsageLimit || '0')
191-
const planBase = Number(basePrice)
192-
const calculatedLimit = planBase + newCreditBalance
193-
194-
if (calculatedLimit > currentLimit) {
195-
await tx
196-
.update(userStats)
197-
.set({ currentUsageLimit: calculatedLimit.toString() })
198-
.where(eq(userStats.userId, entityId))
199-
newUsageLimit = calculatedLimit
200-
} else {
201-
newUsageLimit = currentLimit
202-
}
126+
const [existingStats] = await db
127+
.select({ id: userStats.id })
128+
.from(userStats)
129+
.where(eq(userStats.userId, entityId))
130+
.limit(1)
131+
132+
if (!existingStats) {
133+
await db.insert(userStats).values({
134+
id: nanoid(),
135+
userId: entityId,
136+
})
203137
}
138+
}
204139

205-
return { newCreditBalance, newUsageLimit }
206-
})
140+
await addCredits(entityType, entityId, amount)
207141

208-
const { newCreditBalance, newUsageLimit } = result
142+
let newCreditBalance: number
143+
if (entityType === 'organization') {
144+
const [orgData] = await db
145+
.select({ creditBalance: organization.creditBalance })
146+
.from(organization)
147+
.where(eq(organization.id, entityId))
148+
.limit(1)
149+
newCreditBalance = Number.parseFloat(orgData?.creditBalance || '0')
150+
} else {
151+
const [stats] = await db
152+
.select({ creditBalance: userStats.creditBalance })
153+
.from(userStats)
154+
.where(eq(userStats.userId, entityId))
155+
.limit(1)
156+
newCreditBalance = Number.parseFloat(stats?.creditBalance || '0')
157+
}
158+
159+
await setUsageLimitForCredits(entityType, entityId, plan, seats, newCreditBalance)
160+
161+
let newUsageLimit: number
162+
if (entityType === 'organization') {
163+
const [orgData] = await db
164+
.select({ orgUsageLimit: organization.orgUsageLimit })
165+
.from(organization)
166+
.where(eq(organization.id, entityId))
167+
.limit(1)
168+
newUsageLimit = Number.parseFloat(orgData?.orgUsageLimit || '0')
169+
} else {
170+
const [stats] = await db
171+
.select({ currentUsageLimit: userStats.currentUsageLimit })
172+
.from(userStats)
173+
.where(eq(userStats.userId, entityId))
174+
.limit(1)
175+
newUsageLimit = Number.parseFloat(stats?.currentUsageLimit || '0')
176+
}
209177

210178
logger.info('Admin API: Issued credits', {
211179
resolvedUserId,

0 commit comments

Comments
 (0)