Whop × Athletes Untapped

Integration Guide

Whop runs alongside Stripe in production — no rip-and-replace. Checkout, subscriptions, coach payouts, and the earnings dashboard switch over per-coach. Stripe stays live as the fallback until you’re ready to cut over.

Stripe Checkout → Whop Checkout Stripe Connect → Whop Payouts Flask + Firestore stays Next.js + MUI stays No new dependencies

Start Here — Drop This Into Your Repo

This file gives Cursor and Claude Code full context about your Whop migration. Every file path, schema change, and code example — ready to implement.

Save as CLAUDE.md in your project root. Open Cursor. Say “implement the Whop migration.”

You are integrating Whop into Athletes Untapped, a two-sided coaching marketplace built with Flask + Next.js 14 (Pages Router) + Firestore. Whop replaces Stripe for checkout, subscriptions, coach onboarding, and per-lesson payouts. Both providers run side-by-side in production. Routing is controlled by a PAYMENT_PROVIDER env var (global default) and a per-coach payment_provider field in Firestore. Read CLAUDE.md for all file paths, schema changes, and code examples. Start with the Whop Python REST client (back_end/services/whop_client.py), then implement flows one by one: athlete checkout (server.py:889), subscriptions (stripe_service.py:554), coach onboarding (server.py:715), per-lesson payouts (event_service.py:563), and webhooks.

Rollout Strategy

Whop runs alongside Stripe in production. No staging environment needed, no new dependencies. Routing is controlled by an env var with per-coach override using a Firestore field.

PAYMENT_PROVIDER env var defaults to "stripe". Per-coach override: coach.payment_provider == "whop" in their Firestore document. All routing lives in back_end/services/payment_router.py — one check, not scattered across files.
1

Build

Whop client module, webhook controller, Firestore field additions, both flow paths. Stripe stays default. Deploy with zero behavior change.

2

Internal Test

Set payment_provider: "whop" on your own test coach in Firestore. Book a session, complete a lesson, verify the payout — real money.

3

Small Cohort

Enable Whop for 5–10 trusted coaches. Verify checkout, subscription renewals, per-lesson payouts, and notification parity.

4

Full Rollout

Flip PAYMENT_PROVIDER=whop globally. Stripe path stays in code as fallback until you’re confident.

Payment Flows

Four distinct money flows get a Whop path. Each runs behind the provider routing check.

Athlete Checkout (One-Time + Subscriptions)

Athlete
Selects Package
Backend
Whop Checkout Session
Whop
Hosted Payment Page
Webhook
payment.succeeded
Firestore
Sessions Credited

Replaces stripe.PaymentIntent.create in server.py:889 and stripe.Subscription.create in stripe_service.py:554. One-time packs and weekly/monthly subscriptions both use Whop checkout — plan type controls the billing cycle.

Coach Onboarding (Payouts Setup)

Coach
Clicks Setup
Backend
Create Whop Company
Whop
Identity Verification
Firestore
whop_verified: true

Replaces stripe.Account.create(type="express") in server.py:715 and stripe.AccountLink.create in server.py:726. Each coach becomes a Whop child company with identity verification built in.

Per-Lesson Coach Payouts

Both Confirm
Lesson Complete
Backend
Whop Transfer
Coach
80% Received

Replaces stripe.Transfer.create in event_service.py:563. Same trigger (both parties confirm lesson), same 80/20 split — different API call. Coach withdraws from their Whop wallet.

Coach Earnings Dashboard

Coach
Opens /payments
Frontend
Whop Embedded Components
Coach
Balance + Payouts

Replaces stripe.Balance.retrieve and stripe.BalanceTransaction.list in server.py:1512+. Whop provides ready-made <BalanceElement> and <PayoutElement> React components.

Your Code → Whop

How your existing Firestore collections and Python models map to Whop entities. These are the fields that get added.

Your SchemaFileWhop EntityNew Fields
Coach back_end/models/coach.py Company (child) whop_company_id, whop_verified, whop_product_id, whop_plan_ids, payment_provider
Athlete back_end/models/athlete.py User whop_user_id
Connection back_end/models/connection.py Membership whop_membership_id, whop_product_id
Transaction server.py:1088 / stripe_service.py:475 Payment whop_payment_id, whop_checkout_id, provider
(new) Firestore collection Webhook Event whop_webhook_events collection for idempotency

What Changes

Side-by-side: the Stripe calls you have today vs. the Whop equivalents.

Stripe (Today)

  • PaymentIntent.create for checkout
  • Subscription.create for recurring
  • Account.create(express) for coaches
  • AccountLink.create for onboarding
  • Transfer.create per-lesson payout
  • Balance.retrieve for dashboard
  • billing_portal.Session.create

