Loading...
Loading...
Compare original and translation side by side
https://api.creem.iohttps://test-api.creem.iox-api-keyhttps://api.creem.iohttps://test-api.creem.iox-api-key// 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'
};// 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'
};| Method | Endpoint | Description |
|---|---|---|
| POST | | Create checkout session |
| GET | | Retrieve checkout |
| 请求方法 | 端点 | 描述 |
|---|---|---|
| POST | | 创建结账会话 |
| GET | | 获取结账会话信息 |
| Method | Endpoint | Description |
|---|---|---|
| POST | | Create product |
| GET | | Retrieve product |
| GET | | List all products |
| 请求方法 | 端点 | 描述 |
|---|---|---|
| POST | | 创建产品 |
| GET | | 获取产品信息 |
| GET | | 列出所有产品 |
| Method | Endpoint | Description |
|---|---|---|
| GET | | Retrieve customer |
| GET | | Retrieve by email |
| GET | | List all customers |
| POST | | Generate portal link |
| 请求方法 | 端点 | 描述 |
|---|---|---|
| GET | | 获取客户信息 |
| GET | | 通过邮箱获取客户信息 |
| GET | | 列出所有客户 |
| POST | | 生成客户门户链接 |
| Method | Endpoint | Description |
|---|---|---|
| GET | | Retrieve subscription |
| POST | | Update subscription |
| POST | | Upgrade plan |
| POST | | Cancel subscription |
| POST | | Pause subscription |
| POST | | Resume subscription |
| 请求方法 | 端点 | 描述 |
|---|---|---|
| GET | | 获取订阅信息 |
| POST | | 更新订阅 |
| POST | | 升级订阅计划 |
| POST | | 取消订阅 |
| POST | | 暂停订阅 |
| POST | | 恢复订阅 |
| Method | Endpoint | Description |
|---|---|---|
| POST | | Activate license |
| POST | | Validate license |
| POST | | Deactivate license |
| 请求方法 | 端点 | 描述 |
|---|---|---|
| POST | | 激活许可证 |
| POST | | 验证许可证 |
| POST | | 停用许可证 |
| Method | Endpoint | Description |
|---|---|---|
| POST | | Create discount |
| GET | | Retrieve discount |
| GET | | Retrieve by code |
| DELETE | | Delete discount |
| 请求方法 | 端点 | 描述 |
|---|---|---|
| POST | | 创建折扣 |
| GET | | 获取折扣信息 |
| GET | | 通过折扣码获取折扣信息 |
| DELETE | | 删除折扣 |
| Method | Endpoint | Description |
|---|---|---|
| GET | | Get transaction |
| GET | | List transactions |
| 请求方法 | 端点 | 描述 |
|---|---|---|
| GET | | 获取交易信息 |
| GET | | 列出交易记录 |
// 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;
};checkout_idorder_idcustomer_idsubscription_idproduct_idrequest_idsignature// 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;
};checkout_idorder_idcustomer_idsubscription_idproduct_idrequest_idsignatureimport 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 });
}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 });
}// 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();
};// 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();
};// 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();
};// 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();
};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;
};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;
};| Event | When | Action |
|---|---|---|
| Payment successful | Grant access, create user |
| New subscription created | Sync to database |
| Recurring payment processed | Extend access period |
| User/merchant canceled | Revoke at period end |
| Period ended without payment | Retries may happen |
| Trial started | Grant trial access |
| Subscription paused | Pause features |
| Subscription modified | Sync changes |
| Refund processed | May revoke access |
| Chargeback opened | Handle dispute |
| 事件 | 触发时机 | 操作建议 |
|---|---|---|
| 付款成功 | 授予访问权限、创建用户 |
| 新订阅创建 | 同步到数据库 |
| 定期付款处理完成 | 延长访问期限 |
| 用户/商户取消订阅 | 到期后撤销访问权限 |
| 订阅期结束且未付款 | 可能会有付款重试 |
| 试用开始 | 授予试用访问权限 |
| 订阅暂停 | 暂停相关功能 |
| 订阅被修改 | 同步变更信息 |
| 退款处理完成 | 可能需要撤销访问权限 |
| 退单申请发起 | 处理争议 |
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}`);
}
};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.io4242 4242 4242 42424000 0000 0000 00024000 0000 0000 9995https://test-api.creem.io4242 4242 4242 42424000 0000 0000 00024000 0000 0000 9995REFERENCE.mdWEBHOOKS.mdWORKFLOWS.mdREFERENCE.mdWEBHOOKS.mdWORKFLOWS.md