refactor(subscription): tighten payment flow boundaries

This commit is contained in:
2026-06-30 15:36:49 +08:00
parent 61504050e5
commit 218df59345
9 changed files with 640 additions and 286 deletions
@@ -0,0 +1,149 @@
import { describe, expect, it } from "vitest";
import { PaymentPlan } from "@/data/dto/payment";
import {
findSelectedSubscriptionPlan,
getDefaultSubscriptionPlanId,
toCoinsOfferPlanViews,
toVipOfferPlanViews,
} from "../subscription-screen.helpers";
function makePlan(
overrides: Partial<Parameters<typeof PaymentPlan.from>[0]>,
): PaymentPlan {
return PaymentPlan.from({
planId: "plan",
planName: "Plan",
orderType: "vip_monthly",
vipDays: 30,
dolAmount: null,
creditBalance: 0,
amountCents: 1990,
originalAmountCents: null,
dailyPriceCents: null,
currency: "USD",
...overrides,
});
}
describe("subscription screen helpers", () => {
it("maps at most three VIP plans and all coin plans", () => {
const plans = [
makePlan({ planId: "vip_monthly", planName: "Monthly" }),
makePlan({ planId: "vip_quarterly", planName: "Quarterly", vipDays: 90 }),
makePlan({ planId: "vip_annual", planName: "Annual", vipDays: 365 }),
makePlan({ planId: "vip_extra", planName: "Extra", vipDays: 30 }),
makePlan({
planId: "coin_1000",
planName: "Coins",
orderType: "coins_1000",
vipDays: null,
dolAmount: 1000,
amountCents: 990,
}),
];
expect(toVipOfferPlanViews(plans).map((plan) => plan.id)).toEqual([
"vip_monthly",
"vip_quarterly",
"vip_annual",
]);
expect(toCoinsOfferPlanViews(plans)).toMatchObject([
{
id: "coin_1000",
coins: 1000,
price: "9,9",
currency: "US$",
},
]);
});
it("finds selected coin plans in VIP mode", () => {
const selected = findSelectedSubscriptionPlan({
canSubscribeVip: true,
selectedPlanId: "coin_1000",
vipPlans: [
{
id: "vip_monthly",
title: "Monthly",
price: "19,9",
currency: "usd",
originalPrice: "",
},
],
coinPlans: [{ id: "coin_1000", coins: 1000, price: "9,9", currency: "US$" }],
});
expect(selected?.id).toBe("coin_1000");
});
it("selects the first VIP plan by default when VIP can be purchased", () => {
expect(
getDefaultSubscriptionPlanId({
canSubscribeVip: true,
selectedPlanId: "",
status: "ready",
isLoadingPlans: false,
selectedPlan: null,
vipPlans: [
{
id: "vip_monthly",
title: "Monthly",
price: "19,9",
currency: "usd",
originalPrice: "",
},
],
coinPlans: [],
}),
).toBe("vip_monthly");
});
it("selects the first coin plan by default in top-up-only mode", () => {
expect(
getDefaultSubscriptionPlanId({
canSubscribeVip: false,
selectedPlanId: "",
status: "ready",
isLoadingPlans: false,
selectedPlan: null,
vipPlans: [],
coinPlans: [
{
id: "coin_1000",
coins: 1000,
price: "9,9",
currency: "US$",
},
],
}),
).toBe("coin_1000");
});
it("does not override an existing selected plan", () => {
expect(
getDefaultSubscriptionPlanId({
canSubscribeVip: false,
selectedPlanId: "coin_1000",
status: "ready",
isLoadingPlans: false,
selectedPlan: {
id: "coin_1000",
coins: 1000,
price: "9,9",
currency: "US$",
},
vipPlans: [],
coinPlans: [
{
id: "coin_1000",
coins: 1000,
price: "9,9",
currency: "US$",
},
],
}),
).toBeNull();
});
});