fix: usage limits for v1 api

This commit is contained in:
John Yeo
2025-09-03 18:47:47 -07:00
parent 66f73b6b2d
commit 28b800e713
8 changed files with 69 additions and 24 deletions

View File

@@ -435,17 +435,16 @@ export class AutumnInt {
stripe = {
connect: async (params: {
testApiKey: string;
liveApiKey: string;
successUrl: string;
defaultCurrency: string;
secret_key: string;
success_url: string;
default_currency: string;
}) => {
const data = await this.post(`/org/stripe`, params);
const data = await this.post(`/organization/stripe`, params);
return data;
},
delete: async () => {
const data = await this.delete(`/org/stripe`);
const data = await this.delete(`/organization/stripe`);
return data;
},
};

View File

@@ -67,7 +67,9 @@ apiRouter.post("/setup_payment", handleSetupPayment);
// Analytics
apiRouter.use("/query", analyticsRouter);
apiRouter.use("/platform", platformRouter);
// Used for tests...
apiRouter.post("/organization/stripe", handleConnectStripe);
// apiRouter.delete("/org/stripe", handleDeleteStripe);
apiRouter.delete("/organization/stripe", handleDeleteStripe);
export { apiRouter };

View File

@@ -2,6 +2,7 @@ import {
getFeatureBalance,
cusEntsContainFeature,
getUnlimitedAndUsageAllowed,
getPaidFeatureBalance,
} from "@/internal/customers/cusProducts/cusEnts/cusEntUtils.js";
import { featureToCreditSystem } from "@/internal/features/creditSystemUtils.js";
import {
@@ -88,6 +89,8 @@ export const getV1CheckResponse = ({
internalFeatureId: feature.internal_id!,
});
console.log(`Feature ${feature.id}, Usage allowed: ${usageAllowed}`);
if (unlimited || usageAllowed) {
balances.push({
feature_id: feature.id,
@@ -103,7 +106,6 @@ export const getV1CheckResponse = ({
}),
});
allowed = true;
// continue;
break;
}
@@ -116,6 +118,11 @@ export const getV1CheckResponse = ({
entityId,
});
let totalPaidAllowance = getPaidFeatureBalance({
cusEnts,
internalFeatureId: feature.internal_id!,
});
let newBalance: any = {
feature_id: feature.id,
required,
@@ -129,7 +136,7 @@ export const getV1CheckResponse = ({
balances.push(newBalance);
// allowed = allowed && actual! >= required;
allowed = actual! >= required;
allowed = actual! + (totalPaidAllowance || 0) >= required;
if (allowed) {
break;

View File

@@ -32,6 +32,7 @@ import {
getSummedEntityBalances,
} from "./entBalanceUtils.js";
import { Decimal } from "decimal.js";
import { logger } from "better-auth";
export const getCusEntMasterBalance = ({
cusEnt,
@@ -343,9 +344,11 @@ export const getResetBalance = ({
export const getUnlimitedAndUsageAllowed = ({
cusEnts,
internalFeatureId,
includeUsageLimit = true,
}: {
cusEnts: FullCustomerEntitlement[];
internalFeatureId: string;
includeUsageLimit?: boolean;
}) => {
// Unlimited
@@ -360,7 +363,7 @@ export const getUnlimitedAndUsageAllowed = ({
(ent) =>
ent.internal_feature_id === internalFeatureId &&
ent.usage_allowed &&
nullish(ent.entitlement.usage_limit)
(includeUsageLimit ? nullish(ent.entitlement.usage_limit) : true)
);
return { unlimited, usageAllowed };
@@ -424,6 +427,32 @@ export const getFeatureBalance = ({
return balance;
};
export const getPaidFeatureBalance = ({
cusEnts,
internalFeatureId,
}: {
cusEnts: FullCustomerEntitlement[];
internalFeatureId: string;
}) => {
let paidAllowance = 0;
try {
for (const cusEnt of cusEnts) {
if (cusEnt.internal_feature_id !== internalFeatureId) continue;
if (notNullish(cusEnt.entitlement.usage_limit)) {
paidAllowance = new Decimal(paidAllowance)
.plus(cusEnt.entitlement.usage_limit!)
.minus(cusEnt.entitlement.allowance || 0)
.toNumber();
}
}
} catch (error) {
logger.error(`Failed to get paid feature balance`, { error });
}
return paidAllowance;
};
export const cusEntsContainFeature = ({
cusEnts,
feature,

View File

@@ -11,7 +11,10 @@ import {
import { getCusEntsInFeatures } from "@/internal/customers/cusUtils/cusUtils.js";
import { featureToCreditSystem } from "@/internal/features/creditSystemUtils.js";
import { getFeatureBalance } from "@/internal/customers/cusProducts/cusEnts/cusEntUtils.js";
import {
getFeatureBalance,
getPaidFeatureBalance,
} from "@/internal/customers/cusProducts/cusEnts/cusEntUtils.js";
import { Decimal } from "decimal.js";
import {

View File

@@ -86,10 +86,9 @@ export const clearOrg = async ({
} catch (error) {}
try {
await autumn.stripe.connect({
testApiKey: process.env.STRIPE_TEST_KEY!,
liveApiKey: process.env.STRIPE_TEST_KEY!,
successUrl: "https://useautumn.com",
defaultCurrency: "usd",
secret_key: process.env.STRIPE_TEST_KEY!,
success_url: "https://useautumn.com",
default_currency: "usd",
});
} catch (error: any) {
console.error("Error reconnecting stripe", error.message);

View File

@@ -11,10 +11,15 @@ export const formatTimestamp = (timestamp: number | null | undefined) => {
export const formatUnixToDate = (
unix: number | null | undefined,
excludeYear = false,
excludeYear = false
) => {
if (!unix) return "";
return format(new Date(unix), excludeYear ? "d MMM" : "d MMM yyyy");
try {
const date = format(new Date(unix), excludeYear ? "d MMM" : "d MMM yyyy");
return date;
} catch (error) {
return "";
}
};
export const formatUnixToDateTime = (unix: number | null | undefined) => {

View File

@@ -45,20 +45,21 @@ export const AttachInfo = () => {
);
});
let text = `You are switching this customer to version ${product.version} of ${product.name}.`;
if (preview.due_next_cycle?.due_at) {
text += `Their features will update immediately and from ${formatUnixToDate(preview.due_next_cycle?.due_at)} onwards, they will pay any new prices${usagePriceExists ? " (including usage from the last cycle)" : ""}.`;
}
return (
<>
<span>
You are switching this customer to version {product.version} of{" "}
{product.name}. Their features will update immediately and from{" "}
{format(preview.due_next_cycle?.due_at, "d MMM")} onwards, they will
pay any new prices
{usagePriceExists ? " (including usage from the last cycle)" : ""}.
{/* You are switching this customer to version {product.version} of{" "}
{product.name}. */}
{text}
</span>
</>
);
const text = `You are switching this customer to version ${product.version} of ${product.name}.`;
// let text = `The customer is currently on ${currentProduct.name} v${currentProduct.version}. Switching to v${product.version} will update the customer's features immediately, and from ${formatUnixToDate(preview.due_next_cycle.due_at)} onwards they will pay any new prices`;
// if (usagePriceExists) {