关于 creem
creem 是面向开发者的 CREEM 支付集成 Skill,适用于 SaaS 和数字产品。它通过 API 示例与实施指引,涵盖结账、订阅、Webhook 和许可证密钥。
主要功能
- 01创建结账会话
- 02管理订阅生命周期
- 03验证 Webhook 签名
- 04激活与验证许可证
- 05生成客户账单门户链接
适用场景
- 01为 SaaS 产品接入托管结账
- 02处理订阅续费与取消
- 03激活软件许可证密钥
- 04保护支付 Webhook 处理程序
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ armitage-labs/ creem-skills --skill creem安装后在 Claude Code 中输入/creem确认已加载。
详细介绍
CREEM API 集成技能
你是集成 CREEM 的专家。CREEM 是面向 SaaS 和数字业务的名义销售商(MoR)支付平台。你可以帮助开发者实现结账流程、管理订阅、处理 Webhook,并使用许可证密钥。
核心概念
CREEM 作为法定销售商(名义销售商)运营,负责税务合规、支付处理和退款。集成时:
- 生产环境 API:
https://api.creem.io - 测试环境 API:
https://test-api.creem.io - 身份验证:在
x-api-key标头中提供从仪表板获取的 API 密钥 - 价格:始终以美分为单位(1000 = $10.00)
- 货币:使用大写的三字母 ISO 代码(USD、EUR 等)
身份验证设置
务必正确配置身份验证:
// Environment variables (never commit API keys)
const API_KEY = process.env.CREEM_API_KEY;
const BASE_URL = process.env.NODE_ENV === 'production'
? 'https://api.creem.io'
: 'https://test-api.creem.io';
// All requests require the x-api-key header
const headers = {
'x-api-key': API_KEY,
'Content-Type': 'application/json'
};
快速参考:API 端点
结账
| 方法 | 端点 | 描述 |
|---|---|---|
| POST | /v1/checkouts | 创建结账会话 |
| GET | /v1/checkouts?checkout_id={id} | 获取结账信息 |
产品
| 方法 | 端点 | 描述 |
|---|---|---|
| POST | /v1/products | 创建产品 |
| GET | /v1/products?product_id={id} | 获取产品信息 |
| GET | /v1/products/search | 列出所有产品 |
客户
| 方法 | 端点 | 描述 |
|---|---|---|
| GET | /v1/customers?customer_id={id} | 获取客户信息 |
| GET | /v1/customers?email={email} | 按电子邮件获取客户信息 |
| GET | /v1/customers/list | 列出所有客户 |
| POST | /v1/customers/billing | 生成门户链接 |
订阅
| 方法 | 端点 | 描述 |
|---|---|---|
| GET | /v1/subscriptions?subscription_id={id} | 获取订阅信息 |
| POST | /v1/subscriptions/{id} | 更新订阅 |
| POST | /v1/subscriptions/{id}/upgrade | 升级方案 |
| POST | /v1/subscriptions/{id}/cancel | 取消订阅 |
| POST | /v1/subscriptions/{id}/pause | 暂停订阅 |
| POST | /v1/subscriptions/{id}/resume | 恢复订阅 |
许可证
| 方法 | 端点 | 描述 |
|---|---|---|
| POST | /v1/licenses/activate | 激活许可证 |
| POST | /v1/licenses/validate | 验证许可证 |
| POST | /v1/licenses/deactivate | 停用许可证 |
折扣
| 方法 | 端点 | 描述 |
|---|---|---|
| POST | /v1/discounts | 创建折扣 |
| GET | /v1/discounts?discount_id={id} | 获取折扣信息 |
| GET | /v1/discounts?discount_code={code} | 按代码获取折扣信息 |
| DELETE | /v1/discounts/{id}/delete | 删除折扣 |
交易
| 方法 | 端点 | 描述 |
|---|---|---|
| GET | /v1/transactions?transaction_id={id} | 获取交易信息 |
| GET | /v1/transactions/search | 列出交易记录 |
实现模式
1. 创建结账会话
最常见的集成模式——将用户重定向到 CREEM 托管的结账页面:
// POST /v1/checkouts
const createCheckout = async (productId: string, options?: {
requestId?: string;
successUrl?: string;
customerEmail?: string;
discountCode?: string;
units?: number;
metadata?: Record<string, any>;
}) => {
const response = await fetch(`${BASE_URL}/v1/checkouts`, {
method: 'POST',
headers,
body: JSON.stringify({
product_id: productId,
request_id: options?.requestId,
success_url: options?.successUrl,
customer: options?.customerEmail ? { email: options.customerEmail } : undefined,
discount_code: options?.discountCode,
units: options?.units,
metadata: options?.metadata
})
});
const checkout = await response.json();
// Redirect user to: checkout.checkout_url
return checkout;
};
支付后的成功 URL 查询参数:
checkout_id- 结账会话 IDorder_id- 创建的订单customer_id- 客户 IDsubscription_id- 订阅(如果为周期性订阅)product_id- 购买的产品request_id- 你的跟踪 ID(如果已提供)signature- 用于验证的 HMAC 签名
2. Webhook 处理程序
重要:务必验证 Webhook 签名,以防止欺诈。
import crypto from 'crypto';
// Signature verification
function verifyWebhookSignature(payload: string, signature: string, secret: string): boolean {
const computed = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
return computed === signature;
}
// Webhook handler
export async function handleWebhook(req: Request) {
const signature = req.headers.get('creem-signature');
const rawBody = await req.text();
if (!verifyWebhookSignature(rawBody, signature!, process.env.CREEM_WEBHOOK_SECRET!)) {
return new Response('Invalid signature', { status: 401 });
}
const event = JSON.parse(rawBody);
switch (event.eventType) {
case 'checkout.completed':
// Payment successful - grant access
await handleCheckoutCompleted(event.object);
break;
case 'subscription.paid':
// Recurring payment - extend access
await handleSubscriptionPaid(event.object);
break;
case 'subscription.canceled':
// Subscription ended - revoke access at period end
await handleSubscriptionCanceled(event.object);
break;
case 'subscription.expired':
// Subscription expired - payment retries may happen
await handleSubscriptionExpired(event.object);
break;
case 'refund.created':
// Refund processed - may need to revoke access
await handleRefund(event.object);
break;
}
return new Response('OK', { status: 200 });
}
3. 许可证密钥管理
适用于桌面应用、CLI 工具或需要激活的软件:
// Activate on first use
const activateLicense = async (licenseKey: string, instanceName: string) => {
const response = await fetch(`${BASE_URL}/v1/licenses/activate`, {
method: 'POST',
headers,
body: JSON.stringify({
key: licenseKey,
instance_name: instanceName // e.g., "johns-macbook-pro"
})
});
const result = await response.json();
// Store result.instance.id locally for future validation
return result;
};
// Validate on app startup
const validateLicense = async (licenseKey: string, instanceId: string) => {
const response = await fetch(`${BASE_URL}/v1/licenses/validate`, {
method: 'POST',
headers,
body: JSON.stringify({
key: licenseKey,
instance_id: instanceId
})
});
const result = await response.json();
// result.status: "active" | "inactive" | "expired" | "disabled"
return result;
};
// Deactivate when user switches device
const deactivateLicense = async (licenseKey: string, instanceId: string) => {
const response = await fetch(`${BASE_URL}/v1/licenses/deactivate`, {
method: 'POST',
headers,
body: JSON.stringify({
key: licenseKey,
instance_id: instanceId
})
});
return response.json();
};
4. 订阅管理
// Update seat count
const updateSubscriptionSeats = async (subscriptionId: string, itemId: string, newUnits: number) => {
const response = await fetch(`${BASE_URL}/v1/subscriptions/${subscriptionId}`, {
method: 'POST',
headers,
body: JSON.stringify({
items: [{ id: itemId, units: newUnits }],
update_behavior: 'proration-charge-immediately' // or 'proration-charge', 'proration-none'
})
});
return response.json();
};
// Upgrade to different plan
const upgradeSubscription = async (subscriptionId: string, newProductId: string) => {
const response = await fetch(`${BASE_URL}/v1/subscriptions/${subscriptionId}/upgrade`, {
method: 'POST',
headers,
body: JSON.stringify({
product_id: newProductId,
update_behavior: 'proration-charge-immediately'
})
});
return response.json();
};
// Cancel subscription
const cancelSubscription = async (subscriptionId: string, immediate: boolean = false) => {
const response = await fetch(`${BASE_URL}/v1/subscriptions/${subscriptionId}/cancel`, {
method: 'POST',
headers,
body: JSON.stringify({
mode: immediate ? 'immediate' : 'scheduled' // scheduled = at period end
})
});
return response.json();
};
5. 客户门户
让客户自行管理订阅:
const getCustomerPortalLink = async (customerId: string) => {
const response = await fetch(`${BASE_URL}/v1/customers/billing`, {
method: 'POST',
headers,
body: JSON.stringify({ customer_id: customerId })
});
const { customer_portal_link } = await response.json();
return customer_portal_link;
};
Webhook 事件参考
| 事件 | 时机 | 操作 |
|---|---|---|
checkout.completed | 付款成功 | 授予访问权限,创建用户 |
subscription.active | 创建新订阅 | 同步到数据库 |
subscription.paid | 处理续费付款 | 延长访问期限 |
subscription.canceled | 用户/商家取消订阅 | 在当前周期结束时撤销访问权限 |
subscription.expired | 周期结束且未付款 | 可能会进行重试 |
subscription.trialing | 试用开始 | 授予试用访问权限 |
subscription.paused | 订阅已暂停 | 暂停功能 |
subscription.update | 订阅已修改 | 同步变更 |
refund.created | 已处理退款 | 可能会撤销访问权限 |
dispute.created | 已发起拒付 | 处理争议 |
错误处理
所有端点都返回标准 HTTP 状态码:
const handleApiResponse = async (response: Response) => {
if (response.ok) {
return response.json();
}
switch (response.status) {
case 400: throw new Error('Bad Request - Check parameters');
case 401: throw new Error('Unauthorized - Invalid API key');
case 403: throw new Error('Forbidden - Insufficient permissions or limit reached');
case 404: throw new Error('Not Found - Resource does not exist');
case 429: throw new Error('Rate Limited - Too many requests');
case 500: throw new Error('Server Error - Contact support');
default: throw new Error(`Unexpected error: ${response.status}`);
}
};
测试模式
务必先在测试模式下开发:
- 使用
https://test-api.creem.io作为基础 URL - 使用控制面板中的测试 API 密钥
- 测试卡:
4242 4242 4242 4242- 成功4000 0000 0000 0002- 被拒绝4000 0000 0000 9995- 余额不足
常见集成检查清单
实现 CREEM 时:
-
环境设置
- 将 API 密钥存储在环境变量中
- 配置测试/生产环境的基础 URL
- 设置 webhook 端点
-
结账流程
- 使用 product_id 创建结账会话
- 添加 request_id 以便跟踪
- 设置带验证的 success_url
- 处理 checkout.completed webhook
-
订阅处理
- 处理续订的 subscription.paid 事件
- 处理撤销访问权限的 subscription.canceled 事件
- 实现客户门户链接
- 存储 subscription_id 以便管理
-
许可证密钥(如适用)
- 实现首次使用时激活
- 在每次应用启动时验证
- 处理设备转移时的停用
-
安全
- 验证 webhook 签名
- 切勿在客户端公开 API 密钥
- 验证 success URL 签名
文件参考
详细信息请参阅:
REFERENCE.md- 包含所有字段的完整 API 参考WEBHOOKS.md- Webhook 载荷示例WORKFLOWS.md- 分步集成模式
需要帮助?
评论
支持 Markdown