All files / src/billing/subscription subscription.service.ts

100% Statements 31/31
100% Branches 5/5
100% Functions 4/4
100% Lines 28/28

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 972x         2x 2x   2x 2x       2x     7x   7x                     5x     5x       5x 1x       4x       4x     3x 3x     3x           3x       2x                   4x 4x 1x   3x   4x             2x       2x 1x     1x      
import {
  ConflictException,
  Injectable,
  NotFoundException,
} from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
 
import { Subscription } from './entities/subscription.entity';
import { Product } from '../product/entities/product.entity';
import { CreateSubscriptionInput, PeriodType } from './types/index';
 
@Injectable()
export class SubscriptionService {
  constructor(
    @InjectRepository(Subscription)
    private readonly subscriptionRepository: Repository<Subscription>,
    @InjectRepository(Product)
    private readonly productRepository: Repository<Product>,
  ) {}
 
  /**
   * @description 결제 후 실행되는 구독 생성 내부 함수
   * @param input 유저정보, 상품정보, 결제정보
   * @returns
   */
  async createSubscription(
    input: CreateSubscriptionInput,
  ): Promise<Subscription> {
    const { userId, productId, period, paymentId } = input;
 
    // 동일 거래 중복 방지
    const duplicateSubscription = await this.subscriptionRepository.findOne({
      where: { payment: { id: paymentId } } as any,
    });
 
    if (duplicateSubscription) {
      throw new ConflictException('해당 결제의 구독이 이미 생성되었습니다.');
    }
 
    // product 존재 확인
    const product = await this.productRepository.findOne({
      where: { id: productId } as any,
    });
 
    if (!product) throw new NotFoundException('존재하지 않는 상품입니다.');
 
    // 구독의 유효기간 설정
    const startedAt = new Date();
    const expiredAt = this._calculateExpirationDate(startedAt, period);
 
    // 구독 객체 생성
    const subscription = this.subscriptionRepository.create({
      userId,
      productId,
      expiredAt,
    } as any);
 
    const result = (await this.subscriptionRepository.save(
      subscription,
    )) as unknown as Subscription;
 
    return result;
  }
 
  /**
   * 월간/연간에 따라 시작일로부터 만료 날짜 계산해주는 함수
   * @param start : 시작일
   * @param type : MONTHLY / YEARLY
   * @returns 계산된 날짜
   */
  _calculateExpirationDate(start: Date, type: PeriodType) {
    const estimateDate = new Date(start);
    if (type === 'YEARLY') {
      estimateDate.setFullYear(estimateDate.getFullYear() + 1);
    } else {
      estimateDate.setMonth(estimateDate.getMonth() + 1);
    }
    return estimateDate;
  }
 
  /**
   * @description 유저의 현재 구독 정보 확인하는 함수
   */
  async getCurrentSubscription(userId: number) {
    const subscription = await this.subscriptionRepository.findOne({
      where: { user: { id: userId } },
    });
 
    if (!subscription) {
      return null;
    }
 
    return subscription;
  }
}