whop
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseWhop Platform Expert
Whop平台专家指南
Whop is a platform for selling digital products, memberships, courses, and community access.
Whop是一个用于销售数字产品、会员服务、课程及社区访问权限的平台。
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
- 构建会员/订阅产品
- 集成Whop结账与支付流程
- 管理课程及教育内容
- 根据会员身份限制内容访问
- 处理支付事件的Webhooks
- 基于OAuth构建Whop应用
Quick Start
快速入门
Authentication
认证
typescript
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)
typescript
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密钥类型:
- 企业API密钥 - 用于访问自身企业数据(控制台 → 设置 → API密钥)
- 应用API密钥 - 用于多租户应用(控制台 → 开发者 → 你的应用)
Core Operations
核心操作
Create Product
创建产品
typescript
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",
}),
});typescript
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
创建套餐
typescript
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",
}),
});typescript
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 以美分为单位
currency: "usd",
}),
});Create Checkout
创建结账流程
typescript
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 urltypescript
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();
// 重定向用户至该urlMembership Management
会员管理
List Memberships
列出会员
typescript
const response = await fetch(
"https://api.whop.com/api/v5/memberships?valid=true",
{ headers }
);
const { data: memberships } = await response.json();typescript
const response = await fetch(
"https://api.whop.com/api/v5/memberships?valid=true",
{ headers }
);
const { data: memberships } = await response.json();Check User Access
检查用户访问权限
typescript
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;
}typescript
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
验证许可证密钥
typescript
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;
}typescript
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
取消会员
typescript
await fetch(
`https://api.whop.com/api/v5/memberships/${membershipId}/cancel`,
{
method: "POST",
headers,
}
);typescript
await fetch(
`https://api.whop.com/api/v5/memberships/${membershipId}/cancel`,
{
method: "POST",
headers,
}
);Payments
支付管理
Get Payment
获取支付记录
typescript
const payment = await fetch(
`https://api.whop.com/api/v5/payments/${paymentId}`,
{ headers }
);typescript
const payment = await fetch(
`https://api.whop.com/api/v5/payments/${paymentId}`,
{ headers }
);Refund Payment
退款
typescript
await fetch(`https://api.whop.com/api/v5/payments/${paymentId}/refund`, {
method: "POST",
headers,
body: JSON.stringify({
amount: 1000, // Optional: partial refund in cents
}),
});typescript
await fetch(`https://api.whop.com/api/v5/payments/${paymentId}/refund`, {
method: "POST",
headers,
body: JSON.stringify({
amount: 1000, // 可选:部分退款,以美分为单位
}),
});Courses
课程管理
Create Course
创建课程
typescript
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",
}),
});typescript
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
创建章节
typescript
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,
}),
});typescript
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
创建课时
typescript
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,
}),
});typescript
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", // 或 'text', 'quiz', 'assignment'
video_url: "https://youtube.com/watch?v=...",
order: 1,
}),
});Mark Lesson Complete
标记课时为已完成
typescript
await fetch(
`https://api.whop.com/api/v5/course-lessons/${lessonId}/mark-as-completed`,
{
method: "POST",
headers: {
Authorization: `Bearer ${userAccessToken}`,
},
}
);typescript
await fetch(
`https://api.whop.com/api/v5/course-lessons/${lessonId}/mark-as-completed`,
{
method: "POST",
headers: {
Authorization: `Bearer ${userAccessToken}`,
},
}
);Webhooks
Webhooks
Setup Endpoint
设置端点
typescript
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:
| Event | Trigger |
|---|---|
| Payment completed |
| Payment failed |
| Membership started |
| Membership ended |
| Recurring payment succeeded |
| Chargeback initiated |
typescript
import crypto from "crypto";
export async function POST(req: Request) {
const body = await req.text();
const signature = req.headers.get("x-whop-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事件:
| 事件 | 触发条件 |
|---|---|
| 支付完成 |
| 支付失败 |
| 会员生效 |
| 会员失效 |
| 定期支付成功 |
| 退单申请发起 |
OAuth Apps
OAuth应用
Redirect to Whop OAuth
重定向至Whop OAuth授权页
typescript
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();typescript
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");
// 重定向用户
window.location.href = authUrl.toString();Handle Callback
处理回调
typescript
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
}typescript
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();
// 安全存储令牌
}Middleware (Access Control)
中间件(访问控制)
typescript
// 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
// 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
TypeScript类型定义
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;
}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;
}Error Handling
错误处理
typescript
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();
}| Error | Meaning |
|---|---|
| Invalid API key |
| Insufficient permissions |
| Resource doesn't exist |
| Rate limited |
typescript
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();
}| 错误 | 含义 |
|---|---|
| API密钥无效 |
| 权限不足 |
| 资源不存在 |
| 请求频率超限 |
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
建议:
- 验证Webhook签名
- 加密存储令牌
- 服务端校验会员状态
- 全程使用HTTPS
禁止:
- 客户端暴露API密钥
- 信任客户端发送的会员数据
- 跳过Webhook验证
- 记录敏感数据
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
- 创建Whop账号及企业主体
- 从控制台获取API密钥
- 设置环境变量
- 创建产品及套餐
- 实现结账流程
- 添加会员访问权限校验
- 设置Webhook端点
- 处理支付事件
- 端到端流程测试
Resources
资源
- Dashboard: https://whop.com/dashboard
- API Docs: https://docs.whop.com
- API Base: https://api.whop.com/api/v5
- 控制台: https://whop.com/dashboard
- API文档: https://docs.whop.com
- API基础地址: https://api.whop.com/api/v5