Logo文档
支付

支付配置

学习如何在 NEXTDEVKIT 中为 Stripe 和 Creem 配置定价和支付计划

💰 支付类型

NEXTDEVKIT 支持两种支付类型,两个提供商都支持:

export enum PaymentType {
  SUBSCRIPTION = "subscription",
  ONE_TIME = "one-time",
}

export enum PlanInterval {
  MONTH = "monthly",
  YEAR = "yearly",
}

🎯 提供商配置

首先,您需要在 src/config/index.ts 中设置支付提供商:

payment: {
  provider: "stripe", // 或 "creem"
  currency: "USD",
  yearlyDiscount: 20,
  redirectAfterCheckout: "/app/dashboard",
  plans: {
    // ... 您的计划配置
  }
}

在提供商之间切换

要从 Stripe 切换到 Creem 或反之:

  1. 更新提供商
// 对于 Stripe
provider: "stripe"

// 对于 Creem  
provider: "creem"
  1. 更新环境变量
# 对于 Stripe
STRIPE_SECRET_KEY="sk_test_your_key"
STRIPE_WEBHOOK_SECRET="whsec_your_secret"

# 对于 Creem
CREEM_API_KEY="ck_test_your_key"
CREEM_WEBHOOK_SECRET="your_webhook_secret"
  1. 更新价格/产品 ID
# 相同的环境变量名对两个提供商都有效
NEXT_PUBLIC_PRICE_ID_PRO_MONTHLY="your_monthly_id"
NEXT_PUBLIC_PRICE_ID_PRO_YEARLY="your_yearly_id"
NEXT_PUBLIC_PRICE_ID_LIFETIME="your_lifetime_id"

📋 定义计划和产品

您可以在 NEXTDEVKIT 项目的配置文件中管理计划和产品。您可以定义不同类型的计划:

🆓 免费计划

免费计划是未购买任何计划的用户的默认计划,或者可以用于访问产品的限制版本。

由于这不是付费计划,您不需要定义任何价格或为其附加产品 ID。

export const appConfig = {
  payment: {
    plans: {
      free: {
        id: "free",
        isFree: true,
      },
    }
  },
};

🏢 企业计划

企业计划不是真正的计划,但会在定价表中显示一个联系表单链接,客户可以联系您以获得产品访问权限。

由于这不是付费计划,您不需要定义任何价格或为其附加产品 ID。

export const appConfig = {
  payment: {
    plans: {
      enterprise: {
        id: "enterprise",
        isEnterprise: true,
        highlighted: true,
      },
    }
  },
};

🔄 订阅计划和 💎 一次性购买计划

计划代表您应用程序的产品或服务,每个计划都是定价表中的一列。它具有以下属性:

  • popular:如果此计划应该突出显示为推荐 ⭐
  • highlighted:从定价表中突出显示计划 ✨
  • prices:为此计划定义价格 💰

一个计划可以有多个价格,例如月付和年付价格和/或您支持的每种货币。

价格具有以下属性:

  • type:价格类型,可以是 PaymentType.SUBSCRIPTIONPaymentType.ONE_TIME
  • priceId:来自您的支付提供商的产品 ID 🆔
  • interval:价格间隔,可以是 PlanInterval.MONTHPlanInterval.YEAR
  • amount:价格金额 💵
  • trialPeriodDays:试用期天数,如果您不想提供试用期请省略 🎁

完整计划配置示例

payment: {
  provider: "stripe", // 或 "creem"
  currency: "USD",
  yearlyDiscount: 20,
  redirectAfterCheckout: "/app/dashboard",
  plans: {
    free: {
      id: "free",
      isFree: true,
    },
    pro: {
      id: "pro",
      prices: [
        {
          type: PaymentType.SUBSCRIPTION,
          priceId: process.env.NEXT_PUBLIC_PRICE_ID_PRO_MONTHLY as string,
          amount: 9.9,
          interval: PlanInterval.MONTH,
          trialPeriodDays: 7,
        },
        {
          type: PaymentType.SUBSCRIPTION,
          priceId: process.env.NEXT_PUBLIC_PRICE_ID_PRO_YEARLY as string,
          amount: 99,
          interval: PlanInterval.YEAR,
          trialPeriodDays: 30,
        },
      ],
      popular: true,
    },
    lifetime: {
      id: "lifetime",
      prices: [
        {
          type: PaymentType.ONE_TIME,
          priceId: process.env.NEXT_PUBLIC_PRICE_ID_LIFETIME as string,
          amount: 399,
        },
      ],
      isLifetime: true,
    },
    enterprise: {
      id: "enterprise",
      isEnterprise: true,
      highlighted: true,
    },
  },
}

