whop について
whopは、Whop上でデジタル商品、メンバーシップ、コース、コミュニティへのアクセスを販売・管理する開発者向けスキルガイドです。API v5の例を通じて、チェックアウト、会員アクセス、決済、Webhook、OAuthの連携方法を説明します。
主な機能
- 01商品、プラン、チェックアウト設定の作成
- 02メンバーシップの照会と管理
- 03アクセス確認とライセンス検証
- 04決済情報の取得と返金
- 05コース、章、レッスンの管理
- 06WebhookとOAuthアプリの設定
活用シーン
- 01メンバーシップ型サブスクリプション商品の構築
- 02Whopのチェックアウトと決済の連携
- 03会員状態に応じたコンテンツ制限
- 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 Unauthorized | API キーが無効です |
403 Forbidden | 権限が不十分です |
404 Not Found | リソースが存在しません |
429 Too Many Requests | レート制限に達しました |
セキュリティのベストプラクティス
実施すること:
- Webhook の署名を検証する
- トークンを暗号化して保存する
- サーバー側でメンバーシップのステータスを確認する
- すべての通信で HTTPS を使用する
実施しないこと:
- API キーをクライアント側に公開する
- クライアントから送信されたメンバーシップデータを信用する
- Webhook の検証を省略する
- 機密データをログに記録する
実装チェックリスト
- Whop アカウントと会社を作成する
- Dashboard から API キーを取得する
- 環境変数を設定する
- 製品とプランを作成する
- チェックアウトのフローを実装する
- メンバーシップのアクセス確認を追加する
- Webhook エンドポイントを設定する
- 決済イベントを処理する
- エンドツーエンドのフローをテストする
リソース
- ダッシュボード: https://whop.com/dashboard
- API ドキュメント: https://docs.whop.com
- API ベース URL: https://api.whop.com/api/v5
コメント
Markdown 対応