Stripe 支付对接
面向 Realm Admin 的 Stripe 对接操作指南。跟着做完,你的用户就能通过 Stripe 完成订阅支付和积分包购买。
面向 Realm Admin 的 Stripe 对接操作指南。跟着做完,你的用户就能通过 Stripe 完成订阅支付和积分包购买。
给谁看
负责管理 Herald 计费配置的 Realm Admin。全程在管理后台操作,不需要写代码。
前置条件
- Herald 管理后台可以正常访问,你有管理员账号
- 已创建至少一个 Realm 和一个 Client App
- 已有 Stripe 账号。没有的话去 Stripe 官网 注册
核心概念
Herald 不维护本地商品目录。你在 Stripe Dashboard 里创建 Product 和 Price,然后在 Herald 里配置一条 Entitlement Mapping,告诉系统"Stripe 的 prod_xxxx 对应 Herald 的 pro-plan"。用户付款后,Stripe 通过 Webhook 通知 Herald,Herald 用 metadata 里的 herald_entitlement_key 找到对应的映射,创建订阅投影、发放积分。
数据流向:Stripe Product → Herald Entitlement Mapping → 用户 Checkout → Stripe Webhook(带 herald_* metadata)→ Herald 订阅投影 + 积分发放
Step 1: 配置 Stripe 连接
在 Herald 管理后台填入 Stripe 凭证。
- 在左侧菜单找到 Payment Providers,点击进入
- 找到 Stripe,点击 Configure
- 填写配置表单:
- Publishable Key(必填):以
pk_开头。在 Stripe Dashboard → Developers → API Keys 中获取。测试环境用pk_test_开头的 key - Secret Key(必填):以
sk_开头。测试环境用sk_test_开头的 key - Webhook Secret(必填):以
whsec_开头。下一步获取
- Publishable Key(必填):以
- 点击 Save
保存有效凭证后 Stripe 即启用,没有单独的启用开关。Herald 通过 API Key 前缀自动判断环境:sk_test_ 走测试环境,sk_live_ 走生产环境。
Step 2: 配置 Stripe Webhook
Stripe 通过 Webhook 把支付结果推送给 Herald。这是整个对接中最关键的一步。
创建 Webhook 端点
- 打开 Stripe Dashboard → Developers → Webhooks
- 确认处于测试模式(右上角 toggle),开发阶段先配测试环境
- 点击 Add endpoint
- 填写:
- Endpoint URL:
https://你的Herald域名/api/third/pay/{realmId}/stripe/webhooks- 把
{realmId}替换成你的 realm ID,比如admin - 本地开发用 ngrok 等工具暴露 Herald 服务
- 把
- Listen to:选择 Events(不是 Account events)
- Endpoint URL:
选择 Webhook 事件
点击 Select events,勾选以下事件。缺任何一个会导致对应的支付流程断裂。
结账事件
| 事件 | Stripe 中的路径 | Herald 处理逻辑 |
|---|---|---|
checkout.session.completed | Checkout → checkout.session.completed | 完成结账,创建订阅或完成一次性支付 |
checkout.session.expired | Checkout → checkout.session.expired | 结账会话过期,标记支付尝试失败 |
checkout.session.async_payment_succeeded | Checkout → checkout.session.async_payment_succeeded | 异步支付(如银行转账)成功,完成支付尝试 |
checkout.session.async_payment_failed | Checkout → checkout.session.async_payment_failed | 异步支付失败,标记支付尝试失败 |
订阅事件
| 事件 | Stripe 中的路径 | Herald 处理逻辑 |
|---|---|---|
customer.subscription.created | Customers → customer.subscription.created | 新订阅创建,同步订阅投影并发放积分 |
customer.subscription.updated | Customers → customer.subscription.updated | 订阅升降级处理 |
customer.subscription.paused | Customers → customer.subscription.paused | 订阅暂停,同步状态 |
customer.subscription.resumed | Customers → customer.subscription.resumed | 订阅恢复,同步状态 |
customer.subscription.deleted | Customers → customer.subscription.deleted | 订阅取消,回收积分 |
支付与退款事件
| 事件 | Stripe 中的路径 | Herald 处理逻辑 |
|---|---|---|
payment_intent.succeeded | Payments → payment_intent.succeeded | 一次性支付(积分包购买)成功 |
payment_intent.payment_failed | Payments → payment_intent.payment_failed | 支付失败,标记支付尝试 |
charge.refunded | Payments → charge.refunded | 退款,按比例回收积分 |
charge.dispute.created | Payments → charge.dispute.created | 争议发起,标记订阅为争议状态 |
charge.dispute.closed | Payments → charge.dispute.closed | 争议结案:败诉则取消订阅,胜诉则恢复 |
账单与发票事件
| 事件 | Stripe 中的路径 | Herald 处理逻辑 |
|---|---|---|
invoice.payment_succeeded | Billing → invoice.payment_succeeded | 续费成功,发放续费积分并同步订阅状态 |
invoice.payment_failed | Billing → invoice.payment_failed | 账单支付失败,标记支付尝试 |
invoice.payment_action_required | Billing → invoice.payment_action_required | 需要客户操作(如 3D Secure),记录日志 |
invoice.created | Billing → invoice.created | 同步 Stripe 发票到 Herald 外部发票 |
invoice.finalized | Billing → invoice.finalized | 同步 Stripe 发票到 Herald 外部发票 |
invoice.paid | Billing → invoice.paid | 同步 Stripe 发票到 Herald 外部发票 |
invoice.voided | Billing → invoice.voided | 同步 Stripe 发票到 Herald 外部发票 |
以上 20 个事件必须全部勾选。Herald 收到不在列表中的事件会记录日志并返回 200,安全忽略。
获取 Webhook Secret
- 端点创建完成后,在端点详情页找到 Signing secret
- 点击 Reveal,复制以
whsec_开头的值 - 回到 Herald 管理后台 → Payment Providers → Stripe 配置,填入 Webhook Secret
生产环境
上线前,切换 Stripe Dashboard 到生产模式,重复以上步骤创建生产环境的 Webhook 端点。生产环境的 API Key 和 Webhook Secret 与测试环境完全独立。
Step 3: 在 Stripe 创建商品
在 Stripe Dashboard → Products 中创建你的产品。
订阅商品
- 点击 Add product
- 填写产品名称(比如 "AI Writing Assistant Pro")
- 价格设为 Recurring,选择月付或年付
- 创建完成后记下 Product ID(形如
prod_xxxxxxxxxxxx)和 Price ID(形如price_xxxxxxxxxxxx)
Herald 使用 Stripe 的 inline pricing 创建 Checkout Session,不需要在 Stripe 中预创建 Price。但 Product ID 是映射的必要字段。
积分包商品(一次性购买)
- 同样在 Products 页面创建
- 价格设为 One time
- 记下 Product ID
Step 4: 同步 Stripe 商品到 Herald
在 Herald 中把 Stripe 的商品同步为 Entitlement Mapping。
- 在 Herald 管理后台左侧菜单点击 Entitlement Mappings
- 点击 Sync Provider Products 按钮
- 选择支付方 Stripe
- 等待同步完成
同步完成后,你会看到从 Stripe 拉取的商品列表。每条记录包含:
- External Product ID(如
prod_xxxx) - External Price ID(如
price_xxxx) - 自动生成的 Entitlement Key(格式为
stripe-{normalized_product_id}) - 归属的积分账户(新同步的映射默认挂在 Realm 的注册接收池)
- 积分策略状态
列表的主标签是 Stripe 商品的产品名(Product name)。产品名缺失时回退到 External Product ID,再不行给占位,不会出现空标签。产品过滤器按产品名匹配,外部 ID 命中也算。
价格展示按 Stripe 单位换算。Stripe 同步的价格是最小货币单位整数(例如 999 表示 9.99),列表和详情会换算成主货币单位(9.99)。这条换算分支只对 Stripe 生效,Creem 不会走到这里。
同步会把 Stripe Product.metadata 和 Price.metadata(你在 Stripe 后台填的商户自定义键值对)一起拉过来,写进 mapping 详情的展示信息里,只读。重新同步以 Stripe 当前值为准覆盖。Herald 不回写 Stripe metadata,权威源是 Stripe 后台。
计费周期(订阅周期)取自 Stripe Price.recurring.interval(day/week/month/year),前端只读,不接受人工输入。保存 / 更新时即使提交了周期值,后端也不会用它覆盖同步值。
自动生成的 Entitlement Key 通常不是你想要的。下一步来调整。
Step 5: 配置 Entitlement Mapping
每条映射告诉 Herald:"Stripe 的这个商品 = Herald 的这个权益"。
- 在 Entitlement Mappings 列表中,找到要配置的映射
- 点击编辑,修改以下字段:
- Entitlement Key(必填):改成你能识别的名字,比如
pro-monthly。这个 key 在 SDK 查询、积分策略、Webhook 处理中都会用到,创建后尽量不改 - Billing Type:Recurring(订阅)或 OneTime(一次性购买)
- Points Per Period:每个计费周期发放多少积分
- Grant On Subscribe:首次订阅时是否发放积分
- Validity Days:积分有效期(天),0 或不填表示永不过期
- Enabled:是否启用。禁用后 Webhook 仍更新订阅投影,但不触发积分发放
- Entitlement Key(必填):改成你能识别的名字,比如
- 点击保存
归属积分账户
每条映射必须挂在某个积分账户上。同步进来的映射默认挂在本 Realm 的注册接收池账户。如果这个套餐属于某条独立业务线,去 积分账户 页面把它挪到对应的积分账户。
积分账户决定了两件事:用户购买后积分进哪个池、哪些 Client App 能消费这些积分。积分账户的概念见计费架构。
Stripe Metadata 导入(可选)
Stripe Product 和 Price 支持 metadata。如果在 Stripe Dashboard 中给 Product 或 Price 加了 herald_entitlement_key,同步时 Herald 会自动读取。但这只是导入来源,不是 source of truth。 Herald 本地配置优先。
Step 6: 用户支付流程
配置完成后,用户侧的流程如下。
订阅支付(Checkout Session)
- 用户在你的应用中选择套餐
- 你的应用调用 Herald 的 Checkout API(传入
entitlement_key、payment_provider=stripe) - Herald 查 Entitlement Mapping 找到 Stripe 商品 ID
- Herald 调 Stripe API 创建 Checkout Session,metadata 写入
herald_entitlement_key、herald_user_id、herald_client_app_id、herald_realm_id、herald_billing_kind - 返回 Stripe 支付页面 URL
- 用户在 Stripe 页面完成付款
- Stripe 发
checkout.session.completed和customer.subscription.createdWebhook - Herald 从 metadata 解析 entitlement_key,创建订阅投影、发放积分
- 后续每个计费周期,Stripe 自动扣款并发
invoice.payment_succeeded,Herald 发放续费积分
一次性支付(积分包 / Payment Intent)
- 用户选择积分包
- 你的应用调用 Herald Payment Attempt API
- Herald 创建 PaymentAttempt,调用 Stripe Payment Intent API
- 返回
client_secret - 前端用 Stripe.js 展示支付表单
- 用户完成支付,Stripe 发
payment_intent.succeededWebhook - Herald 完成支付、发放 topup_credit
退款
用户在 Stripe Dashboard 中发起退款时:
- Stripe 发
charge.refundedWebhook - Herald 根据退款类型回收积分(充值退款按比例回收,订阅退款回收未使用积分)
Step 7: 确认结果
查看 Entitlement Mapping 状态
- 在 Entitlement Mappings 页面,确认映射的 Enabled 状态和积分策略配置
查看订阅状态
- 在左侧菜单点击 Subscriptions,查看订阅投影列表
- 确认
entitlement_key、status、payment_provider字段正确
查看变更历史
- 点击 Subscription History,查看订阅的完整变更时间线
- 每条变更记录包含事件类型、entitlement 变更、时间戳
Webhook 正常回调的话,状态会在几秒内更新。迟迟没变化,看下面的常见问题。
常见问题
Webhook 没收到
- 确认 Herald 部署的公网地址可以从 Stripe 服务器访问(本地开发用 ngrok)
- 确认 Webhook 端点 URL 中的 realm ID 正确
- 在 Stripe Dashboard → Webhooks → 你的端点,查看 Attempts 日志
- 确认 Step 1 中的 Webhook Secret 和端点的 Signing secret 一致
Webhook 签名验证失败
- Webhook Secret 填错了(必须以
whsec_开头) - Herald 前面有反向代理修改了请求体(Nginx 的
proxy_request_buffering关闭时可能出现) - 签名时间戳超过 15 分钟(时钟不同步)
支付成功但订阅未激活
大概率是 Webhook 没到达 Herald。检查 Herald 日志里有没有收到 POST /api/third/pay/{realmId}/stripe/webhooks 请求。
常见原因:
- Stripe Dashboard 里的 Webhook URL 配错了
- 服务器防火墙拦截了 Stripe 的回调请求
- SSL 证书有问题
Entitlement Mapping 里看不到 Stripe 商品
确认三点:
- Stripe 的 API Key 配置正确(Payment Providers 页面)
- 已执行过 Sync Provider Products
- Stripe Dashboard 中确实有 Product
测试卡号
Stripe 测试环境使用以下卡号:
- 成功支付:
4242 4242 4242 4242 - 需要验证:
4000 0025 0000 3155 - 被拒绝:
4000 0000 0000 0002
过期日期填任何未来日期,CVC 填任何 3 位数字。
操作清单
- Payment Providers 页面配置了 Stripe(Publishable Key、Secret Key、Webhook Secret 已填入并启用)
- Stripe Dashboard 创建了 Webhook 端点,20 个事件全部勾选
- Webhook Secret 和 Stripe 端点的 Signing secret 一致
- 在 Stripe 创建了 Product,记下了 Product ID
- 在 Herald Entitlement Mappings 页面执行了 Sync Provider Products
- 检查并调整了 Entitlement Key 和积分策略配置
- 映射状态为 Enabled
- 用测试 Key 和测试卡号跑通了一次支付流程