Skip to content

Commit 7bf63df

Browse files
jankeromnesroboquat
authored andcommitted
[server][dashboard] When creating a new Stripe customer, also create a Usage-Based Subscription for them
1 parent e1b335c commit 7bf63df

File tree

5 files changed

+36
-8
lines changed

5 files changed

+36
-8
lines changed

components/dashboard/src/teams/TeamUsageBasedBilling.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ export default function TeamUsageBasedBilling() {
2121
const { teams } = useContext(TeamsContext);
2222
const location = useLocation();
2323
const team = getCurrentTeam(location, teams);
24-
const { showUsageBasedUI } = useContext(PaymentContext);
24+
const { showUsageBasedUI, currency } = useContext(PaymentContext);
2525
const [stripeCustomerId, setStripeCustomerId] = useState<string | undefined>();
2626
const [isLoading, setIsLoading] = useState<boolean>(true);
2727
const [showBillingSetupModal, setShowBillingSetupModal] = useState<boolean>(false);
@@ -68,7 +68,7 @@ export default function TeamUsageBasedBilling() {
6868
(async () => {
6969
const setupIntentId = params.get("setup_intent")!;
7070
window.history.replaceState({}, "", window.location.pathname);
71-
await getGitpodService().server.subscribeTeamToStripe(team.id, setupIntentId);
71+
await getGitpodService().server.subscribeTeamToStripe(team.id, setupIntentId, currency);
7272
const pendingCustomer = { pendingSince: Date.now() };
7373
setPendingStripeCustomer(pendingCustomer);
7474
window.localStorage.setItem(`pendingStripeCustomerForTeam${team.id}`, JSON.stringify(pendingCustomer));

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

+2-1
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ import {
5656
import { RemotePageMessage, RemoteTrackMessage, RemoteIdentifyMessage } from "./analytics";
5757
import { IDEServer } from "./ide-protocol";
5858
import { InstallationAdminSettings, TelemetryData } from "./installation-admin-protocol";
59+
import { Currency } from "./plans";
5960

6061
export interface GitpodClient {
6162
onInstanceUpdate(instance: WorkspaceInstance): void;
@@ -273,7 +274,7 @@ export interface GitpodServer extends JsonRpcServer<GitpodClient>, AdminServer,
273274
getStripePublishableKey(): Promise<string>;
274275
getStripeSetupIntentClientSecret(): Promise<string>;
275276
findStripeCustomerIdForTeam(teamId: string): Promise<string | undefined>;
276-
subscribeTeamToStripe(teamId: string, setupIntentId: string): Promise<void>;
277+
subscribeTeamToStripe(teamId: string, setupIntentId: string, currency: Currency): Promise<void>;
277278
getStripePortalUrlForTeam(teamId: string): Promise<string>;
278279

279280
/**

components/server/ee/src/user/stripe-service.ts

+16
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import { inject, injectable } from "inversify";
88
import Stripe from "stripe";
99
import { Team, User } from "@gitpod/gitpod-protocol";
10+
import { Currency } from "@gitpod/gitpod-protocol/lib/plans";
1011
import { Config } from "../../../src/config";
1112

1213
@injectable()
@@ -113,4 +114,19 @@ export class StripeService {
113114
});
114115
return session.url;
115116
}
117+
118+
async createSubscriptionForCustomer(customerId: string, currency: Currency): Promise<void> {
119+
// FIXME(janx): Use configmap.
120+
const prices = {
121+
EUR: "price_1LAE0AGadRXm50o3xjegX0Kd",
122+
USD: "price_1LAE0AGadRXm50o3rKoktPiJ",
123+
};
124+
const startOfNextMonth = new Date(new Date().toISOString().slice(0, 7) + "-01"); // First day of this month (YYYY-MM-01)
125+
startOfNextMonth.setMonth(startOfNextMonth.getMonth() + 1); // Add one month
126+
await this.getStripe().subscriptions.create({
127+
customer: customerId,
128+
items: [{ price: prices[currency] }],
129+
billing_cycle_anchor: Math.round(startOfNextMonth.getTime() / 1000),
130+
});
131+
}
116132
}

components/server/ee/src/workspace/gitpod-server-impl.ts

+9-4
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ import {
7676
TeamSubscriptionSlot,
7777
TeamSubscriptionSlotResolved,
7878
} from "@gitpod/gitpod-protocol/lib/team-subscription-protocol";
79-
import { Plans } from "@gitpod/gitpod-protocol/lib/plans";
79+
import { Currency, Plans } from "@gitpod/gitpod-protocol/lib/plans";
8080
import * as pThrottle from "p-throttle";
8181
import { formatDate } from "@gitpod/gitpod-protocol/lib/util/date-time";
8282
import { FindUserByIdentityStrResult, UserService } from "../../../src/user/user-service";
@@ -1892,14 +1892,19 @@ export class GitpodServerEEImpl extends GitpodServerImpl {
18921892
}
18931893
}
18941894

1895-
async subscribeTeamToStripe(ctx: TraceContext, teamId: string, setupIntentId: string): Promise<void> {
1895+
async subscribeTeamToStripe(
1896+
ctx: TraceContext,
1897+
teamId: string,
1898+
setupIntentId: string,
1899+
currency: Currency,
1900+
): Promise<void> {
18961901
const user = this.checkAndBlockUser("subscribeUserToStripe");
18971902
await this.ensureIsUsageBasedFeatureFlagEnabled(user);
18981903
await this.guardTeamOperation(teamId, "update");
18991904
const team = await this.teamDB.findTeamById(teamId);
19001905
try {
1901-
await this.stripeService.createCustomerForTeam(user, team!, setupIntentId);
1902-
// TODO(janx): Create a Stripe usage-based Subscription for the customer
1906+
const customer = await this.stripeService.createCustomerForTeam(user, team!, setupIntentId);
1907+
await this.stripeService.createSubscriptionForCustomer(customer.id, currency);
19031908
} catch (error) {
19041909
log.error(`Failed to subscribe team '${teamId}' to Stripe`, error);
19051910
throw new ResponseError(ErrorCodes.INTERNAL_SERVER_ERROR, `Failed to subscribe team '${teamId}' to Stripe`);

components/server/src/workspace/gitpod-server-impl.ts

+7-1
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ import { InstallationAdminTelemetryDataProvider } from "../installation-admin/te
162162
import { LicenseEvaluator } from "@gitpod/licensor/lib";
163163
import { Feature } from "@gitpod/licensor/lib/api";
164164
import { getExperimentsClient } from "../experiments";
165+
import { Currency } from "@gitpod/gitpod-protocol/lib/plans";
165166

166167
// shortcut
167168
export const traceWI = (ctx: TraceContext, wi: Omit<LogContext, "userId">) => TraceContext.setOWI(ctx, wi); // userId is already taken care of in WebsocketConnectionManager
@@ -3042,7 +3043,12 @@ export class GitpodServerImpl implements GitpodServerWithTracing, Disposable {
30423043
async findStripeCustomerIdForTeam(ctx: TraceContext, teamId: string): Promise<string | undefined> {
30433044
throw new ResponseError(ErrorCodes.SAAS_FEATURE, `Not implemented in this version`);
30443045
}
3045-
async subscribeTeamToStripe(ctx: TraceContext, teamId: string, setupIntentId: string): Promise<void> {
3046+
async subscribeTeamToStripe(
3047+
ctx: TraceContext,
3048+
teamId: string,
3049+
setupIntentId: string,
3050+
currency: Currency,
3051+
): Promise<void> {
30463052
throw new ResponseError(ErrorCodes.SAAS_FEATURE, `Not implemented in this version`);
30473053
}
30483054
async getStripePortalUrlForTeam(ctx: TraceContext, teamId: string): Promise<string> {

0 commit comments

Comments
 (0)