feat: sending allowance_used threshold type

This commit is contained in:
John Yeo
2025-09-09 10:20:33 -07:00
parent 65601d42ef
commit 25c5ac39cd
3 changed files with 172 additions and 10 deletions

View File

@@ -1,4 +1,5 @@
import { WebhookEventType } from "@autumn/shared";
import RecaseError from "@/utils/errorUtils.js";
import { ErrCode, WebhookEventType } from "@autumn/shared";
import express, { Router } from "express";
import { Webhook } from "svix";
@@ -15,11 +16,15 @@ const verifyAutumnWebhook = async (req: any, res: any) => {
const svix_signature = headers["svix-signature"];
if (!svix_id || !svix_timestamp || !svix_signature) {
res.status(400).json({
success: false,
throw new RecaseError({
message: "Error: Missing svix headers",
code: ErrCode.InvalidInputs,
});
return;
// res.status(400).json({
// success: false,
// message: "Error: Missing svix headers",
// });
// return;
}
let evt: any;
@@ -30,12 +35,16 @@ const verifyAutumnWebhook = async (req: any, res: any) => {
"svix-signature": svix_signature as string,
});
} catch (err) {
console.log("Error: Could not verify webhook");
res.status(400).json({
success: false,
throw new RecaseError({
message: "Error: Could not verify webhook",
code: ErrCode.InvalidInputs,
});
return;
// console.log("Error: Could not verify webhook");
// res.status(400).json({
// success: false,
// message: "Error: Could not verify webhook",
// });
// return;
}
return evt;

View File

@@ -5,7 +5,6 @@ import { orgToVersion } from "@/utils/versionUtils.js";
import { APIVersion } from "@autumn/shared";
import { AttachParams } from "../../../cusProducts/AttachParams.js";
import { nullish } from "@/utils/genUtils.js";
import { toSuccessUrl } from "@/internal/orgs/orgUtils/convertOrgUtils.js";
export const getAttachParams = async ({
req,

View File

@@ -1,5 +1,6 @@
import { DrizzleCli } from "@/db/initDrizzle.js";
import { sendSvixEvent } from "@/external/svix/svixHelpers.js";
import { EntityService } from "@/internal/api/entities/EntityService.js";
import { getSingleEntityResponse } from "@/internal/api/entities/getEntityUtils.js";
import { getV2CheckResponse } from "@/internal/api/entitled/checkUtils/getV2CheckResponse.js";
import { getCustomerDetails } from "@/internal/customers/cusUtils/getCustomerDetails.js";
@@ -38,6 +39,139 @@ export const mergeNewCusEntsIntoCusProducts = ({
return cusProducts;
};
export const sendSvixThresholdReachedEvent = async ({
db,
org,
env,
features,
logger,
feature,
fullCus,
thresholdType,
}: {
db: DrizzleCli;
org: Organization;
env: AppEnv;
features: Feature[];
logger: any;
feature: Feature;
fullCus: FullCustomer;
thresholdType: "limit_reached" | "allowance_used";
}) => {
const cusDetails = await getCustomerDetails({
db,
customer: fullCus,
org,
env,
features,
logger,
cusProducts: fullCus.customer_products,
expand: [],
});
if (fullCus.entity) {
const entities = await EntityService.list({
db,
internalCustomerId: fullCus.internal_id,
});
fullCus.entities = entities;
await getSingleEntityResponse({
org,
env,
features,
fullCus,
entityId: fullCus.entity.id,
});
}
await sendSvixEvent({
org: org,
env: env,
eventType: WebhookEventType.CustomerThresholdReached,
data: {
threshold_type: thresholdType,
customer: cusDetails,
feature: toAPIFeature({ feature }),
},
});
logger.info(`Sent Svix event for threshold reached (type: ${thresholdType})`);
return;
};
export const handleAllowanceUsed = async ({
db,
org,
env,
features,
logger,
cusEnts,
newCusEnts,
feature,
fullCus,
}: {
db: DrizzleCli;
org: Organization;
env: AppEnv;
cusEnts: FullCusEntWithFullCusProduct[];
newCusEnts: FullCusEntWithFullCusProduct[];
feature: Feature;
fullCus: FullCustomer;
features: Feature[];
logger: any;
}) => {
// Allowance used...
// Make sure overage allowed is false
for (const cusEnt of cusEnts) {
cusEnt.usage_allowed = false;
}
for (const cusEnt of newCusEnts) {
cusEnt.usage_allowed = false;
}
const prevCheckResponse = await getV2CheckResponse({
fullCus,
cusEnts,
creditSystems: [],
feature,
org,
cusProducts: fullCus.customer_products,
apiVersion: APIVersion.v1_2,
});
const v2CheckResponse = await getV2CheckResponse({
fullCus,
cusEnts: newCusEnts,
creditSystems: [],
feature,
org,
cusProducts: fullCus.customer_products,
apiVersion: APIVersion.v1_2,
});
// console.log(`Handling allowance used for feature: ${feature.id}`);
// console.log(
// `Prev: allowed (${prevCheckResponse.allowed}), balance (${prevCheckResponse.balance})`
// );
// console.log(
// `Current: allowed (${v2CheckResponse.allowed}), balance (${v2CheckResponse.balance})`
// );
if (prevCheckResponse.allowed === true && v2CheckResponse.allowed === false) {
await sendSvixThresholdReachedEvent({
db,
org,
env,
features,
logger,
feature,
fullCus,
thresholdType: "allowance_used",
});
}
};
export const handleThresholdReached = async ({
db,
feature,
@@ -104,6 +238,11 @@ export const handleThresholdReached = async ({
});
if (fullCus.entity) {
const entities = await EntityService.list({
db,
internalCustomerId: fullCus.internal_id,
});
fullCus.entities = entities;
await getSingleEntityResponse({
org,
env,
@@ -124,8 +263,23 @@ export const handleThresholdReached = async ({
},
});
logger.info("Sent Svix event for threshold reached");
logger.info(
"Sent Svix event for threshold reached (type: limit_reached)"
);
return;
}
await handleAllowanceUsed({
db,
org,
env,
features,
logger,
cusEnts,
newCusEnts,
feature,
fullCus,
});
return;
} catch (error: any) {
logger.error("Failed to handle threshold reached", {
error,