(params: {
userId: string
creditsToCharge: number
repoUrl?: string
context: string
operationName?: string
logger: Logger
trackEvent: TrackEventFn
insufficientCreditsEvent: AnalyticsEvent
getUserUsageData: GetUserUsageDataFn
consumeCreditsWithFallback: ConsumeCreditsWithFallbackFn
ensureSubscriberBlockGrant?: (params: {
userId: string
logger: Logger
}) => Promise<unknown>
})
| 141 | } |
| 142 | |
| 143 | export const checkCreditsAndCharge = async (params: { |
| 144 | userId: string |
| 145 | creditsToCharge: number |
| 146 | repoUrl?: string |
| 147 | context: string |
| 148 | operationName?: string |
| 149 | logger: Logger |
| 150 | trackEvent: TrackEventFn |
| 151 | insufficientCreditsEvent: AnalyticsEvent |
| 152 | getUserUsageData: GetUserUsageDataFn |
| 153 | consumeCreditsWithFallback: ConsumeCreditsWithFallbackFn |
| 154 | ensureSubscriberBlockGrant?: (params: { |
| 155 | userId: string |
| 156 | logger: Logger |
| 157 | }) => Promise<unknown> |
| 158 | }): Promise<HandlerResult<{ creditsUsed: number }>> => { |
| 159 | const { |
| 160 | userId, |
| 161 | creditsToCharge, |
| 162 | repoUrl, |
| 163 | context, |
| 164 | operationName, |
| 165 | logger, |
| 166 | trackEvent, |
| 167 | insufficientCreditsEvent, |
| 168 | getUserUsageData, |
| 169 | consumeCreditsWithFallback, |
| 170 | ensureSubscriberBlockGrant, |
| 171 | } = params |
| 172 | |
| 173 | if (creditsToCharge <= 0) { |
| 174 | return { ok: true, data: { creditsUsed: 0 } } |
| 175 | } |
| 176 | |
| 177 | // Ensure subscription block grant exists before checking credits. |
| 178 | // This creates the grant (if eligible) so its credits appear in the balance below. |
| 179 | // When the function is provided, always include subscription credits in the balance: |
| 180 | // error/null results mean subscription grants have 0 balance, so including them is harmless. |
| 181 | const includeSubscriptionCredits = !!ensureSubscriberBlockGrant |
| 182 | if (ensureSubscriberBlockGrant) { |
| 183 | try { |
| 184 | await ensureSubscriberBlockGrant({ userId, logger }) |
| 185 | } catch (error) { |
| 186 | logger.error( |
| 187 | { error, userId }, |
| 188 | 'Error ensuring subscription block grant in credit check', |
| 189 | ) |
| 190 | // Fail open: proceed with subscription credits included in balance check |
| 191 | } |
| 192 | } |
| 193 | |
| 194 | const { |
| 195 | balance: { totalRemaining }, |
| 196 | nextQuotaReset, |
| 197 | } = await getUserUsageData({ userId, logger, includeSubscriptionCredits }) |
| 198 | |
| 199 | if (totalRemaining <= 0 || totalRemaining < creditsToCharge) { |
| 200 | trackEvent({ |
no test coverage detected