12 KiB
12 KiB
Item and Product Fixtures
Imports
import { items } from "@tests/utils/fixtures/items.js";
import { products } from "@tests/utils/fixtures/products.js";
import { TestFeature } from "@tests/setup/v2Features.js";
Available Test Features
enum TestFeature {
Dashboard = "dashboard", // Boolean feature
Messages = "messages", // Single use (prepaid/consumable)
Users = "users", // Continuous use (seats)
Workflows = "workflows", // Continuous use
Admin = "admin", // Continuous use
AdminRights = "admin_rights", // Boolean
Words = "words", // Single use (pay per use)
Storage = "storage", // Single use (prepaid)
Credits = "credits", // Credit system
Action1 = "action1", // Single use
Action2 = "action2", // Single use
Action3 = "action3", // Single use
Credits2 = "credits2", // Credit system
}
Item Fixtures (items.*)
Boolean Features
items.dashboard() // On/off access (TestFeature.Dashboard)
items.adminRights() // Admin rights access (TestFeature.AdminRights)
Free Metered (resets monthly)
items.monthlyMessages({ includedUsage?: number, entityFeatureId?, resetUsageWhenEnabled? }) // Default: 100
items.monthlyWords({ includedUsage?: number, entityFeatureId?, resetUsageWhenEnabled? }) // Default: 100
items.monthlyCredits({ includedUsage?: number, rolloverConfig? }) // Default: 100
items.monthlyUsers({ includedUsage?: number }) // Default: 5
items.freeUsers({ includedUsage?: number }) // Default: 5 (same as monthlyUsers)
items.free({ featureId, includedUsage?: number }) // Default: 100 — generic free metered
items.unlimitedMessages() // No usage cap
items.lifetimeMessages({ includedUsage?: number, entityFeatureId? }) // Default: 100, never resets (interval: null)
Rollover Features
import { RolloverExpiryDurationType } from "@autumn/shared";
items.monthlyMessagesWithRollover({
includedUsage?: number, // Default: 100
rolloverConfig: { // REQUIRED
max: number | null, // Maximum rollover amount (null = unlimited)
length: number, // Number of periods to keep rollovers
duration: RolloverExpiryDurationType, // Month, Year, etc.
},
})
Prepaid (purchase upfront, recurring)
items.prepaidMessages({
includedUsage?: number, // Default: 0 (free balance before paid)
billingUnits?: number, // Default: 100 (units per pack)
price?: number, // Default: 10 ($ per pack)
config?: ProductItemConfig,
entityFeatureId?: string,
})
items.prepaidUsers({
includedUsage?: number, // Default: 0
billingUnits?: number, // Default: 1 (per seat)
})
// Generic prepaid for any feature
items.prepaid({
featureId: string,
price?: number, // Default: 10
billingUnits?: number, // Default: 100
includedUsage?: number,// Default: 0
config?: ProductItemConfig,
entityFeatureId?: string,
})
Tiered Prepaid (graduated pricing)
items.tieredPrepaidMessages({
includedUsage?: number, // Default: 0
billingUnits?: number, // Default: 100
tiers?: { to: number | "inf"; amount: number }[], // Default: [{ to: 500, amount: 10 }, { to: "inf", amount: 5 }]
config?: ProductItemConfig,
})
Graduated pricing: first 500 units at $10/pack, remaining at $5/pack (100 units/pack).
Volume Prepaid
items.volumePrepaidMessages({
includedUsage?: number, // Default: 0
billingUnits?: number, // Default: 100
tiers?: { to: number | "inf"; amount: number; flat_amount?: number | null }[],
config?: ProductItemConfig,
})
Volume-based: whole quantity charged at whichever tier it falls into.
One-Off (no recurring charges)
items.oneOffMessages({ includedUsage?: number, billingUnits?: number, price?: number }) // Defaults: 0, 100, $10
items.oneOffWords({ includedUsage?: number, billingUnits?: number, price?: number }) // Defaults: 0, 100, $10
items.oneOffStorage({ includedUsage?: number, billingUnits?: number, price?: number }) // Defaults: 0, 100, $10
items.tieredOneOffMessages({ includedUsage?: number, billingUnits?: number, tiers? }) // Graduated one-off
Consumable (pay-per-use/arrears)
items.consumableMessages({
includedUsage?: number, // Default: 0 (free before overage)
entityFeatureId?: string,
interval?: ProductItemInterval,
maxPurchase?: number, // Sets usage_limit = maxPurchase + includedUsage
price?: number, // Default: 0.10
}) // $0.10 per unit overage
items.consumableWords({
includedUsage?: number, // Default: 0
entityFeatureId?: string,
interval?: ProductItemInterval,
}) // $0.05 per unit overage
// Generic consumable for any feature
items.consumable({
featureId: string,
includedUsage?: number, // Default: 0
price?: number, // Default: 0.10
billingUnits?: number, // Default: 1
entityFeatureId?: string,
interval?: ProductItemInterval,
maxPurchase?: number,
})
Tiered Consumable (graduated pay-per-use)
items.tieredConsumableMessages({
includedUsage?: number, // Default: 0
billingUnits?: number, // Default: 1
tiers?: { to: number | "inf"; amount: number }[], // Default: [{ to: 500, amount: 0.10 }, { to: "inf", amount: 0.05 }]
})
Allocated (prorated seats)
items.allocatedUsers({ includedUsage?: number }) // Default: 0, $10/seat (TestFeature.Users)
items.allocatedMessages({ includedUsage?: number }) // Default: 0, $10/unit (TestFeature.Messages)
items.allocatedWorkflows({ includedUsage?: number }) // Default: 0, $10/workflow (TestFeature.Workflows)
items.freeAllocatedUsers({ includedUsage?: number, entityFeatureId? }) // Default: 5, no price (TestFeature.Users)
items.freeAllocatedWorkflows({ includedUsage?: number, entityFeatureId? }) // Default: 5, no price (TestFeature.Workflows)
Base Prices
items.monthlyPrice({ price?: number }) // Default: $20/month
items.annualPrice({ price?: number }) // Default: $200/year
items.oneOffPrice({ price?: number }) // Default: $50 one-time
Product Fixtures (products.*)
products.base() — FREE Product (no base price)
products.base({
items: ProductItem[],
id?: string, // Default: "base"
isDefault?: boolean, // Default: false — set true for default free tier
isAddOn?: boolean, // Default: false
trialDays?: number, // Optional trial
})
products.pro() — $20/month
products.pro({ items: ProductItem[], id?: string }) // Default ID: "pro"
products.premium() — $50/month
products.premium({ items: ProductItem[], id?: string }) // Default ID: "premium"
products.growth() — $100/month
products.growth({ items: ProductItem[], id?: string }) // Default ID: "growth"
products.ultra() — $200/month
products.ultra({ items: ProductItem[], id?: string }) // Default ID: "ultra"
products.proAnnual() — $200/year
products.proAnnual({ items: ProductItem[], id?: string }) // Default ID: "pro-annual"
products.proWithTrial() — $20/month + trial
products.proWithTrial({
items: ProductItem[],
id?: string, // Default: "pro-trial"
trialDays?: number, // Default: 7
cardRequired?: boolean,// Default: true
})
products.premiumWithTrial() — $50/month + trial
products.premiumWithTrial({
items: ProductItem[],
id?: string, // Default: "premium-trial"
trialDays?: number, // Default: 7
cardRequired?: boolean,// Default: true
})
products.baseWithTrial() — Free + trial
products.baseWithTrial({
items: ProductItem[],
id?: string, // Default: "base-trial"
trialDays?: number, // Default: 7
cardRequired?: boolean,// Default: false
})
products.defaultTrial() — Default + $20/month + trial (no card required)
products.defaultTrial({
items: ProductItem[],
id?: string, // Default: "default-trial"
trialDays?: number, // Default: 7
cardRequired?: boolean,// Default: false
})
is_default: true — auto-assigned to new customers.
products.oneOff() — $10 one-time
products.oneOff({ items: ProductItem[], id?: string }) // Default ID: "one-off"
products.recurringAddOn() — $20/month add-on
products.recurringAddOn({ items: ProductItem[], id?: string }) // Default ID: "addon"
is_add_on: true — doesn't replace existing products.
products.oneOffAddOn() — $10 one-time add-on
products.oneOffAddOn({ items: ProductItem[], id?: string }) // Default ID: "one-off-addon"
is_add_on: true.
Product Fixture Summary Table
| Product | Built-in Base Price | Default ID | Notes |
|---|---|---|---|
products.base |
None (free) | "base" | isDefault, isAddOn options |
products.pro |
$20/mo | "pro" | |
products.premium |
$50/mo | "premium" | |
products.growth |
$100/mo | "growth" | |
products.ultra |
$200/mo | "ultra" | |
products.proAnnual |
$200/yr | "pro-annual" | |
products.proWithTrial |
$20/mo + trial | "pro-trial" | trialDays, cardRequired |
products.premiumWithTrial |
$50/mo + trial | "premium-trial" | trialDays, cardRequired |
products.baseWithTrial |
None + trial | "base-trial" | cardRequired: false |
products.defaultTrial |
$20/mo + trial | "default-trial" | is_default: true, cardRequired: false |
products.oneOff |
$10 one-time | "one-off" | |
products.recurringAddOn |
$20/mo add-on | "addon" | is_add_on: true |
products.oneOffAddOn |
$10 one-time add-on | "one-off-addon" | is_add_on: true |
NEVER add items.monthlyPrice() to products.pro() — it already has $20/mo built in. Same for premium ($50), growth ($100), ultra ($200).
Common Patterns
Free Product
const messagesItem = items.monthlyMessages({ includedUsage: 100 });
const free = products.base({ items: [messagesItem] });
Paid Product (Pro)
const messagesItem = items.monthlyMessages({ includedUsage: 1000 });
const pro = products.pro({ items: [messagesItem] });
// Total: $20/mo + 1000 messages
Custom Pricing
const priceItem = items.monthlyPrice({ price: 30 });
const messagesItem = items.monthlyMessages({ includedUsage: 500 });
const custom = products.base({
id: "custom",
items: [priceItem, messagesItem],
});
// Total: $30/mo + 500 messages
Prepaid Product
const prepaidItem = items.prepaidMessages({
includedUsage: 0, // No free messages
billingUnits: 100, // 1 pack = 100 messages
price: 10, // $10 per pack
});
const prepaidPro = products.base({ id: "prepaid", items: [prepaidItem] });
Allocated Seats
const seatsItem = items.allocatedUsers({ includedUsage: 3 });
// 3 free seats, $10/seat overage (prorated)
const team = products.base({ id: "team", items: [seatsItem] });
Multiple Feature Types
const priceItem = items.monthlyPrice({ price: 50 });
const messagesItem = items.monthlyMessages({ includedUsage: 500 });
const seatsItem = items.allocatedUsers({ includedUsage: 5 });
const dashboardItem = items.dashboard();
const enterprise = products.base({
id: "enterprise",
items: [priceItem, messagesItem, seatsItem, dashboardItem],
});
Billing Behavior Summary
| Item Type | On Attach | On Update | On Cycle End |
|---|---|---|---|
| Base Price | Charged | Prorated | Renewed |
| Consumable | Not charged | Not charged | Overage billed |
| Allocated | Charged for overage | Prorated for current overage | Renewed |
| Prepaid | Charged (packs * price) | Refund old + Charge new | Renewed |
| One-Off | Charged once | N/A | Not renewed |