mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-08-08 12:45:55 +08:00
fix(server): avoid error when other prices added but logic is not released (#6191)
This commit is contained in:
@@ -1,8 +1,4 @@
|
|||||||
import {
|
import { BadGatewayException, ForbiddenException } from '@nestjs/common';
|
||||||
BadGatewayException,
|
|
||||||
ForbiddenException,
|
|
||||||
InternalServerErrorException,
|
|
||||||
} from '@nestjs/common';
|
|
||||||
import {
|
import {
|
||||||
Args,
|
Args,
|
||||||
Context,
|
Context,
|
||||||
@@ -48,11 +44,11 @@ class SubscriptionPrice {
|
|||||||
@Field()
|
@Field()
|
||||||
currency!: string;
|
currency!: string;
|
||||||
|
|
||||||
@Field()
|
@Field(() => Int, { nullable: true })
|
||||||
amount!: number;
|
amount?: number | null;
|
||||||
|
|
||||||
@Field()
|
@Field(() => Int, { nullable: true })
|
||||||
yearlyAmount!: number;
|
yearlyAmount?: number | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ObjectType('UserSubscription')
|
@ObjectType('UserSubscription')
|
||||||
@@ -176,64 +172,39 @@ export class SubscriptionResolver {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
return Object.entries(group).map(([plan, prices]) => {
|
function findPrice(plan: SubscriptionPlan) {
|
||||||
const yearly = prices.find(
|
const prices = group[plan];
|
||||||
price =>
|
|
||||||
decodeLookupKey(
|
|
||||||
// @ts-expect-error empty lookup key is filtered out
|
|
||||||
price.lookup_key
|
|
||||||
)[1] === SubscriptionRecurring.Yearly
|
|
||||||
);
|
|
||||||
const monthly = prices.find(
|
|
||||||
price =>
|
|
||||||
decodeLookupKey(
|
|
||||||
// @ts-expect-error empty lookup key is filtered out
|
|
||||||
price.lookup_key
|
|
||||||
)[1] === SubscriptionRecurring.Monthly
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!yearly || !monthly) {
|
if (!prices) {
|
||||||
throw new InternalServerErrorException(
|
return null;
|
||||||
'The prices are not configured correctly.'
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const monthlyPrice = prices.find(p => p.recurring?.interval === 'month');
|
||||||
|
const yearlyPrice = prices.find(p => p.recurring?.interval === 'year');
|
||||||
|
const currency = monthlyPrice?.currency ?? yearlyPrice?.currency ?? 'usd';
|
||||||
return {
|
return {
|
||||||
type: 'fixed',
|
currency,
|
||||||
plan: plan as SubscriptionPlan,
|
amount: monthlyPrice?.unit_amount,
|
||||||
currency: monthly.currency,
|
yearlyAmount: yearlyPrice?.unit_amount,
|
||||||
amount: monthly.unit_amount ?? 0,
|
|
||||||
yearlyAmount: yearly.unit_amount ?? 0,
|
|
||||||
};
|
};
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated
|
|
||||||
*/
|
|
||||||
@Mutation(() => String, {
|
|
||||||
deprecationReason: 'use `createCheckoutSession` instead',
|
|
||||||
description: 'Create a subscription checkout link of stripe',
|
|
||||||
})
|
|
||||||
async checkout(
|
|
||||||
@CurrentUser() user: CurrentUser,
|
|
||||||
@Args({ name: 'recurring', type: () => SubscriptionRecurring })
|
|
||||||
recurring: SubscriptionRecurring,
|
|
||||||
@Args('idempotencyKey') idempotencyKey: string
|
|
||||||
) {
|
|
||||||
const session = await this.service.createCheckoutSession({
|
|
||||||
user,
|
|
||||||
plan: SubscriptionPlan.Pro,
|
|
||||||
recurring,
|
|
||||||
redirectUrl: `${this.config.baseUrl}/upgrade-success`,
|
|
||||||
idempotencyKey,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!session.url) {
|
|
||||||
throw new BadGatewayException('Failed to create checkout session.');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return session.url;
|
// extend it when new plans are added
|
||||||
|
const fixedPlans = [SubscriptionPlan.Pro];
|
||||||
|
|
||||||
|
return fixedPlans.reduce((prices, plan) => {
|
||||||
|
const price = findPrice(plan);
|
||||||
|
|
||||||
|
if (price && (price.amount || price.yearlyAmount)) {
|
||||||
|
prices.push({
|
||||||
|
type: 'fixed',
|
||||||
|
plan,
|
||||||
|
...price,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return prices;
|
||||||
|
}, [] as SubscriptionPrice[]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Mutation(() => String, {
|
@Mutation(() => String, {
|
||||||
|
|||||||
@@ -65,7 +65,9 @@ export class SubscriptionService {
|
|||||||
) {}
|
) {}
|
||||||
|
|
||||||
async listPrices() {
|
async listPrices() {
|
||||||
return this.stripe.prices.list();
|
return this.stripe.prices.list({
|
||||||
|
active: true,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async createCheckoutSession({
|
async createCheckoutSession({
|
||||||
|
|||||||
@@ -114,9 +114,6 @@ type Mutation {
|
|||||||
changeEmail(email: String!, token: String!): UserType!
|
changeEmail(email: String!, token: String!): UserType!
|
||||||
changePassword(newPassword: String!, token: String!): UserType!
|
changePassword(newPassword: String!, token: String!): UserType!
|
||||||
|
|
||||||
"""Create a subscription checkout link of stripe"""
|
|
||||||
checkout(idempotencyKey: String!, recurring: SubscriptionRecurring!): String! @deprecated(reason: "use `createCheckoutSession` instead")
|
|
||||||
|
|
||||||
"""Create a subscription checkout link of stripe"""
|
"""Create a subscription checkout link of stripe"""
|
||||||
createCheckoutSession(input: CreateCheckoutSessionInput!): String!
|
createCheckoutSession(input: CreateCheckoutSessionInput!): String!
|
||||||
|
|
||||||
@@ -275,11 +272,11 @@ enum SubscriptionPlan {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type SubscriptionPrice {
|
type SubscriptionPrice {
|
||||||
amount: Int!
|
amount: Int
|
||||||
currency: String!
|
currency: String!
|
||||||
plan: SubscriptionPlan!
|
plan: SubscriptionPlan!
|
||||||
type: String!
|
type: String!
|
||||||
yearlyAmount: Int!
|
yearlyAmount: Int
|
||||||
}
|
}
|
||||||
|
|
||||||
enum SubscriptionRecurring {
|
enum SubscriptionRecurring {
|
||||||
|
|||||||
+2
-2
@@ -108,8 +108,8 @@ const SubscriptionSettings = () => {
|
|||||||
? '0'
|
? '0'
|
||||||
: price
|
: price
|
||||||
? recurring === SubscriptionRecurring.Monthly
|
? recurring === SubscriptionRecurring.Monthly
|
||||||
? String(price.amount / 100)
|
? String((price.amount ?? 0) / 100)
|
||||||
: String(price.yearlyAmount / 100)
|
: String((price.yearlyAmount ?? 0) / 100)
|
||||||
: '?';
|
: '?';
|
||||||
|
|
||||||
const t = useAFFiNEI18N();
|
const t = useAFFiNEI18N();
|
||||||
|
|||||||
+8
-5
@@ -51,11 +51,14 @@ const Settings = () => {
|
|||||||
const detail = planDetail.get(price.plan);
|
const detail = planDetail.get(price.plan);
|
||||||
|
|
||||||
if (detail?.type === 'fixed') {
|
if (detail?.type === 'fixed') {
|
||||||
detail.price = (price.amount / 100).toFixed(2);
|
detail.price = ((price.amount ?? 0) / 100).toFixed(2);
|
||||||
detail.yearlyPrice = (price.yearlyAmount / 100 / 12).toFixed(2);
|
detail.yearlyPrice = ((price.yearlyAmount ?? 0) / 100 / 12).toFixed(2);
|
||||||
detail.discount = Math.floor(
|
detail.discount =
|
||||||
(1 - price.yearlyAmount / 12 / price.amount) * 100
|
price.yearlyAmount && price.amount
|
||||||
).toString();
|
? Math.floor(
|
||||||
|
(1 - price.yearlyAmount / 12 / price.amount) * 100
|
||||||
|
).toString()
|
||||||
|
: undefined;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -538,8 +538,8 @@ export type PricesQuery = {
|
|||||||
type: string;
|
type: string;
|
||||||
plan: SubscriptionPlan;
|
plan: SubscriptionPlan;
|
||||||
currency: string;
|
currency: string;
|
||||||
amount: number;
|
amount: number | null;
|
||||||
yearlyAmount: number;
|
yearlyAmount: number | null;
|
||||||
}>;
|
}>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user