mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-08-05 19:40:30 +08:00
feat(server): add lifetime plan flag (#7443)
This commit is contained in:
@@ -1,6 +1,10 @@
|
|||||||
import type { Stripe } from 'stripe';
|
import type { Stripe } from 'stripe';
|
||||||
|
|
||||||
import { defineStartupConfig, ModuleConfig } from '../../fundamentals/config';
|
import {
|
||||||
|
defineRuntimeConfig,
|
||||||
|
defineStartupConfig,
|
||||||
|
ModuleConfig,
|
||||||
|
} from '../../fundamentals/config';
|
||||||
|
|
||||||
export interface PaymentStartupConfig {
|
export interface PaymentStartupConfig {
|
||||||
stripe?: {
|
stripe?: {
|
||||||
@@ -11,10 +15,20 @@ export interface PaymentStartupConfig {
|
|||||||
} & Stripe.StripeConfig;
|
} & Stripe.StripeConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface PaymentRuntimeConfig {
|
||||||
|
showLifetimePrice: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
declare module '../config' {
|
declare module '../config' {
|
||||||
interface PluginsConfig {
|
interface PluginsConfig {
|
||||||
payment: ModuleConfig<PaymentStartupConfig>;
|
payment: ModuleConfig<PaymentStartupConfig, PaymentRuntimeConfig>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
defineStartupConfig('plugins.payment', {});
|
defineStartupConfig('plugins.payment', {});
|
||||||
|
defineRuntimeConfig('plugins.payment', {
|
||||||
|
showLifetimePrice: {
|
||||||
|
desc: 'Whether enable lifetime price and allow user to pay for it.',
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|||||||
@@ -121,8 +121,14 @@ export class SubscriptionService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const lifetimePriceEnabled = await this.config.runtime.fetch(
|
||||||
|
'plugins.payment/showLifetimePrice'
|
||||||
|
);
|
||||||
|
|
||||||
const list = await this.stripe.prices.list({
|
const list = await this.stripe.prices.list({
|
||||||
active: true,
|
active: true,
|
||||||
|
// only list recurring prices if lifetime price is not enabled
|
||||||
|
...(lifetimePriceEnabled ? {} : { type: 'recurring' }),
|
||||||
});
|
});
|
||||||
|
|
||||||
return list.data.filter(price => {
|
return list.data.filter(price => {
|
||||||
@@ -807,6 +813,16 @@ export class SubscriptionService {
|
|||||||
recurring: SubscriptionRecurring,
|
recurring: SubscriptionRecurring,
|
||||||
variant?: SubscriptionPriceVariant
|
variant?: SubscriptionPriceVariant
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
|
if (recurring === SubscriptionRecurring.Lifetime) {
|
||||||
|
const lifetimePriceEnabled = await this.config.runtime.fetch(
|
||||||
|
'plugins.payment/showLifetimePrice'
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!lifetimePriceEnabled) {
|
||||||
|
throw new ActionForbidden();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const prices = await this.stripe.prices.list({
|
const prices = await this.stripe.prices.list({
|
||||||
lookup_keys: [encodeLookupKey(plan, recurring, variant)],
|
lookup_keys: [encodeLookupKey(plan, recurring, variant)],
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import {
|
|||||||
FeatureManagementService,
|
FeatureManagementService,
|
||||||
} from '../../src/core/features';
|
} from '../../src/core/features';
|
||||||
import { EventEmitter } from '../../src/fundamentals';
|
import { EventEmitter } from '../../src/fundamentals';
|
||||||
import { ConfigModule } from '../../src/fundamentals/config';
|
import { Config, ConfigModule } from '../../src/fundamentals/config';
|
||||||
import {
|
import {
|
||||||
CouponType,
|
CouponType,
|
||||||
encodeLookupKey,
|
encodeLookupKey,
|
||||||
@@ -969,6 +969,45 @@ const invoice: Stripe.Invoice = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
test('should not be able to checkout for lifetime recurring if not enabled', async t => {
|
||||||
|
const { service, stripe, u1 } = t.context;
|
||||||
|
|
||||||
|
Sinon.stub(stripe.subscriptions, 'list').resolves({ data: [] } as any);
|
||||||
|
await t.throwsAsync(
|
||||||
|
() =>
|
||||||
|
service.createCheckoutSession({
|
||||||
|
user: u1,
|
||||||
|
plan: SubscriptionPlan.Pro,
|
||||||
|
recurring: SubscriptionRecurring.Lifetime,
|
||||||
|
redirectUrl: '',
|
||||||
|
idempotencyKey: '',
|
||||||
|
}),
|
||||||
|
{ message: 'You are not allowed to perform this action.' }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should be able to checkout for lifetime recurring', async t => {
|
||||||
|
const { service, stripe, u1, app } = t.context;
|
||||||
|
const config = app.get(Config);
|
||||||
|
await config.runtime.set('plugins.payment/showLifetimePrice', true);
|
||||||
|
|
||||||
|
Sinon.stub(stripe.subscriptions, 'list').resolves({ data: [] } as any);
|
||||||
|
Sinon.stub(stripe.prices, 'list').resolves({
|
||||||
|
data: [PRICES[PRO_LIFETIME]],
|
||||||
|
} as any);
|
||||||
|
const sessionStub = Sinon.stub(stripe.checkout.sessions, 'create');
|
||||||
|
|
||||||
|
await service.createCheckoutSession({
|
||||||
|
user: u1,
|
||||||
|
plan: SubscriptionPlan.Pro,
|
||||||
|
recurring: SubscriptionRecurring.Lifetime,
|
||||||
|
redirectUrl: '',
|
||||||
|
idempotencyKey: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
t.true(sessionStub.calledOnce);
|
||||||
|
});
|
||||||
|
|
||||||
test('should be able to subscribe to lifetime recurring', async t => {
|
test('should be able to subscribe to lifetime recurring', async t => {
|
||||||
// lifetime payment isn't a subscription, so we need to trigger the creation by invoice payment event
|
// lifetime payment isn't a subscription, so we need to trigger the creation by invoice payment event
|
||||||
const { service, stripe, db, u1, event } = t.context;
|
const { service, stripe, db, u1, event } = t.context;
|
||||||
|
|||||||
Reference in New Issue
Block a user