Skip to content

[ubp] add chargebee cost center on cancellation #15067

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions components/ee/payment-endpoint/BUILD.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ packages:
deps:
- components/gitpod-db:lib
- components/gitpod-protocol:lib
- components/usage-api/typescript:lib
config:
packaging: offline-mirror
yarnLock: ${coreYarnLockBase}/../yarn.lock
Expand All @@ -24,6 +25,7 @@ packages:
deps:
- components/gitpod-db:lib
- components/gitpod-protocol:lib
- components/usage-api/typescript:lib
- :dbtest
config:
packaging: library
Expand Down Expand Up @@ -54,6 +56,7 @@ packages:
- components/gitpod-db:dbtest-init
- components/gitpod-db:lib
- components/gitpod-protocol:lib
- components/usage-api/typescript:lib
config:
packaging: library
yarnLock: ${coreYarnLockBase}/../yarn.lock
Expand Down
1 change: 1 addition & 0 deletions components/ee/payment-endpoint/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"dependencies": {
"@gitpod/gitpod-db": "0.1.5",
"@gitpod/gitpod-protocol": "0.1.5",
"@gitpod/usage-api": "0.1.5",
"@octokit/rest": "18.5.6",
"@octokit/webhooks": "9.17.0",
"body-parser": "^1.19.2",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,30 @@
* See License.enterprise.txt in the project root folder.
*/

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for all the formatter changes. seems like this component didn't get the big formatting update last time.

import { inject, injectable } from 'inversify';

import { SubscriptionService } from '../accounting/subscription-service';
import { AccountingDB } from '@gitpod/gitpod-db/lib/accounting-db';
import { log, LogContext } from '@gitpod/gitpod-protocol/lib/util/logging';
import { SubscriptionMapperFactory } from './subscription-mapper';
import { Plans } from '@gitpod/gitpod-protocol/lib/plans';
import { Chargebee as chargebee } from './chargebee-types';
import { EventHandler } from './chargebee-event-handler';
import { UpgradeHelper } from './upgrade-helper';
import { inject, injectable } from "inversify";

import { SubscriptionService } from "../accounting/subscription-service";
import { AccountingDB } from "@gitpod/gitpod-db/lib/accounting-db";
import { log, LogContext } from "@gitpod/gitpod-protocol/lib/util/logging";
import { SubscriptionMapper } from "./subscription-mapper";
import { Plans } from "@gitpod/gitpod-protocol/lib/plans";
import { Chargebee as chargebee } from "./chargebee-types";
import { EventHandler } from "./chargebee-event-handler";
import { UpgradeHelper } from "./upgrade-helper";
import { formatDate } from "@gitpod/gitpod-protocol/lib/util/date-time";
import { getUpdatedAt } from './chargebee-subscription-helper';
import { UserPaidSubscription } from '@gitpod/gitpod-protocol/lib/accounting-protocol';
import { DBSubscriptionAdditionalData } from '@gitpod/gitpod-db/lib/typeorm/entity/db-subscription';
import { getUpdatedAt } from "./chargebee-subscription-helper";
import { UserPaidSubscription } from "@gitpod/gitpod-protocol/lib/accounting-protocol";
import { DBSubscriptionAdditionalData } from "@gitpod/gitpod-db/lib/typeorm/entity/db-subscription";

@injectable()
export class SubscriptionHandler implements EventHandler<chargebee.SubscriptionEventV2> {
@inject(SubscriptionService) protected readonly subscriptionService: SubscriptionService;
@inject(AccountingDB) protected readonly db: AccountingDB;
@inject(SubscriptionMapperFactory) protected readonly mapperFactory: SubscriptionMapperFactory;
@inject(SubscriptionMapper) protected readonly mapper: SubscriptionMapper;
@inject(UpgradeHelper) protected readonly upgradeHelper: UpgradeHelper;

canHandle(event: chargebee.Event<any>): boolean {
if (event.event_type.startsWith('subscription')) {
if (event.event_type.startsWith("subscription")) {
const evt = event as chargebee.Event<chargebee.SubscriptionEventV2>;
const plan = Plans.getById(evt.content.subscription.plan_id);
return !!plan && !plan.team;
Expand All @@ -44,16 +44,16 @@ export class SubscriptionHandler implements EventHandler<chargebee.SubscriptionE
log.debug(logContext, `Start SubscriptionHandler.handleSingleEvent`, { eventType });
try {
if (!event.content.subscription) {
log.error(logContext, 'Ignoring event, because it does not contain a subscription', event);
log.error(logContext, "Ignoring event, because it does not contain a subscription", event);
} else {
try {
await this.storeAdditionalData(event.content.subscription, event.content.invoice);
} catch(err) {
log.error(logContext, 'Failed to store additional subscription data', event);
} catch (err) {
log.error(logContext, "Failed to store additional subscription data", event);
}
}

if (event.event_type === 'subscription_changed') {
if (event.event_type === "subscription_changed") {
await this.checkAndChargeForUpgrade(userId, chargebeeSubscription);
}

Expand All @@ -70,11 +70,12 @@ export class SubscriptionHandler implements EventHandler<chargebee.SubscriptionE
const paymentReference = subscription.id;
const coupons = subscription.coupons;
const mrr = subscription.mrr || 0;
const nextBilling = subscription.next_billing_at && new Date(subscription.next_billing_at * 1000).toISOString() || '';
let lastInvoice = '';
const nextBilling =
(subscription.next_billing_at && new Date(subscription.next_billing_at * 1000).toISOString()) || "";
let lastInvoice = "";
let lastInvoiceAmount: number = 0;
if (invoice) {
lastInvoice = invoice.date && new Date(invoice.date * 1000).toISOString() || '';
lastInvoice = (invoice.date && new Date(invoice.date * 1000).toISOString()) || "";
lastInvoiceAmount = invoice.total || 0;
}

Expand All @@ -84,7 +85,7 @@ export class SubscriptionHandler implements EventHandler<chargebee.SubscriptionE
mrr,
lastInvoice,
lastInvoiceAmount,
nextBilling
nextBilling,
};
await this.db.storeSubscriptionAdditionalData(data);
}
Expand All @@ -100,18 +101,28 @@ export class SubscriptionHandler implements EventHandler<chargebee.SubscriptionE
protected async checkAndChargeForUpgrade(userId: string, chargebeeSubscription: chargebee.Subscription) {
const gitpodSubscriptions = await this.db.findSubscriptionForUserByPaymentRef(userId, chargebeeSubscription.id);
if (gitpodSubscriptions.length === 0) {
throw new Error(`Expected existing Gitpod subscription for PaymentRef ${chargebeeSubscription.id} and user ${userId}, found none.`);
throw new Error(
`Expected existing Gitpod subscription for PaymentRef ${chargebeeSubscription.id} and user ${userId}, found none.`,
);
}
const currentGitpodSubscription = gitpodSubscriptions[0]; // Ordered by startDate DESC
const currentGitpodSubscription = gitpodSubscriptions[0]; // Ordered by startDate DESC
const oldPlan = Plans.getById(currentGitpodSubscription.planId)!;
const newPlan = Plans.getById(chargebeeSubscription.plan_id)!;

if (newPlan.pricePerMonth > oldPlan.pricePerMonth) {
// Upgrade: Charge for it!
const diffInCents = (newPlan.pricePerMonth * 100) - (oldPlan.pricePerMonth * 100);
const diffInCents = newPlan.pricePerMonth * 100 - oldPlan.pricePerMonth * 100;
const upgradeTimestamp = getUpdatedAt(chargebeeSubscription);
const description = `Difference on Upgrade from '${oldPlan.name}' to '${newPlan.name}' (${formatDate(upgradeTimestamp)})`;
await this.upgradeHelper.chargeForUpgrade(userId, chargebeeSubscription.id, diffInCents, description, upgradeTimestamp);
const description = `Difference on Upgrade from '${oldPlan.name}' to '${newPlan.name}' (${formatDate(
upgradeTimestamp,
)})`;
await this.upgradeHelper.chargeForUpgrade(
userId,
chargebeeSubscription.id,
diffInCents,
description,
upgradeTimestamp,
);
}
}

Expand All @@ -123,14 +134,13 @@ export class SubscriptionHandler implements EventHandler<chargebee.SubscriptionE
protected async mapToGitpodSubscription(userId: string, event: chargebee.Event<chargebee.SubscriptionEventV2>) {
await this.db.transaction(async (db) => {
const subscriptions = await db.findAllSubscriptionsForUser(userId);
const userPaidSubscriptions = subscriptions.filter(s => UserPaidSubscription.is(s));
const userPaidSubscriptions = subscriptions.filter((s) => UserPaidSubscription.is(s));

const mapper = this.mapperFactory.newMapper();
const delta = mapper.map(userPaidSubscriptions, event).getResult();
const delta = this.mapper.map(userPaidSubscriptions, event).getResult();

await Promise.all([
...delta.updates.map(s => db.storeSubscription(s)),
...delta.inserts.map(s => db.newSubscription(s))
...delta.updates.map((s) => db.storeSubscription(s)),
...delta.inserts.map((s) => db.newSubscription(s)),
]);
});
}
Expand All @@ -142,4 +152,4 @@ export class SubscriptionHandler implements EventHandler<chargebee.SubscriptionE
// 3. Apply all events to handleSingleEvent
// 4. Compare result with the one from the API
}
}
}
Loading