creem について
creemは、SaaSやデジタル製品へのCREEM決済導入を支援する開発者向けスキルです。APIの例と実装手順を通じて、チェックアウト、サブスクリプション、Webhook、ライセンスキーを扱います。
主な機能
- 01チェックアウトセッションの作成
- 02サブスクリプション管理
- 03Webhook署名の検証
- 04ライセンスの有効化と検証
- 05顧客向け請求ポータルリンクの生成
活用シーン
- 01SaaS製品へのホスト型チェックアウト導入
- 02定期決済や解約の処理
- 03ソフトウェアライセンスキーの有効化
- 04決済Webhookハンドラーの保護
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ armitage-labs/ creem-skills --skill creemインストール後、Claude Code で/creemを入力して読み込まれたか確認します。
詳細
CREEM API 統合スキル
SaaS やデジタルビジネス向けの Merchant of Record(MoR)決済プラットフォームである CREEM の統合に精通しています。開発者によるチェックアウトフローの実装、サブスクリプションの管理、Webhook の処理、ライセンスキーの取り扱いを支援します。
基本概念
CREEM は法的な販売者(Merchant of Record)として、税務コンプライアンス、決済処理、返金を担います。統合時には、次の点に注意してください。
- 本番 API:
https://api.creem.io - テスト API:
https://test-api.creem.io - 認証:ダッシュボードから取得した API キーを
x-api-keyヘッダーに指定します - 価格:常にセント単位です(1000 = $10.00)
- 通貨:3 文字の 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}`);
}
};
テストモード
まずは必ずテストモードで開発してください。
- ベース URL として
https://test-api.creem.ioを使用します - ダッシュボードからテスト 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- ステップごとのインテグレーションパターン
お困りですか?
- ドキュメント:https://docs.creem.io
- ダッシュボード:https://creem.io/dashboard
- サポート:[email protected]
コメント
Markdown 対応