🔧 提供商特定说明

Stripe 配置

  • priceId:使用 Stripe 价格 ID(以 price_ 开头)
  • 产品:在 Stripe 仪表板中创建产品和价格
  • Webhooks:在 /api/webhooks/stripe 设置 webhook 端点

Creem 配置

  • priceId:使用 Creem 产品 ID(以 prod_ 开头)
  • 产品:在 Creem 仪表板中创建产品
  • Webhooks:在 /api/webhooks/creem 设置 webhook 端点

📝 定价表的计划信息

您可以在 src/config/marketing/pricing.ts 中为每个计划定义定价表的信息。

您可以为每个计划定义标题、描述和功能数组。

我们建议您使用 t() 函数获取计划信息的翻译,然后在 /messages/ 文件夹中定义翻译。

export async function getPricingConfig(): Promise<PricingConfig> {
  const t = await getTranslations("pricing");
  const priceConfig = appConfig.payment;
  const plans: PricePlan[] = [];

  if (priceConfig.plans.free) {
    plans.push({
      ...priceConfig.plans.free,
      name: t("products.free.title"),
      description: t("products.free.description"),
      features: [
        t("products.free.features.feature1"),
        t("products.free.features.feature2"),
        t("products.free.features.feature3"),
      ],
    });
  }

  if (priceConfig.plans.pro) {
    plans.push({
      ...priceConfig.plans.pro,
      name: t("products.pro.title"),
      description: t("products.pro.description"),
      features: [
        t("products.pro.features.feature1"),
        t("products.pro.features.feature2"),
        t("products.pro.features.feature3"),
        t("products.pro.features.feature4"),
      ],
    });
  }

  if (priceConfig.plans.lifetime) {
    plans.push({
      ...priceConfig.plans.lifetime,
      name: t("products.lifetime.title"),
      description: t("products.lifetime.description"),
      features: [
        t("products.lifetime.features.feature1"),
        t("products.lifetime.features.feature2"),
        t("products.lifetime.features.feature3"),
      ],
    });
  }

  if (priceConfig.plans.enterprise) {
    plans.push({
      ...priceConfig.plans.enterprise,
      name: t("products.enterprise.title"),
      description: t("products.enterprise.description"),
      features: [
        t("products.enterprise.features.feature1"),
        t("products.enterprise.features.feature2"),
        t("products.enterprise.features.feature3"),
        t("products.enterprise.features.feature4"),
        t("products.enterprise.features.feature5"),
      ],
    });
  }

  return {
    title: t("title"),
    subtitle: t("subtitle"),
    frequencies: [t("frequencies.monthly"), t("frequencies.yearly")],
    yearlyDiscount: priceConfig.yearlyDiscount,
    plans,
  };
}

🧪 测试配置

测试环境变量

# Stripe 测试模式
STRIPE_SECRET_KEY="sk_test_..."
STRIPE_WEBHOOK_SECRET="whsec_test_..."

# Creem 测试模式  
CREEM_API_KEY="ck_test_..."
CREEM_WEBHOOK_SECRET="test_webhook_secret"

# 测试价格/产品 ID
NEXT_PUBLIC_PRICE_ID_PRO_MONTHLY="test_price_monthly"
NEXT_PUBLIC_PRICE_ID_PRO_YEARLY="test_price_yearly"
NEXT_PUBLIC_PRICE_ID_LIFETIME="test_price_lifetime"

🔧 配置故障排除

常见配置问题

找不到提供商

  • 检查 provider 设置为 "stripe" 或 "creem"
  • 验证存在正确的提供商实现

缺少环境变量

  • 确保设置了所有必需的环境变量
  • 检查变量名中的拼写错误
  • 验证测试与生产密钥

无效的价格/产品 ID

  • 确认 ID 在您的支付提供商仪表板中存在
  • 检查 ID 格式是否符合提供商要求
  • 验证测试与生产 ID

计划配置错误

  • 确保计划结构符合预期格式
  • 检查必需字段是否存在
  • 验证数据类型与接口定义匹配

🎯 下一步

现在您的支付配置已设置: