feat(payment): sync plans and add coins rules

This commit is contained in:
2026-06-29 14:11:50 +08:00
parent 274fe87ced
commit e372d58b4c
24 changed files with 1099 additions and 224 deletions
@@ -0,0 +1,76 @@
import { describe, expect, it } from "vitest";
import { z } from "zod";
import { PaymentPlan, PaymentPlansResponse } from "@/data/dto/payment";
describe("PaymentPlan", () => {
it("parses the camelCase payment plan shape", () => {
const plan = PaymentPlan.from({
planId: "vip_monthly",
planName: "VIP Monthly",
orderType: "vip_monthly",
vipDays: 30,
dolAmount: null,
creditBalance: 3000,
amountCents: 1999,
originalAmountCents: 2499,
dailyPriceCents: 66,
currency: "JPY",
});
expect(plan.planId).toBe("vip_monthly");
expect(plan.creditBalance).toBe(3000);
expect(plan.toJson()).toEqual({
planId: "vip_monthly",
planName: "VIP Monthly",
orderType: "vip_monthly",
vipDays: 30,
dolAmount: null,
creditBalance: 3000,
amountCents: 1999,
originalAmountCents: 2499,
dailyPriceCents: 66,
currency: "JPY",
});
});
it("rejects the legacy snake_case payment plan shape", () => {
expect(() =>
PaymentPlan.fromJson({
plan_id: "vip_monthly",
plan_name: "VIP Monthly",
order_type: "vip_monthly",
amount_cents: 1999,
original_amount_cents: 2499,
daily_price_cents: 66,
currency: "JPY",
vip_days: 30,
dol_amount: null,
}),
).toThrow(z.ZodError);
});
});
describe("PaymentPlansResponse", () => {
it("parses plans from the new backend response data shape", () => {
const response = PaymentPlansResponse.from({
plans: [
{
planId: "dol_1000",
planName: "1000 Credits",
orderType: "dol",
vipDays: null,
dolAmount: 1000,
creditBalance: 1000,
amountCents: 990,
originalAmountCents: null,
dailyPriceCents: null,
currency: "USD",
},
],
});
expect(response.plans).toHaveLength(1);
expect(response.plans[0]?.dolAmount).toBe(1000);
});
});