diff --git a/components/ee/payment-endpoint/src/accounting/team-subscription-service.ts b/components/ee/payment-endpoint/src/accounting/team-subscription-service.ts index 5d1d792d9b13e8..0aaa686f80f18d 100644 --- a/components/ee/payment-endpoint/src/accounting/team-subscription-service.ts +++ b/components/ee/payment-endpoint/src/accounting/team-subscription-service.ts @@ -350,6 +350,10 @@ export class TeamSubscriptionService { const subscriptionsFromTS = subscriptions.filter(s => AssignedTeamSubscription.is(s)); return new SubscriptionModel(userId, subscriptionsFromTS); } + + async findTeamSubscriptionSlotsByAssignee(assigneeId: string): Promise { + return this.db.findSlotsByAssignee(assigneeId); + } } const flatten = (input: T[][]): T[] => { diff --git a/components/server/ee/src/user/user-deletion-service.ts b/components/server/ee/src/user/user-deletion-service.ts index 04200846320ebe..050e9b401ec1af 100644 --- a/components/server/ee/src/user/user-deletion-service.ts +++ b/components/server/ee/src/user/user-deletion-service.ts @@ -10,11 +10,15 @@ import { SubscriptionService } from "@gitpod/gitpod-payment-endpoint/lib/account import { Plans } from "@gitpod/gitpod-protocol/lib/plans"; import { ChargebeeService } from "./chargebee-service"; import { EnvEE } from "../env"; +import { TeamSubscriptionService } from "@gitpod/gitpod-payment-endpoint/lib/accounting"; +import { log } from '@gitpod/gitpod-protocol/lib/util/logging'; + @injectable() export class UserDeletionServiceEE extends UserDeletionService { @inject(ChargebeeService) protected readonly chargebeeService: ChargebeeService; @inject(SubscriptionService) protected readonly subscriptionService: SubscriptionService; @inject(EnvEE) protected readonly env: EnvEE; + @inject(TeamSubscriptionService) protected readonly teamSubscriptionService: TeamSubscriptionService; async deleteUser(id: string): Promise { const user = await this.db.findUserById(id); @@ -22,26 +26,43 @@ export class UserDeletionServiceEE extends UserDeletionService { throw new Error(`No user with id ${id} found!`); } + const errors = []; if (this.env.enablePayment) { - const now = new Date().toISOString(); - const subscriptions = await this.subscriptionService.getNotYetCancelledSubscriptions(user, now); + const now = new Date(); + const subscriptions = await this.subscriptionService.getNotYetCancelledSubscriptions(user, now.toISOString()); for (const subscription of subscriptions) { - const planId = subscription.planId!; - if (Plans.isFreeNonTransientPlan(planId)) { - // only delete those plans that are persisted in the DB - await this.subscriptionService.unsubscribe(user.id, now, planId); - } else if (Plans.isFreePlan(planId)) { - // we do not care about transient plans - continue; - } else { - // cancel Chargebee subscriptions - const subscriptionId = subscription.uid; - const chargebeeSubscriptionId = subscription.paymentReference!; - await this.chargebeeService.cancelSubscription(chargebeeSubscriptionId, { userId: user.id }, { subscriptionId, chargebeeSubscriptionId }); + try { + const planId = subscription.planId!; + const paymentReference = subscription.paymentReference; + if (Plans.isFreeNonTransientPlan(planId)) { + // only delete those plans that are persisted in the DB + await this.subscriptionService.unsubscribe(user.id, now.toISOString(), planId); + } else if (Plans.isFreePlan(planId)) { + // we do not care about transient plans + continue; + } else { + if (!paymentReference) { + const teamSlots = await this.teamSubscriptionService.findTeamSubscriptionSlotsByAssignee(id); + teamSlots.forEach(async ts => await this.teamSubscriptionService.deactivateSlot(ts.teamSubscriptionId, ts.id, now)) + } else if (paymentReference.startsWith("github:")) { + throw new Error("Cannot delete user subscription from GitHub") + } else { + // cancel Chargebee subscriptions + const subscriptionId = subscription.uid; + const chargebeeSubscriptionId = subscription.paymentReference!; + await this.chargebeeService.cancelSubscription(chargebeeSubscriptionId, { userId: user.id }, { subscriptionId, chargebeeSubscriptionId }); + } + } + } catch (error) { + errors.push(error); + log.error("Error cancelling subscription", error, { subscription }) } } } - return super.deleteUser(id); + await super.deleteUser(id); + if (errors) { + throw new Error(errors.join("\n")) + } } -} \ No newline at end of file +}