import { Injectable } from '@nestjs/common' import { isNil } from 'lodash' import { PlanNotFoundError } from '../../../../errors/plan-not-found.error' import { CancellationsReasons, Patient, PaymentProviders, Plan, Subscription } from '../../../../models' import { PlanRepository } from '../../../../repositories/plan.repository' import { GoogleInAppSubscription, GoogleInAppSubscriptionService, SubscriptionService } from '../../../../services' import { CheckSubscription } from './check-subscription' @Injectable() export class GoogleCheckSubscription implements CheckSubscription { private plansCached: { [key: string]: Plan } = {} private get packageName(): string { return globalThis.secret.app_package_ids['default'].google } constructor( private readonly subscriptionService: SubscriptionService, private readonly planRepository: PlanRepository, private readonly googleInAppSubscription: GoogleInAppSubscriptionService, ) {} async check(patient: Patient): Promise { const { subscription } = patient if (isNil(subscription)) return false const planCode = await this.getPlanCode(subscription.planId) const token = await this.subscriptionService.getToken(subscription) const googleInfo = await this.googleInAppSubscription.get(token, planCode, this.packageName) await this.checkIfNeedsCancel(patient, googleInfo) await this.checkIfNeedsUncancel(patient, googleInfo) return patient.subscriber } private async checkIfNeedsCancel(patient: Patient, googleInfo: GoogleInAppSubscription): Promise { if (!patient.subscriber) return const { subscription } = patient const expirationGoogleInApp = googleInfo?.expiryTimeMillis && new Date(+googleInfo?.expiryTimeMillis) await this.updateExpiresDate(subscription, expirationGoogleInApp) if (subscription?.expiresAt >= new Date()) return subscription.cancellationAt = new Date() await this.subscriptionService.cancelSubscription({ patient, cancellationReason: { id: CancellationsReasons.BY_PROVIDER }, cancellationBy: { id: patient.id }, expiresAt: subscription?.expiresAt, }) } private async checkIfNeedsUncancel(patient: Patient, googleInfo: GoogleInAppSubscription): Promise { if (patient.subscriber) return if (isNil(googleInfo)) return const { subscription } = patient const expirationGoogleInApp = googleInfo?.expiryTimeMillis && new Date(+googleInfo?.expiryTimeMillis) if (expirationGoogleInApp < new Date()) return subscription.expiresAt = expirationGoogleInApp await this.subscriptionService.uncancel(subscription) } private async updateExpiresDate(subscription: Subscription, expirationGoogleInApp: Date): Promise { if (!isNil(subscription?.expiresAt)) return if (isNil(expirationGoogleInApp)) return await this.subscriptionService.updateExpiresDate(subscription, expirationGoogleInApp) subscription.expiresAt = expirationGoogleInApp } private async getPlanCode(id: string): Promise { return (await this.getPlan(id)).getPaymentProvider(PaymentProviders.GOOGLE).code } private async getPlan(id: string): Promise { const plan = (this.plansCached[id] = this.plansCached[id] ?? (await this.planRepository.get(id))) if (isNil(plan)) throw new PlanNotFoundError(id) return plan } }