跳过主要内容
文档目录

Creem

如何设置和使用 Creem 处理支付和订阅

MinuteShip 使用 Creem 进行支付管理,支持一次性支付和订阅支付。

设置

MinuteShip 模板默认提供三种价格计划:免费计划、专业版订阅计划(月度/年度)和终身计划(一次性支付),按照以下步骤设置:

创建 Creem 账户

在 creem.io 创建 Creem 账户,无需信用卡即可注册。

获取 API 密钥

从 Creem 控制台获取您的 API 密钥:

  • 进入到 Creem 控制台 > Developers > API & Webhooks,新建 API Key
  • 复制 API 密钥(注意:测试模式以 creem_test_ 开头,生产模式以 creem_live_ 开头)
  • 将其保存到环境变量文件中作为 CREEM_API_KEY

设置 Webhook

设置 Webhook 并获取您的 Webhook 密钥:

  • 进入到 Creem 控制台 > Developers > API & Webhooks,新建 Webhook
  • 添加 Webhook URL:https://YOUR-DOMAIN.com/api/webhooks/creem
  • Creem 会自动监听所有支付和订阅相关事件,包括:
    • checkout.completed
    • subscription.active
    • subscription.paid
    • subscription.update
    • subscription.canceled
    • subscription.scheduled_cancel
    • subscription.expired
    • subscription.past_due
    • subscription.unpaid
    • subscription.paused
    • subscription.trialing
  • 保存 Webhook 设置,复制新建的 Webhook 签名密钥
  • 将其保存到环境变量文件中作为 CREEM_WEBHOOK_SECRET

创建产品和价格计划

在 Creem 中创建产品并设置价格计划:

  • 进入到 Creem 控制台 > Commerce > Products
  • 创建专业版订阅计划的产品:
    • 点击创建产品
    • 名称:专业版计划
    • 描述:具有订阅价格的高级功能
    • 添加月度订阅价格:
      • 价格:$9.90(货币选择 USD)
      • 计费类型:recurring
      • 计费周期:every-month
      • 保存并复制产品 ID(以 prod_ 开头),这将用于 VITE_CREEM_PRODUCT_PRO_MONTHLY
  • 复制月度订阅产品,这次添加年度订阅价格:
    • 价格:$99.00(货币选择 USD)
    • 计费类型:recurring
    • 计费周期:every-year
    • 保存并复制产品 ID(以 prod_ 开头),这将用于 VITE_CREEM_PRODUCT_PRO_YEARLY
  • 创建终身计划的产品:
    • 点击创建产品
    • 名称:终身计划
    • 描述:终身访问的一次性支付
    • 添加价格:
      • 价格:$199.00(货币选择 USD)
      • 计费类型:one_time
      • 保存并复制产品 ID(以 prod_ 开头),这将用于 VITE_CREEM_PRODUCT_LIFETIME

配置环境变量

添加以下环境变量:

.env

# 支付提供商
VITE_PAYMENT_PROVIDER=creem

# 设置为 'true' 使用 Creem 测试 API,省略或设置为 'false' 使用生产环境
# CREEM_DEBUG=true

# API Key 和 Webhook Secret
CREEM_API_KEY=creem_test_...
CREEM_WEBHOOK_SECRET=...

# Product IDs
VITE_CREEM_PRODUCT_PRO_MONTHLY=prod_...
VITE_CREEM_PRODUCT_PRO_YEARLY=prod_...
VITE_CREEM_PRODUCT_LIFETIME=prod_...

更新网站配置

更新 src/config/website.ts 中的 payment 部分,配置价格计划 — 金额、货币、周期和计划元数据。enable、provider 和 priceId 字段会根据环境变量(VITE_PAYMENT_PROVIDER 和 VITE_CREEM_PRODUCT_*)自动解析,无需硬编码。

您必须配置此部分,使其与您在 Creem 中创建的产品一致:

src/config/website.ts

payment: {
  enable: isPaymentEnabled,              // ← 自动:当 VITE_PAYMENT_PROVIDER 设置时为 true
  provider: isPaymentEnabled ? paymentProvider : undefined, // ← 自动:'creem'
  price: {
    plans: {
      free: {
        id: 'free',
        prices: [],
        isFree: true,
        isLifetime: false,
      },
      pro: {
        id: 'pro',
        prices: [
          {
            type: 'subscription',
            priceId: priceIds.proMonthly,  // ← 自动:来自 VITE_CREEM_PRODUCT_PRO_MONTHLY
            amount: 990,                   // 金额,单位为分($9.90)
            currency: 'USD',
            interval: 'month',
          },
          {
            type: 'subscription',
            priceId: priceIds.proYearly,   // ← 自动:来自 VITE_CREEM_PRODUCT_PRO_YEARLY
            amount: 9900,                  // 金额,单位为分($99.00)
            currency: 'USD',
            interval: 'year',
          },
        ],
        isFree: false,
        isLifetime: false,
        popular: true,
      },
      lifetime: {
        id: 'lifetime',
        prices: [
          {
            type: 'one_time',
            priceId: priceIds.lifetime,     // ← 自动:来自 VITE_CREEM_PRODUCT_LIFETIME
            amount: 19900,                  // 金额,单位为分($199.00)
            currency: 'USD',
            allowPromotionCode: true,
          },
        ],
        isFree: false,
        isLifetime: true,
      },
    },
  },
},

