第一步:技术栈准备与环境配置
在编写代码前,必须完成开发环境的隔离与初始化。
1. 开启测试模式并获取配置
进入Creem商家后台Dashboard,将顶部“Test Mode”开关打开。此模式下所有交易均为模拟,不会产生真实资金流动。

API端点:https://test-api.creem.io
API密钥:在Developers → Your API Key页面获取(以 creem_test_ 开头)。
测试银行卡号:用于模拟各种支付场景,比如:4242 4242 4242 4242(成功支付)、4000 0000 0000 9995(资金不足失败)、4000 0000 0000 5126(异步失败/延迟退款)。
2. 配置项目环境变量
将所有敏感信息和环境配置通过环境变量管理,严禁硬编码。
#Creem API 配置 (示例 .env 文件)
CREEM_API_URL= https://test-api.creem.io
CREEM_API_KEY=creem_test_你的测试API密钥
CREEM_WEBHOOK_SECRET=你的Webhook密钥
#测试商品ID
NEXT_PUBLIC_CREEM_PRODUCT_PRO_MONTHLY=prod_测试商品ID3. 为Webhook配置公网地址(本地开发)
Creem的Webhook需要向一个公网可访问的URL发送请求。本地开发时,可以使用 ngrok 等工具进行内网穿透。

