Whop runs alongside Stripe in production — no rip-and-replace. Your Next.js 14 frontend, Express API, MongoDB, JWT auth, and all non-payment routes stay untouched. Platform treasury model preserved — fees stay calculated in-app. One env var controls the rollout.
This file gives Cursor and Claude full context about your Whop migration. Every file path in backend-code/ and next-frontend/, schema change, and JavaScript code example — ready to implement.
Save as CLAUDE.md in your project root. Open Cursor. Say “implement the Whop migration.”
Whop runs alongside Stripe in production. No staging needed, no new dependencies. Routing is controlled by an env var with per-Organization override. The platform treasury model is preserved — fees calculated in-app, not via Stripe application_fee_amount.
PAYMENT_PROVIDER env var defaults to "stripe". Per-org override: Organization.payment_provider === "whop". All routing lives in getPaymentProvider() in backend-code/constants/constants.js — one check, not scattered across 80+ files.Whop service client, webhook controller, Mongoose schema additions, both checkout paths. Stripe stays default. Deploy with zero behavior change.
Set payment_provider: "whop" on your own Organization. Buy test tickets with real money. Verify webhooks, ledger entries, and outbound partner webhooks.
Enable Whop for 5–10 trusted host organizations. Verify payout timing, conversion tracking, and multi-currency behavior.
Flip PAYMENT_PROVIDER=whop globally. Stripe path stays in code as fallback until you’re confident.
Platform Treasury model: all payments collect to Flite’s Whop account, then fees are calculated in-app via transparentFeeCalculation() and host share is transferred out. Razorpay INR stays in phase 1.
How your existing Mongoose schemas map to Whop. All backend files in backend-code/.
| Your Schema | File | Whop Entity | New Fields |
|---|---|---|---|
Organization |
backend-code/models/organizationModel.js |
Company (connected) | whop_company_id, whop_verification_status, payment_provider |
Vendor |
backend-code/models/vendorModel.js |
Company (connected) | whop_company_id, whop_verification_status |
Order |
backend-code/models/orderModel.js |
Payment | whop_payment_id, whop_checkout_session_id, whop_refund_id |
OverallPayout |
backend-code/models/OverallPayout.js |
Payout | whop_payout_id, whop_transfer_id |
VendorPayout |
backend-code/models/vendorPayoutModel.js |
Payout | whop_payout_id, whop_transfer_id |
Transaction |
backend-code/models/transactionsModel.js |
Payment / Transfer / Payout | whop_payment_id, whop_transfer_id, whop_payout_id |
User |
backend-code/models/user.js |
Customer (by email) | No changes — matched via checkout metadata |
EventRequest |
backend-code/models/eventRequestModel.js |
(unchanged) | Promoter commissions tracked in Transaction |
CustomFee |
backend-code/models/customFeeModel.js |
(unchanged) | Fee calculation stays in utils/helper.js |
Side-by-side: the Stripe calls you have today vs. the Whop equivalents.
paymentIntents.create — tickets, ads, blasts, reservationsaccounts.create (Express) — host/vendor onboardingaccountLinks.create — KYC linktransfers.create — platform → hostpayouts.create — host → bankrefunds.create — full + partialPOST /checkout_sessions — all payment contextsPOST /companies — host/vendor onboardingPOST /companies/{id}/account_links — KYCPOST /transfers — platform → hostPOST /payments/{id}/refund — full + partialpayment_intent.succeededpayment_intent.amount_capturable_updatedpayment_intent.payment_failedpayment.succeededpayment.failedpayment.refundedcompany.created / company.updatedpayout.completedYour existing Stripe event handlers map to these Whop events. Both webhook endpoints run simultaneously.
| Stripe Event | Whop Event | What Happens |
|---|---|---|
payment_intent.succeeded |
payment.succeeded |
Mark order complete, create ledger entry, fire outbound webhook, track conversions |
payment_intent.payment_failed |
payment.failed |
Mark order failed, log error |
payment_intent.amount_capturable_updated |
— | Hold/capture flow — handle via checkout completion instead |
| (Stripe Connect account events) | company.created |
Set whop_company_id on Organization |
| (Stripe Connect status changes) | company.updated |
Update whop_verification_status, gate checkout/payout actions |
| (Transfer/payout completion) | payout.completed |
Update OverallPayout status, reconcile ledger |
webhookUrl configured continue receiving the same payload shape for order.completed events, regardless of whether Stripe or Whop processed the payment.Where Whop fits. Orange is new, green stays unchanged. Both providers coexist.
next-frontend/ (Vercel) backend-code/ (AWS Amplify) ┌───────────────────────┐ ┌──────────────────────────┐ │ Next.js 14 (App Router) │ │ Express API (port 5001) │ │ CheckoutFlowPopup.jsx │ │ server.js + 16+ controllers│ │ PaymentForm/ components │ ──> │ order.controller.js │ │ vendonConnects/ (onb.) │ │ stripe.controller.js │ └───────────────────────┘ └───────────┬──────────────┘ │ getPaymentProvider(org) │ ┌───────┴───────┐ │ │ ┌──────────┴────┐ ┌────┴──────────┐ │ Stripe (default) │ │ Whop (new) │ │ PaymentIntents │ │ Checkout │ │ Connect Express │ │ Companies │ │ Transfers │ │ Transfers │ │ 5 instances/curr │ │ 1 key, all curr │ └─────────────────┘ └────────────────┘ │ ┌───────────────────────┴───┐ │ MongoDB (~79 models) │ │ Organization, Order, Vendor, │ │ Transaction, OrderPayout, ... │ └────────────────────────────┘ Webhooks (both live): POST /api/stripe-webhook-{usd,cad,gbp,sgd,inr} ← Stripe POST /api/whop-webhook ← Whop
Connected organizations go through verification before they can receive payouts. Maps to Whop company verification status.
| State | Meaning | Can Do | Blocked |
|---|---|---|---|
| pending | Company created, onboarding not started | Create events, sell tickets | Receive payouts |
| kyc_pending | KYC submitted, under review | Create events, sell tickets | Receive payouts |
| restricted | Partially verified, limited capabilities | Sell tickets (capped volume) | Full payouts |
| active | Fully verified | Everything — sell, receive payouts, manage bank | Nothing |
| suspended | Account flagged or deactivated | View dashboard | Sell tickets, receive payouts |
One week to first live dollar. Stripe stays live the entire time.
Most of your stack is unchanged. Whop replaces only the payment provider layer.
axios, frontend uses redirect/iframe (no Stripe Elements equivalent needed). New code: backend-code/services/whop.service.js, backend-code/controlles/whop.controller.js, one frontend checkout component, and field additions to existing Mongoose schemas.