Note

如果您正在设置环境,现在可以回到环境配置文档并继续。本文档的其余部分可以稍后阅读。


开发环境

保持应用使用正常的本地地址,仅通过 HTTPS 隧道接收 Webhook。先在 .env.local 中配置测试模式变量:

.env.local

VITE_BASE_URL=http://localhost:3000
VITE_PAYMENT_PROVIDER=creem
CREEM_DEBUG=true
CREEM_API_KEY=creem_test_...
CREEM_WEBHOOK_SECRET=...
VITE_CREEM_PRODUCT_PRO_MONTHLY=prod_...
VITE_CREEM_PRODUCT_PRO_YEARLY=prod_...
VITE_CREEM_PRODUCT_LIFETIME=prod_...

启动应用,然后在另一个终端使用 Cloudflared 暴露 3000 端口:

pnpm dev

# 在第二个终端中运行
cloudflared --config /dev/null tunnel \
  --url http://localhost:3000 \
  --no-autoupdate \
  --protocol quic

在 Creem 控制台切换到测试模式,将生成的地址注册为 https://xxxx.trycloudflare.com/api/webhooks/creem。该隧道只用于 Creem 的 Webhook 请求;本地访问网站时仍保持 VITE_BASE_URL=http://localhost:3000。

成功结账后,Creem 会通过 /settings/payment?request_id=... 将浏览器带回应用。MinuteShip 会等待具有相同引用的支付记录,然后跳转到 /settings/billing。

模板还提供 Creem 测试模式 E2E 套件:

pnpm e2e:creem

Note

Creem 提供完整的测试环境。使用以 creem_test_ 开头的 API 密钥时,所有操作都在沙盒中进行,不会产生真实交易。

生产环境

  1. 进入到 Creem 控制台 > Developers > API & Webhooks
  2. 添加生产环境的 Webhook URL:https://YOUR-DOMAIN.com/api/webhooks/creem
  3. 将 API 密钥从测试密钥(creem_test_)切换为生产密钥(creem_live_)
  4. 确保您的环境变量已更新为生产密钥和产品 ID

Webhook 事件

Creem 支持以下 Webhook 事件:

事件描述
checkout.completed结账会话完成
subscription.active新订阅创建(仅用于同步)
subscription.paid订阅支付成功(用于激活访问权限)
subscription.update订阅更新(计划变更、周期续期)
subscription.canceled订阅被取消
subscription.scheduled_cancel订阅标记为在当前计费周期结束时取消
subscription.past_due支付失败,订阅等待重试
subscription.expired支付已过期但仍可恢复;在本地记录为 past_due
subscription.unpaid收款重试已经用尽,访问权限暂停
subscription.trialing订阅进入试用期
subscription.paused订阅被暂停

Note

Creem 推荐使用 subscription.paid 事件来激活用户访问权限,而非 subscription.active 事件。subscription.active 仅用于数据同步。

只有 subscription.canceled 是终态。处于 expired 或 past-due 状态的订阅仍可在支付成功后恢复为有效状态。

Webhook 响应与重试

结果HTTP 状态码
支付功能未启用200
Creem 不是当前配置的支付提供商404
缺少负载或 creem-signature 请求头400
签名无效400
处理成功或安全地忽略重复事件200
支付提供商或数据库临时故障500

如果生命周期事件早于 checkout.completed 到达,支付记录还不存在,MinuteShip 会返回 500 让 Creem 重试。处理逻辑具有幂等性,重复投递不会创建额外支付记录。

Note

模板目前不处理 Creem 的退款和争议事件。在 Creem 中退款不会自动撤销访问权限;请手动撤销,或在 src/payment/provider/creem.ts 中添加处理逻辑。

客户门户

每次成功支付后,您的客户会收到一封包含客户门户链接的邮件。客户门户允许他们:

  • 查看订阅详情
  • 管理订阅(升级、暂停、取消)
  • 查看支付历史
  • 更新付款方式

测试卡

要测试 Creem 集成,请使用 Creem 的测试模式和测试信用卡:

  • 4242 4242 4242 4242 - 成功支付
  • 4000 0000 0000 0002 - 支付失败

Note

使用测试模式(API 密钥以 creem_test_ 开头)时,所有交易都在沙盒环境中进行,不会产生真实费用。

最佳实践

  1. 保护 API 密钥:永远不要在客户端代码中暴露您的 Creem API 密钥
  2. 验证 Webhook 签名:始终验证 Webhook 事件的 creem-signature 签名
  3. 优雅处理错误:当支付失败时提供用户友好的错误消息
  4. 彻底测试 Webhooks:确保所有 Webhook 事件都得到正确处理

参考资料