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 = { |
export const environment = { |
||||
lastPublish: '{BUILD_TIMESTAMP}', |
lastPublish: '{BUILD_TIMESTAMP}', |
||||
production: true, |
production: true, |
||||
|
stripePublicKey: '{STRIPE_PUBLIC_KEY}', |
||||
version: `v${require('../../../../package.json').version}` |
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'; |
import { Currency, ViewMode } from '@prisma/client'; |
||||
|
|
||||
export interface UserSettings { |
export interface UserSettings { |
||||
baseCurrency: Currency; |
baseCurrency?: Currency; |
||||
locale: string; |
locale: string; |
||||
viewMode: ViewMode; |
viewMode?: ViewMode; |
||||
} |
} |
||||
|
Loading…
Reference in new issue