Add A Paid Plan
Add a plan, provider Price IDs, display copy, and entitlements.
Plan configuration lives in src/modules/billing/config/plan.ts. Pricing UI and Checkout both resolve prices through planId + priceId, so new plans need provider Price IDs, translation copy, and entitlements.
Create Product and Price in the provider
The default provider is Stripe:
const paymentConfig = {
provider: "stripe",
};Create monthly, yearly, or one-time prices in the provider dashboard first.
Add environment variables
Add public Price IDs to .env.local and .env.example:
NEXT_PUBLIC_TEAM_MONTHLY_PRICE_ID=
NEXT_PUBLIC_TEAM_YEARLY_PRICE_ID=Only put Price IDs here, never secret keys.
Extend plans
{
id: "team",
rank: 30,
prices: [
{
priceId: process.env.NEXT_PUBLIC_TEAM_MONTHLY_PRICE_ID ?? "",
billingModel: BillingModel.Subscription,
amount: 4900,
currency: "usd",
interval: PricingInterval.Month,
entitlements: [
{
entitlementType: EntitlementType.Quota,
balance: 3000,
priority: 10,
isRollOver: false,
resourceType: EntitlementResourceType.Credit,
refreshInterval: RefreshInterval.Monthly,
},
],
},
],
isFree: false,
isLifetime: false,
storageLimit: 100 * 1024 * 1024 * 1024,
}rank controls ordering and usually represents upgrade level.
Add pricing copy
Pricing cards read from:
src/i18n/messages/en/pricing.json
src/i18n/messages/zh/pricing.jsonAdd plans.team.name, description, features, and limits as required by the component.
Align entitlements and storage
prices[].entitlements grants resources after webhook success. plan.storageLimit is mostly for Dashboard display. Upload API enforcement requires a Storage entitlement.
Configure webhook
Provider webhooks must point to:
POST /api/payment/webhookSet the provider signing secret, such as STRIPE_WEBHOOK_SECRET.
After adding a plan, verify that pricing renders, Checkout opens, and webhook success creates payment_subscriptions / user_entitlements.