fix: send price:null when schedule plan customize removes base price
buildCustomizeBasePrice treated price=0 as falsy (!0 === true), silently dropping the price override. The server then fell back to the original plan price, causing free-first-phase schedules to show a non-zero total and redirect to checkout instead of creating directly. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -76,7 +76,8 @@ export function buildCustomizeBasePrice({ items }: { items: ProductItem[] }) {
|
||||
const priceItem = items.find(
|
||||
(item) => item.price != null && !item.feature_id,
|
||||
);
|
||||
if (!priceItem?.price || !priceItem.interval) return undefined;
|
||||
if (!priceItem || priceItem.price === 0) return null;
|
||||
if (!priceItem.interval) return undefined;
|
||||
return {
|
||||
amount: priceItem.price,
|
||||
interval: priceItem.interval,
|
||||
@@ -100,10 +101,10 @@ export function buildCustomize({
|
||||
if (!items) return undefined;
|
||||
const planItems = buildCustomizeItems({ items, features });
|
||||
const basePrice = buildCustomizeBasePrice({ items });
|
||||
if (!planItems && !basePrice) return undefined;
|
||||
if (!planItems && basePrice === undefined) return undefined;
|
||||
return {
|
||||
...(planItems ? { items: planItems } : {}),
|
||||
...(basePrice ? { price: basePrice } : {}),
|
||||
...(basePrice !== undefined ? { price: basePrice } : {}),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -87,10 +87,17 @@ describe("buildCustomizeBasePrice", () => {
|
||||
expect(result!.interval).toBe("month");
|
||||
});
|
||||
|
||||
test("returns undefined when no base price item exists", () => {
|
||||
test("returns null when base price item was removed", () => {
|
||||
const result = buildCustomizeBasePrice({ items: [featurePriceItem] });
|
||||
|
||||
expect(result).toBeUndefined();
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
|
||||
test("returns null when price is zero (free)", () => {
|
||||
const zeroPriceItem = { ...basePriceItem, price: 0 } as ProductItem;
|
||||
const result = buildCustomizeBasePrice({ items: [zeroPriceItem] });
|
||||
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
|
||||
test("returns undefined when price item has no interval", () => {
|
||||
@@ -118,7 +125,7 @@ describe("buildCustomizeBasePrice", () => {
|
||||
test("ignores feature items that happen to have a price", () => {
|
||||
const result = buildCustomizeBasePrice({ items: [featurePriceItem] });
|
||||
|
||||
expect(result).toBeUndefined();
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -207,11 +214,12 @@ describe("buildCustomize", () => {
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
|
||||
test("returns items when only free features are present", () => {
|
||||
test("returns items and price:null when base price was removed", () => {
|
||||
const result = buildCustomize({ items: [freeFeatureItem], features });
|
||||
|
||||
expect(result).toBeDefined();
|
||||
expect(result!.items).toBeDefined();
|
||||
expect(result!.price).toBeNull();
|
||||
});
|
||||
|
||||
test("returns price when only base price is customized", () => {
|
||||
@@ -223,12 +231,12 @@ describe("buildCustomize", () => {
|
||||
expect(result).not.toHaveProperty("items");
|
||||
});
|
||||
|
||||
test("returns items when only feature items are customized", () => {
|
||||
test("returns items and price:null when items have no base price", () => {
|
||||
const result = buildCustomize({ items: [featurePriceItem], features });
|
||||
|
||||
expect(result).toBeDefined();
|
||||
expect(result!.items).toBeDefined();
|
||||
expect(result).not.toHaveProperty("price");
|
||||
expect(result!.price).toBeNull();
|
||||
});
|
||||
|
||||
test("returns both price and items when fully customized", () => {
|
||||
|
||||
Reference in New Issue
Block a user