|
|
@ -17,7 +17,7 @@ import { |
|
|
} from '@ghostfolio/common/types'; |
|
|
} from '@ghostfolio/common/types'; |
|
|
|
|
|
|
|
|
import { Injectable, Logger } from '@nestjs/common'; |
|
|
import { Injectable, Logger } from '@nestjs/common'; |
|
|
import { Subscription } from '@prisma/client'; |
|
|
import { Prisma, Subscription } from '@prisma/client'; |
|
|
import { addMilliseconds, isBefore } from 'date-fns'; |
|
|
import { addMilliseconds, isBefore } from 'date-fns'; |
|
|
import ms, { StringValue } from 'ms'; |
|
|
import ms, { StringValue } from 'ms'; |
|
|
import Stripe from 'stripe'; |
|
|
import Stripe from 'stripe'; |
|
|
@ -108,11 +108,13 @@ export class SubscriptionService { |
|
|
duration = '1 year', |
|
|
duration = '1 year', |
|
|
durationExtension, |
|
|
durationExtension, |
|
|
price, |
|
|
price, |
|
|
|
|
|
stripeCheckoutSessionId, |
|
|
userId |
|
|
userId |
|
|
}: { |
|
|
}: { |
|
|
duration?: StringValue; |
|
|
duration?: StringValue; |
|
|
durationExtension?: StringValue; |
|
|
durationExtension?: StringValue; |
|
|
price: number; |
|
|
price: number; |
|
|
|
|
|
stripeCheckoutSessionId?: string; |
|
|
userId: string; |
|
|
userId: string; |
|
|
}) { |
|
|
}) { |
|
|
let expiresAt = addMilliseconds(new Date(), ms(duration)); |
|
|
let expiresAt = addMilliseconds(new Date(), ms(duration)); |
|
|
@ -125,6 +127,7 @@ export class SubscriptionService { |
|
|
data: { |
|
|
data: { |
|
|
expiresAt, |
|
|
expiresAt, |
|
|
price, |
|
|
price, |
|
|
|
|
|
stripeCheckoutSessionId, |
|
|
user: { |
|
|
user: { |
|
|
connect: { |
|
|
connect: { |
|
|
id: userId |
|
|
id: userId |
|
|
@ -136,25 +139,42 @@ export class SubscriptionService { |
|
|
|
|
|
|
|
|
public async createSubscriptionViaStripe(aCheckoutSessionId: string) { |
|
|
public async createSubscriptionViaStripe(aCheckoutSessionId: string) { |
|
|
try { |
|
|
try { |
|
|
let durationExtension: StringValue; |
|
|
|
|
|
|
|
|
|
|
|
const session = |
|
|
const session = |
|
|
await this.stripe.checkout.sessions.retrieve(aCheckoutSessionId); |
|
|
await this.stripe.checkout.sessions.retrieve(aCheckoutSessionId); |
|
|
|
|
|
|
|
|
|
|
|
if (session.payment_status !== 'paid' || session.status !== 'complete') { |
|
|
|
|
|
throw new Error( |
|
|
|
|
|
`Stripe Checkout Session '${aCheckoutSessionId}' has not been paid (status=${session.status}, payment_status=${session.payment_status})` |
|
|
|
|
|
); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
const subscriptionOffer: SubscriptionOffer = JSON.parse( |
|
|
const subscriptionOffer: SubscriptionOffer = JSON.parse( |
|
|
session.metadata.subscriptionOffer ?? '{}' |
|
|
session.metadata.subscriptionOffer ?? '{}' |
|
|
); |
|
|
); |
|
|
|
|
|
|
|
|
if (subscriptionOffer) { |
|
|
const durationExtension = subscriptionOffer?.durationExtension; |
|
|
durationExtension = subscriptionOffer.durationExtension; |
|
|
|
|
|
|
|
|
try { |
|
|
|
|
|
await this.createSubscription({ |
|
|
|
|
|
durationExtension, |
|
|
|
|
|
price: session.amount_total / 100, |
|
|
|
|
|
stripeCheckoutSessionId: session.id, |
|
|
|
|
|
userId: session.client_reference_id |
|
|
|
|
|
}); |
|
|
|
|
|
} catch (error) { |
|
|
|
|
|
if ( |
|
|
|
|
|
error instanceof Prisma.PrismaClientKnownRequestError && |
|
|
|
|
|
error.code === 'P2002' |
|
|
|
|
|
) { |
|
|
|
|
|
Logger.log( |
|
|
|
|
|
`Stripe Checkout Session '${session.id}' has already been redeemed`, |
|
|
|
|
|
'SubscriptionService' |
|
|
|
|
|
); |
|
|
|
|
|
} else { |
|
|
|
|
|
throw error; |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
await this.createSubscription({ |
|
|
|
|
|
durationExtension, |
|
|
|
|
|
price: session.amount_total / 100, |
|
|
|
|
|
userId: session.client_reference_id |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
return session.client_reference_id; |
|
|
return session.client_reference_id; |
|
|
} catch (error) { |
|
|
} catch (error) { |
|
|
Logger.error(error, 'SubscriptionService'); |
|
|
Logger.error(error, 'SubscriptionService'); |
|
|
|