mcpField
W
whop

whop

github.com/raintree-technologyマーケティング・グロース42 回インストール
リポジトリ

whop について

whopは、Whop上でデジタル商品、メンバーシップ、コース、コミュニティへのアクセスを販売・管理する開発者向けスキルガイドです。API v5の例を通じて、チェックアウト、会員アクセス、決済、Webhook、OAuthの連携方法を説明します。

主な機能

  1. 01商品、プラン、チェックアウト設定の作成
  2. 02メンバーシップの照会と管理
  3. 03アクセス確認とライセンス検証
  4. 04決済情報の取得と返金
  5. 05コース、章、レッスンの管理
  6. 06WebhookとOAuthアプリの設定

活用シーン

  1. 01メンバーシップ型サブスクリプション商品の構築
  2. 02Whopのチェックアウトと決済の連携
  3. 03会員状態に応じたコンテンツ制限
  4. 04決済イベントの処理とコース管理

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/raintree-technology/claude-starter --skill whop

インストール後、Claude Code で/whopを入力して読み込まれたか確認します。

詳細

Whop プラットフォームエキスパート

Whop は、デジタル製品、メンバーシップ、コース、コミュニティへのアクセスを販売するためのプラットフォームです。

使用する場面

  • メンバーシップ/サブスクリプション製品を構築する
  • Whop のチェックアウトと決済を連携する
  • コースと教育コンテンツを管理する
  • メンバーシップのステータスに基づいてコンテンツへのアクセスを制限する
  • 決済イベントの Webhook を処理する
  • OAuth を使用して Whop アプリを構築する

クイックスタート

認証
const headers = {
  Authorization: `Bearer ${process.env.WHOP_API_KEY}`,
  "Content-Type": "application/json",
};

const response = await fetch("https://api.whop.com/api/v5/me", { headers });

API キーの種類:

  • Company API Keys - 自社のデータ(Dashboard → Settings → API Keys)
  • App API Keys - マルチテナントアプリ(Dashboard → Developer → Your App)

主な操作

製品を作成する
const product = await fetch("https://api.whop.com/api/v5/products", {
  method: "POST",
  headers,
  body: JSON.stringify({
    title: "Premium Membership",
    description: "Access to all content",
    visibility: "visible",
  }),
});
プランを作成する
const plan = await fetch("https://api.whop.com/api/v5/plans", {
  method: "POST",
  headers,
  body: JSON.stringify({
    product_id: "prod_xxx",
    billing_period: 1,
    billing_period_unit: "month",
    price: 2999, // $29.99 in cents
    currency: "usd",
  }),
});
チェックアウトを作成する
const checkout = await fetch(
  "https://api.whop.com/api/v5/checkout-configurations",
  {
    method: "POST",
    headers,
    body: JSON.stringify({
      plan_id: "plan_xxx",
      success_url: "https://yourapp.com/success",
      cancel_url: "https://yourapp.com/cancel",
      metadata: { user_id: "123" },
    }),
  }
);

const { url } = await checkout.json();
// Redirect user to url

メンバーシップ管理

メンバーシップを一覧表示する
const response = await fetch(
  "https://api.whop.com/api/v5/memberships?valid=true",
  { headers }
);

const { data: memberships } = await response.json();
ユーザーのアクセス権を確認する
async function checkAccess(userId: string, productId: string): Promise<boolean> {
  const response = await fetch(
    `https://api.whop.com/api/v5/memberships?user_id=${userId}&product_id=${productId}&valid=true`,
    { headers }
  );

  const { data } = await response.json();
  return data.length > 0;
}
ライセンスキーを検証する
async function validateLicense(licenseKey: string): Promise<boolean> {
  const response = await fetch(
    `https://api.whop.com/api/v5/memberships?license_key=${licenseKey}`,
    { headers }
  );

  const { data } = await response.json();
  return data.length > 0 && data[0].valid;
}
メンバーシップをキャンセルする
await fetch(
  `https://api.whop.com/api/v5/memberships/${membershipId}/cancel`,
  {
    method: "POST",
    headers,
  }
);

決済

決済を取得する
const payment = await fetch(
  `https://api.whop.com/api/v5/payments/${paymentId}`,
  { headers }
);
決済を返金する
await fetch(`https://api.whop.com/api/v5/payments/${paymentId}/refund`, {
  method: "POST",
  headers,
  body: JSON.stringify({
    amount: 1000, // Optional: partial refund in cents
  }),
});

コース

コースを作成する
const course = await fetch("https://api.whop.com/api/v5/courses", {
  method: "POST",
  headers,
  body: JSON.stringify({
    product_id: "prod_xxx",
    title: "Complete Web Development",
    description: "Learn fullstack from scratch",
    visibility: "visible",
  }),
});
チャプターを作成する
const chapter = await fetch("https://api.whop.com/api/v5/course-chapters", {
  method: "POST",
  headers,
  body: JSON.stringify({
    course_id: "course_xxx",
    title: "Introduction to JavaScript",
    order: 1,
  }),
});
レッスンを作成する
const lesson = await fetch("https://api.whop.com/api/v5/course-lessons", {
  method: "POST",
  headers,
  body: JSON.stringify({
    chapter_id: "chapter_xxx",
    title: "Variables and Data Types",
    content: "Lesson content in markdown...",
    type: "video", // or 'text', 'quiz', 'assignment'
    video_url: "https://youtube.com/watch?v=...",
    order: 1,
  }),
});
レッスンを完了済みにする
await fetch(
  `https://api.whop.com/api/v5/course-lessons/${lessonId}/mark-as-completed`,
  {
    method: "POST",
    headers: {
      Authorization: `Bearer ${userAccessToken}`,
    },
  }
);

