fix: type errors
This commit is contained in:
@@ -271,12 +271,14 @@ export const createStripeInArrearPrice = async ({
|
||||
...productData,
|
||||
...priceAmountData,
|
||||
currency: org.default_currency || "usd",
|
||||
recurring: {
|
||||
interval: recurringData.interval,
|
||||
interval_count: recurringData.interval_count,
|
||||
meter: meter.id,
|
||||
usage_type: "metered",
|
||||
},
|
||||
recurring: recurringData?.interval
|
||||
? {
|
||||
interval: recurringData.interval,
|
||||
interval_count: recurringData.interval_count,
|
||||
meter: meter.id,
|
||||
usage_type: "metered",
|
||||
}
|
||||
: undefined,
|
||||
nickname: `Autumn Price (${relatedEnt.feature.name})`,
|
||||
});
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
type Organization,
|
||||
type Price,
|
||||
type Product,
|
||||
TierBehavior,
|
||||
priceToStripeTiersMode,
|
||||
TierInfinite,
|
||||
type UsagePriceConfig,
|
||||
} from "@autumn/shared";
|
||||
@@ -71,12 +71,14 @@ export const createStripePrepaid = async ({
|
||||
}) => {
|
||||
const relatedEnt = getPriceEntitlement(price, entitlements);
|
||||
|
||||
let recurringData;
|
||||
let recurringData: Partial<Stripe.PriceCreateParams.Recurring> | undefined;
|
||||
if (price.config!.interval !== BillingInterval.OneOff) {
|
||||
recurringData = billingIntervalToStripe({
|
||||
interval: price.config!.interval,
|
||||
intervalCount: price.config!.interval_count,
|
||||
});
|
||||
recurringData = {
|
||||
...billingIntervalToStripe({
|
||||
interval: price.config!.interval,
|
||||
intervalCount: price.config!.interval_count,
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
const config = price.config as UsagePriceConfig;
|
||||
@@ -114,8 +116,7 @@ export const createStripePrepaid = async ({
|
||||
config.stripe_price_id = stripePrice.id;
|
||||
} else {
|
||||
const tiers = prepaidToStripeTiers({ price, org });
|
||||
const tiersMode =
|
||||
price.tier_behavior === TierBehavior.VolumeBased ? "volume" : "graduated";
|
||||
const tiersMode = priceToStripeTiersMode({ price });
|
||||
|
||||
let priceAmountData = {};
|
||||
if (tiers.length === 1) {
|
||||
|
||||
@@ -25,7 +25,7 @@ export const billingIntervalToStripe = ({
|
||||
}: {
|
||||
interval: BillingInterval;
|
||||
intervalCount?: number | null;
|
||||
}): Stripe.PriceCreateParams.Recurring | Record<string, any> => {
|
||||
}): Partial<Stripe.PriceCreateParams.Recurring> => {
|
||||
const finalCount = intervalCount ?? 1;
|
||||
switch (interval) {
|
||||
case BillingInterval.Week:
|
||||
|
||||
@@ -126,6 +126,7 @@ export const constructArrearItem = ({
|
||||
featureId,
|
||||
includedUsage = 10000,
|
||||
price = 0.1,
|
||||
tiers,
|
||||
billingUnits = 1000,
|
||||
config = {
|
||||
on_increase: OnIncrease.ProrateImmediately,
|
||||
@@ -140,6 +141,7 @@ export const constructArrearItem = ({
|
||||
featureId: string;
|
||||
includedUsage?: number;
|
||||
price?: number;
|
||||
tiers?: { amount: number; to: number | "inf" }[];
|
||||
billingUnits?: number;
|
||||
config?: ProductItemConfig;
|
||||
rolloverConfig?: RolloverConfig;
|
||||
@@ -152,7 +154,8 @@ export const constructArrearItem = ({
|
||||
feature_id: featureId,
|
||||
usage_model: UsageModel.PayPerUse,
|
||||
included_usage: includedUsage,
|
||||
price: price,
|
||||
price: tiers ? undefined : price,
|
||||
tiers: tiers,
|
||||
billing_units: billingUnits,
|
||||
interval: interval,
|
||||
interval_count: intervalCount,
|
||||
|
||||
@@ -100,7 +100,7 @@ test.concurrent(`${chalk.yellowBright("attach-prepaid-volume: immediate switch,
|
||||
product_id: premium.id,
|
||||
options: [{ feature_id: TestFeature.Messages, quantity: newQuantity }],
|
||||
});
|
||||
expect(preview.total).toBeGreaterThan(0);
|
||||
expect(preview.total).toEqual(30);
|
||||
|
||||
await autumnV1.billing.attach({
|
||||
customer_id: customerId,
|
||||
@@ -201,7 +201,7 @@ test.concurrent(`${chalk.yellowBright("attach-prepaid-volume: immediate switch,
|
||||
product_id: premium.id,
|
||||
options: [{ feature_id: TestFeature.Messages, quantity: newQuantity }],
|
||||
});
|
||||
expect(preview.total).toBeGreaterThan(0);
|
||||
expect(preview.total).toEqual(10 + 30);
|
||||
|
||||
await autumnV1.billing.attach({
|
||||
customer_id: customerId,
|
||||
|
||||
@@ -327,3 +327,98 @@ test.concurrent(`${chalk.yellowBright("attach-prepaid-volume: quantity 0 → no
|
||||
latestTotal: BASE_PRICE,
|
||||
});
|
||||
});
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════════
|
||||
// TEST 5: 4 tiers with included usage — volume pricing at correct tier
|
||||
// ═══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
/**
|
||||
* 4 tiers (billingUnits = 100):
|
||||
* Tier 1: 0-200 units @ $15/pack
|
||||
* Tier 2: 201-500 units @ $10/pack
|
||||
* Tier 3: 501-1000 units @ $7/pack
|
||||
* Tier 4: 1001+ units @ $5/pack
|
||||
*
|
||||
* With 100 included (free) and 900 total quantity:
|
||||
* Paid packs: (900 - 100) / 100 = 8 packs
|
||||
* Volume: all 8 packs at tier 3 rate ($7) = $56
|
||||
* Total: $20 base + $56 = $76
|
||||
*/
|
||||
test.concurrent(`${chalk.yellowBright("attach-prepaid-volume: 4 tiers, 100 included, 900 total → $76")}`, async () => {
|
||||
const customerId = "attach-prepaid-volume-4tier-incl";
|
||||
const quantity = 900;
|
||||
const includedUsage = 100;
|
||||
|
||||
// 4-tier pricing structure
|
||||
const fourTiers = [
|
||||
{ to: 200, amount: 15 },
|
||||
{ to: 500, amount: 10 },
|
||||
{ to: 1000, amount: 7 },
|
||||
{ to: "inf" as const, amount: 5 },
|
||||
];
|
||||
|
||||
// Paid packs after free: (900 - 100) / 100 = 8 packs
|
||||
// 8 packs falls into tier 3 (800 paid units = 501-1000 range)
|
||||
// Volume pricing: all 8 packs at $7 = $56
|
||||
const expectedPrepaidCost = 8 * 7;
|
||||
|
||||
const volumeItem = items.volumePrepaidMessages({
|
||||
includedUsage,
|
||||
billingUnits: BILLING_UNITS,
|
||||
tiers: fourTiers,
|
||||
});
|
||||
|
||||
const pro = products.pro({
|
||||
id: "pro-volume-4tier",
|
||||
items: [volumeItem],
|
||||
});
|
||||
|
||||
const { autumnV1, ctx } = await initScenario({
|
||||
customerId,
|
||||
setup: [
|
||||
s.customer({ paymentMethod: "success" }),
|
||||
s.products({ list: [pro] }),
|
||||
],
|
||||
actions: [],
|
||||
});
|
||||
|
||||
// Preview must reflect volume pricing at tier 3
|
||||
const preview = await autumnV1.billing.previewAttach({
|
||||
customer_id: customerId,
|
||||
product_id: pro.id,
|
||||
options: [{ feature_id: TestFeature.Messages, quantity }],
|
||||
});
|
||||
expect(preview.total).toBe(BASE_PRICE + expectedPrepaidCost);
|
||||
|
||||
await autumnV1.billing.attach({
|
||||
customer_id: customerId,
|
||||
product_id: pro.id,
|
||||
options: [{ feature_id: TestFeature.Messages, quantity }],
|
||||
redirect_mode: "if_required",
|
||||
});
|
||||
|
||||
const customer = await autumnV1.customers.get<ApiCustomerV3>(customerId);
|
||||
|
||||
await expectProductActive({ customer, productId: pro.id });
|
||||
|
||||
// Balance should equal total quantity (free + paid)
|
||||
expectCustomerFeatureCorrect({
|
||||
customer,
|
||||
featureId: TestFeature.Messages,
|
||||
balance: quantity,
|
||||
usage: 0,
|
||||
});
|
||||
|
||||
await expectCustomerInvoiceCorrect({
|
||||
customer,
|
||||
count: 1,
|
||||
latestTotal: BASE_PRICE + expectedPrepaidCost,
|
||||
});
|
||||
|
||||
await expectSubToBeCorrect({
|
||||
db: ctx.db,
|
||||
customerId,
|
||||
org: ctx.org,
|
||||
env: ctx.env,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { expect, test } from "bun:test";
|
||||
import type { ApiCustomerV3 } from "@autumn/shared";
|
||||
import { type ApiCustomerV3 } from "@autumn/shared";
|
||||
import { expectCustomerFeatureCorrect } from "@tests/integration/billing/utils/expectCustomerFeatureCorrect";
|
||||
import { expectCustomerInvoiceCorrect } from "@tests/integration/billing/utils/expectCustomerInvoiceCorrect";
|
||||
import { expectSubToBeCorrect } from "@tests/merged/mergeUtils/expectSubCorrect";
|
||||
|
||||
@@ -0,0 +1,235 @@
|
||||
import { expect, test } from "bun:test";
|
||||
import type { ApiCustomerV3 } from "@autumn/shared";
|
||||
import { expectCustomerFeatureCorrect } from "@tests/integration/billing/utils/expectCustomerFeatureCorrect";
|
||||
import { expectCustomerInvoiceCorrect } from "@tests/integration/billing/utils/expectCustomerInvoiceCorrect";
|
||||
import { calculateProration } from "@tests/integration/billing/utils/proration/calculateProration";
|
||||
import { expectSubToBeCorrect } from "@tests/merged/mergeUtils/expectSubCorrect";
|
||||
import { TestFeature } from "@tests/setup/v2Features";
|
||||
import { items } from "@tests/utils/fixtures/items";
|
||||
import { products } from "@tests/utils/fixtures/products";
|
||||
import { advanceTestClock } from "@tests/utils/stripeUtils";
|
||||
import { initScenario, s } from "@tests/utils/testInitUtils/initScenario";
|
||||
import chalk from "chalk";
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════════
|
||||
// PAID-TO-PAID: TIER BEHAVIOR TRANSITIONS
|
||||
// ═══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
const BILLING_UNITS = 100;
|
||||
|
||||
// Tiers for prepaid pricing tests (same for graduated and volume)
|
||||
const PREPAID_TIERS = [
|
||||
{ to: 500, amount: 10 },
|
||||
{ to: "inf" as const, amount: 5 },
|
||||
];
|
||||
|
||||
// 4.8 Graduated prepaid to volume prepaid (mid-cycle with proration)
|
||||
test.concurrent(`${chalk.yellowBright("p2p: graduated prepaid to volume prepaid (mid-cycle proration)")}`, async () => {
|
||||
const quantity = 800;
|
||||
const basePrice = 20;
|
||||
|
||||
// Graduated calculation: 5 packs × $10 + 3 packs × $5 = $65
|
||||
const graduatedCost = 5 * 10 + 3 * 5;
|
||||
|
||||
// Volume calculation: 8 packs × $5 (tier 2 rate for all) = $40
|
||||
const volumeCost = 8 * 5;
|
||||
|
||||
const graduatedItem = items.tieredPrepaidMessages({
|
||||
includedUsage: 0,
|
||||
billingUnits: BILLING_UNITS,
|
||||
tiers: PREPAID_TIERS,
|
||||
});
|
||||
const priceItem = items.monthlyPrice({ price: basePrice });
|
||||
const pro = products.base({ id: "pro", items: [graduatedItem, priceItem] });
|
||||
|
||||
const { customerId, autumnV1, ctx, testClockId } = await initScenario({
|
||||
customerId: "p2p-grad-to-vol-proration",
|
||||
setup: [
|
||||
s.customer({ paymentMethod: "success", testClock: true }),
|
||||
s.products({ list: [pro] }),
|
||||
],
|
||||
actions: [
|
||||
s.attach({
|
||||
productId: "pro",
|
||||
options: [{ feature_id: TestFeature.Messages, quantity }],
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
// Verify initial invoice (base price + graduated prepaid)
|
||||
const initialCustomer =
|
||||
await autumnV1.customers.get<ApiCustomerV3>(customerId);
|
||||
await expectCustomerInvoiceCorrect({
|
||||
customer: initialCustomer,
|
||||
count: 1,
|
||||
latestTotal: basePrice + graduatedCost,
|
||||
});
|
||||
|
||||
// Advance 15 days (mid-cycle)
|
||||
const advancedTo = await advanceTestClock({
|
||||
stripeCli: ctx.stripeCli,
|
||||
testClockId: testClockId!,
|
||||
numberOfDays: 15,
|
||||
});
|
||||
|
||||
// Change to volume prepaid with same quantity
|
||||
const volumeItem = items.volumePrepaidMessages({
|
||||
includedUsage: 0,
|
||||
billingUnits: BILLING_UNITS,
|
||||
tiers: PREPAID_TIERS,
|
||||
});
|
||||
|
||||
const updateParams = {
|
||||
customer_id: customerId,
|
||||
product_id: pro.id,
|
||||
items: [volumeItem, priceItem],
|
||||
};
|
||||
|
||||
const preview = await autumnV1.subscriptions.previewUpdate(updateParams);
|
||||
|
||||
// Calculate prorated difference: credit old graduated, charge new volume
|
||||
const proratedGraduated = await calculateProration({
|
||||
customerId,
|
||||
advancedTo,
|
||||
amount: graduatedCost,
|
||||
});
|
||||
const proratedVolume = await calculateProration({
|
||||
customerId,
|
||||
advancedTo,
|
||||
amount: volumeCost,
|
||||
});
|
||||
|
||||
// Volume is cheaper, so this should be a credit (negative)
|
||||
const expectedAmount = proratedVolume - proratedGraduated;
|
||||
|
||||
expect(preview.total).toEqual(expectedAmount);
|
||||
|
||||
await autumnV1.subscriptions.update(updateParams);
|
||||
|
||||
const customer = await autumnV1.customers.get<ApiCustomerV3>(customerId);
|
||||
|
||||
// Balance should remain at 800 (quantity unchanged)
|
||||
expectCustomerFeatureCorrect({
|
||||
customer,
|
||||
featureId: TestFeature.Messages,
|
||||
balance: quantity,
|
||||
usage: 0,
|
||||
});
|
||||
|
||||
await expectCustomerInvoiceCorrect({
|
||||
customer,
|
||||
count: 2,
|
||||
latestTotal: preview.total,
|
||||
});
|
||||
|
||||
await expectSubToBeCorrect({
|
||||
db: ctx.db,
|
||||
customerId,
|
||||
org: ctx.org,
|
||||
env: ctx.env,
|
||||
});
|
||||
});
|
||||
|
||||
// Tiers for consumable pricing tests
|
||||
const CONSUMABLE_TIERS = [
|
||||
{ to: 500, amount: 0.1 },
|
||||
{ to: "inf" as const, amount: 0.05 },
|
||||
];
|
||||
|
||||
// 4.9 Volume prepaid to graduated tiered consumable
|
||||
test.concurrent(`${chalk.yellowBright("p2p: volume prepaid to graduated tiered consumable")}`, async () => {
|
||||
const quantity = 800;
|
||||
const basePrice = 20;
|
||||
|
||||
// Volume prepaid: 8 packs × $5 = $40
|
||||
const volumeCost = 8 * 5;
|
||||
|
||||
const volumeItem = items.volumePrepaidMessages({
|
||||
includedUsage: 0,
|
||||
billingUnits: BILLING_UNITS,
|
||||
tiers: PREPAID_TIERS,
|
||||
});
|
||||
const priceItem = items.monthlyPrice({ price: basePrice });
|
||||
const pro = products.base({ id: "pro", items: [volumeItem, priceItem] });
|
||||
|
||||
const { customerId, autumnV1, ctx } = await initScenario({
|
||||
customerId: "p2p-vol-to-tiered-cons",
|
||||
setup: [
|
||||
s.customer({ paymentMethod: "success" }),
|
||||
s.products({ list: [pro] }),
|
||||
],
|
||||
actions: [
|
||||
s.attach({
|
||||
productId: "pro",
|
||||
options: [{ feature_id: TestFeature.Messages, quantity }],
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
// Track some usage (600 of 800 = 200 remaining)
|
||||
const messagesUsage = 600;
|
||||
await autumnV1.track(
|
||||
{
|
||||
customer_id: customerId,
|
||||
feature_id: TestFeature.Messages,
|
||||
value: messagesUsage,
|
||||
},
|
||||
{ timeout: 2000 },
|
||||
);
|
||||
|
||||
// Verify customer has usage tracked
|
||||
const customerBefore =
|
||||
await autumnV1.customers.get<ApiCustomerV3>(customerId);
|
||||
expect(customerBefore.features[TestFeature.Messages].usage).toBe(
|
||||
messagesUsage,
|
||||
);
|
||||
|
||||
// Change to tiered consumable with 100 included
|
||||
// tier_behavior should be undefined (defaults to graduated)
|
||||
const tieredConsumableItem = items.tieredConsumableMessages({
|
||||
includedUsage: 100,
|
||||
billingUnits: 1,
|
||||
tiers: CONSUMABLE_TIERS,
|
||||
});
|
||||
|
||||
// Verify the item has no tier_behavior set (defaults to graduated)
|
||||
expect(tieredConsumableItem.tier_behavior).toBeUndefined();
|
||||
|
||||
const updateParams = {
|
||||
customer_id: customerId,
|
||||
product_id: pro.id,
|
||||
items: [tieredConsumableItem, priceItem],
|
||||
};
|
||||
|
||||
const preview = await autumnV1.subscriptions.previewUpdate(updateParams);
|
||||
|
||||
// Should refund full prepaid amount ($40)
|
||||
expect(preview.total).toBe(-volumeCost);
|
||||
|
||||
await autumnV1.subscriptions.update(updateParams);
|
||||
|
||||
const customer = await autumnV1.customers.get<ApiCustomerV3>(customerId);
|
||||
|
||||
// Usage should be preserved (600 tracked)
|
||||
// New included usage is 100, so balance = 100 - 600 = -500 (in overage)
|
||||
expectCustomerFeatureCorrect({
|
||||
customer,
|
||||
featureId: TestFeature.Messages,
|
||||
includedUsage: tieredConsumableItem.included_usage,
|
||||
balance: tieredConsumableItem.included_usage - messagesUsage,
|
||||
usage: messagesUsage,
|
||||
});
|
||||
|
||||
await expectCustomerInvoiceCorrect({
|
||||
customer,
|
||||
count: 2,
|
||||
latestTotal: preview.total,
|
||||
});
|
||||
|
||||
await expectSubToBeCorrect({
|
||||
db: ctx.db,
|
||||
customerId,
|
||||
org: ctx.org,
|
||||
env: ctx.env,
|
||||
});
|
||||
});
|
||||
@@ -579,6 +579,31 @@ const consumableWords = ({
|
||||
interval,
|
||||
});
|
||||
|
||||
/**
|
||||
* Tiered consumable messages - graduated pricing with tiers (pay-per-use).
|
||||
* Default tiers: 0-500 units at $0.10/unit, 501+ at $0.05/unit.
|
||||
* IMPORTANT: Last tier MUST have `to: "inf"` - Stripe requires a catch-all tier.
|
||||
* Note: tier_behavior is undefined, defaulting to graduated pricing.
|
||||
*/
|
||||
const tieredConsumableMessages = ({
|
||||
includedUsage = 0,
|
||||
billingUnits = 1,
|
||||
tiers = [
|
||||
{ to: 500, amount: 0.1 },
|
||||
{ to: "inf", amount: 0.05 },
|
||||
],
|
||||
}: {
|
||||
includedUsage?: number;
|
||||
billingUnits?: number;
|
||||
tiers?: { to: number | "inf"; amount: number }[];
|
||||
} = {}): LimitedItem =>
|
||||
constructArrearItem({
|
||||
featureId: TestFeature.Messages,
|
||||
tiers: tiers as { to: number; amount: number }[],
|
||||
billingUnits,
|
||||
includedUsage,
|
||||
}) as LimitedItem;
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
// ALLOCATED / SEATS (prorated billing)
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
@@ -701,6 +726,7 @@ export const items = {
|
||||
consumable,
|
||||
consumableMessages,
|
||||
consumableWords,
|
||||
tieredConsumableMessages,
|
||||
|
||||
// Allocated
|
||||
allocatedUsers,
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { UsageTier } from "@models/productModels/priceModels/priceConfig/us
|
||||
import { Infinite } from "@models/productModels/productEnums";
|
||||
import { roundUsageToNearestBillingUnit } from "@utils/billingUtils/usageUtils/roundUsageToNearestBillingUnit";
|
||||
import { nullish } from "@utils/utils";
|
||||
import Decimal from "decimal.js";
|
||||
import { Decimal } from "decimal.js";
|
||||
|
||||
export const volumeTiersToLineAmount = ({
|
||||
tiers,
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import type { Organization } from "@models/orgModels/orgTable";
|
||||
import { TierBehavior } from "@models/productModels/priceModels/priceConfig/usagePriceConfig";
|
||||
import type { Price } from "@models/productModels/priceModels/priceModels";
|
||||
import type { FullProduct } from "@models/productModels/productModels";
|
||||
import { orgToCurrency } from "@utils/orgUtils/convertOrgUtils";
|
||||
@@ -8,6 +7,7 @@ import { priceToStripePrepaidV2Tiers } from "@utils/productUtils/priceUtils/conv
|
||||
import { priceToStripeProductName } from "@utils/productUtils/priceUtils/convertPrice/priceToStripeProductName";
|
||||
import { priceToStripeRecurringParams } from "@utils/productUtils/priceUtils/convertPrice/priceToStripeRecurringParams";
|
||||
import type Stripe from "stripe";
|
||||
import { priceToStripeTiersMode } from "./priceToStripeTiersMode";
|
||||
|
||||
export const priceToStripeCreatePriceParams = ({
|
||||
price,
|
||||
@@ -41,9 +41,7 @@ export const priceToStripeCreatePriceParams = ({
|
||||
};
|
||||
|
||||
const tiers = priceToStripePrepaidV2Tiers({ price, entitlement, org });
|
||||
|
||||
const tiersMode =
|
||||
price.tier_behavior === TierBehavior.VolumeBased ? "volume" : "graduated";
|
||||
const tiersMode = priceToStripeTiersMode({ price });
|
||||
|
||||
let priceAmountData = {};
|
||||
if (tiers.length === 1) {
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
import { type Price, TierBehavior } from "../../../..";
|
||||
|
||||
export const priceToStripeTiersMode = ({ price }: { price: Price }) => {
|
||||
return price.tier_behavior === TierBehavior.VolumeBased
|
||||
? "volume"
|
||||
: "graduated";
|
||||
};
|
||||
@@ -1,10 +1,12 @@
|
||||
import { priceIsTieredOneOff } from "@utils/productUtils/priceUtils/classifyPrice/priceIsTieredOneOff.js";
|
||||
import { priceToAllowanceInPacks } from "@utils/productUtils/priceUtils/convertPrice/priceToAllowanceInPacks.js";
|
||||
import { priceToStripeCreatePriceParams } from "@utils/productUtils/priceUtils/convertPrice/priceToStripeCreatePriceParams.js";
|
||||
import { priceToStripeTiersMode } from "./convertPrice/priceToStripeTiersMode.js";
|
||||
|
||||
export * from "./classifyPrice/priceIsTieredOneOff.js";
|
||||
export * from "./classifyPriceUtils.js";
|
||||
export * from "./convertAmountUtils.js";
|
||||
export * from "./convertPrice/priceToStripeTiersMode.js";
|
||||
export * from "./convertPriceUtils.js";
|
||||
export * from "./findPrice/findPriceByFeatureId.js";
|
||||
export * from "./formatPriceUtils.js";
|
||||
@@ -14,6 +16,7 @@ export const priceUtils = {
|
||||
convert: {
|
||||
toAllowanceInPacks: priceToAllowanceInPacks,
|
||||
toStripeCreatePriceParams: priceToStripeCreatePriceParams,
|
||||
toStripeTiersMode: priceToStripeTiersMode,
|
||||
},
|
||||
|
||||
isTieredOneOff: priceIsTieredOneOff,
|
||||
|
||||
@@ -41,6 +41,7 @@ export const productItemToPlanItemParamsV1 = ({
|
||||
billing_units: planItemV1.price.billing_units,
|
||||
billing_method: planItemV1.price.billing_method,
|
||||
max_purchase: planItemV1.price.max_purchase ?? undefined,
|
||||
tier_behavior: planItemV1.price.tier_behavior ?? undefined,
|
||||
}
|
||||
: undefined,
|
||||
proration: planItemV1.proration
|
||||
|
||||
Reference in New Issue
Block a user