mirror of
https://github.com/OneUptime/oneuptime.git
synced 2026-04-06 00:32:12 +02:00
fix fmt
This commit is contained in:
@@ -13,6 +13,8 @@ import SubscriptionStatus from 'Common/Types/Billing/SubscriptionStatus';
|
||||
import BaseService from './BaseService';
|
||||
import Email from 'Common/Types/Email';
|
||||
|
||||
export type SubscriptionItem = Stripe.SubscriptionItem;
|
||||
|
||||
export interface PaymentMethod {
|
||||
id: string;
|
||||
type: string;
|
||||
@@ -101,6 +103,60 @@ export class BillingService extends BaseService {
|
||||
);
|
||||
}
|
||||
|
||||
public async subscribeToMeteredPlan(data: {
|
||||
projectId: ObjectID;
|
||||
customerId: string;
|
||||
serverMeteredPlans: Array<typeof ServerMeteredPlan>;
|
||||
trialDate: Date | null;
|
||||
defaultPaymentMethodId?: string | undefined;
|
||||
promoCode?: string | undefined;
|
||||
}): Promise<{
|
||||
meteredSubscriptionId: string;
|
||||
trialEndsAt: Date | null;
|
||||
}> {
|
||||
const meteredPlanSubscriptionParams: Stripe.SubscriptionCreateParams = {
|
||||
customer: data.customerId,
|
||||
|
||||
items: data.serverMeteredPlans.map(
|
||||
(item: typeof ServerMeteredPlan) => {
|
||||
return {
|
||||
price: item.getMeteredPlan()?.getPriceId()!,
|
||||
};
|
||||
}
|
||||
),
|
||||
trial_end:
|
||||
data.trialDate && OneUptimeDate.isInTheFuture(data.trialDate)
|
||||
? OneUptimeDate.toUnixTimestamp(data.trialDate)
|
||||
: 'now',
|
||||
};
|
||||
|
||||
if (data.promoCode) {
|
||||
meteredPlanSubscriptionParams.coupon = data.promoCode;
|
||||
}
|
||||
|
||||
if (data.defaultPaymentMethodId) {
|
||||
meteredPlanSubscriptionParams.default_payment_method =
|
||||
data.defaultPaymentMethodId;
|
||||
}
|
||||
|
||||
// Create metered subscriptions
|
||||
const meteredSubscription: Stripe.Response<Stripe.Subscription> =
|
||||
await this.stripe.subscriptions.create(
|
||||
meteredPlanSubscriptionParams
|
||||
);
|
||||
|
||||
for (const serverMeteredPlan of data.serverMeteredPlans) {
|
||||
await serverMeteredPlan.updateCurrentQuantity(data.projectId, {
|
||||
meteredPlanSubscriptionId: meteredSubscription.id,
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
meteredSubscriptionId: meteredSubscription.id,
|
||||
trialEndsAt: data.trialDate,
|
||||
};
|
||||
}
|
||||
|
||||
public async subscribeToPlan(data: {
|
||||
projectId: ObjectID;
|
||||
customerId: string;
|
||||
@@ -152,25 +208,8 @@ export class BillingService extends BaseService {
|
||||
: 'now',
|
||||
};
|
||||
|
||||
const meteredPlanSubscriptionParams: Stripe.SubscriptionCreateParams = {
|
||||
customer: data.customerId,
|
||||
|
||||
items: data.serverMeteredPlans.map(
|
||||
(item: typeof ServerMeteredPlan) => {
|
||||
return {
|
||||
price: item.getMeteredPlan()?.getPriceId()!,
|
||||
};
|
||||
}
|
||||
),
|
||||
trial_end:
|
||||
trialDate && data.plan.getTrialPeriod() > 0
|
||||
? OneUptimeDate.toUnixTimestamp(trialDate)
|
||||
: 'now',
|
||||
};
|
||||
|
||||
if (data.promoCode) {
|
||||
subscriptionParams.coupon = data.promoCode;
|
||||
meteredPlanSubscriptionParams.coupon = data.promoCode;
|
||||
}
|
||||
|
||||
if (data.defaultPaymentMethodId) {
|
||||
@@ -182,20 +221,17 @@ export class BillingService extends BaseService {
|
||||
await this.stripe.subscriptions.create(subscriptionParams);
|
||||
|
||||
// Create metered subscriptions
|
||||
const meteredSubscription: Stripe.Response<Stripe.Subscription> =
|
||||
await this.stripe.subscriptions.create(
|
||||
meteredPlanSubscriptionParams
|
||||
);
|
||||
|
||||
for (const serverMeteredPlan of data.serverMeteredPlans) {
|
||||
await serverMeteredPlan.updateCurrentQuantity(data.projectId, {
|
||||
meteredPlanSubscriptionId: meteredSubscription.id,
|
||||
});
|
||||
}
|
||||
const meteredSubscription: {
|
||||
meteredSubscriptionId: string;
|
||||
trialEndsAt: Date | null;
|
||||
} = await this.subscribeToMeteredPlan({
|
||||
...data,
|
||||
trialDate,
|
||||
});
|
||||
|
||||
return {
|
||||
subscriptionId: subscription.id,
|
||||
meteredSubscriptionId: meteredSubscription.id,
|
||||
meteredSubscriptionId: meteredSubscription.meteredSubscriptionId,
|
||||
trialEndsAt:
|
||||
trialDate && data.plan.getTrialPeriod() > 0 ? trialDate : null,
|
||||
};
|
||||
@@ -258,7 +294,7 @@ export class BillingService extends BaseService {
|
||||
// check if this pricing exists
|
||||
|
||||
const pricingExists: boolean = subscription.items.data.some(
|
||||
(item: Stripe.SubscriptionItem) => {
|
||||
(item: SubscriptionItem) => {
|
||||
return item.price?.id === meteredPlan.getPriceId();
|
||||
}
|
||||
);
|
||||
@@ -266,11 +302,9 @@ export class BillingService extends BaseService {
|
||||
if (pricingExists) {
|
||||
// update the quantity.
|
||||
const subscriptionItemId: string | undefined =
|
||||
subscription.items.data.find(
|
||||
(item: Stripe.SubscriptionItem) => {
|
||||
return item.price?.id === meteredPlan.getPriceId();
|
||||
}
|
||||
)?.id;
|
||||
subscription.items.data.find((item: SubscriptionItem) => {
|
||||
return item.price?.id === meteredPlan.getPriceId();
|
||||
})?.id;
|
||||
|
||||
if (!subscriptionItemId) {
|
||||
throw new BadDataException('Subscription Item not found');
|
||||
@@ -285,7 +319,7 @@ export class BillingService extends BaseService {
|
||||
);
|
||||
} else {
|
||||
// add the pricing.
|
||||
const subscriptionItem: Stripe.SubscriptionItem =
|
||||
const subscriptionItem: SubscriptionItem =
|
||||
await this.stripe.subscriptionItems.create({
|
||||
subscription: subscriptionId,
|
||||
price: meteredPlan.getPriceId(),
|
||||
@@ -325,6 +359,62 @@ export class BillingService extends BaseService {
|
||||
}
|
||||
}
|
||||
|
||||
public async removeSubscriptionItem(
|
||||
subscriptionId: string,
|
||||
subscriptionItemId: string,
|
||||
isMeteredSubscriptionItem: boolean
|
||||
): Promise<void> {
|
||||
if (!this.isBillingEnabled()) {
|
||||
throw new BadDataException(
|
||||
'Billing is not enabled for this server.'
|
||||
);
|
||||
}
|
||||
|
||||
const subscription: Stripe.Response<Stripe.Subscription> =
|
||||
await this.stripe.subscriptions.retrieve(subscriptionId);
|
||||
|
||||
if (!subscription) {
|
||||
throw new BadDataException('Subscription not found');
|
||||
}
|
||||
|
||||
if (subscription.status === 'canceled') {
|
||||
// subscription is canceled.
|
||||
return;
|
||||
}
|
||||
|
||||
const subscriptionItemOptions: Stripe.SubscriptionItemDeleteParams =
|
||||
isMeteredSubscriptionItem
|
||||
? {
|
||||
proration_behavior: 'create_prorations',
|
||||
clear_usage: true,
|
||||
}
|
||||
: {};
|
||||
|
||||
await this.stripe.subscriptionItems.del(
|
||||
subscriptionItemId,
|
||||
subscriptionItemOptions
|
||||
);
|
||||
}
|
||||
|
||||
public async getSubscriptionItems(
|
||||
subscriptionId: string
|
||||
): Promise<Array<SubscriptionItem>> {
|
||||
if (!this.isBillingEnabled()) {
|
||||
throw new BadDataException(
|
||||
'Billing is not enabled for this server.'
|
||||
);
|
||||
}
|
||||
|
||||
const subscription: Stripe.Response<Stripe.Subscription> =
|
||||
await this.stripe.subscriptions.retrieve(subscriptionId);
|
||||
|
||||
if (!subscription) {
|
||||
throw new BadDataException('Subscription not found');
|
||||
}
|
||||
|
||||
return subscription.items.data;
|
||||
}
|
||||
|
||||
public async changePlan(data: {
|
||||
projectId: ObjectID;
|
||||
subscriptionId: string;
|
||||
|
||||
@@ -2,6 +2,7 @@ import AddOwnerInfoToProjects from './AddOwnerInfoToProject';
|
||||
import DataMigrationBase from './DataMigrationBase';
|
||||
import MigrateDefaultUserNotificationRule from './MigrateDefaultUserNotificationRule';
|
||||
import MigrateDefaultUserNotificationSetting from './MigrateDefaultUserSettingNotification';
|
||||
import MigrateToMeteredSubscription from './MigrateToMeteredSubscription';
|
||||
|
||||
// This is the order in which the migrations will be run. Add new migrations to the end of the array.
|
||||
|
||||
@@ -9,6 +10,7 @@ const DataMigrations: Array<DataMigrationBase> = [
|
||||
new MigrateDefaultUserNotificationRule(),
|
||||
new AddOwnerInfoToProjects(),
|
||||
new MigrateDefaultUserNotificationSetting(),
|
||||
new MigrateToMeteredSubscription(),
|
||||
];
|
||||
|
||||
export default DataMigrations;
|
||||
|
||||
99
Workers/DataMigrations/MigrateToMeteredSubscription.ts
Normal file
99
Workers/DataMigrations/MigrateToMeteredSubscription.ts
Normal file
@@ -0,0 +1,99 @@
|
||||
import DataMigrationBase from './DataMigrationBase';
|
||||
import LIMIT_MAX from 'Common/Types/Database/LimitMax';
|
||||
import ProjectService from 'CommonServer/Services/ProjectService';
|
||||
import Project from 'Model/Models/Project';
|
||||
import BillingService, {
|
||||
SubscriptionItem,
|
||||
} from 'CommonServer/Services/BillingService';
|
||||
import { IsBillingEnabled } from 'CommonServer/Config';
|
||||
import AllMeteredPlans from 'CommonServer/Types/Billing/MeteredPlan/AllMeteredPlans';
|
||||
import QueryHelper from 'CommonServer/Types/Database/QueryHelper';
|
||||
import Sleep from 'Common/Types/Sleep';
|
||||
|
||||
export default class MigrateToMeteredSubscription extends DataMigrationBase {
|
||||
public constructor() {
|
||||
super('MigrateToMeteredSubscription');
|
||||
}
|
||||
|
||||
public override async migrate(): Promise<void> {
|
||||
if (!IsBillingEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
const projects: Array<Project> = await ProjectService.findBy({
|
||||
query: {
|
||||
paymentProviderMeteredSubscriptionId: QueryHelper.isNull(),
|
||||
},
|
||||
select: {
|
||||
_id: true,
|
||||
paymentProviderSubscriptionId: true,
|
||||
paymentProviderCustomerId: true,
|
||||
trialEndsAt: true,
|
||||
},
|
||||
limit: LIMIT_MAX,
|
||||
skip: 0,
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
|
||||
for (const project of projects) {
|
||||
if (!project.paymentProviderSubscriptionId) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!project.paymentProviderCustomerId) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// remove subscription item.
|
||||
const subscriptionItems: Array<SubscriptionItem> =
|
||||
await BillingService.getSubscriptionItems(
|
||||
project.paymentProviderSubscriptionId
|
||||
);
|
||||
|
||||
for (const subscriptionItem of subscriptionItems) {
|
||||
if (
|
||||
subscriptionItem.plan.id ===
|
||||
'price_1N6Cg9ANuQdJ93r7veN7YgsH' ||
|
||||
subscriptionItem.plan.id ===
|
||||
'price_1N6B9EANuQdJ93r7fj3bhcWP'
|
||||
) {
|
||||
await BillingService.removeSubscriptionItem(
|
||||
project.paymentProviderSubscriptionId,
|
||||
subscriptionItem.id,
|
||||
true
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// add metered subscription item and update metered quantity.
|
||||
const meteredPlan: {
|
||||
meteredSubscriptionId: string;
|
||||
} = await BillingService.subscribeToMeteredPlan({
|
||||
projectId: project.id!,
|
||||
customerId: project.paymentProviderCustomerId!,
|
||||
serverMeteredPlans: AllMeteredPlans,
|
||||
trialDate: project.trialEndsAt || null,
|
||||
});
|
||||
|
||||
// update project with metered subscription id.
|
||||
await ProjectService.updateOneById({
|
||||
id: project.id!,
|
||||
data: {
|
||||
paymentProviderMeteredSubscriptionId:
|
||||
meteredPlan.meteredSubscriptionId,
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
|
||||
await Sleep.sleep(500);
|
||||
}
|
||||
}
|
||||
|
||||
public override async rollback(): Promise<void> {
|
||||
return;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user