157 lines
4.0 KiB
TypeScript
157 lines
4.0 KiB
TypeScript
export type BillingPlanId = "starter" | "basic" | "pro" | "ultimate";
|
|
export type PaidBillingPlanId = Exclude<BillingPlanId, "starter">;
|
|
|
|
export type BillingPlanDefinition = {
|
|
id: BillingPlanId;
|
|
name: string;
|
|
price: string;
|
|
period: string;
|
|
monthlyImageLimit: number | null;
|
|
cnyFen: number;
|
|
};
|
|
|
|
export const BILLING_PLAN_DEFINITIONS: BillingPlanDefinition[] = [
|
|
{
|
|
id: "starter",
|
|
name: "Starter",
|
|
price: "¥0",
|
|
period: "/month",
|
|
monthlyImageLimit: 200,
|
|
cnyFen: 0,
|
|
},
|
|
{
|
|
id: "basic",
|
|
name: "Basic",
|
|
price: "¥69",
|
|
period: "/month",
|
|
monthlyImageLimit: 1200,
|
|
cnyFen: 6900,
|
|
},
|
|
{
|
|
id: "pro",
|
|
name: "Pro",
|
|
price: "¥199",
|
|
period: "/month",
|
|
monthlyImageLimit: 6000,
|
|
cnyFen: 19900,
|
|
},
|
|
{
|
|
id: "ultimate",
|
|
name: "Ultimate",
|
|
price: "¥599",
|
|
period: "/month",
|
|
monthlyImageLimit: null,
|
|
cnyFen: 59900,
|
|
},
|
|
];
|
|
|
|
const PAID_PLAN_IDS: PaidBillingPlanId[] = ["basic", "pro", "ultimate"];
|
|
|
|
const STRIPE_PRICE_ENV: Record<PaidBillingPlanId, string> = {
|
|
basic: "STRIPE_PRICE_ID_BASIC",
|
|
pro: "STRIPE_PRICE_ID_PRO",
|
|
ultimate: "STRIPE_PRICE_ID_ULTIMATE",
|
|
};
|
|
|
|
export function isBillingPlanId(value: string): value is BillingPlanId {
|
|
return BILLING_PLAN_DEFINITIONS.some((plan) => plan.id === value);
|
|
}
|
|
|
|
export function isPaidBillingPlanId(value: string): value is PaidBillingPlanId {
|
|
return PAID_PLAN_IDS.includes(value as PaidBillingPlanId);
|
|
}
|
|
|
|
export function getBillingPlanById(planId: BillingPlanId) {
|
|
return BILLING_PLAN_DEFINITIONS.find((plan) => plan.id === planId) || null;
|
|
}
|
|
|
|
export function getPaidPlanById(planId: PaidBillingPlanId) {
|
|
return BILLING_PLAN_DEFINITIONS.find((plan) => plan.id === planId) || null;
|
|
}
|
|
|
|
export function getPaidPlanAmountFen(planId: PaidBillingPlanId) {
|
|
return getPaidPlanById(planId)?.cnyFen ?? 0;
|
|
}
|
|
|
|
export function getMonthlyImageLimitByPlanId(planId: BillingPlanId) {
|
|
return getBillingPlanById(planId)?.monthlyImageLimit ?? null;
|
|
}
|
|
|
|
export function resolvePlanByMonthlyImageLimit(limit: number | null | undefined): BillingPlanDefinition {
|
|
const ultimatePlan =
|
|
BILLING_PLAN_DEFINITIONS.find((plan) => plan.id === "ultimate") || BILLING_PLAN_DEFINITIONS[0];
|
|
|
|
if (limit == null) {
|
|
return ultimatePlan;
|
|
}
|
|
|
|
const exactMatch = BILLING_PLAN_DEFINITIONS.find(
|
|
(plan) => plan.monthlyImageLimit != null && plan.monthlyImageLimit === limit
|
|
);
|
|
|
|
if (exactMatch) {
|
|
return exactMatch;
|
|
}
|
|
|
|
const sortedPlans = BILLING_PLAN_DEFINITIONS.filter((plan) => plan.monthlyImageLimit != null).sort(
|
|
(a, b) => (a.monthlyImageLimit ?? 0) - (b.monthlyImageLimit ?? 0)
|
|
);
|
|
|
|
for (const plan of sortedPlans) {
|
|
if ((plan.monthlyImageLimit ?? 0) >= limit) {
|
|
return plan;
|
|
}
|
|
}
|
|
|
|
return ultimatePlan;
|
|
}
|
|
|
|
export function getStripePriceIdForPlan(planId: PaidBillingPlanId) {
|
|
const envName = STRIPE_PRICE_ENV[planId];
|
|
const value = process.env[envName];
|
|
return value && value.trim().length > 0 ? value.trim() : null;
|
|
}
|
|
|
|
export function getCheckoutEnabledPlans() {
|
|
return PAID_PLAN_IDS.reduce<Record<PaidBillingPlanId, boolean>>((acc, planId) => {
|
|
acc[planId] = Boolean(getStripePriceIdForPlan(planId));
|
|
return acc;
|
|
}, {
|
|
basic: false,
|
|
pro: false,
|
|
ultimate: false,
|
|
});
|
|
}
|
|
|
|
function getEnv(name: string) {
|
|
const value = process.env[name];
|
|
return value && value.trim().length > 0 ? value.trim() : null;
|
|
}
|
|
|
|
export function getWechatPayConfig() {
|
|
const appId = getEnv("WECHAT_PAY_APP_ID");
|
|
const mchId = getEnv("WECHAT_PAY_MCH_ID");
|
|
const privateKey = getEnv("WECHAT_PAY_PRIVATE_KEY");
|
|
const serialNo = getEnv("WECHAT_PAY_SERIAL_NO");
|
|
const apiV3Key = getEnv("WECHAT_PAY_API_V3_KEY");
|
|
const platformPublicKey = getEnv("WECHAT_PAY_PLATFORM_PUBLIC_KEY");
|
|
|
|
if (!appId || !mchId || !privateKey || !serialNo || !apiV3Key || !platformPublicKey) {
|
|
return null;
|
|
}
|
|
|
|
return {
|
|
appId,
|
|
mchId,
|
|
privateKey,
|
|
serialNo,
|
|
apiV3Key,
|
|
platformPublicKey,
|
|
notifyUrl: getEnv("WECHAT_PAY_NOTIFY_URL"),
|
|
};
|
|
}
|
|
|
|
export function isWechatPayEnabled() {
|
|
return Boolean(getWechatPayConfig());
|
|
}
|