From 7d47793afb4d9595a1196f7cec433584b540a20e Mon Sep 17 00:00:00 2001 From: Ali BARIN Date: Thu, 6 Apr 2023 13:08:49 +0000 Subject: [PATCH 1/4] feat: add subscription update and cancel logics --- .../src/controllers/paddle/webhooks.ee.ts | 4 ++ ...83738_add_subscription_id_in_usage_data.ts | 18 ++++++ .../queries/get-billing-and-usage.ee.ts | 6 +- .../src/graphql/queries/get-invoices.ee.ts | 2 +- .../src/graphql/queries/get-usage-data.ee.ts | 15 +++-- .../backend/src/helpers/billing/paddle.ee.ts | 1 + .../backend/src/helpers/billing/plans.ee.ts | 13 ++-- .../src/helpers/billing/webhooks.ee.ts | 37 +++++++++++- packages/backend/src/models/execution-step.ts | 2 +- packages/backend/src/models/flow.ts | 2 +- .../backend/src/models/subscription.ee.ts | 24 ++++++++ packages/backend/src/models/usage-data.ee.ts | 21 ++++--- packages/backend/src/models/user.ts | 60 +++++++++++++------ packages/backend/src/routes/paddle.ee.ts | 14 ++--- .../UsageDataInformation/index.ee.tsx | 5 +- 15 files changed, 170 insertions(+), 54 deletions(-) create mode 100644 packages/backend/src/db/migrations/20230402183738_add_subscription_id_in_usage_data.ts diff --git a/packages/backend/src/controllers/paddle/webhooks.ee.ts b/packages/backend/src/controllers/paddle/webhooks.ee.ts index 41ed90da..bd8ebb2d 100644 --- a/packages/backend/src/controllers/paddle/webhooks.ee.ts +++ b/packages/backend/src/controllers/paddle/webhooks.ee.ts @@ -12,6 +12,10 @@ export default async (request: IRequest, response: Response) => { if (request.body.alert_name === 'subscription_created') { await Billing.webhooks.handleSubscriptionCreated(request); + } else if (request.body.alert_name === 'subscription_updated') { + await Billing.webhooks.handleSubscriptionUpdated(request); + } else if (request.body.alert_name === 'subscription_cancelled') { + await Billing.webhooks.handleSubscriptionCancelled(request); } else if (request.body.alert_name === 'subscription_payment_succeeded') { await Billing.webhooks.handleSubscriptionPaymentSucceeded(request); } diff --git a/packages/backend/src/db/migrations/20230402183738_add_subscription_id_in_usage_data.ts b/packages/backend/src/db/migrations/20230402183738_add_subscription_id_in_usage_data.ts new file mode 100644 index 00000000..e2018caf --- /dev/null +++ b/packages/backend/src/db/migrations/20230402183738_add_subscription_id_in_usage_data.ts @@ -0,0 +1,18 @@ +import { Knex } from 'knex'; +import appConfig from '../../config/app'; + +export async function up(knex: Knex): Promise { + if (!appConfig.isCloud) return; + + return knex.schema.table('usage_data', (table) => { + table.uuid('subscription_id').references('id').inTable('subscriptions'); + }); +} + +export async function down(knex: Knex): Promise { + if (!appConfig.isCloud) return; + + return knex.schema.table('usage_data', (table) => { + table.dropColumn('subscription_id'); + }); +} diff --git a/packages/backend/src/graphql/queries/get-billing-and-usage.ee.ts b/packages/backend/src/graphql/queries/get-billing-and-usage.ee.ts index ae6ed957..7c786b96 100644 --- a/packages/backend/src/graphql/queries/get-billing-and-usage.ee.ts +++ b/packages/backend/src/graphql/queries/get-billing-and-usage.ee.ts @@ -13,7 +13,7 @@ const getBillingAndUsage = async ( context: Context ) => { const persistedSubscription = await context.currentUser.$relatedQuery( - 'subscription' + 'currentSubscription' ); const subscription = persistedSubscription @@ -39,8 +39,8 @@ const paidSubscription = (subscription: Subscription): TSubscription => { title: currentPlan.limit, action: { type: 'link', - text: 'Change plan', - src: '/settings/billing/change-plan', + text: 'Cancel plan', + src: subscription.cancelUrl, }, }, nextBillAmount: { diff --git a/packages/backend/src/graphql/queries/get-invoices.ee.ts b/packages/backend/src/graphql/queries/get-invoices.ee.ts index c745a4c6..49bad7c5 100644 --- a/packages/backend/src/graphql/queries/get-invoices.ee.ts +++ b/packages/backend/src/graphql/queries/get-invoices.ee.ts @@ -6,7 +6,7 @@ const getInvoices = async ( _params: unknown, context: Context ) => { - const subscription = await context.currentUser.$relatedQuery('subscription'); + const subscription = await context.currentUser.$relatedQuery('currentSubscription'); if (!subscription) { return; diff --git a/packages/backend/src/graphql/queries/get-usage-data.ee.ts b/packages/backend/src/graphql/queries/get-usage-data.ee.ts index a5dfc69b..40f604a3 100644 --- a/packages/backend/src/graphql/queries/get-usage-data.ee.ts +++ b/packages/backend/src/graphql/queries/get-usage-data.ee.ts @@ -1,6 +1,7 @@ import appConfig from '../../config/app'; import Context from '../../types/express/context'; +// TODO: remove as getBillingAndUsageData query has been introduced const getUsageData = async ( _parent: unknown, _params: unknown, @@ -9,18 +10,20 @@ const getUsageData = async ( if (!appConfig.isCloud) return; const usageData = await context.currentUser - .$relatedQuery('usageData') + .$relatedQuery('currentUsageData') .throwIfNotFound(); - const paymentPlan = await context.currentUser - .$relatedQuery('paymentPlan') + const subscription = await usageData + .$relatedQuery('subscription') .throwIfNotFound(); + const plan = subscription.plan; + const computedUsageData = { - name: paymentPlan.name, - allowedTaskCount: paymentPlan.taskCount, + name: plan.name, + allowedTaskCount: plan.quota, consumedTaskCount: usageData.consumedTaskCount, - remainingTaskCount: paymentPlan.taskCount - usageData.consumedTaskCount, + remainingTaskCount: plan.quota - usageData.consumedTaskCount, nextResetAt: usageData.nextResetAt, }; diff --git a/packages/backend/src/helpers/billing/paddle.ee.ts b/packages/backend/src/helpers/billing/paddle.ee.ts index 40594016..f558713d 100644 --- a/packages/backend/src/helpers/billing/paddle.ee.ts +++ b/packages/backend/src/helpers/billing/paddle.ee.ts @@ -24,6 +24,7 @@ const getSubscription = async (subscriptionId: number) => { }; const getInvoices = async (subscriptionId: number) => { + // TODO: iterate over previous subscriptions and include their invoices const data = { vendor_id: appConfig.paddleVendorId, vendor_auth_code: appConfig.paddleVendorAuthCode, diff --git a/packages/backend/src/helpers/billing/plans.ee.ts b/packages/backend/src/helpers/billing/plans.ee.ts index 98bdcac9..45b2fea5 100644 --- a/packages/backend/src/helpers/billing/plans.ee.ts +++ b/packages/backend/src/helpers/billing/plans.ee.ts @@ -2,15 +2,14 @@ const plans = [ { name: '10k - monthly', limit: '10,000', + quota: 10000, price: '€20', productId: '47384', - }, - { - name: '30k - monthly', - limit: '30,000', - price: '€50', - productId: '47419', - }, + } ]; +export function getPlanById(id: string) { + return plans.find((plan) => plan.productId === id); +} + export default plans; diff --git a/packages/backend/src/helpers/billing/webhooks.ee.ts b/packages/backend/src/helpers/billing/webhooks.ee.ts index 46930a8e..056c754f 100644 --- a/packages/backend/src/helpers/billing/webhooks.ee.ts +++ b/packages/backend/src/helpers/billing/webhooks.ee.ts @@ -3,7 +3,30 @@ import Subscription from '../../models/subscription.ee'; import Billing from './index.ee'; const handleSubscriptionCreated = async (request: IRequest) => { - await Subscription.query().insertAndFetch(formatSubscription(request)); + const subscription = await Subscription.query().insertAndFetch(formatSubscription(request)); + await subscription.$relatedQuery('usageData').insert(formatUsageData(request)); +}; + +const handleSubscriptionUpdated = async (request: IRequest) => { + await Subscription + .query() + .findOne({ + paddle_subscription_id: request.body.subscription_id, + }) + .patch(formatSubscription(request)); +}; + +const handleSubscriptionCancelled = async (request: IRequest) => { + const subscription = await Subscription + .query() + .findOne({ + paddle_subscription_id: request.body.subscription_id, + }) + .patchAndFetch(formatSubscription(request)); + + if (request.body.status === 'deleted') { + await subscription.$query().delete(); + } }; const handleSubscriptionPaymentSucceeded = async (request: IRequest) => { @@ -22,6 +45,8 @@ const handleSubscriptionPaymentSucceeded = async (request: IRequest) => { nextBillDate: remoteSubscription.next_payment.date, lastBillDate: remoteSubscription.last_payment.date, }); + + await subscription.$relatedQuery('usageData').insert(formatUsageData(request)); }; const formatSubscription = (request: IRequest) => { @@ -37,8 +62,18 @@ const formatSubscription = (request: IRequest) => { }; }; +const formatUsageData = (request: IRequest) => { + return { + userId: JSON.parse(request.body.passthrough).id, + consumedTaskCount: 0, + nextResetAt: request.body.next_bill_date, + }; +}; + const webhooks = { handleSubscriptionCreated, + handleSubscriptionUpdated, + handleSubscriptionCancelled, handleSubscriptionPaymentSucceeded, }; diff --git a/packages/backend/src/models/execution-step.ts b/packages/backend/src/models/execution-step.ts index 6b52d15d..b26a3766 100644 --- a/packages/backend/src/models/execution-step.ts +++ b/packages/backend/src/models/execution-step.ts @@ -67,7 +67,7 @@ class ExecutionStep extends Base { if (!execution.testRun && !this.isFailed) { const flow = await execution.$relatedQuery('flow'); const user = await flow.$relatedQuery('user'); - const usageData = await user.$relatedQuery('usageData'); + const usageData = await user.$relatedQuery('currentUsageData'); await usageData.increaseConsumedTaskCountByOne(); } diff --git a/packages/backend/src/models/flow.ts b/packages/backend/src/models/flow.ts index 6eaab50f..ed57c1f3 100644 --- a/packages/backend/src/models/flow.ts +++ b/packages/backend/src/models/flow.ts @@ -136,7 +136,7 @@ class Flow extends Base { if (!appConfig.isCloud) return; const user = await this.$relatedQuery('user'); - const usageData = await user.$relatedQuery('usageData'); + const usageData = await user.$relatedQuery('currentUsageData'); const hasExceeded = await usageData.checkIfLimitExceeded(); diff --git a/packages/backend/src/models/subscription.ee.ts b/packages/backend/src/models/subscription.ee.ts index 5cadc24b..1d639a97 100644 --- a/packages/backend/src/models/subscription.ee.ts +++ b/packages/backend/src/models/subscription.ee.ts @@ -1,5 +1,7 @@ import Base from './base'; import User from './user'; +import UsageData from './usage-data.ee'; +import { getPlanById } from '../helpers/billing/plans.ee'; class Subscription extends Base { id!: string; @@ -12,6 +14,8 @@ class Subscription extends Base { nextBillAmount!: string; nextBillDate!: string; lastBillDate?: string; + usageData?: UsageData[]; + currentUsageData?: UsageData; static tableName = 'subscriptions'; @@ -51,7 +55,27 @@ class Subscription extends Base { to: 'users.id', }, }, + usageData: { + relation: Base.HasManyRelation, + modelClass: UsageData, + join: { + from: 'subscriptions.id', + to: 'usage_data.subscription_id', + }, + }, + currentUsageData: { + relation: Base.HasOneRelation, + modelClass: UsageData, + join: { + from: 'subscriptions.id', + to: 'usage_data.subscription_id', + }, + }, }); + + get plan() { + return getPlanById(this.paddlePlanId); + } } export default Subscription; diff --git a/packages/backend/src/models/usage-data.ee.ts b/packages/backend/src/models/usage-data.ee.ts index 2e508f12..fdf10962 100644 --- a/packages/backend/src/models/usage-data.ee.ts +++ b/packages/backend/src/models/usage-data.ee.ts @@ -1,14 +1,16 @@ import { raw } from 'objection'; import Base from './base'; import User from './user'; -import PaymentPlan from './payment-plan.ee'; +import Subscription from './subscription.ee'; +import { getPlanById } from '../helpers/billing/plans.ee'; class UsageData extends Base { id!: string; userId!: string; + subscriptionId?: string; consumedTaskCount!: number; nextResetAt!: string; - paymentPlan?: PaymentPlan; + subscription?: Subscription; static tableName = 'usage_data'; @@ -19,6 +21,7 @@ class UsageData extends Base { properties: { id: { type: 'string', format: 'uuid' }, userId: { type: 'string', format: 'uuid' }, + subscriptionId: { type: 'string', format: 'uuid' }, consumedTaskCount: { type: 'integer' }, nextResetAt: { type: 'string' }, }, @@ -33,20 +36,22 @@ class UsageData extends Base { to: 'users.id', }, }, - paymentPlan: { + subscription: { relation: Base.BelongsToOneRelation, - modelClass: PaymentPlan, + modelClass: Subscription, join: { - from: 'usage_data.user_id', - to: 'payment_plans.user_id', + from: 'usage_data.subscription_id', + to: 'subscriptions.id', }, }, }); async checkIfLimitExceeded() { - const paymentPlan = await this.$relatedQuery('paymentPlan'); + const subscription = await this.$relatedQuery('subscription'); - return this.consumedTaskCount >= paymentPlan.taskCount; + const plan = subscription.plan; + + return this.consumedTaskCount >= plan.quota; } async increaseConsumedTaskCountByOne() { diff --git a/packages/backend/src/models/user.ts b/packages/backend/src/models/user.ts index 3815e693..ff55c78d 100644 --- a/packages/backend/src/models/user.ts +++ b/packages/backend/src/models/user.ts @@ -1,15 +1,14 @@ import { QueryContext, ModelOptions } from 'objection'; +import bcrypt from 'bcrypt'; +import crypto from 'crypto'; import { DateTime } from 'luxon'; import appConfig from '../config/app'; import Base from './base'; +import ExtendedQueryBuilder from './query-builder'; import Connection from './connection'; import Flow from './flow'; import Step from './step'; import Execution from './execution'; -import ExecutionStep from './execution-step'; -import bcrypt from 'bcrypt'; -import crypto from 'crypto'; -import PaymentPlan from './payment-plan.ee'; import UsageData from './usage-data.ee'; import Subscription from './subscription.ee'; @@ -26,9 +25,10 @@ class User extends Base { flows?: Flow[]; steps?: Step[]; executions?: Execution[]; - paymentPlan?: PaymentPlan; - usageData?: UsageData; - subscription?: Subscription; + usageData?: UsageData[]; + currentUsageData?: UsageData; + subscriptions?: Subscription[]; + currentSubscription?: Subscription; static tableName = 'users'; @@ -86,29 +86,43 @@ class User extends Base { to: 'executions.flow_id', }, }, - paymentPlan: { - relation: Base.HasOneRelation, - modelClass: PaymentPlan, - join: { - from: 'payment_plans.user_id', - to: 'users.id', - }, - }, usageData: { - relation: Base.HasOneRelation, + relation: Base.HasManyRelation, modelClass: UsageData, join: { from: 'usage_data.user_id', to: 'users.id', }, }, - subscription: { + currentUsageData: { + relation: Base.HasOneRelation, + modelClass: UsageData, + join: { + from: 'usage_data.user_id', + to: 'users.id', + }, + filter(builder: ExtendedQueryBuilder) { + builder.orderBy('created_at', 'desc').first(); + }, + }, + subscriptions: { + relation: Base.HasManyRelation, + modelClass: Subscription, + join: { + from: 'subscriptions.user_id', + to: 'users.id', + }, + }, + currentSubscription: { relation: Base.HasOneRelation, modelClass: Subscription, join: { from: 'subscriptions.user_id', to: 'users.id', }, + filter(builder: ExtendedQueryBuilder) { + builder.orderBy('created_at', 'desc').first(); + }, }, }); @@ -167,6 +181,18 @@ class User extends Base { await this.generateHash(); } } + + async $afterInsert(queryContext: QueryContext) { + await super.$afterInsert(queryContext); + + if (appConfig.isCloud) { + await this.$relatedQuery('usageData').insert({ + userId: this.id, + consumedTaskCount: 0, + nextResetAt: DateTime.now().plus({ days: 30 }).toISODate(), + }); + } + } } export default User; diff --git a/packages/backend/src/routes/paddle.ee.ts b/packages/backend/src/routes/paddle.ee.ts index 46e96b65..3f6aa004 100644 --- a/packages/backend/src/routes/paddle.ee.ts +++ b/packages/backend/src/routes/paddle.ee.ts @@ -6,13 +6,13 @@ const router = Router(); const exposeError = (handler: RequestHandler) => - async (req: IRequest, res: Response, next: NextFunction) => { - try { - await handler(req, res, next); - } catch (err) { - next(err); - } - }; + async (req: IRequest, res: Response, next: NextFunction) => { + try { + await handler(req, res, next); + } catch (err) { + next(err); + } + }; router.post('/webhooks', exposeError(webhooksHandler)); diff --git a/packages/web/src/components/UsageDataInformation/index.ee.tsx b/packages/web/src/components/UsageDataInformation/index.ee.tsx index b2162001..4cd47a2f 100644 --- a/packages/web/src/components/UsageDataInformation/index.ee.tsx +++ b/packages/web/src/components/UsageDataInformation/index.ee.tsx @@ -191,7 +191,8 @@ export default function UsageDataInformation() { - + } From 3bfc428dfe51cbae52bfb66d0374f568ecd14d1a Mon Sep 17 00:00:00 2001 From: Faruk AYDIN Date: Fri, 7 Apr 2023 22:35:56 +0200 Subject: [PATCH 2/4] feat: Add inTrial getter method to User model --- packages/backend/src/models/usage-data.ee.ts | 11 ++++++++++- packages/backend/src/models/user.ts | 13 +++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/packages/backend/src/models/usage-data.ee.ts b/packages/backend/src/models/usage-data.ee.ts index fdf10962..4c01d15d 100644 --- a/packages/backend/src/models/usage-data.ee.ts +++ b/packages/backend/src/models/usage-data.ee.ts @@ -11,6 +11,7 @@ class UsageData extends Base { consumedTaskCount!: number; nextResetAt!: string; subscription?: Subscription; + user?: User; static tableName = 'usage_data'; @@ -47,6 +48,12 @@ class UsageData extends Base { }); async checkIfLimitExceeded() { + const user = await this.$relatedQuery('user'); + + if (user.inTrial) { + return false; + } + const subscription = await this.$relatedQuery('subscription'); const plan = subscription.plan; @@ -55,7 +62,9 @@ class UsageData extends Base { } async increaseConsumedTaskCountByOne() { - return await this.$query().patch({ consumedTaskCount: raw('consumed_task_count + 1') }); + return await this.$query().patch({ + consumedTaskCount: raw('consumed_task_count + 1'), + }); } } diff --git a/packages/backend/src/models/user.ts b/packages/backend/src/models/user.ts index ff55c78d..8e7337ee 100644 --- a/packages/backend/src/models/user.ts +++ b/packages/backend/src/models/user.ts @@ -126,6 +126,19 @@ class User extends Base { }, }); + get inTrial() { + if (!this.trialExpiryDate) { + return false; + } + + const expiryDate = DateTime.fromJSDate( + this.trialExpiryDate as unknown as Date + ); + const now = DateTime.now(); + + return now < expiryDate; + } + login(password: string) { return bcrypt.compare(password, this.password); } From c4dc0509c2ddd7ade20fe682838438ca8a44cf6f Mon Sep 17 00:00:00 2001 From: Faruk AYDIN Date: Fri, 7 Apr 2023 22:55:18 +0200 Subject: [PATCH 3/4] feat: Implement getTrialStatus graphQL query --- .../graphql/queries/get-trial-status.ee.ts | 19 ++++++++++ .../backend/src/graphql/query-resolvers.ts | 2 ++ packages/backend/src/graphql/schema.graphql | 5 +++ .../backend/src/models/subscription.ee.ts | 4 +++ packages/backend/src/models/user.ts | 36 ++++++++++++------- 5 files changed, 53 insertions(+), 13 deletions(-) create mode 100644 packages/backend/src/graphql/queries/get-trial-status.ee.ts diff --git a/packages/backend/src/graphql/queries/get-trial-status.ee.ts b/packages/backend/src/graphql/queries/get-trial-status.ee.ts new file mode 100644 index 00000000..58a8f936 --- /dev/null +++ b/packages/backend/src/graphql/queries/get-trial-status.ee.ts @@ -0,0 +1,19 @@ +import appConfig from '../../config/app'; +import Context from '../../types/express/context'; + +const getTrialStatus = async ( + _parent: unknown, + _params: unknown, + context: Context +) => { + if (!appConfig.isCloud) return; + + const inTrial = await context.currentUser.inTrial(); + if (!inTrial) return; + + return { + expireAt: context.currentUser.trialExpiryDate, + }; +}; + +export default getTrialStatus; diff --git a/packages/backend/src/graphql/query-resolvers.ts b/packages/backend/src/graphql/query-resolvers.ts index b7263799..71e44020 100644 --- a/packages/backend/src/graphql/query-resolvers.ts +++ b/packages/backend/src/graphql/query-resolvers.ts @@ -17,6 +17,7 @@ import getPaddleInfo from './queries/get-paddle-info.ee'; import getBillingAndUsage from './queries/get-billing-and-usage.ee'; import getInvoices from './queries/get-invoices.ee'; import getAutomatischInfo from './queries/get-automatisch-info'; +import getTrialStatus from './queries/get-trial-status.ee'; import healthcheck from './queries/healthcheck'; const queryResolvers = { @@ -39,6 +40,7 @@ const queryResolvers = { getBillingAndUsage, getInvoices, getAutomatischInfo, + getTrialStatus, healthcheck, }; diff --git a/packages/backend/src/graphql/schema.graphql b/packages/backend/src/graphql/schema.graphql index 4dec2ecc..578a06d3 100644 --- a/packages/backend/src/graphql/schema.graphql +++ b/packages/backend/src/graphql/schema.graphql @@ -40,6 +40,7 @@ type Query { getBillingAndUsage: GetBillingAndUsage getInvoices: [Invoice] getAutomatischInfo: GetAutomatischInfo + getTrialStatus: GetTrialStatus healthcheck: AppHealth } @@ -474,6 +475,10 @@ type GetAutomatischInfo { isCloud: Boolean } +type GetTrialStatus { + expireAt: String +} + type GetBillingAndUsage { subscription: Subscription usage: Usage diff --git a/packages/backend/src/models/subscription.ee.ts b/packages/backend/src/models/subscription.ee.ts index 1d639a97..216e5e52 100644 --- a/packages/backend/src/models/subscription.ee.ts +++ b/packages/backend/src/models/subscription.ee.ts @@ -76,6 +76,10 @@ class Subscription extends Base { get plan() { return getPlanById(this.paddlePlanId); } + + get isActive() { + return this.status === 'active' || this.status === 'past_due'; + } } export default Subscription; diff --git a/packages/backend/src/models/user.ts b/packages/backend/src/models/user.ts index 8e7337ee..f83b6c01 100644 --- a/packages/backend/src/models/user.ts +++ b/packages/backend/src/models/user.ts @@ -126,19 +126,6 @@ class User extends Base { }, }); - get inTrial() { - if (!this.trialExpiryDate) { - return false; - } - - const expiryDate = DateTime.fromJSDate( - this.trialExpiryDate as unknown as Date - ); - const now = DateTime.now(); - - return now < expiryDate; - } - login(password: string) { return bcrypt.compare(password, this.password); } @@ -178,6 +165,29 @@ class User extends Base { this.trialExpiryDate = DateTime.now().plus({ days: 30 }).toISODate(); } + async inTrial() { + if (!appConfig.isCloud) { + return false; + } + + if (!this.trialExpiryDate) { + return false; + } + + const subscription = await this.$relatedQuery('currentSubscription'); + + if (subscription?.isActive) { + return false; + } + + const expiryDate = DateTime.fromJSDate( + this.trialExpiryDate as unknown as Date + ); + const now = DateTime.now(); + + return now < expiryDate; + } + async $beforeInsert(queryContext: QueryContext) { await super.$beforeInsert(queryContext); await this.generateHash(); From c8baf9b0d74227638a5bbf6841b03497a45cec4e Mon Sep 17 00:00:00 2001 From: Faruk AYDIN Date: Fri, 7 Apr 2023 23:09:24 +0200 Subject: [PATCH 4/4] feat: Check if user has active subscription or not for execution --- packages/backend/src/models/usage-data.ee.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/backend/src/models/usage-data.ee.ts b/packages/backend/src/models/usage-data.ee.ts index 4c01d15d..047d0bf1 100644 --- a/packages/backend/src/models/usage-data.ee.ts +++ b/packages/backend/src/models/usage-data.ee.ts @@ -50,12 +50,16 @@ class UsageData extends Base { async checkIfLimitExceeded() { const user = await this.$relatedQuery('user'); - if (user.inTrial) { + if (await user.inTrial()) { return false; } const subscription = await this.$relatedQuery('subscription'); + if (!subscription.isActive) { + return true; + } + const plan = subscription.plan; return this.consumedTaskCount >= plan.quota;