mirror of https://github.com/ghostfolio/ghostfolio
Browse Source
* Set up stripe for subscriptions * Update permissions and add discount * Update changelogpull/179/head
Thomas
4 years ago
committed by
GitHub
30 changed files with 453 additions and 81 deletions
@ -0,0 +1,57 @@ |
|||
import { ConfigurationService } from '@ghostfolio/api/services/configuration.service'; |
|||
import { RequestWithUser } from '@ghostfolio/common/types'; |
|||
import { |
|||
Body, |
|||
Controller, |
|||
Get, |
|||
HttpException, |
|||
Inject, |
|||
Post, |
|||
Req, |
|||
Res, |
|||
UseGuards |
|||
} from '@nestjs/common'; |
|||
import { REQUEST } from '@nestjs/core'; |
|||
import { AuthGuard } from '@nestjs/passport'; |
|||
import { StatusCodes, getReasonPhrase } from 'http-status-codes'; |
|||
|
|||
import { SubscriptionService } from './subscription.service'; |
|||
|
|||
@Controller('subscription') |
|||
export class SubscriptionController { |
|||
public constructor( |
|||
private readonly configurationService: ConfigurationService, |
|||
@Inject(REQUEST) private readonly request: RequestWithUser, |
|||
private readonly subscriptionService: SubscriptionService |
|||
) {} |
|||
|
|||
@Get('stripe/callback') |
|||
public async stripeCallback(@Req() req, @Res() res) { |
|||
await this.subscriptionService.createSubscription( |
|||
req.query.checkoutSessionId |
|||
); |
|||
|
|||
res.redirect(`${this.configurationService.get('ROOT_URL')}/account`); |
|||
} |
|||
|
|||
@Post('stripe/checkout-session') |
|||
@UseGuards(AuthGuard('jwt')) |
|||
public async createCheckoutSession( |
|||
@Body() { couponId, priceId }: { couponId: string; priceId: string } |
|||
) { |
|||
try { |
|||
return await this.subscriptionService.createCheckoutSession({ |
|||
couponId, |
|||
priceId, |
|||
userId: this.request.user.id |
|||
}); |
|||
} catch (error) { |
|||
console.error(error); |
|||
|
|||
throw new HttpException( |
|||
getReasonPhrase(StatusCodes.BAD_REQUEST), |
|||
StatusCodes.BAD_REQUEST |
|||
); |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,13 @@ |
|||
import { ConfigurationService } from '@ghostfolio/api/services/configuration.service'; |
|||
import { PrismaService } from '@ghostfolio/api/services/prisma.service'; |
|||
import { Module } from '@nestjs/common'; |
|||
|
|||
import { SubscriptionController } from './subscription.controller'; |
|||
import { SubscriptionService } from './subscription.service'; |
|||
|
|||
@Module({ |
|||
imports: [], |
|||
controllers: [SubscriptionController], |
|||
providers: [ConfigurationService, PrismaService, SubscriptionService] |
|||
}) |
|||
export class SubscriptionModule {} |
@ -0,0 +1,88 @@ |
|||
import { ConfigurationService } from '@ghostfolio/api/services/configuration.service'; |
|||
import { PrismaService } from '@ghostfolio/api/services/prisma.service'; |
|||
import { Injectable } from '@nestjs/common'; |
|||
import { addDays } from 'date-fns'; |
|||
import Stripe from 'stripe'; |
|||
|
|||
@Injectable() |
|||
export class SubscriptionService { |
|||
private stripe: Stripe; |
|||
|
|||
public constructor( |
|||
private readonly configurationService: ConfigurationService, |
|||
private prisma: PrismaService |
|||
) { |
|||
this.stripe = new Stripe( |
|||
this.configurationService.get('STRIPE_SECRET_KEY'), |
|||
{ |
|||
apiVersion: '2020-08-27' |
|||
} |
|||
); |
|||
} |
|||
|
|||
public async createCheckoutSession({ |
|||
couponId, |
|||
priceId, |
|||
userId |
|||
}: { |
|||
couponId?: string; |
|||
priceId: string; |
|||
userId: string; |
|||
}) { |
|||
const checkoutSessionCreateParams: Stripe.Checkout.SessionCreateParams = { |
|||
cancel_url: `${this.configurationService.get('ROOT_URL')}/account`, |
|||
client_reference_id: userId, |
|||
line_items: [ |
|||
{ |
|||
price: priceId, |
|||
quantity: 1 |
|||
} |
|||
], |
|||
metadata: { |
|||
user_id: userId |
|||
}, |
|||
mode: 'subscription', |
|||
payment_method_types: ['card'], |
|||
success_url: `${this.configurationService.get( |
|||
'ROOT_URL' |
|||
)}/api/subscription/stripe/callback?checkoutSessionId={CHECKOUT_SESSION_ID}` |
|||
}; |
|||
|
|||
if (couponId) { |
|||
checkoutSessionCreateParams.discounts = [ |
|||
{ |
|||
coupon: couponId |
|||
} |
|||
]; |
|||
} |
|||
|
|||
const session = await this.stripe.checkout.sessions.create( |
|||
checkoutSessionCreateParams |
|||
); |
|||
|
|||
return { |
|||
sessionId: session.id |
|||
}; |
|||
} |
|||
|
|||
public async createSubscription(aCheckoutSessionId: string) { |
|||
try { |
|||
const session = await this.stripe.checkout.sessions.retrieve( |
|||
aCheckoutSessionId |
|||
); |
|||
|
|||
await this.prisma.subscription.create({ |
|||
data: { |
|||
expiresAt: addDays(new Date(), 365), |
|||
User: { |
|||
connect: { |
|||
id: session.client_reference_id |
|||
} |
|||
} |
|||
} |
|||
}); |
|||
} catch (error) { |
|||
console.error(error); |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,7 @@ |
|||
import { Currency, ViewMode } from '@prisma/client'; |
|||
|
|||
export interface UserSettingsParams { |
|||
currency?: Currency; |
|||
userId: string; |
|||
viewMode?: ViewMode; |
|||
} |
@ -1,5 +1,6 @@ |
|||
export const environment = { |
|||
lastPublish: '{BUILD_TIMESTAMP}', |
|||
production: true, |
|||
stripePublicKey: '{STRIPE_PUBLIC_KEY}', |
|||
version: `v${require('../../../../package.json').version}` |
|||
}; |
|||
|
@ -0,0 +1,6 @@ |
|||
export interface Subscription { |
|||
coupon?: number; |
|||
couponId?: string; |
|||
price: number; |
|||
priceId: string; |
|||
} |
@ -1,7 +1,7 @@ |
|||
import { Currency, ViewMode } from '@prisma/client'; |
|||
|
|||
export interface UserSettings { |
|||
baseCurrency: Currency; |
|||
baseCurrency?: Currency; |
|||
locale: string; |
|||
viewMode: ViewMode; |
|||
viewMode?: ViewMode; |
|||
} |
|||
|
Loading…
Reference in new issue