mcpField
C
creem

creem

github.com/armitage-labs開発270 回インストール12 週間前に更新
リポジトリ

creem について

creemは、SaaSやデジタル製品へのCREEM決済導入を支援する開発者向けスキルです。APIの例と実装手順を通じて、チェックアウト、サブスクリプション、Webhook、ライセンスキーを扱います。

主な機能

  1. 01チェックアウトセッションの作成
  2. 02サブスクリプション管理
  3. 03Webhook署名の検証
  4. 04ライセンスの有効化と検証
  5. 05顧客向け請求ポータルリンクの生成

活用シーン

  1. 01SaaS製品へのホスト型チェックアウト導入
  2. 02定期決済や解約の処理
  3. 03ソフトウェアライセンスキーの有効化
  4. 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 - チェックアウトセッション ID
  • order_id - 作成された注文
  • customer_id - 顧客 ID
  • subscription_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}`);
  }
};

テストモード

まずは必ずテストモードで開発してください。

  1. ベース URL として https://test-api.creem.io を使用します
  2. ダッシュボードからテスト API キーを取得します
  3. テストカード:
    • 4242 4242 4242 4242 - 成功
    • 4000 0000 0000 0002 - 拒否
    • 4000 0000 0000 9995 - 残高不足

一般的なインテグレーションのチェックリスト

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 の署名を検証します

ファイルの参照先

詳細については、以下を参照してください。

  • REFERENCE.md - 全フィールドを含む完全な API リファレンス
  • WEBHOOKS.md - Webhook ペイロードの例
  • WORKFLOWS.md - ステップごとのインテグレーションパターン

お困りですか?

コメント