fix: adjust free monthly credits for orgs (#23798)
* add org monthly credits env variable * use fallback for env variable * remove comment --------- Co-authored-by: CarinaWolli <wollencarina@gmail.com>
This commit is contained in:
co-authored by
CarinaWolli
parent
a62c74f79e
commit
2be388373f
@@ -203,6 +203,7 @@ NEXT_PUBLIC_IS_PREMIUM_NEW_PLAN=0
|
||||
NEXT_PUBLIC_STRIPE_PREMIUM_NEW_PLAN_PRICE=
|
||||
STRIPE_TEAM_MONTHLY_PRICE_ID=
|
||||
NEXT_PUBLIC_STRIPE_CREDITS_PRICE_ID=
|
||||
ORG_MONTHLY_CREDITS=
|
||||
STRIPE_TEAM_PRODUCT_ID=
|
||||
# It is a price ID in the product with id STRIPE_ORG_PRODUCT_ID
|
||||
STRIPE_ORG_MONTHLY_PRICE_ID=
|
||||
|
||||
@@ -435,8 +435,8 @@ describe("CreditService", () => {
|
||||
expect(result).toBe(1500); // (3 members * 1000 price) / 2
|
||||
});
|
||||
|
||||
it("should calculate credits with 20% multiplier for organizations", async () => {
|
||||
vi.stubEnv("STRIPE_ORG_MONTHLY_PRICE_ID", "price_org_monthly");
|
||||
it("should calculate credits for organizations using ORG_MONTHLY_CREDITS", async () => {
|
||||
vi.stubEnv("ORG_MONTHLY_CREDITS", "1500");
|
||||
const mockTeamRepo = {
|
||||
findTeamWithMembers: vi.fn().mockResolvedValue({
|
||||
id: 1,
|
||||
@@ -453,15 +453,31 @@ describe("CreditService", () => {
|
||||
mockTeamBillingService.getSubscriptionStatus
|
||||
);
|
||||
|
||||
const mockStripeBillingService = {
|
||||
getPrice: vi.fn().mockResolvedValue({ unit_amount: 3700 }),
|
||||
const result = await creditService.getMonthlyCredits(1);
|
||||
expect(result).toBe(3000); // 2 members * 1500 credits per seat
|
||||
});
|
||||
|
||||
it("should calculate credits for organizations with default 1000 credits per seat", async () => {
|
||||
// Clear ORG_MONTHLY_CREDITS to test default behavior
|
||||
vi.stubEnv("ORG_MONTHLY_CREDITS", undefined);
|
||||
const mockTeamRepo = {
|
||||
findTeamWithMembers: vi.fn().mockResolvedValue({
|
||||
id: 1,
|
||||
isOrganization: true,
|
||||
members: [{ accepted: true }, { accepted: true }, { accepted: true }],
|
||||
}),
|
||||
};
|
||||
vi.spyOn(StripeBillingService.prototype, "getPrice").mockImplementation(
|
||||
mockStripeBillingService.getPrice
|
||||
vi.mocked(TeamRepository).mockImplementation(() => mockTeamRepo as any);
|
||||
|
||||
const mockTeamBillingService = {
|
||||
getSubscriptionStatus: vi.fn().mockResolvedValue("active"),
|
||||
};
|
||||
vi.spyOn(InternalTeamBilling.prototype, "getSubscriptionStatus").mockImplementation(
|
||||
mockTeamBillingService.getSubscriptionStatus
|
||||
);
|
||||
|
||||
const result = await creditService.getMonthlyCredits(1);
|
||||
expect(result).toBe(1480); // (2 members * 3700 price) * 0.2
|
||||
expect(result).toBe(3000); // 3 members * 1000 credits per seat (default)
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -588,8 +588,6 @@ export class CreditService {
|
||||
|
||||
if (!team) return 0;
|
||||
|
||||
let totalMonthlyCredits = 0;
|
||||
|
||||
const teamBillingService = new InternalTeamBilling(team);
|
||||
const subscriptionStatus = await teamBillingService.getSubscriptionStatus();
|
||||
|
||||
@@ -599,25 +597,25 @@ export class CreditService {
|
||||
|
||||
const activeMembers = team.members.filter((member) => member.accepted).length;
|
||||
|
||||
const billingService = new StripeBillingService();
|
||||
if (team.isOrganization) {
|
||||
const orgMonthlyCredits = process.env.ORG_MONTHLY_CREDITS;
|
||||
const creditsPerSeat = orgMonthlyCredits ? parseInt(orgMonthlyCredits) : 1000;
|
||||
return activeMembers * creditsPerSeat;
|
||||
}
|
||||
|
||||
const priceId = team.isOrganization
|
||||
? process.env.STRIPE_ORG_MONTHLY_PRICE_ID
|
||||
: process.env.STRIPE_TEAM_MONTHLY_PRICE_ID;
|
||||
const billingService = new StripeBillingService();
|
||||
const priceId = process.env.STRIPE_TEAM_MONTHLY_PRICE_ID;
|
||||
|
||||
if (!priceId) {
|
||||
log.warn("Monthly price ID not configured", { teamId, isOrganization: team.isOrganization });
|
||||
log.warn("Monthly price ID not configured", { teamId });
|
||||
return 0;
|
||||
}
|
||||
|
||||
const monthlyPrice = await billingService.getPrice(priceId || "");
|
||||
const monthlyPrice = await billingService.getPrice(priceId);
|
||||
const pricePerSeat = monthlyPrice.unit_amount ?? 0;
|
||||
const creditsPerSeat = pricePerSeat * 0.5;
|
||||
|
||||
// Teams get 50% of the price as credits, organizations get 20%
|
||||
const creditMultiplier = team.isOrganization ? 0.2 : 0.5;
|
||||
totalMonthlyCredits = activeMembers * pricePerSeat * creditMultiplier;
|
||||
|
||||
return totalMonthlyCredits;
|
||||
return activeMembers * creditsPerSeat;
|
||||
}
|
||||
|
||||
calculateCreditsFromPrice(price: number) {
|
||||
|
||||
@@ -120,6 +120,7 @@
|
||||
"NEXT_PUBLIC_STRIPE_PREMIUM_PLAN_PRICE_MONTHLY",
|
||||
"NEXT_PUBLIC_STRIPE_PRICING_TABLE_PUBLISHABLE_KEY",
|
||||
"NEXT_PUBLIC_STRIPE_CREDITS_PRICE_ID",
|
||||
"ORG_MONTHLY_CREDITS",
|
||||
"NEXT_PUBLIC_PLAIN_CHAT_ID",
|
||||
"NEXT_PUBLIC_PLAIN_CHAT_EXCLUDED_PATHS",
|
||||
"NEXT_PUBLIC_BOOKER_NUMBER_OF_DAYS_TO_LOAD",
|
||||
@@ -187,6 +188,7 @@
|
||||
"STRIPE_TEAM_MONTHLY_PRICE_ID",
|
||||
"STRIPE_TEAM_PRODUCT_ID",
|
||||
"STRIPE_ORG_MONTHLY_PRICE_ID",
|
||||
"ORG_MONTHLY_CREDITS",
|
||||
"TANDEM_BASE_URL",
|
||||
"TANDEM_CLIENT_ID",
|
||||
"TANDEM_CLIENT_SECRET",
|
||||
@@ -331,6 +333,7 @@
|
||||
"STRIPE_TEAM_MONTHLY_PRICE_ID",
|
||||
"NEXT_PUBLIC_STRIPE_CREDITS_PRICE_ID",
|
||||
"STRIPE_TEAM_PRODUCT_ID",
|
||||
"ORG_MONTHLY_CREDITS",
|
||||
"STRIPE_ORG_MONTHLY_PRICE_ID",
|
||||
"STRIPE_ORG_PRODUCT_ID",
|
||||
"NEXT_PUBLIC_API_V2_URL",
|
||||
|
||||
Reference in New Issue
Block a user