116 lines
3.0 KiB
TypeScript
116 lines
3.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;
|
|
};
|
|
|
|
export const BILLING_PLAN_DEFINITIONS: BillingPlanDefinition[] = [
|
|
{
|
|
id: "starter",
|
|
name: "Starter",
|
|
price: "¥0",
|
|
period: "/month",
|
|
monthlyImageLimit: 200,
|
|
},
|
|
{
|
|
id: "basic",
|
|
name: "Basic",
|
|
price: "¥69",
|
|
period: "/month",
|
|
monthlyImageLimit: 1200,
|
|
},
|
|
{
|
|
id: "pro",
|
|
name: "Pro",
|
|
price: "¥199",
|
|
period: "/month",
|
|
monthlyImageLimit: 6000,
|
|
},
|
|
{
|
|
id: "ultimate",
|
|
name: "Ultimate",
|
|
price: "¥599",
|
|
period: "/month",
|
|
monthlyImageLimit: null,
|
|
},
|
|
];
|
|
|
|
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 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,
|
|
});
|
|
}
|