products.ts
Products, plans, displayed prices, and access granted after purchase.
config/products.ts exports a Record<string, ProductConfig> object. Top-level keys are product IDs, and each product's plans object is keyed by plan ID. You can define your own product and plan IDs. Names such as saavo_starter, license, and startup_monthly are only template examples.
Replace the Price IDs
All six default priceId values end in _replace_me and are placeholders. Before enabling Checkout, you must replace them with real Price IDs from the current Stripe account. The displayed salePrice does not determine the charge. The Stripe Price defines the actual amount and billing interval.
Product keys
Product keys must be nonempty strings. You can add, remove, or change them to suit your project. The configured product keys form the ProductId union type. If there are no products, you can export an empty object.
Product objects
Prop
Type
plans
plans contains the product's plans and has the type Record<string, PlanConfig>. Plan keys must be nonempty strings. Different products can use different keys. The configured plan keys form the PlanIdOf<P> and PlanId union types.
Plan objects
Prop
Type
Subscription plan fields
When type is recurring, the following fields are also required:
Prop
Type
features
An array of plan selling points with the type LocalizedText[]. The UI displays them in array order. Omit this field if you do not need to display selling points.
presentation
Purchase page display settings. These affect only the text on public product and purchase pages. They do not change prices, purchase status, or access rights.
Prop
Type
purchaseFacts has the type LocalizedText[] and contains information buyers should know. specifications is an array of specifications, each containing label: LocalizedText, value: LocalizedText, and an optional tone: "default" | "positive". highlights is an array of highlight cards, each containing icon, title, and description. icon accepts documentation, access, source, or payment.
When you specify presentation, you must include purchaseFacts, specifications, and highlights. These arrays can be empty.
couponOffers
An array of dynamic coupon offers activated through /offer/:id links. If specified, the array must contain at least one entry.
Prop
Type
Offers can also override features and presentation, using the same format as the plan fields. Offers cannot change priceId, billing intervals, purchase status, or access rights, and cannot contain nested offers.
access
The roles and entitlements granted after a successful purchase. This field is optional for one-time plans and required for subscription plans. For a subscription plan, at least one of the roles and entitlements arrays must be nonempty.
roles
An array of roles. Each entry can be a role key or an object with grant parameters.
Prop
Type
config accepts priority: number, description: string, metadata: Record<string, unknown>, and the Unix millisecond timestamps effective.startsAt and effective.endsAt.
entitlements
An array of entitlements. One-time plans can use entitlement keys or objects. Subscription plans must use objects.
Prop
Type
The entitlement object's config is required for subscription plans.
Prop
Type
When kind is numeric, you can also specify:
Prop
Type
reset defines the quota reset period. alignment: "calendar" supports days, weeks, months, and years. alignment: "fixed" supports only days and weeks. count must be a positive integer. quotaUsedStrategy accepts clear, drain, or keep. You can also use { mode: "override", quotaUsed: number } to specify the used quota.
Template defaults
The template's default configuration contains only one product. The product and plan IDs in the tables are examples that you can replace to suit your business. Before renaming or removing products, you must also update references in pages, purchase flows, the affiliate program, upgrade suggestions, and other affected features. Do not assume these features will still work after changing only this configuration.
| Product ID | name.value | name.key | Plan count |
|---|---|---|---|
saavo_starter | Sample SaaS subscription | components.billing.product.name | 6 |
description.value is Example monthly and yearly plans to replace with your product terms., and its text key is components.billing.product.description.
Plans
| Plan ID | Title | Interval | salePrice | priceId | Flags |
|---|---|---|---|---|---|
license | Hobby | Monthly | $19 | price_hobby_monthly_replace_me | default: true |
hobby_yearly | Hobby | Yearly | $190 | price_hobby_yearly_replace_me | — |
startup_monthly | Startup | Monthly | $49 | price_startup_monthly_replace_me | recommended: true |
startup_yearly | Startup | Yearly | $490 | price_startup_yearly_replace_me | recommended: true |
enterprise_monthly | Enterprise | Monthly | $149 | price_enterprise_monthly_replace_me | — |
enterprise_yearly | Enterprise | Yearly | $1,490 | price_enterprise_yearly_replace_me | — |
All six plans have type set to recurring, intervalCount set to 1, and allowRepurchase set to false. The three yearly plans also have a valueHint of “Two months free”.
Access rights
| Plans | Role key | Entitlement key | Entitlement type | Priority |
|---|---|---|---|---|
license, hobby_yearly | premium | starter_download | boolean | 100 |
startup_monthly, startup_yearly | premium | starter_download | boolean | 200 |
enterprise_monthly, enterprise_yearly | premium | starter_download | boolean | 300 |
premium and starter_download are also example keys in the template's default configuration, defined in roles.ts and entitlements.ts, respectively. When changing these definitions, update their references in this file as well.