whop-kit 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +354 -0
  2. package/dist/index.js +1 -1
  3. package/package.json +2 -2
package/README.md ADDED
@@ -0,0 +1,354 @@
1
+ # whop-kit
2
+
3
+ Framework-agnostic toolkit for building apps with [Whop](https://whop.com) authentication, payments, and memberships.
4
+
5
+ Use whop-kit to add Whop OAuth, subscription management, webhook handling, and plan gating to any JavaScript framework — Next.js, Astro, SvelteKit, Express, or anything with `fetch` and `crypto`.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm install whop-kit jose
11
+ ```
12
+
13
+ `jose` is a peer dependency used for JWT signing/verification.
14
+
15
+ ## Modules
16
+
17
+ Import only what you need via subpath exports:
18
+
19
+ ```typescript
20
+ import { definePlans } from "whop-kit/core";
21
+ import { createSessionToken, verifySessionToken } from "whop-kit/auth";
22
+ import { buildAuthorizationUrl, verifyWebhookSignature } from "whop-kit/whop";
23
+ import { createConfigManager } from "whop-kit/config";
24
+ import { createSubscriptionHelpers } from "whop-kit/subscriptions";
25
+ import { sendEmail } from "whop-kit/email";
26
+ import { cn, formatDate } from "whop-kit/utils";
27
+ ```
28
+
29
+ ## Quick Start
30
+
31
+ ### 1. Define your plans
32
+
33
+ ```typescript
34
+ import { definePlans } from "whop-kit/core";
35
+
36
+ export const plans = definePlans({
37
+ free: {
38
+ name: "Free",
39
+ description: "Get started",
40
+ priceMonthly: 0,
41
+ priceYearly: 0,
42
+ features: ["3 projects", "Community support"],
43
+ highlighted: false,
44
+ },
45
+ pro: {
46
+ name: "Pro",
47
+ description: "For power users",
48
+ priceMonthly: 29,
49
+ priceYearly: 290,
50
+ features: ["Unlimited projects", "Priority support"],
51
+ highlighted: true,
52
+ },
53
+ });
54
+
55
+ plans.hasMinimum("pro", "free"); // true
56
+ plans.hasMinimum("free", "pro"); // false
57
+ plans.defaultPlan; // "free"
58
+ plans.keys; // ["free", "pro"]
59
+ ```
60
+
61
+ Key order defines the hierarchy — first is lowest, last is highest.
62
+
63
+ ### 2. Handle authentication
64
+
65
+ ```typescript
66
+ import {
67
+ createSessionToken,
68
+ verifySessionToken,
69
+ encodeSecret,
70
+ } from "whop-kit/auth";
71
+
72
+ const secret = encodeSecret(process.env.SESSION_SECRET);
73
+
74
+ // After OAuth callback — create a session
75
+ const token = await createSessionToken(
76
+ {
77
+ userId: "user_123",
78
+ whopUserId: "user_xxxxx",
79
+ email: "user@example.com",
80
+ name: "Jane",
81
+ profileImageUrl: null,
82
+ plan: "pro",
83
+ cancelAtPeriodEnd: false,
84
+ isAdmin: false,
85
+ },
86
+ secret,
87
+ );
88
+
89
+ // On subsequent requests — verify the session
90
+ const session = await verifySessionToken(token, secret, plans.keys, plans.defaultPlan);
91
+ ```
92
+
93
+ ### 3. OAuth with Whop
94
+
95
+ ```typescript
96
+ import { buildAuthorizationUrl, exchangeCodeForTokens, getWhopUser } from "whop-kit/whop";
97
+
98
+ // Step 1: Redirect user to Whop
99
+ const { url, codeVerifier, state } = await buildAuthorizationUrl(
100
+ "https://myapp.com/api/auth/callback",
101
+ "app_xxxxx", // your Whop App ID
102
+ );
103
+ // Store codeVerifier + state in a cookie, then redirect to `url`
104
+
105
+ // Step 2: Handle the callback
106
+ const tokens = await exchangeCodeForTokens(code, codeVerifier, redirectUri, clientId);
107
+ const user = await getWhopUser(tokens.access_token);
108
+ // user.sub = "user_xxxxx", user.email, user.name, etc.
109
+ ```
110
+
111
+ ### 4. Verify webhooks
112
+
113
+ ```typescript
114
+ import { verifyWebhookSignature } from "whop-kit/whop";
115
+
116
+ const isValid = await verifyWebhookSignature(
117
+ rawBody,
118
+ {
119
+ "webhook-id": request.headers.get("webhook-id"),
120
+ "webhook-signature": request.headers.get("webhook-signature"),
121
+ "webhook-timestamp": request.headers.get("webhook-timestamp"),
122
+ },
123
+ process.env.WHOP_WEBHOOK_SECRET,
124
+ );
125
+ ```
126
+
127
+ ### 5. Check access
128
+
129
+ ```typescript
130
+ import { checkWhopAccess } from "whop-kit/whop";
131
+
132
+ const { hasAccess } = await checkWhopAccess(
133
+ "user_xxxxx", // Whop user ID
134
+ "prod_xxxxx", // product or experience ID
135
+ "your-api-key",
136
+ );
137
+ ```
138
+
139
+ ### 6. Manage subscriptions
140
+
141
+ Subscription helpers use a database adapter pattern — bring your own ORM:
142
+
143
+ ```typescript
144
+ import { createSubscriptionHelpers } from "whop-kit/subscriptions";
145
+
146
+ const subs = createSubscriptionHelpers(myDbAdapter, plans.defaultPlan, plans.keys);
147
+
148
+ await subs.activateMembership("user_xxxxx", "pro", "mem_xxxxx");
149
+ const result = await subs.getSubscriptionDetails(userId);
150
+ const status = await subs.getUserSubscriptionStatus(userId); // "active" | "canceling" | "free"
151
+ ```
152
+
153
+ ### 7. Configuration
154
+
155
+ Key-value config with in-memory caching and env var fallback:
156
+
157
+ ```typescript
158
+ import { createConfigManager } from "whop-kit/config";
159
+
160
+ const config = createConfigManager({
161
+ store: myConfigStore, // implements { get(key): Promise<string|null>, set(key, value): Promise<void> }
162
+ envMap: {
163
+ whop_app_id: "WHOP_APP_ID",
164
+ whop_api_key: "WHOP_API_KEY",
165
+ },
166
+ cacheTtlMs: 30_000, // 30s default
167
+ });
168
+
169
+ const appId = await config.get("whop_app_id"); // checks cache → env → store
170
+ ```
171
+
172
+ ### 8. Send emails
173
+
174
+ ```typescript
175
+ import { sendEmail, emailWrapper, escapeHtml } from "whop-kit/email";
176
+
177
+ const result = await sendEmail(
178
+ { provider: "resend", apiKey: "re_xxxxx" },
179
+ {
180
+ to: "user@example.com",
181
+ from: "hello@myapp.com",
182
+ subject: "Welcome!",
183
+ html: emailWrapper(
184
+ `<h1>Hi ${escapeHtml(name)}</h1><p>Welcome to the app.</p>`,
185
+ "My App",
186
+ ),
187
+ },
188
+ );
189
+ ```
190
+
191
+ Supports Resend and SendGrid via direct `fetch` — no SDK needed.
192
+
193
+ ## Adapter Pattern
194
+
195
+ whop-kit is framework-agnostic through two adapter interfaces:
196
+
197
+ ### CookieAdapter
198
+
199
+ Implement per framework to enable cookie-based sessions:
200
+
201
+ ```typescript
202
+ import type { CookieAdapter } from "whop-kit/auth";
203
+
204
+ // Next.js
205
+ import { cookies } from "next/headers";
206
+
207
+ const nextCookies: CookieAdapter = {
208
+ async get(name) {
209
+ return (await cookies()).get(name)?.value;
210
+ },
211
+ async set(name, value, options) {
212
+ (await cookies()).set(name, value, options);
213
+ },
214
+ async delete(name) {
215
+ (await cookies()).set(name, "", { maxAge: 0, path: "/" });
216
+ },
217
+ };
218
+
219
+ // Astro
220
+ const astroCookies = (Astro): CookieAdapter => ({
221
+ get: (name) => Astro.cookies.get(name)?.value,
222
+ set: (name, value, opts) => Astro.cookies.set(name, value, opts),
223
+ delete: (name) => Astro.cookies.delete(name),
224
+ });
225
+ ```
226
+
227
+ ### DbAdapter
228
+
229
+ Implement per ORM for subscription management:
230
+
231
+ ```typescript
232
+ import type { DbAdapter } from "whop-kit/subscriptions";
233
+
234
+ // Prisma example
235
+ const prismaAdapter: DbAdapter = {
236
+ async findUserById(id) {
237
+ return prisma.user.findUnique({
238
+ where: { id },
239
+ select: { plan: true, whopMembershipId: true, cancelAtPeriodEnd: true },
240
+ });
241
+ },
242
+ async upsertMembership(whopUserId, plan, membershipId) {
243
+ await prisma.user.upsert({
244
+ where: { whopUserId },
245
+ update: { plan, whopMembershipId: membershipId, cancelAtPeriodEnd: false },
246
+ create: { whopUserId, plan, whopMembershipId: membershipId },
247
+ });
248
+ },
249
+ // ... other methods
250
+ };
251
+ ```
252
+
253
+ ### ConfigStore
254
+
255
+ Implement for persistent config storage:
256
+
257
+ ```typescript
258
+ import type { ConfigStore } from "whop-kit/config";
259
+
260
+ // Prisma example
261
+ const prismaConfigStore: ConfigStore = {
262
+ async get(key) {
263
+ const row = await prisma.systemConfig.findUnique({ where: { key } });
264
+ return row?.value ?? null;
265
+ },
266
+ async set(key, value) {
267
+ await prisma.systemConfig.upsert({
268
+ where: { key },
269
+ update: { value },
270
+ create: { key, value },
271
+ });
272
+ },
273
+ };
274
+ ```
275
+
276
+ ## API Reference
277
+
278
+ ### `whop-kit/core`
279
+
280
+ | Export | Description |
281
+ |--------|-------------|
282
+ | `definePlans(metadata)` | Create a type-safe plan system with hierarchy, ranks, and helpers |
283
+ | `PlanMetadataEntry` | Shape of each plan entry |
284
+ | `PlanSystem<K>` | The resolved plan system with all derived helpers |
285
+ | `BillingInterval` | `"monthly" \| "yearly"` |
286
+
287
+ ### `whop-kit/auth`
288
+
289
+ | Export | Description |
290
+ |--------|-------------|
291
+ | `createSessionToken(session, secret, options?)` | Create a signed JWT |
292
+ | `verifySessionToken(token, secret, planKeys, defaultPlan)` | Verify and decode a JWT |
293
+ | `setSessionCookie(session, secret, cookies, isProduction?)` | Set session + login indicator cookies |
294
+ | `clearSessionCookie(cookies, isProduction?)` | Clear session cookies |
295
+ | `getSessionFromCookie(cookies, secret, planKeys, defaultPlan, refreshPlan?)` | Read session from cookies with optional DB refresh |
296
+ | `generateSecret(byteLength?)` | Generate a hex-encoded secret |
297
+ | `encodeSecret(secret)` | Encode a string secret to Uint8Array |
298
+ | `Session` | Session payload interface |
299
+ | `CookieAdapter` | Cookie interface to implement per framework |
300
+
301
+ ### `whop-kit/whop`
302
+
303
+ | Export | Description |
304
+ |--------|-------------|
305
+ | `buildAuthorizationUrl(redirectUri, clientId)` | Build OAuth URL with PKCE |
306
+ | `exchangeCodeForTokens(code, verifier, redirectUri, clientId)` | Exchange auth code for tokens |
307
+ | `getWhopUser(accessToken)` | Fetch user profile from OIDC endpoint |
308
+ | `checkWhopAccess(whopUserId, resourceId, apiKey)` | Real-time access check |
309
+ | `fetchWhopPlanDetails(planId, apiKey)` | Fetch plan pricing from Whop API |
310
+ | `getEffectivePrice(details)` | Extract the effective price from plan details |
311
+ | `uncancelMembership(membershipId, apiKey)` | Reverse a pending cancellation |
312
+ | `verifyWebhookSignature(body, headers, secret)` | HMAC-SHA256 webhook verification |
313
+
314
+ ### `whop-kit/config`
315
+
316
+ | Export | Description |
317
+ |--------|-------------|
318
+ | `createConfigManager(options)` | Create a config manager with caching + env fallback |
319
+ | `ConfigStore` | Persistent store interface to implement |
320
+ | `ConfigManager` | The config manager with `get`, `set`, `setMany`, `clearCache` |
321
+
322
+ ### `whop-kit/subscriptions`
323
+
324
+ | Export | Description |
325
+ |--------|-------------|
326
+ | `createSubscriptionHelpers(db, defaultPlan, planKeys)` | Create all subscription CRUD helpers |
327
+ | `DbAdapter` | Database interface to implement per ORM |
328
+ | `SubscriptionStatus` | `"active" \| "canceling" \| "free"` |
329
+
330
+ ### `whop-kit/email`
331
+
332
+ | Export | Description |
333
+ |--------|-------------|
334
+ | `sendEmail(config, options)` | Send via Resend or SendGrid |
335
+ | `escapeHtml(text)` | Escape HTML for safe email rendering |
336
+ | `emailWrapper(body, footerText)` | Wrap content in a standard email layout |
337
+
338
+ ### `whop-kit/utils`
339
+
340
+ | Export | Description |
341
+ |--------|-------------|
342
+ | `cn(...classes)` | Merge class names, filtering falsy values |
343
+ | `monthlyEquivalent(yearlyTotal)` | Calculate monthly price from yearly |
344
+ | `formatDate(date)` | Format a date as "Jan 1, 2026" |
345
+
346
+ ## Templates
347
+
348
+ Official starter templates built on whop-kit:
349
+
350
+ - **[whop-saas-starter](https://github.com/whopio/whop-saas-starter)** — Next.js SaaS with auth, payments, dashboard, and docs
351
+
352
+ ## License
353
+
354
+ MIT
package/dist/index.js CHANGED
@@ -2,8 +2,8 @@ export { clearSessionCookie, createSessionToken, encodeSecret, generateSecret, g
2
2
  export { createConfigManager } from './chunk-PLZUFPEG.js';
3
3
  export { definePlans } from './chunk-ZQ44KZRC.js';
4
4
  export { emailWrapper, escapeHtml, sendEmail } from './chunk-KQQGVBBH.js';
5
+ export { buildAuthorizationUrl, checkWhopAccess, exchangeCodeForTokens, fetchWhopPlanDetails, getEffectivePrice, getWhopUser, randomString, sha256, uncancelMembership, verifyWebhookSignature } from './chunk-EYK3S5U2.js';
5
6
  export { createSubscriptionHelpers } from './chunk-NS32METI.js';
6
7
  export { cn, formatDate, monthlyEquivalent } from './chunk-BLQ7T7NQ.js';
7
- export { buildAuthorizationUrl, checkWhopAccess, exchangeCodeForTokens, fetchWhopPlanDetails, getEffectivePrice, getWhopUser, randomString, sha256, uncancelMembership, verifyWebhookSignature } from './chunk-EYK3S5U2.js';
8
8
  //# sourceMappingURL=index.js.map
9
9
  //# sourceMappingURL=index.js.map
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "whop-kit",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Framework-agnostic toolkit for building apps with Whop authentication, payments, and memberships",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "author": "Colin McDermott",
8
8
  "repository": {
9
9
  "type": "git",
10
- "url": "https://github.com/colinmcd/whop-kit"
10
+ "url": "https://github.com/colinmcdermott/whop-kit"
11
11
  },
12
12
  "exports": {
13
13
  ".": {