mcpField
W
whop

whop

github.com/raintree-technologyMarketing & growth42 installs
Repository

About whop

whop is a skill guide for developers building on Whop to sell and manage digital products, memberships, courses, and community access. Its API v5 examples cover checkout, membership access, payments, webhooks, and OAuth.

Key features

  1. 01Create products, plans, and checkout configurations
  2. 02Query and manage memberships
  3. 03Check access and validate licenses
  4. 04Retrieve payments and issue refunds
  5. 05Manage courses, chapters, and lessons
  6. 06Configure webhooks and OAuth apps

Use cases

  1. 01Build membership subscription products
  2. 02Integrate Whop checkout and payments
  3. 03Gate content by membership status
  4. 04Handle payment events and manage courses

Install

Copy the command and run it in a terminal.

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

After installing, type/whopin Claude Code to confirm it loaded.

Details

From the project README

Whop Platform Expert

Whop is a platform for selling digital products, memberships, courses, and community access.

When to Use

  • Building membership/subscription products
  • Integrating Whop checkout and payments
  • Managing courses and educational content
  • Gating content by membership status
  • Handling webhooks for payment events
  • Building Whop apps with OAuth

Quick Start

Authentication
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 Key Types:

  • Company API Keys - Your own company data (Dashboard → Settings → API Keys)
  • App API Keys - Multi-tenant apps (Dashboard → Developer → Your App)

Core Operations

Create Product
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",
  }),
});
Create Plan
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",
  }),
});
Create Checkout
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

Membership Management

List Memberships
const response = await fetch(
  "https://api.whop.com/api/v5/memberships?valid=true",
  { headers }
);

const { data: memberships } = await response.json();
Check User Access
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;
}
Validate License Key
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;
}
Cancel Membership
await fetch(
  `https://api.whop.com/api/v5/memberships/${membershipId}/cancel`,
  {
    method: "POST",
    headers,
  }
);

Payments

Get Payment
const payment = await fetch(
  `https://api.whop.com/api/v5/payments/${paymentId}`,
  { headers }
);
Refund Payment
await fetch(`https://api.whop.com/api/v5/payments/${paymentId}/refund`, {
  method: "POST",
  headers,
  body: JSON.stringify({
    amount: 1000, // Optional: partial refund in cents
  }),
});

Courses

Create Course
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",
  }),
});
Create Chapter
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,
  }),
});
Create Lesson
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,
  }),
});
Mark Lesson Complete
await fetch(
  `https://api.whop.com/api/v5/course-lessons/${lessonId}/mark-as-completed`,
  {
    method: "POST",
    headers: {
      Authorization: `Bearer ${userAccessToken}`,
    },
  }
);

Webhooks

Setup Endpoint
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 });
}

Key webhook events:

EventTrigger
payment.succeededPayment completed
payment.failedPayment failed
membership.activatedMembership started
membership.deactivatedMembership ended
invoice.paidRecurring payment succeeded
dispute.createdChargeback initiated

OAuth Apps

Redirect to 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();
Handle Callback
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 (Access Control)

// 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 Types

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;
}

Error Handling

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();
}
ErrorMeaning
401 UnauthorizedInvalid API key
403 ForbiddenInsufficient permissions
404 Not FoundResource doesn't exist
429 Too Many RequestsRate limited

Security Best Practices

DO:

  • Verify webhook signatures
  • Store tokens encrypted
  • Check membership status server-side
  • Use HTTPS everywhere

DON'T:

  • Expose API keys client-side
  • Trust client-sent membership data
  • Skip webhook verification
  • Log sensitive data

Implementation Checklist

  • Create Whop account and company
  • Get API keys from Dashboard
  • Set environment variables
  • Create products and plans
  • Implement checkout flow
  • Add membership access checking
  • Set up webhook endpoint
  • Handle payment events
  • Test end-to-end flow

Resources

Comments

Markdown supported