Whop (After)

  • checkout_sessions.create for checkout
  • Plans with renewal_period for recurring
  • companies.create (child) for coaches
  • Whop-hosted identity verification
  • transfers.create per-lesson payout
  • Embedded <BalanceElement>
  • Whop membership management

Frontend (Today)

  • <PaymentElement> in BookASession.jsx
  • <CardElement> in AthletePaymentSettings.jsx
  • <Elements> wrapper in StripeProvider.jsx
  • Custom earnings display in payments/index.jsx

Frontend (After)

  • Redirect to Whop hosted checkout
  • Payment methods managed by Whop
  • No Stripe wrapper needed for Whop coaches
  • <PayoutElement> + <BalanceElement>

Webhook Mapping

Your existing Stripe event handlers map to these Whop events. Both webhook endpoints run simultaneously.

Stripe EventWhop EventWhat Happens
payment_intent.succeeded payment.succeeded Mark transaction paid, credit sessions, send confirmation SMS + email
payment_intent.payment_failed payment.failed Mark transaction failed, send failure notification
invoice.payment_succeeded (renewal) membership.went_valid Create renewal transaction, credit sessions for the period
invoice.payment_failed membership.went_invalid Mark subscription canceled, notify athlete
customer.subscription.deleted membership.went_invalid Move to past subscriptions, clean up connection
customer.subscription.updated membership.went_invalid Handle cancel-at-period-end state changes
customer.subscription.created membership.went_valid Log + track (no DB changes, same as today)
Both endpoints stay live. /api/v1/stripe/webhook handles in-flight Stripe transactions. /api/v1/whop/webhook handles Whop events. Both coexist until full cutover. Whop uses event (not type) for the event name.

Architecture

Where Whop fits in your stack. Orange is new, green stays unchanged. Both providers coexist.

                      Next.js Frontend                      Flask Backend
                     (Pages Router)                        (server.py)
                           |                                    |
                     BookASession.jsx  ───── POST ─────  /api/v1/create/payment/intent
                     payments/index.jsx                          |
                                                    ┌───────────┴───────────┐
                                                    │  payment_router.py    │
                                                    │  get_provider(coach)  │
                                                    └───────┬───────┬───────┘
                                                            │       │
                                              ┌─────────────┘       └─────────────┐
                                              │                                   │
                                     Stripe (legacy)                      Whop (new)
                                 stripe.PaymentIntent.*               whop.create_checkout_session()
                                 stripe.Transfer.create               whop.create_transfer()
                                 stripe.Account.create                whop.create_company()
                                              │                                   │
                                    /api/v1/stripe/webhook              /api/v1/whop/webhook
                                              │                                   │
                                              └─────────┬─────────────────────────┘
                                                        │
                                               Firestore (transactions, connections, users)
                                               Brevo emails  |  Twilio SMS  |  PostHog events

Coach Onboarding States

Each coach transitions through these states as they onboard onto Whop payouts.

StateMeaningCan DoBlocked
No Whop Account Coach hasn't started Whop setup Everything via Stripe Whop checkout, Whop payouts
Company Created whop_company_id set, verification pending Products/plans created Whop checkout (until verified)
Verified whop_verified: true, ready for payouts Full Whop flow: checkout, payouts, dashboard Nothing
Active on Whop payment_provider: "whop" All payments route through Whop Stripe (intentionally bypassed)

Timeline

One week to first live dollar. Stripe stays live the entire time.

Day 1–2
Build
  • whop_client.py + payment_router.py
  • Webhook controller + idempotency
  • Firestore field additions
  • All 4 flow paths (behind routing)
  • Deploy — Stripe still default
Day 3–4
Internal Test
  • Onboard test coach via Whop
  • Real checkout + subscription
  • Complete a lesson, verify payout
  • Check Brevo/Twilio notifications
Day 5–6
Small Cohort
  • 5–10 real coaches on Whop
  • Athletes booking sessions
  • Webhook parity confirmed
  • Per-lesson payouts validated
Day 7
Full Rollout
  • Flip PAYMENT_PROVIDER=whop
  • Stripe path stays as fallback
  • Monitor error rates + payouts
  • First live dollar

What Stays the Same

Most of your stack is unchanged. Whop replaces only the payment provider layer.

Firebase Auth
Firestore (all data)
Firedantic ORM
Flask backend
Next.js 14 frontend
Material UI (MUI)
Brevo emails
Twilio SMS
PostHog analytics
AWS ECS Fargate
Stripe (as fallback)
No new dependencies
~ Coach model (new fields)
~ BookASession.jsx (redirect)
~ payments/index.jsx (embed)
Bottom line: Your auth, database, ORM, frontend framework, UI library, notifications, analytics, and hosting are all untouched. Stripe stays in the codebase as a live fallback. The only additions are a Whop REST client, a webhook handler, and new Firestore fields on existing documents.