feat: integrate Stripe payment with subscription plans

Add Stripe payment integration across the project:

- Add @stripe/stripe-js dependency
- Configure Stripe environment variables (secret key, publishable key, webhook secret) for dev, local, and production environments
- Add Product/Price IDs for monthly, quarterly, and annual subscription tiers
- Extend subscription plan data with voiceMinutesPerDay quotas (30/45/60 minutes per tier)
- Update .gitignore to exclude implementation_plan.md
This commit is contained in:
2026-06-16 10:21:02 +08:00
parent 17741320ff
commit 0548a08cbb
25 changed files with 1220 additions and 26 deletions
+48
View File
@@ -0,0 +1,48 @@
/**
* Stripe product / price id 映射
*
* 为什么不直接存到 `subscription-plans.ts`
* - product/price id 是部署环境配置(不同环境不同 product)
* - 套餐本身的 `name` / `price` / `highlight` 是业务常量(不随环境变)
* - 分离后,套餐表只包含业务字段,产品 id 走 env —— 避免硬编码 + 生产安全
*
* env 命名规范:
* - STRIPE_PRODUCT_<PLAN_ID_UPPER> → Product ID
* - STRIPE_PRICE_<PLAN_ID_UPPER> → Price ID
* - 例:STRIPE_PRODUCT_MONTHLY, STRIPE_PRICE_QUARTERLY
*/
import type { SubscriptionPlanId } from "@/data/constants/subscription-plans";
/**
* 获取 Stripe Product ID(从 env
*
* @throws Error 如果 env 未配置(避免 silent failure
*/
export function getPlanProductId(planId: SubscriptionPlanId): string {
const envKey = `STRIPE_PRODUCT_${planId.toUpperCase()}`;
const productId = process.env[envKey];
if (!productId || productId.startsWith("prod_replace_")) {
throw new Error(
`Stripe product id is not configured. Set ${envKey} in .env.local.`,
);
}
return productId;
}
/**
* 获取 Stripe Price ID(从 env
*
* Price ID 是真正用在 `stripe.checkout.sessions.create({ line_items: [{ price: "price_xxx" }] })` 里的
*
* @throws Error 如果 env 未配置
*/
export function getPlanPriceId(planId: SubscriptionPlanId): string {
const envKey = `STRIPE_PRICE_${planId.toUpperCase()}`;
const priceId = process.env[envKey];
if (!priceId || priceId.startsWith("price_replace_")) {
throw new Error(
`Stripe price id is not configured. Set ${envKey} in .env.local.`,
);
}
return priceId;
}