creem

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

CREEM API Integration Skill

CREEM API集成技能

You are an expert at integrating CREEM, a Merchant of Record (MoR) payment platform for SaaS and digital businesses. You help developers implement checkout flows, manage subscriptions, handle webhooks, and work with license keys.
您是CREEM集成方面的专家,CREEM是面向SaaS和数字业务的商户收单方(MoR)支付平台。您可以帮助开发者实现结账流程、管理订阅、处理Webhook以及操作许可证密钥。

Core Concepts

核心概念

CREEM acts as the legal seller (Merchant of Record), handling tax compliance, payment processing, and refunds. When integrating:
  • Production API:
    https://api.creem.io
  • Test API:
    https://test-api.creem.io
  • Authentication:
    x-api-key
    header with API key from dashboard
  • Prices: Always in cents (1000 = $10.00)
  • Currencies: Three-letter ISO codes in uppercase (USD, EUR, etc.)
CREEM作为合法的销售方(商户收单方),负责处理税务合规、支付处理和退款事宜。集成时需注意:
  • 生产环境API
    https://api.creem.io
  • 测试环境API
    https://test-api.creem.io
  • 身份验证:使用仪表板获取的API key放在
    x-api-key
    请求头中
  • 价格单位:始终以美分为单位(1000 = 10.00美元)
  • 货币代码:使用大写的三位ISO代码(如USD、EUR等)

Authentication Setup

身份验证设置

Always configure authentication properly:
typescript
// 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'
};
请务必正确配置身份验证:
typescript
// 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'
};

Quick Reference: API Endpoints

快速参考:API端点

Checkouts

结账

MethodEndpointDescription
POST
/v1/checkouts
Create checkout session
GET
/v1/checkouts?checkout_id={id}
Retrieve checkout
请求方法端点描述
POST
/v1/checkouts
创建结账会话
GET
/v1/checkouts?checkout_id={id}
获取结账会话信息

Products

产品

MethodEndpointDescription
POST
/v1/products
Create product
GET
/v1/products?product_id={id}
Retrieve product
GET
/v1/products/search
List all products
请求方法端点描述
POST
/v1/products
创建产品
GET
/v1/products?product_id={id}
获取产品信息
GET
/v1/products/search
列出所有产品

Customers

客户

MethodEndpointDescription
GET
/v1/customers?customer_id={id}
Retrieve customer
GET
/v1/customers?email={email}
Retrieve by email
GET
/v1/customers/list
List all customers
POST
/v1/customers/billing
Generate portal link
请求方法端点描述
GET
/v1/customers?customer_id={id}
获取客户信息
GET
/v1/customers?email={email}
通过邮箱获取客户信息
GET
/v1/customers/list
列出所有客户
POST
/v1/customers/billing
生成客户门户链接

Subscriptions

订阅

MethodEndpointDescription
GET
/v1/subscriptions?subscription_id={id}
Retrieve subscription
POST
/v1/subscriptions/{id}
Update subscription
POST
/v1/subscriptions/{id}/upgrade
Upgrade plan
POST
/v1/subscriptions/{id}/cancel
Cancel subscription
POST
/v1/subscriptions/{id}/pause
Pause subscription
POST
/v1/subscriptions/{id}/resume
Resume subscription
请求方法端点描述
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
恢复订阅

Licenses

许可证

MethodEndpointDescription
POST
/v1/licenses/activate
Activate license
POST
/v1/licenses/validate
Validate license
POST
/v1/licenses/deactivate
Deactivate license
请求方法端点描述
POST
/v1/licenses/activate
激活许可证
POST
/v1/licenses/validate
验证许可证
POST
/v1/licenses/deactivate
停用许可证

Discounts

折扣

MethodEndpointDescription
POST
/v1/discounts
Create discount
GET
/v1/discounts?discount_id={id}
Retrieve discount
GET
/v1/discounts?discount_code={code}
Retrieve by code
DELETE
/v1/discounts/{id}/delete
Delete discount
请求方法端点描述
POST
/v1/discounts
创建折扣
GET
/v1/discounts?discount_id={id}
获取折扣信息
GET
/v1/discounts?discount_code={code}
通过折扣码获取折扣信息
DELETE
/v1/discounts/{id}/delete
删除折扣

Transactions

交易

MethodEndpointDescription
GET
/v1/transactions?transaction_id={id}
Get transaction
GET
/v1/transactions/search
List transactions
请求方法端点描述
GET
/v1/transactions?transaction_id={id}
获取交易信息
GET
/v1/transactions/search
列出交易记录

Implementation Patterns

实现模式

1. Create a Checkout Session

1. 创建结账会话

The most common integration pattern - redirect users to CREEM's hosted checkout:
typescript
// 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;
};
Success URL query parameters after payment:
  • checkout_id
    - Checkout session ID
  • order_id
    - Order created
  • customer_id
    - Customer ID
  • subscription_id
    - Subscription (if recurring)
  • product_id
    - Product purchased
  • request_id
    - Your tracking ID (if provided)
  • signature
    - HMAC signature for verification
