# Creem 支付对接 (/zh/docs/billing-creem-payment)



面向 Realm Admin 的 Creem 对接操作指南。跟着做完，你的用户就能通过 Creem 完成订阅支付。

## 给谁看 [#给谁看]

负责管理 Herald 计费配置的 Realm Admin。不需要写代码，全程在管理后台操作。

## 前置条件 [#前置条件]

* Herald 管理后台可以正常访问，你有管理员账号
* 已创建至少一个 Realm 和一个 Client App
* 已在 [Creem](https://creem.io) 注册账号，拿到 API Key（测试环境用 `ck_test_` 开头的 key）

## Creem 和 Stripe 的区别 [#creem-和-stripe-的区别]

Creem 没有类似 Stripe Product/Price 的 metadata 功能。这意味着 Herald 无法像 Stripe 那样从支付方商品自动导入 entitlement\_key。Creem 的商品信息需要你在 Herald 中手动配置 entitlement 和积分策略。

其他流程（Checkout、Webhook、订阅投影）和 Stripe 基本一致。

## 核心概念 [#核心概念]

Herald 不维护本地商品目录。你在 Creem Dashboard 创建 Product，拿到 Product ID，然后在 Herald 里配置 Entitlement Mapping。用户付款后，Creem 通过 Webhook 通知 Herald，Herald 用 metadata 里的 `herald_entitlement_key`（写入 Checkout 请求，Creem 在 Webhook 中原样返回）找到对应的映射，创建订阅投影、发放积分。

数据流向：Creem Product → Herald Entitlement Mapping（手动配置） → 用户 Checkout → Creem Webhook（返回 herald\_\* metadata）→ Herald 订阅投影 + 积分发放

***

## Step 1: 配置 Creem API Key 和 Webhook [#step-1-配置-creem-api-key-和-webhook]

在 Herald 管理后台配置 Creem 的连接信息。

1. 在左侧菜单找到 **Payment Providers**，点击进入
2. 找到 **Creem**，点击 **Configure**
3. 填写配置表单：
   * **API Key**（必填）：填入在 Creem 后台拿到的 API Key，测试环境用 `ck_test_` 开头的 key
   * **Timeout**（可选）：请求超时时间（秒）
   * **Webhook Secret**（必填）：Webhook 签名验证密钥，创建 Webhook 后获取，见下方
4. 点击 **Save**

保存有效凭证后 Creem 即启用，没有单独的启用开关。

### 创建 Creem Webhook [#创建-creem-webhook]

1. 打开 Creem Dashboard 的 Webhooks 页面

2. 点击 **Create Webhook**

3. 填写：
   * **Name**：Herald Webhook（随意取名）
   * **URL**：`https://你的Herald域名/api/third/pay/{realmId}/creem/webhooks`
     * 把 `{realmId}` 替换成你的 realm ID，比如 `admin`

4. 选择事件——必须勾选以下 12 个，缺任何一个会导致对应的支付流程断裂：

### 结账事件 [#结账事件]

| 事件                   | Herald 处理逻辑                                         |
| -------------------- | --------------------------------------------------- |
| `checkout.completed` | 验证结账 metadata，记录审计状态。订阅创建推迟到 `subscription.paid` 事件 |

### 订阅事件 [#订阅事件]

| 事件                              | Herald 处理逻辑             |
| ------------------------------- | ----------------------- |
| `subscription.paid`             | 订阅首次支付或续费成功，创建订阅投影，发放积分 |
| `subscription.update`           | 订阅升降级处理，调整积分            |
| `subscription.canceled`         | 订阅取消（即时或期末取消），回收未使用积分   |
| `subscription.active`           | 订阅激活，同步订阅状态             |
| `subscription.trialing`         | 订阅试用，同步订阅状态             |
| `subscription.paused`           | 订阅暂停，同步订阅状态             |
| `subscription.past_due`         | 订阅逾期，同步订阅状态             |
| `subscription.scheduled_cancel` | 订阅计划取消，同步订阅状态并设置到期时间    |
| `subscription.expired`          | 订阅过期，取消订阅并回收积分          |

### 退款与争议事件 [#退款与争议事件]

| 事件                | Herald 处理逻辑    |
| ----------------- | -------------- |
| `refund.created`  | 退款，按退款类型回收积分   |
| `dispute.created` | 争议发起，标记订阅为争议状态 |

5. 创建完成后复制 **Signing secret**，回到 Herald Payment Providers → Creem 配置，填入 **Webhook Secret**

### 测试环境和生产环境 [#测试环境和生产环境]

Creem 的 API Key 前缀决定了请求发到哪里：

* `ck_test_` 开头：自动使用 Creem 测试环境 `test-api.creem.io`，不会产生真实扣款
* 其他前缀：使用 Creem 生产环境 `api.creem.io`，会真实扣款

开发阶段用测试 key，上线前换成生产 key。

## Step 2: 在 Creem 创建商品 [#step-2-在-creem-创建商品]

在 Creem Dashboard 创建你的 Product，记下 **Product ID**（形如 `prod_xxxxxxxx`）。Creem 的商品价格等信息在 Creem 后台管理。

Herald 不会自动知道你在 Creem 创建了什么商品。下一步需要同步。

## Step 3: 同步 Creem 商品到 Herald [#step-3-同步-creem-商品到-herald]

1. 在 Herald 管理后台左侧菜单点击 **Entitlement Mappings**
2. 点击 **Sync Provider Products** 按钮
3. 选择支付方 **Creem**
4. 等待同步完成

同步完成后你会看到从 Creem 拉取的商品列表。每条记录包含 External Product ID、自动生成的 Entitlement Key（格式为 `creem-{normalized_product_id}`），以及它默认归属的积分账户（Realm 的注册接收池）。列表的主标签用产品名，缺失时回退到 External Product ID。

Creem 价格按原值展示。Creem 返回的价格本身就是显示值（字符串如 `"9.99"`），展示时直接用，不做 Stripe 那种最小货币单位整数（分）的除以 100 换算。币种跟随 Creem 返回值。两条换算分支（Stripe 与 Creem）是分开的，不会互相污染。

因为 Creem 没有 Product metadata，同步只能拉取商品 ID 和基本信息。entitlement\_key 和积分策略需要手动配置。Creem Product 对象在 `/v1/products/search` 响应里没有原生 `metadata` 字段（Creem 的 `metadata` 是 checkout 会话级，不是 Product 级），所以 Herald 不为 Creem 同步 metadata，mapping 详情里 metadata 一栏按空处理，不伪造。计费周期如果 Creem 响应里有 `billing_period`（形如 `every-month`），前端做文案映射展示，缺失时显示"—"。

## Step 4: 配置 Entitlement Mapping [#step-4-配置-entitlement-mapping]

这一步是 Creem 对接和 Stripe 的主要差异。Stripe 可以从 metadata 导入 entitlement\_key，Creem 不行，必须在 Herald 中手动配置。

1. 在 **Entitlement Mappings** 列表中，找到要配置的 Creem 映射
2. 点击编辑，填写以下字段：
   * **Entitlement Key**（必填）：改成你能识别的名字，比如 `pro-monthly`。这个 key 在 SDK 查询、积分策略、Webhook 处理中都会用到
   * **Billing Type**：Recurring（订阅）或 OneTime（一次性购买）
   * **Points Per Period**：每个计费周期发放多少积分
   * **Grant On Subscribe**：首次订阅时是否发放积分
   * **Validity Days**：积分有效期（天），0 或不填表示永不过期
   * **Enabled**：是否启用。禁用后 Webhook 仍更新订阅投影，但不触发积分发放
3. 点击保存

### 归属积分账户 [#归属积分账户]

每条映射必须挂在某个积分账户上。同步进来的映射默认挂在本 Realm 的注册接收池账户。如果这个套餐属于某条独立业务线，去 **积分账户** 页面把它挪到对应的积分账户。

积分账户决定了两件事：用户购买后积分进哪个池、哪些 Client App 能消费这些积分。积分账户的概念见[计费架构](/docs/billing-overview#积分账户)。

## Step 5: 用户支付流程 [#step-5-用户支付流程]

配置完成后，用户侧的流程如下。

1. 用户在你的应用中选择套餐
2. 你的应用调用 Herald 的 Checkout API（传入 `entitlement_key`、`payment_provider=creem`）
3. Herald 查 Entitlement Mapping 找到 Creem 商品 ID
4. Herald 调 Creem API 创建支付会话，metadata 写入 `herald_entitlement_key`、`herald_user_id`、`herald_client_app_id`、`herald_realm_id`、`herald_billing_kind`
5. 返回 Creem 支付页面 URL
6. 用户在 Creem 页面完成付款
7. Creem 发 Webhook（`checkout.completed` → `subscription.paid`）
8. Herald 从 metadata 解析 entitlement\_key，创建订阅投影、发放积分
9. 后续续费由 Creem 自动处理，发 `subscription.paid` 事件，Herald 发放续费积分

Creem 的 Webhook 会返回 Checkout 请求时写入的 metadata。Herald 靠这个来识别用户和 entitlement。

## Step 6: 确认结果 [#step-6-确认结果]

### 查看 Entitlement Mapping 状态 [#查看-entitlement-mapping-状态]

1. 在 **Entitlement Mappings** 页面，确认映射的 Enabled 状态和积分策略配置

### 查看订阅状态 [#查看订阅状态]

1. 在左侧菜单点击 **Subscriptions**，查看订阅投影列表
2. 确认 `entitlement_key`、`status`、`payment_provider` 字段正确

### 查看变更历史 [#查看变更历史]

1. 点击 **Subscription History**，查看订阅的完整变更时间线

Webhook 正常回调的话，状态会在几秒内更新。迟迟没变化，看下面的常见问题。

***

## 常见问题 [#常见问题]

### Webhook 没收到 [#webhook-没收到]

确保 Herald 部署的公网地址可以从 Creem 服务器访问。Webhook 端点地址格式：`https://你的域名/api/third/pay/{realmId}/creem/webhooks`

在 Creem Dashboard 的 Webhook 配置里检查 URL 是否正确。

### 套餐在应用里看不到 [#套餐在应用里看不到]

1. Entitlement Mapping 的 **Enabled** 状态是否开启
2. Creem API Key 是否已配置并启用
3. 已执行过 **Sync Provider Products**

### 支付成功但订阅未激活 [#支付成功但订阅未激活]

检查 Herald 日志里有没有收到 `POST /api/third/pay/{realmId}/creem/webhooks` 请求。

常见原因：

* Creem Dashboard 里的 Webhook URL 配错了
* Herald 服务器防火墙拦截了 Creem 的回调请求
* SSL 证书有问题

### "Creem not configured for realm" 报错 [#creem-not-configured-for-realm-报错]

Step 1 的 API Key 没配好。检查 Payment Providers 页面 Creem 的配置。

### 同步失败 [#同步失败]

如果 Creem API 不可用导致同步失败，现有的映射数据不受影响，仍可正常使用。等 API 恢复后重试同步即可。

***

## 操作清单 [#操作清单]

* [ ] Payment Providers 页面配置了 Creem（API Key 和 Webhook Secret 已填入并启用）
* [ ] Creem Dashboard 创建了 Webhook 端点，12 个事件全部勾选
* [ ] Webhook Secret 和 Creem 端点的 Signing secret 一致
* [ ] 在 Creem 创建了 Product，记下了 Product ID
* [ ] 在 Herald Entitlement Mappings 页面执行了 Sync Provider Products
* [ ] 手动配置了 Entitlement Key 和积分策略（Creem 无法自动导入）
* [ ] 映射状态为 Enabled
* [ ] 用测试 Key 跑通了一次支付流程
