Files
cfw-autumn/server/src/internal/api/check/runCheckWithTrack.ts
2026-03-08 21:04:34 +00:00

103 lines
2.4 KiB
TypeScript

import {
ApiVersion,
type CheckParams,
type CheckResponseV3,
CheckResponseV3Schema,
FeatureType,
InsufficientBalanceError,
InternalError,
RecaseError,
type TrackParams,
} from "@autumn/shared";
import type { AutumnContext } from "@server/honoUtils/HonoEnv.js";
import { runTrackV2 } from "@server/internal/balances/track/runTrackV2";
import { getTrackFeatureDeductions } from "@server/internal/balances/track/utils/getFeatureDeductions.js";
import { featureToCreditSystem } from "@server/internal/features/creditSystemUtils.js";
import type { CheckData } from "./checkTypes/CheckData.js";
export const runCheckWithTrack = async ({
ctx,
body,
requiredBalance,
checkData,
}: {
ctx: AutumnContext;
body: CheckParams;
requiredBalance: number;
checkData: CheckData;
}): Promise<CheckResponseV3> => {
if (!body.feature_id) {
throw new InternalError({
message: "ran check with track but no feature ID",
});
}
if (ctx.isPublic) {
throw new RecaseError({
message:
"Can't pass in 'send_event: true' when using publishable key for Autumn",
});
}
const featureDeductions = getTrackFeatureDeductions({
ctx,
featureId: body.feature_id,
lock: body.lock,
value: requiredBalance,
});
const trackBody: TrackParams = {
customer_id: body.customer_id,
entity_id: body.entity_id,
feature_id: body.feature_id,
value: requiredBalance,
properties: body.properties,
skip_event: body.skip_event,
overage_behavior: "reject",
lock: body.lock,
};
let allowed = true;
try {
// Use V2_1 to get ApiBalanceV1 format internally
const response = await runTrackV2({
ctx,
body: trackBody,
featureDeductions,
apiVersion: ApiVersion.V2_1,
});
checkData.apiBalance = response.balance ?? undefined;
} catch (error) {
if (error instanceof InsufficientBalanceError) {
allowed = false;
} else {
throw error;
}
}
const { featureToUse, originalFeature } = checkData;
if (
featureToUse.type === FeatureType.CreditSystem &&
featureToUse.id !== originalFeature.id
) {
requiredBalance = featureToCreditSystem({
featureId: originalFeature.id,
creditSystem: featureToUse,
amount: requiredBalance,
});
}
const checkResponse = CheckResponseV3Schema.parse({
allowed,
customer_id: checkData.customerId || "",
entity_id: checkData.entityId,
required_balance: requiredBalance,
balance: checkData.apiBalance ?? null,
lock_key: body.lock?.key,
});
return checkResponse;
};