#启动ngrok,将本地3000端口暴露到公网
ngrok http 3000
复制生成的 Forwarding URL(如 https://xxxx.ngrok-free.app),并在Creem后台的Developers → Webhooks中配置该地址为你的Webhook接收端点(例如 /api/webhooks/creem)。
第二步:核心API调用流程
程序接入的核心是调用Creem的REST API与处理其回调。主要涉及三个关键接口。
1. 创建结账会话 (POST /v1/checkouts)
当用户发起支付时,调用此接口生成一个唯一的支付链接。
// 前端或后端调用示例
const response = awaitfetch(' https://test-api.creem.io/v1/checkouts', {
method: 'POST',
headers: {
'content-type': 'application/json',
'x-api-key': process.env.CREEM_API_KEY, // 使用环境变量中的密钥
},
body: JSON.stringify({
product_id: 'prod_你的商品ID', // 从后台获取
success_url: ' https://yourdomain.com/success', // 支付成功回调地址
customer: {
email: 'customer@example.com',
},
metadata: { // 自定义数据,会在Webhook中原样返回
userId: '123',
orderInfo: 'extra info'
}
}),
});
const checkout = await response.json();
// 获取checkout_url,引导用户跳转至此链接完成支付
const checkoutUrl = checkout.checkout_url;
关键参数说明:
- product_id:在Creem后台创建商品时生成的ID。
- success_url:用户支付成功后,Creem将重定向至此URL,并携带checkout_id和signature等参数。
- metadata:传递自定义业务数据(如内部用户ID),后续Webhook事件会将其回传,用于关联业务。
2. 处理支付成功回调
用户支付完成并被重定向至 success_url 后,您必须验证该请求的真实性并确认最终支付状态。

A. 验证签名
为防止伪造请求,需要对 success_url 回调URL中的 signature 参数进行验证。Creem的签名规则为:将除 signature 外的所有参数按 key=value 格式拼接,用 | 分隔,最后加上 |salt=你的API_KEY,然后对整个字符串进行 SHA-256 哈希。
// 签名验证函数示例 (Node.js环境)
const crypto = require('crypto');
functiongenerateSignature(params, apiKey) {
const data = Object.entries(params)
.filter(([key]) => key !== 'signature')
.map(([key, value]) =>`${key}=${value}`)
.concat(`salt=${apiKey}`)
.join('|');
return crypto.createHash('sha256').update(data).digest('hex');
}
// 使用:计算签名并与URL中的signature参数比对B. 查询支付状态
签名验证通过后,使用回调参数中的 checkout_id 调用查询接口,确认订单状态。
const checkoutId = newURLSearchParams(window.location.search).get('checkout_id');
const response = awaitfetch(` https://test-api.creem.io/v1/checkouts?checkout_id=$ {checkoutId}`, {
headers: {
'x-api-key': process.env.CREEM_API_KEY,
},
});
const checkoutDetails = await response.json();
if (checkoutDetails.status === 'completed') {
// 支付成功,执行发放权益等业务逻辑
}3. 集成客户门户 (POST /v1/customers/billing)
为订阅用户提供管理入口(如取消订阅、更新支付方式)。您需要在系统中记录Creem返回的 customer_id,并在需要时生成门户链接。
const response = awaitfetch(' https://test-api.creem.io/v1/customers/billing', {
method: 'POST',
headers: {
'x-api-key': process.env.CREEM_API_KEY,
'Content-Type': 'application/json'
},
body: JSON.stringify({ customer_id: 'cust_你的客户ID' })
});
const portalData = await response.json();
const customerPortalUrl = portalData.customer_portal_link; // 将此链接提供给用户第三步:配置与处理Webhook事件(关键)
为了保证支付状态(尤其是订阅的自动续费、取消、退款)的实时同步,必须配置并妥善处理Webhook。这是确保业务数据一致性的最关键环节。
1. Webhook事件接收与验证
在您的服务器(如/api/webhooks/creem)创建一个路由来接收Creem的POST请求。首要任务是验证请求签名,确保其来自Creem。

签名头:Creem会在每个Webhook请求的 creem-signature 头中携带签名。
验证方法:使用您在后台配置Webhook时获取的 WEBHOOK_SECRET,对请求体(原始payload)进行HMAC-SHA256计算,将结果与请求头中的签名进行比对。
// Next.js API Route 示例 (src/app/api/webhooks/creem/route.ts)
import { NextRequest, NextResponse } from'next/server';
exportasyncfunctionPOST(req: NextRequest) {
const payload = await req.text(); // 获取原始请求体
const signature = req.headers.get('creem-signature') || '';
// 1. 调用签名验证函数 (需自行实现)
const isValid = verifyWebhookSignature(payload, signature, process.env.CREEM_WEBHOOK_SECRET);
if (!isValid) {
returnnewResponse('Invalid signature', { status: 400 });
}
const event = JSON.parse(payload);
// 2. 根据事件类型分派处理
switch (event.type) {
case'subscription.paid':
// 处理订阅扣款成功
break;
case'subscription.expired':
// 处理订阅到期
break;
// ... 处理其他事件
}
returnNextResponse.json({ received: true });
}2. 核心事件处理逻辑
您至少需要处理以下关键事件来同步您的业务数据库:
| 事件类型 (event.type) | 触发时机 | 您的业务系统应执行的操作 |
|---|---|---|
| subscription.paid | 订阅成功扣款(包括首次和续费) | 激活或续期用户订阅,发放对应权益。 |
| subscription.expired | 订阅到期(包括扣款失败导致的到期) | 将订阅标记为过期,停止或回收相关权益。 |
| subscription.canceled | 订阅被取消(用户主动或您后台操作) | 终止订阅,停止后续扣款与权益发放。 |
| refund.created | 发生退款 | 根据退款金额,部分或全部回收已发放的权益。 |
💡 幂等性处理建议:由于网络等原因,同一事件的Webhook可能被多次发送。您的处理逻辑应保证重复处理相同事件不会导致数据错误(例如,用户权益被重复发放)。可以通过检查数据库中该事件ID是否已处理过来实现。
第四步:完整的端到端测试
在切换至生产环境前,必须在Test Mode下完成端到端(E2E)测试,覆盖主流程和异常场景。

支付流程测试:使用测试卡号,完整走通“创建订单 -> 跳转支付 -> 成功回调 -> 查询状态”的流程。
Webhook接收测试:使用ngrok确保能接收到Creem发送的Webhook,并验证签名、解析事件、更新数据库的整个链路。
异常场景测试:使用特定测试卡号模拟各种失败情况,确保您的系统行为正确:
- 4000 0000 0000 3220 – 需要 3D 安全认证
- 4000 0000 0000 9995 – 资金不足失败
- 4000 0000 0000 0002 – 一般拒绝

客户门户测试:生成门户链接,测试用户能否成功管理其订阅。
第五步:切换至生产环境
完成所有测试并确保无误后,即可部署上线:
在Creem后台关闭“Test Mode”开关。
将项目中的所有环境变量替换为生产环境的值:
确保您的 success_url 和 Webhook URL 均为生产环境的公网可访问地址。
进行一轮生产环境下的健康检查测试(可使用真实银行卡小额支付测试)。
至此,您的程序已成功接入Creem收款功能,能够处理一次性支付与订阅支付,并通过Webhook实现支付状态的实时、可靠同步。
转载作品,原作者:Rachel,文章来源:https://mp.weixin.qq.com/s/1KLA9Xq5gbZ61Iv-4ZT-tg



