import { Injectable } from '@nestjs/common'; import { pricing } from '@gitroom/nestjs-libraries/database/prisma/subscriptions/pricing'; import { SubscriptionRepository } from '@gitroom/nestjs-libraries/database/prisma/subscriptions/subscription.repository'; import { IntegrationService } from '@gitroom/nestjs-libraries/database/prisma/integrations/integration.service'; import { OrganizationService } from '@gitroom/nestjs-libraries/database/prisma/organizations/organization.service'; import { Organization } from '@prisma/client'; import dayjs from 'dayjs'; import { makeId } from '@gitroom/nestjs-libraries/services/make.is'; @Injectable() export class SubscriptionService { constructor( private readonly _subscriptionRepository: SubscriptionRepository, private readonly _integrationService: IntegrationService, private readonly _organizationService: OrganizationService ) {} getSubscriptionByOrganizationId(organizationId: string) { return this._subscriptionRepository.getSubscriptionByOrganizationId( organizationId ); } useCredit( organization: Organization, type = 'ai_images', func: () => Promise ): Promise { return this._subscriptionRepository.useCredit(organization, type, func); } getCode(code: string) { return this._subscriptionRepository.getCode(code); } async deleteSubscription(customerId: string) { await this.modifySubscription( customerId, pricing.FREE.channel || 0, 'FREE' ); return this._subscriptionRepository.deleteSubscriptionByCustomerId( customerId ); } updateCustomerId(organizationId: string, customerId: string) { return this._subscriptionRepository.updateCustomerId( organizationId, customerId ); } async checkSubscription(organizationId: string, subscriptionId: string) { return await this._subscriptionRepository.checkSubscription( organizationId, subscriptionId ); } async modifySubscriptionByOrg( organizationId: string, totalChannels: number, billing: 'FREE' | 'STANDARD' | 'TEAM' | 'PRO' | 'ULTIMATE' ) { if (!organizationId) { return false; } const getCurrentSubscription = (await this._subscriptionRepository.getSubscriptionByOrgId( organizationId ))!; const from = pricing[getCurrentSubscription?.subscriptionTier || 'FREE']; const to = pricing[billing]; const currentTotalChannels = ( await this._integrationService.getIntegrationsList(organizationId) ).filter((f) => !f.disabled); if (currentTotalChannels.length > totalChannels) { await this._integrationService.disableIntegrations( organizationId, currentTotalChannels.length - totalChannels ); } if (from.team_members && !to.team_members) { await this._organizationService.disableOrEnableNonSuperAdminUsers( organizationId, true ); } if (!from.team_members && to.team_members) { await this._organizationService.disableOrEnableNonSuperAdminUsers( organizationId, false ); } if (billing === 'FREE') { await this._integrationService.changeActiveCron(organizationId); } return true; } async modifySubscription( customerId: string, totalChannels: number, billing: 'FREE' | 'STANDARD' | 'TEAM' | 'PRO' | 'ULTIMATE' ) { if (!customerId) { return false; } const getOrgByCustomerId = await this._subscriptionRepository.getOrganizationByCustomerId( customerId ); const getCurrentSubscription = (await this._subscriptionRepository.getSubscriptionByCustomerId( customerId ))!; if ( !getOrgByCustomerId || (getCurrentSubscription && getCurrentSubscription?.isLifetime) ) { return false; } const from = pricing[getCurrentSubscription?.subscriptionTier || 'FREE']; const to = pricing[billing]; const currentTotalChannels = ( await this._integrationService.getIntegrationsList( getOrgByCustomerId?.id! ) ).filter((f) => !f.disabled); if (currentTotalChannels.length > totalChannels) { await this._integrationService.disableIntegrations( getOrgByCustomerId?.id!, currentTotalChannels.length - totalChannels ); } if (from.team_members && !to.team_members) { await this._organizationService.disableOrEnableNonSuperAdminUsers( getOrgByCustomerId?.id!, true ); } if (!from.team_members && to.team_members) { await this._organizationService.disableOrEnableNonSuperAdminUsers( getOrgByCustomerId?.id!, false ); } if (billing === 'FREE') { await this._integrationService.changeActiveCron(getOrgByCustomerId?.id!); } return true; } async createOrUpdateSubscription( isTrailing: boolean, identifier: string, customerId: string, totalChannels: number, billing: 'STANDARD' | 'TEAM' | 'PRO' | 'ULTIMATE', period: 'MONTHLY' | 'YEARLY', cancelAt: number | null, code?: string, org?: string ) { if (!code) { try { const load = await this.modifySubscription( customerId, totalChannels, billing ); if (!load) { return {}; } } catch (e) { return {}; } } return this._subscriptionRepository.createOrUpdateSubscription( isTrailing, identifier, customerId, totalChannels, billing, period, cancelAt, code, org ? { id: org } : undefined ); } getSubscriptionByIdentifier(identifier: string) { return this._subscriptionRepository.getSubscriptionByIdentifier(identifier); } async getSubscription(organizationId: string) { return this._subscriptionRepository.getSubscription(organizationId); } async checkCredits(organization: Organization, checkType = 'ai_images') { // @ts-ignore const type = organization?.subscription?.subscriptionTier || 'FREE'; if (type === 'FREE') { return { credits: 0 }; } // @ts-ignore let date = dayjs(organization.subscription.createdAt); while (date.isBefore(dayjs())) { date = date.add(1, 'month'); } const checkFromMonth = date.subtract(1, 'month'); const imageGenerationCount = checkType === 'ai_images' ? pricing[type].image_generation_count : pricing[type].generate_videos; const totalUse = await this._subscriptionRepository.getCreditsFrom( organization.id, checkFromMonth, checkType ); return { credits: imageGenerationCount - totalUse, }; } async addSubscription(orgId: string, userId: string, subscription: any) { await this._subscriptionRepository.setCustomerId(orgId, userId); return this.createOrUpdateSubscription( false, makeId(5), userId, pricing[subscription].channel!, subscription, 'MONTHLY', null, undefined, orgId ); } }