Webhook

エンドポイントを設定する
import crypto from "crypto";

export async function POST(req: Request) {
  const body = await req.text();
  const signature = req.headers.get("x-whop-signature");

  // Verify signature
  const hash = crypto
    .createHmac("sha256", process.env.WHOP_WEBHOOK_SECRET!)
    .update(body)
    .digest("hex");

  if (hash !== signature) {
    return new Response("Invalid signature", { status: 401 });
  }

  const event = JSON.parse(body);

  switch (event.action) {
    case "payment.succeeded":
      await handlePaymentSuccess(event.data);
      break;

    case "membership.activated":
      await grantAccess(event.data);
      break;

    case "membership.deactivated":
      await revokeAccess(event.data);
      break;

    case "dispute.created":
      await handleDispute(event.data);
      break;
  }

  return Response.json({ received: true });
}

主な Webhook イベント:

イベント発生条件
payment.succeeded決済が完了した
payment.failed決済に失敗した
membership.activatedメンバーシップが開始した
membership.deactivatedメンバーシップが終了した
invoice.paid継続課金に成功した
dispute.createdチャージバックが開始された

OAuth アプリ

Whop OAuth にリダイレクトする
const authUrl = new URL("https://whop.com/oauth");
authUrl.searchParams.set("client_id", process.env.WHOP_CLIENT_ID!);
authUrl.searchParams.set("redirect_uri", "https://yourapp.com/callback");
authUrl.searchParams.set("scope", "memberships:read payments:read");

// Redirect user
window.location.href = authUrl.toString();
コールバックを処理する
export async function GET(req: Request) {
  const url = new URL(req.url);
  const code = url.searchParams.get("code");

  const response = await fetch("https://api.whop.com/api/v5/oauth/token", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      client_id: process.env.WHOP_CLIENT_ID!,
      client_secret: process.env.WHOP_CLIENT_SECRET!,
      code,
      grant_type: "authorization_code",
      redirect_uri: "https://yourapp.com/callback",
    }),
  });

  const { access_token, refresh_token } = await response.json();
  // Store tokens securely
}

ミドルウェア(アクセス制御)

// middleware.ts
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";

export async function middleware(request: NextRequest) {
  const token = request.cookies.get("whop_user_token")?.value;

  if (!token) {
    return NextResponse.redirect(new URL("/login", request.url));
  }

  const response = await fetch("https://api.whop.com/api/v5/me/memberships", {
    headers: { Authorization: `Bearer ${token}` },
  });

  const { data } = await response.json();
  const hasAccess = data.some((m: any) => m.status === "active" && m.valid);

  if (!hasAccess) {
    return NextResponse.redirect(new URL("/subscribe", request.url));
  }

  return NextResponse.next();
}

export const config = {
  matcher: ["/dashboard/:path*", "/premium/:path*"],
};

TypeScript の型

interface WhopMembership {
  id: string;
  user_id: string;
  product_id: string;
  plan_id: string;
  status: "active" | "paused" | "canceled" | "expired";
  valid: boolean;
  license_key: string;
  renews_at: string | null;
  expires_at: string | null;
  cancel_at_period_end: boolean;
}

interface WhopPayment {
  id: string;
  amount: number;
  currency: string;
  status: "succeeded" | "pending" | "failed";
  user_id: string;
  product_id: string;
}

エラー処理

async function safeWhopRequest(url: string, options: RequestInit) {
  const response = await fetch(url, options);

  if (!response.ok) {
    const error = await response.json();
    throw new Error(error.message || "Whop API error");
  }

  return response.json();
}
エラー意味
401 UnauthorizedAPI キーが無効です
403 Forbidden権限が不十分です
404 Not Foundリソースが存在しません
429 Too Many Requestsレート制限に達しました

セキュリティのベストプラクティス

実施すること:

  • Webhook の署名を検証する
  • トークンを暗号化して保存する
  • サーバー側でメンバーシップのステータスを確認する
  • すべての通信で HTTPS を使用する

実施しないこと:

  • API キーをクライアント側に公開する
  • クライアントから送信されたメンバーシップデータを信用する
  • Webhook の検証を省略する
  • 機密データをログに記録する

実装チェックリスト

  • Whop アカウントと会社を作成する
  • Dashboard から API キーを取得する
  • 環境変数を設定する
  • 製品とプランを作成する
  • チェックアウトのフローを実装する
  • メンバーシップのアクセス確認を追加する
  • Webhook エンドポイントを設定する
  • 決済イベントを処理する
  • エンドツーエンドのフローをテストする

リソース

コメント