Files
cozsweet-frontend-nextjs/src/app/subscription/subscription-screen.helpers.ts
T

143 lines
4.4 KiB
TypeScript

import type { PaymentPlan } from "@/data/dto/payment";
import type { CoinsOfferPlanView } from "./components/subscription-coins-offer-section";
import type { SubscriptionPlanView } from "./components/subscription-plan-card";
import type { VipOfferPlanView } from "./components/subscription-vip-offer-section";
export type SubscriptionType = "vip" | "voice";
export const SUBSCRIPTION_BANNER_TITLES: Record<SubscriptionType, string> = {
vip: "Subscribe to become the VIP Member",
voice: "Buy Voice Message Package",
};
export function isVipPlan(plan: PaymentPlan): boolean {
return plan.orderType.startsWith("vip_") || plan.planId.startsWith("vip_");
}
export function isCreditPlan(plan: PaymentPlan): boolean {
const orderType = plan.orderType.toLowerCase();
const planId = plan.planId.toLowerCase();
return (
plan.dolAmount !== null ||
orderType.startsWith("dol_") ||
orderType.startsWith("credit_") ||
planId.startsWith("dol_") ||
planId.startsWith("credit_") ||
orderType.includes("credit") ||
planId.includes("credit")
);
}
function isVoicePackagePlan(plan: PaymentPlan): boolean {
const orderType = plan.orderType.toLowerCase();
const planId = plan.planId.toLowerCase();
return (
isCreditPlan(plan) ||
orderType.includes("voice") ||
planId.includes("voice")
);
}
export function isPlanForType(
plan: PaymentPlan,
subscriptionType: SubscriptionType,
): boolean {
return subscriptionType === "voice"
? isVoicePackagePlan(plan)
: isVipPlan(plan);
}
function currencySymbol(currency: string): string {
const normalized = currency.toUpperCase();
if (normalized === "CNY") return "¥";
if (normalized === "USD") return "$";
return `${currency} `;
}
function formatAmount(amountCents: number | null): string | null {
if (amountCents === null) return null;
if (!Number.isFinite(amountCents)) return null;
return (amountCents / 100).toFixed(2).replace(/\.?0+$/, "");
}
function formatOfferAmount(amountCents: number | null): string {
return (formatAmount(amountCents) ?? "").replace(".", ",");
}
function formatOfferCurrency(currency: string): string {
const normalized = currency.toUpperCase();
if (normalized === "USD") return "usd";
return currency.toLowerCase();
}
function formatCoinCurrency(currency: string): string {
const normalized = currency.toUpperCase();
if (normalized === "USD") return "US$";
if (normalized === "CNY") return "¥";
return `${currency} `;
}
function vipOfferTitle(plan: PaymentPlan): string {
const key = `${plan.planId} ${plan.orderType} ${plan.planName}`.toLowerCase();
if (key.includes("year")) return "连续包年";
if (key.includes("quarter")) return "连续包季";
if (key.includes("month")) return "连续包月";
if (plan.vipDays !== null && plan.vipDays >= 300) return "连续包年";
if (plan.vipDays !== null && plan.vipDays >= 80) return "连续包季";
return plan.planName;
}
function planCaption(
plan: PaymentPlan,
subscriptionType: SubscriptionType,
): string {
if (subscriptionType === "voice") {
return plan.dolAmount === null
? "Voice package"
: `${plan.dolAmount} voice messages`;
}
if (plan.dailyPriceCents !== null) {
const dailyPrice = formatAmount(plan.dailyPriceCents);
return dailyPrice === null
? "Lifetime"
: `${currencySymbol(plan.currency)}${dailyPrice}/day`;
}
if (plan.vipDays === null) return "Lifetime";
const fallbackDailyPrice = plan.amountCents / Math.max(plan.vipDays, 1);
return `${currencySymbol(plan.currency)}${formatAmount(fallbackDailyPrice)}/day`;
}
export function toPlanView(
plan: PaymentPlan,
subscriptionType: SubscriptionType,
): SubscriptionPlanView {
return {
id: plan.planId,
name: plan.planName,
price: formatAmount(plan.amountCents) ?? "",
originalPrice: formatAmount(plan.originalAmountCents),
perDay: planCaption(plan, subscriptionType),
currencySymbol: currencySymbol(plan.currency),
};
}
export function toVipOfferPlanView(plan: PaymentPlan): VipOfferPlanView {
return {
id: plan.planId,
title: vipOfferTitle(plan),
price: formatOfferAmount(plan.amountCents),
currency: formatOfferCurrency(plan.currency),
originalPrice: formatOfferAmount(plan.originalAmountCents),
};
}
export function toCoinsOfferPlanView(plan: PaymentPlan): CoinsOfferPlanView {
return {
id: plan.planId,
coins: plan.dolAmount ?? 0,
price: formatOfferAmount(plan.amountCents),
currency: formatCoinCurrency(plan.currency),
};
}