Skip to content

Commit 4b90751

Browse files
fix(api/billing): stop falling back to ACUC when Autumn check fails (firecrawl#3631)
Autumn is the source of truth for credits and ACUC accounting has diverged for many teams (e.g. teams showing negative remaining_credits in ACUC while being current in Autumn). The previous code ran both checks in parallel and silently fell back to ACUC whenever Autumn returned null on error, producing spurious 402s. The middleware now consults Autumn only and fails open on transient Autumn failures, matching the behavior of browser.ts and scrape-browser.ts. Co-authored-by: firecrawl-spring[bot] <254786068+firecrawl-spring[bot]@users.noreply.github.com>
1 parent 1cb9537 commit 4b90751

1 file changed

Lines changed: 19 additions & 25 deletions

File tree

apps/api/src/routes/shared.ts

Lines changed: 19 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import { RateLimiterMode } from "../types";
1010
import { authenticateUser } from "../controllers/auth";
1111
import { createIdempotencyKey } from "../services/idempotency/create";
1212
import { validateIdempotencyKey } from "../services/idempotency/validate";
13-
import { checkTeamCredits } from "../services/billing/credit_billing";
1413
import { isUrlBlocked } from "../scraper/WebScraper/utils/blocklist";
1514
import { logger } from "../lib/logger";
1615
import {
@@ -120,32 +119,27 @@ export function checkCreditsMiddleware(
120119
}
121120

122121
const requestedCredits = minimum ?? 1;
123-
const useAutumnCheck = !!req.auth.org_id;
124-
125-
const autumnProperties = {
126-
source: "checkCreditsMiddleware",
127-
path: req.path,
128-
};
129-
const [legacyCheck, autumnResult] = await Promise.all([
130-
checkTeamCredits(req.acuc ?? null, req.auth.team_id, requestedCredits),
131-
useAutumnCheck
132-
? autumnService.checkCredits({
133-
teamId: req.auth.team_id,
134-
value: requestedCredits,
135-
properties: autumnProperties,
136-
})
137-
: null,
138-
]);
139-
let { success, remainingCredits, chunk } = legacyCheck;
140-
141-
if (autumnResult !== null) {
142-
success = autumnResult.allowed;
143-
remainingCredits = autumnResult.remaining;
144-
}
145122

146-
if (chunk) {
147-
req.acuc = chunk;
123+
const autumnResult = await autumnService.checkCredits({
124+
teamId: req.auth.team_id,
125+
value: requestedCredits,
126+
properties: {
127+
source: "checkCreditsMiddleware",
128+
path: req.path,
129+
},
130+
});
131+
132+
// Autumn is the source of truth for credits. If it's unavailable
133+
// (returns null), fail open — matches the behavior in browser.ts /
134+
// scrape-browser.ts and avoids turning an Autumn outage into a
135+
// customer outage.
136+
if (autumnResult === null) {
137+
req.account = { remainingCredits: Infinity };
138+
return next();
148139
}
140+
141+
const success = autumnResult.allowed;
142+
const remainingCredits = autumnResult.remaining;
149143
req.account = { remainingCredits };
150144
if (!success) {
151145
if (

0 commit comments

Comments
 (0)