@@ -36,7 +36,7 @@ export async function autoCharge(
3636 const cooldownKey = `auto-recharge-cooldown:${ chunk . team_id } ` ;
3737 const hourlyCounterKey = `auto-recharge-hourly:${ chunk . team_id } ` ;
3838
39- if ( chunk . team_id === "285bb597-6eaf-4b96-801c-51461fc3c543" ) {
39+ if ( chunk . team_id === "285bb597-6eaf-4b96-801c-51461fc3c543" || chunk . team_id === "dec639a0-98ca-4995-95b5-48ac1ffab5b7" ) {
4040 return {
4141 success : false ,
4242 message : "Auto-recharge failed: blocked team" ,
@@ -46,14 +46,11 @@ export async function autoCharge(
4646 }
4747
4848 try {
49- // Check hourly rate limit
50- const hourlyCharges = await redisRateLimitClient . incr ( hourlyCounterKey ) ;
51- if ( hourlyCharges === 1 ) {
52- // Set expiry for the counter if it's new
53- await redisRateLimitClient . expire ( hourlyCounterKey , HOURLY_COUNTER_EXPIRY ) ;
54- }
55-
56- if ( hourlyCharges > MAX_CHARGES_PER_HOUR ) {
49+ // Check hourly rate limit first without incrementing
50+ const currentCharges = await redisRateLimitClient . get ( hourlyCounterKey ) ;
51+ const hourlyCharges = currentCharges ? parseInt ( currentCharges ) : 0 ;
52+
53+ if ( hourlyCharges >= MAX_CHARGES_PER_HOUR ) {
5754 logger . warn (
5855 `Auto-recharge for team ${ chunk . team_id } exceeded hourly limit of ${ MAX_CHARGES_PER_HOUR } ` ,
5956 ) ;
@@ -91,9 +88,10 @@ export async function autoCharge(
9188 remainingCredits : number ;
9289 chunk : AuthCreditUsageChunk ;
9390 } > => {
94- // Recheck the condition inside the lock to prevent race conditions
91+ // Recheck all conditions inside the lock to prevent race conditions
9592 const updatedChunk = await getACUC ( chunk . api_key , false , false ) ;
96- // recheck cooldown
93+
94+ // Recheck cooldown
9795 const cooldownValue = await getValue ( cooldownKey ) ;
9896 if ( cooldownValue ) {
9997 logger . info (
@@ -106,6 +104,19 @@ export async function autoCharge(
106104 chunk,
107105 } ;
108106 }
107+
108+ // Recheck hourly limit inside lock
109+ const currentCharges = await redisRateLimitClient . get ( hourlyCounterKey ) ;
110+ const hourlyCharges = currentCharges ? parseInt ( currentCharges ) : 0 ;
111+ if ( hourlyCharges >= MAX_CHARGES_PER_HOUR ) {
112+ return {
113+ success : false ,
114+ message : "Auto-recharge hourly limit exceeded" ,
115+ remainingCredits : chunk . remaining_credits ,
116+ chunk,
117+ } ;
118+ }
119+
109120 if (
110121 updatedChunk &&
111122 updatedChunk . remaining_credits < autoRechargeThreshold
@@ -131,15 +142,16 @@ export async function autoCharge(
131142
132143 if ( customer && customer . stripe_customer_id ) {
133144 let issueCreditsSuccess = false ;
145+
146+ // Set cooldown BEFORE attempting payment
147+ await setValue ( cooldownKey , "true" , AUTO_RECHARGE_COOLDOWN ) ;
148+
134149 // Attempt to create a payment intent
135150 const paymentStatus = await createPaymentIntent (
136151 chunk . team_id ,
137152 customer . stripe_customer_id ,
138153 ) ;
139154
140- // set cooldown
141- await setValue ( cooldownKey , "true" , AUTO_RECHARGE_COOLDOWN ) ;
142-
143155 // If payment is successful or requires further action, issue credits
144156 if (
145157 paymentStatus . return_status === "succeeded" ||
@@ -163,7 +175,10 @@ export async function autoCharge(
163175 if ( issueCreditsSuccess ) {
164176 // Increment hourly counter and set expiry if it doesn't exist
165177 await redisRateLimitClient . incr ( hourlyCounterKey ) ;
166- await redisRateLimitClient . expire ( hourlyCounterKey , HOURLY_COUNTER_EXPIRY ) ;
178+ await redisRateLimitClient . expire (
179+ hourlyCounterKey ,
180+ HOURLY_COUNTER_EXPIRY ,
181+ ) ;
167182
168183 await sendNotification (
169184 chunk . team_id ,
@@ -188,7 +203,9 @@ export async function autoCharge(
188203 false ,
189204 process . env . SLACK_ADMIN_WEBHOOK_URL ,
190205 ) . catch ( ( error ) => {
191- logger . debug ( `Error sending slack notification: ${ error } ` ) ;
206+ logger . debug (
207+ `Error sending slack notification: ${ error } ` ,
208+ ) ;
192209 } ) ;
193210
194211 // Set cooldown for 1 hour
0 commit comments