最常见的集成模式 - 将用户重定向到CREEM托管的结账页面:
typescript
// 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;
};
付款成功后Success URL的查询参数
  • checkout_id
    - 结账会话ID
  • order_id
    - 创建的订单ID
  • customer_id
    - 客户ID
  • subscription_id
    - 订阅ID(如果是定期订阅)
  • product_id
    - 购买的产品ID
  • request_id
    - 您的跟踪ID(如果提供)
  • signature
    - 用于验证的HMAC签名

2. Webhook Handler

2. Webhook处理器

CRITICAL: Always verify webhook signatures to prevent fraud.
typescript
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 });
}
重要提示:务必验证Webhook签名以防止欺诈。
typescript
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. License Key Management

3. 许可证密钥管理

For desktop apps, CLI tools, or software requiring activation:
typescript
// 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();
};
适用于桌面应用、CLI工具或需要激活的软件:
typescript
// 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. Subscription Management

4. 订阅管理

typescript
// 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();
};
typescript
// 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. Customer Portal

5. 客户门户

Let customers manage their own subscriptions:
typescript
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;
};
让客户自行管理他们的订阅:
typescript
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 Events Reference

Webhook事件参考

EventWhenAction
checkout.completed
Payment successfulGrant access, create user
subscription.active
New subscription createdSync to database
subscription.paid
Recurring payment processedExtend access period
subscription.canceled
User/merchant canceledRevoke at period end
subscription.expired
Period ended without paymentRetries may happen
subscription.trialing
Trial startedGrant trial access
subscription.paused
Subscription pausedPause features
subscription.update
Subscription modifiedSync changes
refund.created
Refund processedMay revoke access
dispute.created
Chargeback openedHandle dispute
事件触发时机操作建议
checkout.completed
付款成功授予访问权限、创建用户
subscription.active
新订阅创建同步到数据库
subscription.paid
定期付款处理完成延长访问期限
subscription.canceled
用户/商户取消订阅到期后撤销访问权限
subscription.expired
订阅期结束且未付款可能会有付款重试
subscription.trialing
试用开始授予试用访问权限
subscription.paused
订阅暂停暂停相关功能
subscription.update
订阅被修改同步变更信息
refund.created
退款处理完成可能需要撤销访问权限
dispute.created
退单申请发起处理争议

Error Handling

错误处理

All endpoints return standard HTTP status codes:
typescript
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}`);
  }
};
所有端点返回标准HTTP状态码:
typescript
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}`);
  }
};

Test Mode

测试模式

Always develop in test mode first:
  1. Use
    https://test-api.creem.io
    as base URL
  2. Use test API key from dashboard
  3. Test cards:
    • 4242 4242 4242 4242
      - Success
    • 4000 0000 0000 0002
      - Declined
    • 4000 0000 0000 9995
      - Insufficient funds
请始终先在测试模式下开发:
  1. 使用
    https://test-api.creem.io
    作为基础URL
  2. 使用仪表板提供的测试API密钥
  3. 测试卡号:
    • 4242 4242 4242 4242
      - 付款成功
    • 4000 0000 0000 0002
      - 付款被拒绝
    • 4000 0000 0000 9995
      - 余额不足

Common Integration Checklist

常见集成检查清单

When implementing CREEM:
  1. Environment Setup
    • Store API key in environment variables
    • Configure base URL for test/production
    • Set up webhook endpoint
  2. Checkout Flow
    • Create checkout session with product_id
    • Include request_id for tracking
    • Set success_url with verification
    • Handle checkout.completed webhook
  3. Subscription Handling
    • Handle subscription.paid for renewals
    • Handle subscription.canceled for access revocation
    • Implement customer portal link
    • Store subscription_id for management
  4. License Keys (if applicable)
    • Implement activate on first use
    • Validate on each app start
    • Handle deactivation for device transfer
  5. Security
    • Verify webhook signatures
    • Never expose API keys client-side
    • Validate success URL signatures
实现CREEM集成时:
  1. 环境设置
    • 将API密钥存储在环境变量中
    • 为测试/生产环境配置基础URL
    • 设置Webhook端点
  2. 结账流程
    • 使用product_id创建结账会话
    • 包含request_id用于跟踪
    • 设置带验证的success_url
    • 处理checkout.completed Webhook
  3. 订阅处理
    • 处理subscription.paid事件以完成续费
    • 处理subscription.canceled事件以撤销访问权限
    • 实现客户门户链接
    • 存储subscription_id用于管理
  4. 许可证密钥(如适用)
    • 首次使用时激活许可证
    • 每次应用启动时验证许可证
    • 处理设备转移时的许可证停用
  5. 安全
    • 验证Webhook签名
    • 切勿在客户端暴露API密钥
    • 验证Success URL的签名

File References

文件参考

For detailed information, see:
  • REFERENCE.md
    - Complete API reference with all fields
  • WEBHOOKS.md
    - Webhook payload examples
  • WORKFLOWS.md
    - Step-by-step integration patterns
如需详细信息,请查看:
  • REFERENCE.md
    - 包含所有字段的完整API参考
  • WEBHOOKS.md
    - Webhook负载示例
  • WORKFLOWS.md
    - 分步集成模式

Need Help?

需要帮助?