import { AffectedResource, ApiVersion, applyResponseVersionChanges, type CheckParams, CheckParamsSchema, CheckQuerySchema, type CheckResponseV3, CheckResponseV3Schema, type ParsedCheckParams, } from "@autumn/shared"; import { isRetryableDbError } from "@/db/dbUtils.js"; import { createRoute } from "@/honoMiddlewares/routeHandler.js"; import { parseCheckParamsForLock } from "@/internal/balances/utils/lock/parseCheckParamsForLock.js"; import { getCheckData } from "./checkUtils/getCheckData.js"; import { getV2CheckResponse } from "./checkUtils/getV2CheckResponse.js"; import { getCheckPreview } from "./getCheckPreview.js"; import { handleProductCheck } from "./handlers/handleProductCheck.js"; import { runCheckWithTrack } from "./runCheckWithTrack.js"; const DEFAULT_REQUIRED_BALANCE = 1; export const handleCheck = createRoute({ versionedQuery: { latest: CheckQuerySchema, [ApiVersion.V1_2]: CheckQuerySchema, }, resource: AffectedResource.Check, body: CheckParamsSchema, handler: async (c) => { const rawBody = c.req.valid("json"); const ctx = c.get("ctx"); const body: ParsedCheckParams = parseCheckParamsForLock({ params: rawBody, }); const { customer_id, product_id, entity_id, required_quantity, required_balance, send_event, with_preview, } = body; // Legacy path - product check if (product_id) { const checkProductResult = await handleProductCheck({ ctx: c.get("ctx"), body: { ...body, product_id }, // Ensure product_id is passed as string }); return c.json(checkProductResult); } const requiredBalance = required_balance ?? required_quantity ?? DEFAULT_REQUIRED_BALANCE; let checkData: Awaited>; try { checkData = await getCheckData({ ctx, body: body as CheckParams & { feature_id: string }, requiredBalance, }); } catch (error) { if (!isRetryableDbError({ error })) { throw error; } const fallbackResponse = CheckResponseV3Schema.parse({ allowed: true, customer_id: customer_id || "", entity_id: entity_id, required_balance: requiredBalance, balance: null, flag: null, }); const featureToUse = ctx.features.find( (feature) => feature.id === body.feature_id, ); const transformedFallbackResponse = featureToUse ? applyResponseVersionChanges({ input: fallbackResponse, targetVersion: ctx.apiVersion, resource: AffectedResource.Check, legacyData: { noCusEnts: false, featureToUse, }, ctx, }) : fallbackResponse; return c.json(transformedFallbackResponse); } let response: CheckResponseV3; if (send_event || body.lock?.enabled) { response = await runCheckWithTrack({ ctx, body, requiredBalance, checkData, }); } else { response = await getV2CheckResponse({ checkData, requiredBalance, }); } const preview = with_preview ? await getCheckPreview({ ctx, checkResponse: response, checkData, customerId: customer_id, entityId: entity_id, }) : undefined; // Version changes will transform V3 -> V2 -> V1 -> V0 based on target API version const transformedResponse = applyResponseVersionChanges({ input: response, targetVersion: ctx.apiVersion, resource: AffectedResource.Check, legacyData: { noCusEnts: checkData.apiBalance === undefined && checkData.apiFlag === undefined, featureToUse: checkData.featureToUse, }, ctx, }); return c.json({ ...transformedResponse, preview, // lock_id: body.lock?.lock_id ?? undefined, }); }, });