feat(payment): implement first recharge offer functionality and UI updates
This commit is contained in:
@@ -31,6 +31,9 @@ describe("PaymentPlan", () => {
|
||||
originalAmountCents: 2499,
|
||||
dailyPriceCents: 66,
|
||||
currency: "JPY",
|
||||
isFirstRechargeOffer: false,
|
||||
firstRechargeDiscountPercent: null,
|
||||
promotionType: null,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -73,4 +76,42 @@ describe("PaymentPlansResponse", () => {
|
||||
expect(response.plans).toHaveLength(1);
|
||||
expect(response.plans[0]?.dolAmount).toBe(1000);
|
||||
});
|
||||
|
||||
it("parses first recharge offer metadata", () => {
|
||||
const response = PaymentPlansResponse.from({
|
||||
isFirstRecharge: true,
|
||||
firstRechargeOffer: {
|
||||
enabled: true,
|
||||
type: "first_recharge_half_price",
|
||||
discountPercent: 50,
|
||||
},
|
||||
plans: [
|
||||
{
|
||||
planId: "vip_monthly",
|
||||
planName: "VIP Monthly",
|
||||
orderType: "vip_monthly",
|
||||
vipDays: 30,
|
||||
dolAmount: null,
|
||||
creditBalance: 3000,
|
||||
amountCents: 995,
|
||||
originalAmountCents: 1990,
|
||||
dailyPriceCents: null,
|
||||
currency: "USD",
|
||||
isFirstRechargeOffer: true,
|
||||
firstRechargeDiscountPercent: 50,
|
||||
promotionType: "first_recharge_half_price",
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
expect(response.isFirstRecharge).toBe(true);
|
||||
expect(response.firstRechargeOffer?.discountPercent).toBe(50);
|
||||
expect(response.plans[0]).toMatchObject({
|
||||
amountCents: 995,
|
||||
originalAmountCents: 1990,
|
||||
isFirstRechargeOffer: true,
|
||||
firstRechargeDiscountPercent: 50,
|
||||
promotionType: "first_recharge_half_price",
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -18,6 +18,9 @@ export class PaymentPlan {
|
||||
declare readonly originalAmountCents: number | null;
|
||||
declare readonly dailyPriceCents: number | null;
|
||||
declare readonly currency: string;
|
||||
declare readonly isFirstRechargeOffer: boolean;
|
||||
declare readonly firstRechargeDiscountPercent: number | null;
|
||||
declare readonly promotionType: string | null;
|
||||
|
||||
private constructor(input: PaymentPlanInput) {
|
||||
const data = PaymentPlanSchema.parse(input);
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
*/
|
||||
import {
|
||||
PaymentPlansResponseSchema,
|
||||
type FirstRechargeOfferData,
|
||||
type PaymentPlansResponseData,
|
||||
type PaymentPlansResponseInput,
|
||||
} from "@/data/schemas/payment/payment_plans_response";
|
||||
@@ -10,11 +11,15 @@ import {
|
||||
import { PaymentPlan } from "./payment_plan";
|
||||
|
||||
export class PaymentPlansResponse {
|
||||
declare readonly isFirstRecharge: boolean;
|
||||
declare readonly firstRechargeOffer: FirstRechargeOfferData | null;
|
||||
declare readonly plans: PaymentPlan[];
|
||||
|
||||
private constructor(input: PaymentPlansResponseInput) {
|
||||
const data = PaymentPlansResponseSchema.parse(input);
|
||||
Object.assign(this, {
|
||||
isFirstRecharge: data.isFirstRecharge,
|
||||
firstRechargeOffer: data.firstRechargeOffer,
|
||||
plans: data.plans.map((plan) => PaymentPlan.from(plan)),
|
||||
});
|
||||
Object.freeze(this);
|
||||
@@ -30,7 +35,11 @@ export class PaymentPlansResponse {
|
||||
|
||||
toJson(): PaymentPlansResponseData {
|
||||
return {
|
||||
isFirstRecharge: this.isFirstRecharge,
|
||||
firstRechargeOffer: this.firstRechargeOffer,
|
||||
plans: this.plans.map((plan) => plan.toJson()),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export type FirstRechargeOffer = FirstRechargeOfferData;
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
{
|
||||
"code": 200,
|
||||
"message": "success",
|
||||
"success": true,
|
||||
"data": {
|
||||
"isFirstRecharge": true,
|
||||
"firstRechargeOffer": {
|
||||
"enabled": true,
|
||||
"type": "first_recharge_half_price",
|
||||
"discountPercent": 50
|
||||
},
|
||||
"plans": [
|
||||
{
|
||||
"planId": "vip_monthly",
|
||||
"planName": "VIP Monthly",
|
||||
"orderType": "vip_monthly",
|
||||
"vipDays": 30,
|
||||
"dolAmount": null,
|
||||
"creditBalance": 3000,
|
||||
"amountCents": 995,
|
||||
"originalAmountCents": 1990,
|
||||
"dailyPriceCents": 33,
|
||||
"currency": "USD",
|
||||
"isFirstRechargeOffer": true,
|
||||
"firstRechargeDiscountPercent": 50,
|
||||
"promotionType": "first_recharge_half_price"
|
||||
},
|
||||
{
|
||||
"planId": "vip_quarterly",
|
||||
"planName": "VIP Quarterly",
|
||||
"orderType": "vip_quarterly",
|
||||
"vipDays": 90,
|
||||
"dolAmount": null,
|
||||
"creditBalance": 9000,
|
||||
"amountCents": 2495,
|
||||
"originalAmountCents": 4990,
|
||||
"dailyPriceCents": 28,
|
||||
"currency": "USD",
|
||||
"isFirstRechargeOffer": true,
|
||||
"firstRechargeDiscountPercent": 50,
|
||||
"promotionType": "first_recharge_half_price"
|
||||
},
|
||||
{
|
||||
"planId": "vip_annual",
|
||||
"planName": "VIP Annual",
|
||||
"orderType": "vip_annual",
|
||||
"vipDays": 365,
|
||||
"dolAmount": null,
|
||||
"creditBalance": 36000,
|
||||
"amountCents": 8995,
|
||||
"originalAmountCents": 17990,
|
||||
"dailyPriceCents": 25,
|
||||
"currency": "USD",
|
||||
"isFirstRechargeOffer": true,
|
||||
"firstRechargeDiscountPercent": 50,
|
||||
"promotionType": "first_recharge_half_price"
|
||||
},
|
||||
{
|
||||
"planId": "dol_1000",
|
||||
"planName": "1000 Credits",
|
||||
"orderType": "dol",
|
||||
"vipDays": null,
|
||||
"dolAmount": 1000,
|
||||
"creditBalance": 1000,
|
||||
"amountCents": 495,
|
||||
"originalAmountCents": 990,
|
||||
"dailyPriceCents": null,
|
||||
"currency": "USD",
|
||||
"isFirstRechargeOffer": true,
|
||||
"firstRechargeDiscountPercent": 50,
|
||||
"promotionType": "first_recharge_half_price"
|
||||
},
|
||||
{
|
||||
"planId": "dol_2000",
|
||||
"planName": "2000 Credits",
|
||||
"orderType": "dol",
|
||||
"vipDays": null,
|
||||
"dolAmount": 2000,
|
||||
"creditBalance": 2000,
|
||||
"amountCents": 845,
|
||||
"originalAmountCents": 1690,
|
||||
"dailyPriceCents": null,
|
||||
"currency": "USD",
|
||||
"isFirstRechargeOffer": true,
|
||||
"firstRechargeDiscountPercent": 50,
|
||||
"promotionType": "first_recharge_half_price"
|
||||
},
|
||||
{
|
||||
"planId": "dol_3000",
|
||||
"planName": "3000 Credits",
|
||||
"orderType": "dol",
|
||||
"vipDays": null,
|
||||
"dolAmount": 3000,
|
||||
"creditBalance": 3000,
|
||||
"amountCents": 1145,
|
||||
"originalAmountCents": 2290,
|
||||
"dailyPriceCents": null,
|
||||
"currency": "USD",
|
||||
"isFirstRechargeOffer": true,
|
||||
"firstRechargeDiscountPercent": 50,
|
||||
"promotionType": "first_recharge_half_price"
|
||||
},
|
||||
{
|
||||
"planId": "dol_5000",
|
||||
"planName": "5000 Credits",
|
||||
"orderType": "dol",
|
||||
"vipDays": null,
|
||||
"dolAmount": 5000,
|
||||
"creditBalance": 5000,
|
||||
"amountCents": 1745,
|
||||
"originalAmountCents": 3490,
|
||||
"dailyPriceCents": null,
|
||||
"currency": "USD",
|
||||
"isFirstRechargeOffer": true,
|
||||
"firstRechargeDiscountPercent": 50,
|
||||
"promotionType": "first_recharge_half_price"
|
||||
},
|
||||
{
|
||||
"planId": "dol_10000",
|
||||
"planName": "10000 Credits",
|
||||
"orderType": "dol",
|
||||
"vipDays": null,
|
||||
"dolAmount": 10000,
|
||||
"creditBalance": 10000,
|
||||
"amountCents": 3245,
|
||||
"originalAmountCents": 6490,
|
||||
"dailyPriceCents": null,
|
||||
"currency": "USD",
|
||||
"isFirstRechargeOffer": true,
|
||||
"firstRechargeDiscountPercent": 50,
|
||||
"promotionType": "first_recharge_half_price"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,9 @@ export const PaymentPlanSchema = z.object({
|
||||
originalAmountCents: z.number().nullable().default(null),
|
||||
dailyPriceCents: z.number().nullable().default(null),
|
||||
currency: z.string(),
|
||||
isFirstRechargeOffer: z.boolean().default(false),
|
||||
firstRechargeDiscountPercent: z.number().nullable().default(null),
|
||||
promotionType: z.string().nullable().default(null),
|
||||
});
|
||||
|
||||
export type PaymentPlanInput = z.input<typeof PaymentPlanSchema>;
|
||||
|
||||
@@ -5,7 +5,15 @@ import { z } from "zod";
|
||||
|
||||
import { PaymentPlanSchema } from "./payment_plan";
|
||||
|
||||
export const FirstRechargeOfferSchema = z.object({
|
||||
enabled: z.boolean(),
|
||||
type: z.string(),
|
||||
discountPercent: z.number(),
|
||||
});
|
||||
|
||||
export const PaymentPlansResponseSchema = z.object({
|
||||
isFirstRecharge: z.boolean().default(false),
|
||||
firstRechargeOffer: FirstRechargeOfferSchema.nullable().default(null),
|
||||
plans: z.array(PaymentPlanSchema),
|
||||
});
|
||||
|
||||
@@ -15,3 +23,4 @@ export type PaymentPlansResponseInput = z.input<
|
||||
export type PaymentPlansResponseData = z.output<
|
||||
typeof PaymentPlansResponseSchema
|
||||
>;
|
||||
export type FirstRechargeOfferData = z.output<typeof FirstRechargeOfferSchema>;
|
||||
|
||||
Reference in New Issue
Block a user