import { HasPermissionGuard } from '@ghostfolio/api/guards/has-permission.guard'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { PropertyService } from '@ghostfolio/api/services/property/property.service'; import { DEFAULT_LANGUAGE_CODE, PROPERTY_COUPONS } from '@ghostfolio/common/config'; import { Coupon } from '@ghostfolio/common/interfaces'; import type { RequestWithUser } from '@ghostfolio/common/types'; import { Body, Controller, Get, HttpCode, HttpException, Inject, Logger, Post, Req, Res, UseGuards } from '@nestjs/common'; import { REQUEST } from '@nestjs/core'; import { AuthGuard } from '@nestjs/passport'; import { Request, Response } from 'express'; import { StatusCodes, getReasonPhrase } from 'http-status-codes'; import { SubscriptionService } from './subscription.service'; @Controller('subscription') export class SubscriptionController { public constructor( private readonly configurationService: ConfigurationService, private readonly propertyService: PropertyService, @Inject(REQUEST) private readonly request: RequestWithUser, private readonly subscriptionService: SubscriptionService ) {} @Post('redeem-coupon') @HttpCode(StatusCodes.OK) @UseGuards(AuthGuard('jwt'), HasPermissionGuard) public async redeemCoupon(@Body() { couponCode }: { couponCode: string }) { if (!this.request.user) { throw new HttpException( getReasonPhrase(StatusCodes.FORBIDDEN), StatusCodes.FORBIDDEN ); } let coupons = ((await this.propertyService.getByKey(PROPERTY_COUPONS)) as Coupon[]) ?? []; const coupon = coupons.find((currentCoupon) => { return currentCoupon.code === couponCode; }); if (coupon === undefined) { throw new HttpException( getReasonPhrase(StatusCodes.BAD_REQUEST), StatusCodes.BAD_REQUEST ); } await this.subscriptionService.createSubscription({ duration: coupon.duration, price: 0, userId: this.request.user.id }); // Destroy coupon coupons = coupons.filter((currentCoupon) => { return currentCoupon.code !== couponCode; }); await this.propertyService.put({ key: PROPERTY_COUPONS, value: JSON.stringify(coupons) }); Logger.log( `Subscription for user '${this.request.user.id}' has been created with a coupon for ${coupon.duration}`, 'SubscriptionController' ); return { message: getReasonPhrase(StatusCodes.OK), statusCode: StatusCodes.OK }; } @Get('stripe/callback') public async stripeCallback( @Req() request: Request, @Res() response: Response ) { const userId = await this.subscriptionService.createSubscriptionViaStripe( request.query.checkoutSessionId ); Logger.log( `Subscription for user '${userId}' has been created via Stripe`, 'SubscriptionController' ); response.redirect( `${this.configurationService.get( 'ROOT_URL' )}/${DEFAULT_LANGUAGE_CODE}/account/membership` ); } @Post('stripe/checkout-session') @UseGuards(AuthGuard('jwt'), HasPermissionGuard) public async createCheckoutSession( @Body() { couponId, priceId }: { couponId: string; priceId: string } ) { try { return await this.subscriptionService.createCheckoutSession({ couponId, priceId, user: this.request.user }); } catch (error) { Logger.error(error, 'SubscriptionController'); throw new HttpException( getReasonPhrase(StatusCodes.BAD_REQUEST), StatusCodes.BAD_REQUEST ); } } }