woyingdang_creemsdk
v1.0.0
Published
Modular Creem Payment SDK for TypeScript/JavaScript projects
Maintainers
Readme
Creem Payment SDK
模块化的 Creem 支付集成 SDK,方便快速集成 Creem 支付功能
版本: 1.0.0 作者: 幽浮喵 (猫娘工程师) 文档: https://docs.creem.io
快速开始
安装
# 复制 SDK 文件到项目中
cp -r lib/creem your-project/lib/环境变量配置
在 .env.local 文件中添加以下配置:
# ============================================
# Creem Payment 配置
# ============================================
# Creem API Key(服务端专用)
# 测试密钥:creem_test_xxxxxxxxxxxxxxxxx
# 生产密钥:creem_sk_xxxxxxxxxxxxxxxxx
CREEM_API_KEY=creem_test_xxxxxxxxxxxxxxxxx
# Creem Public Key(客户端使用)
NEXT_PUBLIC_CREEM_PUBLIC_KEY=creem_pk_xxxxxxxxxxxxxxxxx
# Creem API Base URL(可选)
# 测试环境:https://test-api.creem.io
# 生产环境:https://api.creem.io
CREEM_API_BASE_URL=https://test-api.creem.io
# Creem Webhook Secret(可选)
# 用于验证 Webhook 请求的签名
CREEM_WEBHOOK_SECRET=whsec_xxxxxxxxxxxxxxxxx
# Creem Product IDs(需要在 Creem Dashboard 创建产品)
CREEM_PRODUCT_PRO_MONTHLY=prod_xxxxxxxxxxxxxxxxx
CREEM_PRODUCT_PRO_YEARLY=prod_xxxxxxxxxxxxxxxxx
CREEM_PRODUCT_ENTERPRISE_MONTHLY=prod_xxxxxxxxxxxxxxxxx
CREEM_PRODUCT_ENTERPRISE_YEARLY=prod_xxxxxxxxxxxxxxxxx基础使用
import { createCreemClient } from '@/lib/creem'
// 创建客户端
const client = createCreemClient()
// 创建支付链接
const checkout = await client.checkouts.create({
productId: 'prod_abc123',
successUrl: 'https://example.com/success',
customer: { email: '[email protected]' },
metadata: { userId: '123' }
})
// 跳转到支付页面
window.location.href = checkout.checkout_url功能特性
✨ 核心功能
- ✅ 完整的类型支持 - 完整的 TypeScript 类型定义
- ✅ 链式调用 - 支持链式调用 API
- ✅ 错误处理 - 统一的错误处理和用户友好提示
- ✅ Webhook 处理 - 内置 Webhook 验证和处理
- ✅ 测试模式 - 完整的测试环境支持
- ✅ 工具函数 - 丰富的辅助工具函数
🎯 支持的 API
- Checkout API - 创建支付链接
- Customer API - 客户管理
- Product API - 产品管理
- Subscription API - 订阅管理
- Webhook API - Webhook 事件处理
使用示例
创建支付链接
import { createCreemClient } from '@/lib/creem'
const client = createCreemClient()
const checkout = await client.checkouts.create({
productId: 'prod_abc123',
units: 1,
successUrl: 'https://example.com/checkout/success',
customer: {
email: '[email protected]'
},
metadata: {
userId: '123',
plan: 'pro'
},
customField: [
{
type: 'text',
key: 'company_name',
label: 'Company Name',
optional: true
}
]
})
console.log('Checkout URL:', checkout.checkout_url)获取结账信息
const checkout = await client.checkouts.retrieve('ch_abc123')
console.log('Status:', checkout.status)
console.log('Customer:', checkout.customer)创建客户门户链接
const portal = await client.customers.createBilling({
customerId: 'cust_abc123',
returnUrl: 'https://example.com'
})
console.log('Customer Portal URL:', portal.billing_url)获取产品信息
const product = await client.products.retrieve('prod_abc123')
console.log('Product Name:', product.name)
console.log('Price:', product.price)列出所有产品
const products = await client.products.list({
status: 'active',
billingType: 'recurring'
})
console.log('Products:', products.data)管理订阅
// 获取订阅信息
const subscription = await client.subscriptions.retrieve('sub_abc123')
// 更新订阅
await client.subscriptions.update('sub_abc123', {
units: 5
})
// 升级订阅
await client.subscriptions.upgrade('sub_abc123', {
productId: 'prod_xyz789',
proration: true
})
// 取消订阅
await client.subscriptions.cancel('sub_abc123')Webhook 处理
import { WebhookHandler, WEBHOOK_EVENTS } from '@/lib/creem'
// 创建 Webhook 处理器
const handler = new WebhookHandler({
webhookSecret: process.env.CREEM_WEBHOOK_SECRET!,
handlers: {
'checkout.completed': async (data) => {
console.log('Checkout completed:', data)
// 处理结账完成逻辑
},
'subscription.created': async (data) => {
console.log('Subscription created:', data)
// 处理订阅创建逻辑
},
'subscription.canceled': async (data) => {
console.log('Subscription canceled:', data)
// 处理订阅取消逻辑
}
},
onError: (error, event) => {
console.error('Webhook error:', error)
// 处理错误
}
})
// 在 Next.js API 路由中使用
export async function POST(request: Request) {
return handler.handle(request)
}工具函数使用
import {
verifyWebhookSignature,
getProductId,
formatAmount,
isTestMode,
isValidProductId
} from '@/lib/creem'
// 验证 Webhook 签名
const isValid = verifyWebhookSignature(
payload,
signature,
webhookSecret
)
// 获取产品 ID
const productId = getProductId('pro', 'monthly')
// 格式化金额
const formatted = formatAmount(1999, 'USD') // '$19.99'
// 判断是否为测试环境
if (isTestMode()) {
console.log('Running in test mode')
}
// 验证产品 ID
const isValid = isValidProductId('prod_abc123')Next.js 集成示例
API 路由示例
// app/api/checkout/route.ts
import { createCreemClient, CreemError } from '@/lib/creem'
import { NextRequest, NextResponse } from 'next/server'
export async function POST(request: NextRequest) {
try {
const body = await request.json()
// 创建客户端
const client = createCreemClient()
// 创建支付链接
const checkout = await client.checkouts.create({
productId: getProductId(body.plan, body.billing),
successUrl: `${process.env.NEXT_PUBLIC_SITE_URL}/checkout/success`,
customer: body.email ? { email: body.email } : undefined,
metadata: {
plan: body.plan,
billing: body.billing,
},
})
return NextResponse.json(checkout)
} catch (error) {
if (error instanceof CreemError) {
return NextResponse.json(
{ error: error.getUserMessage() },
{ status: error.statusCode || 500 }
)
}
return NextResponse.json(
{ error: 'Internal server error' },
{ status: 500 }
)
}
}Webhook 路由示例
// app/api/webhooks/creem/route.ts
import { WebhookHandler } from '@/lib/creem'
const handler = new WebhookHandler({
webhookSecret: process.env.CREEM_WEBHOOK_SECRET!,
handlers: {
'checkout.completed': async (data) => {
console.log('Checkout completed:', data)
// 更新数据库
// await db.orders.update({...})
},
'subscription.created': async (data) => {
console.log('Subscription created:', data)
// 创建用户订阅记录
// await db.subscriptions.create({...})
},
'subscription.canceled': async (data) => {
console.log('Subscription canceled:', data)
// 取消用户订阅
// await db.subscriptions.update({...})
}
},
onError: (error, event) => {
console.error('Webhook error:', error, event)
}
})
export async function POST(request: Request) {
return handler.handle(request)
}前端组件示例
// components/Pricing.tsx
'use client'
import { useState } from 'react'
import { createCreemClient, CreemError } from '@/lib/creem'
export function Pricing() {
const [loading, setLoading] = useState(false)
const handleSubscribe = async (plan: string, billing: string) => {
setLoading(true)
try {
const response = await fetch('/api/checkout', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ plan, billing }),
})
const data = await response.json()
if (data.checkout_url) {
// 跳转到支付页面
window.location.href = data.checkout_url
} else {
throw new Error(data.error || 'Failed to create checkout')
}
} catch (error) {
console.error('Checkout error:', error)
alert(error instanceof Error ? error.message : 'Failed to initiate checkout')
} finally {
setLoading(false)
}
}
return (
<button
onClick={() => handleSubscribe('pro', 'monthly')}
disabled={loading}
>
{loading ? 'Processing...' : 'Start Pro Trial'}
</button>
)
}测试模式
测试卡片
使用以下测试卡片号进行测试:
| 卡片号 | 描述 |
|--------|------|
| 4242 4242 4242 4242 | 成功支付 |
| 4000 0000 0000 0002 | 卡片被拒绝 |
| 4000 0000 0000 9995 | 余额不足 |
| 4000 0000 0000 0127 | CVC 错误 |
| 4000 0000 0000 0069 | 卡片过期 |
注意:所有测试卡片都可以使用任何未来日期、任何 CVC 和任何账单信息。
切换测试/生产模式
在 Creem Dashboard 顶部导航栏点击 "Test Mode" 切换。
重要提示: 测试环境和生产环境使用不同的 API 密钥和产品 ID。
错误处理
import { createCreemClient, CreemError } from '@/lib/creem'
const client = createCreemClient()
try {
await client.checkouts.create({...})
} catch (error) {
if (error instanceof CreemError) {
// 判断错误类型
if (error.isAuthError()) {
console.log('认证失败')
} else if (error.isNotFoundError()) {
console.log('资源不存在')
} else if (error.isRateLimitError()) {
console.log('请求过于频繁')
}
// 获取用户友好的错误消息
console.log(error.getUserMessage())
}
}API 参考
CreemClient
方法
| 方法 | 说明 |
|------|------|
| checkouts.create(params) | 创建支付链接 |
| checkouts.retrieve(id) | 获取结账信息 |
| customers.createBilling(params) | 创建客户门户链接 |
| customers.retrieve(id) | 获取客户信息 |
| customers.retrieveByEmail(email) | 根据邮箱获取客户 |
| products.retrieve(id) | 获取产品信息 |
| products.list(params) | 列出产品 |
| subscriptions.retrieve(id) | 获取订阅信息 |
| subscriptions.update(id, params) | 更新订阅 |
| subscriptions.upgrade(id, params) | 升级订阅 |
| subscriptions.cancel(id) | 取消订阅 |
工具函数
| 函数 | 说明 |
|------|------|
| verifyWebhookSignature(payload, signature, secret) | 验证 Webhook 签名 |
| parseWebhookEvent(payload) | 解析 Webhook 事件 |
| getProductId(plan, billing) | 获取产品 ID |
| formatAmount(amount, currency) | 格式化金额 |
| isTestMode() | 判断是否为测试环境 |
| getApiEndpoint() | 获取 API 端点 |
| isValidProductId(productId) | 验证产品 ID 格式 |
| generateRequestId() | 生成请求追踪 ID |
| createSuccessUrl(baseUrl, path, params) | 创建成功跳转 URL |
常见问题
Q: 如何获取 API Key?
A: 访问 Creem Dashboard,进入 Developers > API Keys 创建 API 密钥。测试密钥以 creem_test_ 开头,生产密钥以 creem_sk_ 开头。
Q: 测试环境和生产环境的区别?
A:
- 测试环境:使用测试 API Key 和测试卡片,不会产生真实费用
- 生产环境:使用生产 API Key,处理真实支付
- 两个环境使用不同的 API 端点、API Key 和产品 ID
Q: 如何验证 Webhook 签名?
A: 使用 verifyWebhookSignature 函数验证签名,或使用 WebhookHandler 类自动处理验证。
Q: 支付成功后如何处理?
A: 通过 Webhook 监听 checkout.completed 事件,或在用户跳转回成功页面时查询结账状态。
Q: 如何处理退款?
A: 在 Creem Dashboard 中手动处理退款,或通过 Webhook 监听相关事件。
相关资源
许可证
MIT
支持
如有问题,请提交 Issue 或联系 Creem 官方支持。
发布到 npm
快速发布
cd lib/creem
npm run build
npm publish详细发布指南请参考:
发布前准备
更新 package.json
- 修改包名:
@your-org/creem-sdk - 更新作者信息
- 更新仓库链接
- 修改包名:
构建 TypeScript
npm run build验证构建输出
- 检查
dist/目录 - 确认所有文件正确生成
- 检查
发布到 npm
npm login npm publish
当前包配置
{
"name": "@your-org/creem-sdk",
"version": "1.0.0",
"description": "Modular Creem Payment SDK for TypeScript/JavaScript projects",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"publishConfig": {
"access": "public"
}
}发布检查清单
- [ ] 已更新 package.json 中的包名和作者信息
- [ ] 已更新版本号
- [ ] 已运行
npm run build成功 - [ ] dist 目录包含所有必需文件
- [ ] README.md 文档完整
- [ ] LICENSE 文件存在
- [ ] 已登录 npm
