MCPcopy Create free account
hub / github.com/Kilo-Org/cloud / performAutoTopUpForEntity

Function performAutoTopUpForEntity

apps/web/src/lib/autoTopUp.ts:183–342  ·  view source on GitHub ↗

* Generalized auto-top-up logic that works for both users and organizations.

(
  entity: AutoTopUpEntity,
  traceId: string
)

Source from the content-addressed store, hash-verified

181 * Generalized auto-top-up logic that works for both users and organizations.
182 */
183async function performAutoTopUpForEntity(
184 entity: AutoTopUpEntity,
185 traceId: string
186): Promise<AutoTopUpResult> {
187 const ownerColumn =
188 entity.type === 'user'
189 ? auto_top_up_configs.owned_by_user_id
190 : auto_top_up_configs.owned_by_organization_id;
191 const ownerId = entity.type === 'user' ? entity.user.id : entity.organization.id;
192
193 // Atomically check and acquire lock in a single query using SQL NOW()
194 const [config] = await db
195 .update(auto_top_up_configs)
196 .set({ attempt_started_at: sql`NOW()` })
197 .where(
198 and(
199 eq(ownerColumn, ownerId),
200 or(
201 isNull(auto_top_up_configs.attempt_started_at),
202 lt(
203 auto_top_up_configs.attempt_started_at,
204 sql`NOW() - INTERVAL '${sql.raw(String(ATTEMPT_LOCK_TIMEOUT_SECONDS))} second'`
205 )
206 )
207 )
208 )
209 .returning({
210 id: auto_top_up_configs.id,
211 stripe_payment_method_id: auto_top_up_configs.stripe_payment_method_id,
212 amount_cents: auto_top_up_configs.amount_cents,
213 attempt_started_at: auto_top_up_configs.attempt_started_at,
214 });
215
216 if (!config) {
217 // Either no config exists, or concurrent attempt in progress
218 // Check which case it is
219 const existingConfig = await db.query.auto_top_up_configs.findFirst({
220 where: eq(ownerColumn, ownerId),
221 });
222
223 if (!existingConfig) {
224 await disableAutoTopUpForEntity(entity, 'no_payment_method_saved');
225 return failureResult('no_payment_method_saved');
226 }
227 return failureResult('concurrent_attempt_in_progress');
228 }
229
230 // Re-check balance after acquiring lock to prevent duplicate top-ups
231 // (another request may have completed a top-up while we were waiting for the lock)
232 // We fetch fresh data from DB and compute balance directly to avoid
233 // calling getBalanceForUser which would create a cycle (it calls maybePerformAutoTopUp)
234 const { currentBalance_USD, stripe_customer_id } =
235 await getEntityBalanceAndStripeCustomer(entity);
236 const threshold =
237 entity.type === 'user' ? AUTO_TOP_UP_THRESHOLD_DOLLARS : ORG_AUTO_TOP_UP_THRESHOLD_DOLLARS;
238 if (currentBalance_USD >= threshold) {
239 // Balance is now sufficient, release lock and exit
240 await db

Callers 1

Calls 8

failureResultFunction · 0.90
successResultFunction · 0.90
setMethod · 0.65
updateMethod · 0.65
createMethod · 0.65
sqlFunction · 0.50

Tested by

no test coverage detected