import { Injectable } from '@nestjs/common' import { isEmpty, isNil } from 'lodash' import { NotFoundError, UnprocessableEntityError } from '../../../errors' import { CancellationsReasons, Patient, PaymentProviders, Subscription } from '../../../models' import { SubscriptionRepository } from '../../../repositories' import { SubscriptionService, GoogleInAppSubscriptionService } from '../../../services' import { GoogleInAppStatus, GoogleInAppNotification, InAppStatus } from './types' const CANCELLATION_STATUS: Array = ['canceled', 'expired', 'revoked'] @Injectable() export class GoogleInApp { constructor( private readonly subscriptionRepository: SubscriptionRepository, private readonly subscriptionService: SubscriptionService, private readonly googleInAppSubscriptionService: GoogleInAppSubscriptionService, ) {} async handle({ subscriptionNotification, packageName }: GoogleInAppNotification): Promise { const status: InAppStatus = GoogleInAppStatus[subscriptionNotification.notificationType] if (!status) throw new UnprocessableEntityError('There is no status for this notification type') const subscription = await this.subscriptionRepository.getByTokenAndPaymentProvider( subscriptionNotification.purchaseToken, PaymentProviders.GOOGLE, ) if (!subscription) throw new NotFoundError('There is no subscription with that token and payment provider') if (CANCELLATION_STATUS.includes(status)) return this.handleCancellation(subscription, packageName, subscriptionNotification.subscriptionId, status) } private async handleCancellation( subscription: Subscription, packageName: string, planId: string, status: InAppStatus, ): Promise { if (status === 'expired' && isNil(subscription.cancellationRequestAt)) return const googleSubscription = await this.googleInAppSubscriptionService.get(subscription.token, planId, packageName) if (!googleSubscription) throw new NotFoundError('There is no Google`s subscription for this token') if (isNil(subscription.expiresAt)) subscription.expiresAt = new Date(+googleSubscription.expiryTimeMillis) if (status === 'expired' || status === 'revoked') subscription.cancellationAt = new Date(+googleSubscription.expiryTimeMillis) if (isNil(subscription.cancellationRequestAt) || isEmpty(subscription.cancellationRequestAt)) subscription.cancellationRequestAt = new Date(+googleSubscription.userCancellationTimeMillis) return this.subscriptionService.cancelSubscription({ patient: new Patient({ id: subscription.patientId, subscription }), cancellationReason: { id: CancellationsReasons.IN_APP_CANCELLED, }, cancellationBy: { id: subscription.patientId }, expiresAt: subscription.expiresAt, }) } }