Skip to content

Commit 5c1074b

Browse files
geroplroboquat
authored andcommitted
[server] Introduce BillingMode incl. tests
1 parent 133cc05 commit 5c1074b

File tree

7 files changed

+793
-1
lines changed

7 files changed

+793
-1
lines changed

components/ee/payment-endpoint/src/accounting/subscription-service.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,8 +133,17 @@ export class SubscriptionService {
133133
* @returns Whether the user has an active subscription (user-paid or team s.) at the given date
134134
*/
135135
async hasActivePaidSubscription(userId: string, date: Date): Promise<boolean> {
136+
return (await this.getActivePaidSubscription(userId, date)).length > 0;
137+
}
138+
139+
/**
140+
* @param userId
141+
* @param date The date on which the subscription has to be active
142+
* @returns The list of a active subscriptions (user-paid or team s.) at the given date
143+
*/
144+
async getActivePaidSubscription(userId: string, date: Date): Promise<Subscription[]> {
136145
const subscriptions = await this.accountingDB.findActiveSubscriptionsForUser(userId, date.toISOString());
137-
return subscriptions.filter((s) => Subscription.isActive(s, date.toISOString())).length > 0;
146+
return subscriptions.filter((s) => Subscription.isActive(s, date.toISOString()));
138147
}
139148

140149
async store(db: AccountingDB, model: SubscriptionModel) {

components/gitpod-protocol/src/accounting-protocol.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,9 @@ export namespace Subscription {
199199
export function isActive(s: Subscription, date: string): boolean {
200200
return s.startDate <= date && (s.endDate === undefined || date < s.endDate);
201201
}
202+
export function isCancelled(s: Subscription, date: string): boolean {
203+
return (!!s.cancellationDate && s.cancellationDate < date) || (!!s.endDate && s.endDate < date); // This edge case is meant to handle bad data: If for whatever reason cancellationDate has not been set: treat endDate as such
204+
}
202205
export function isDowngraded(s: Subscription) {
203206
return s.paymentData && s.paymentData.downgradeDate;
204207
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/**
2+
* Copyright (c) 2022 Gitpod GmbH. All rights reserved.
3+
* Licensed under the GNU Affero General Public License (AGPL).
4+
* See License-AGPL.txt in the project root for license information.
5+
*/
6+
7+
/**
8+
* BillingMode is used to answer the following questions:
9+
* - Should UI piece x be displayed for this user/team? (getBillingModeForUser/Team)
10+
* - What model should be used to limit this workspace's capabilities (mayStartWorkspace, setTimeout, workspace class, etc...) (getBillingMode(workspaceInstance.attributionId))
11+
* - How is a workspace session charged for? (getBillingMode(workspaceInstance.attributionId))
12+
*/
13+
export type BillingMode = None | Chargebee | UsageBased;
14+
export namespace BillingMode {
15+
export const NONE: None = {
16+
mode: "none",
17+
};
18+
19+
export function showChargebeeBilling(billingMode: BillingMode): boolean {
20+
if (billingMode.mode === "chargebee") {
21+
return true;
22+
}
23+
return false;
24+
}
25+
26+
export function showChargebeeInvoices(billingMode: BillingMode): boolean {
27+
if (showChargebeeBilling(billingMode)) {
28+
return true;
29+
}
30+
// TODO(gpl) or hasBeenCustomer, so they can access invoices (edge case?)
31+
return false;
32+
}
33+
34+
/** Incl. upgrade and status */
35+
export function showStripeBilling(billingMode: BillingMode): boolean {
36+
return (
37+
billingMode.mode === "usage-based" || (billingMode.mode === "chargebee" && !!billingMode.canUpgradeToUBB)
38+
);
39+
}
40+
41+
export function canSetWorkspaceClass(billingMode: BillingMode): boolean {
42+
// if has any Stripe subscription, either directly or per team
43+
return billingMode.mode === "usage-based";
44+
}
45+
46+
export function canSetCostCenter(billingMode: BillingMode): boolean {
47+
// if has any Stripe Subscription, either directly or per team
48+
return billingMode.mode === "usage-based";
49+
}
50+
}
51+
52+
/** Payment is disabled */
53+
interface None {
54+
mode: "none";
55+
}
56+
57+
/** Sessions is handled with old subscription logic based on Chargebee */
58+
interface Chargebee {
59+
mode: "chargebee";
60+
canUpgradeToUBB?: boolean;
61+
}
62+
63+
/** Session is handld with new usage-based logic */
64+
interface UsageBased {
65+
mode: "usage-based";
66+
}

components/gitpod-protocol/src/team-subscription-protocol.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@ export namespace TeamSubscription2 {
5555
export const isActive = (ts2: TeamSubscription2, date: string): boolean => {
5656
return ts2.startDate <= date && (ts2.endDate === undefined || date < ts2.endDate);
5757
};
58+
export function isCancelled(s: TeamSubscription2, date: string): boolean {
59+
return (!!s.cancellationDate && s.cancellationDate < date) || (!!s.endDate && s.endDate < date); // This edge case is meant to handle bad data: If for whatever reason cancellationDate has not been set: treat endDate as such
60+
}
5861
}
5962

6063
/**

0 commit comments

Comments
 (0)