mirror of
https://github.com/stack-auth/stack.git
synced 2026-06-13 21:01:21 +08:00
### Context We're looking at implementing plan pricing. While doing so, we encountered a problem with Stripe. **Problem:** when we run a stripe operation (purchase), the product info is encoded as part of the stripe metadata request. Stripe encodes metadata as key-value pairs, and the [value has a limit of 500 chars](https://docs.stripe.com/metadata#data). We do this because once we run the stripe operation, stripe fires a webhook event which is caught by our stripe webhook handler syncStripeSubscriptions. This gets the stripe metadata info from the event and then updates our db in prisma. ### Summary of Changes We add a `ProductVersion` table and only pass the `productVersionId` via stripe metadata instead of the whole product json. This `productVersionId` is created by hashing the `productJson`. Since the same product may be ordered differently without being intrinsically different, we add a helper function for ensuring a canonical order to the json. We also pass tenancy id and product id to the table. Since there are existing subscriptions which used to pass the productJson via metadata, we ensure backwards compatibility.
66 lines
1.5 KiB
TypeScript
66 lines
1.5 KiB
TypeScript
/**
|
|
* Plan configuration for Stack Auth pricing tiers.
|
|
*
|
|
* This file defines the limits for each plan and the item IDs used to track them.
|
|
* Import these constants in seed.ts and backend code for limit enforcement.
|
|
*/
|
|
|
|
export const UNLIMITED = 1_000_000_000;
|
|
|
|
/**
|
|
* Item IDs used across the codebase for tracking plan limits.
|
|
*/
|
|
export const ITEM_IDS = {
|
|
seats: "dashboard_admins",
|
|
authUsers: "auth_users",
|
|
emailsPerMonth: "emails_per_month",
|
|
analyticsTimeoutSeconds: "analytics_timeout_seconds",
|
|
analyticsEvents: "analytics_events",
|
|
} as const;
|
|
|
|
export type ItemId = typeof ITEM_IDS[keyof typeof ITEM_IDS];
|
|
|
|
/**
|
|
* The offerings/limits included in a plan.
|
|
*/
|
|
export type PlanProductOfferings = {
|
|
seats: number,
|
|
authUsers: number,
|
|
emailsPerMonth: number,
|
|
analyticsTimeoutSeconds: number,
|
|
analyticsEvents: number,
|
|
};
|
|
|
|
/**
|
|
* Plan limits by plan ID.
|
|
*/
|
|
export const PLAN_LIMITS: {
|
|
free: PlanProductOfferings,
|
|
team: PlanProductOfferings,
|
|
growth: PlanProductOfferings,
|
|
} = {
|
|
free: {
|
|
seats: 1,
|
|
authUsers: 10_000,
|
|
emailsPerMonth: 1_000,
|
|
analyticsTimeoutSeconds: 10,
|
|
analyticsEvents: 100_000,
|
|
},
|
|
team: {
|
|
seats: 4,
|
|
authUsers: 50_000,
|
|
emailsPerMonth: 25_000,
|
|
analyticsTimeoutSeconds: 60,
|
|
analyticsEvents: 500_000,
|
|
},
|
|
growth: {
|
|
seats: UNLIMITED,
|
|
authUsers: UNLIMITED,
|
|
emailsPerMonth: 25_000,
|
|
analyticsTimeoutSeconds: 300,
|
|
analyticsEvents: 1_000_000,
|
|
},
|
|
};
|
|
|
|
export type PlanId = keyof typeof PLAN_LIMITS;
|