Your Whop integration is live (processor ID 7) and Apple Pay renders — but purchases show $0 because your checkout only uses setup mode. This guide fixes that and hardens 5 other integration issues.
This file gives Cursor and Claude Code full context on fixing the $0 Apple Pay issue and hardening your Whop integration. Every file path, code example, and testing checklist — ready to implement.
Save as CLAUDE.md in your BA_V2 project root. Open Cursor. Say “implement the Whop Apple Pay and checkout hardening changes.”
This is the critical issue. Your checkout only uses mode: "setup", which is a $0 card-saving authorization. Apple Pay correctly shows $0 because no money is being collected. Purchases need a different checkout mode.
createWhopCheckoutSession always creates a checkout config with mode: "setup". Setup mode saves a card without charging — Apple Pay shows $0. The actual purchase happens later via POST /payments server-side, where Apple Pay is never involved.We reviewed your codebase scan and found 5 issues that will cause problems at scale. All are fixable with surgical changes to existing files.
All checkout sessions use mode: "setup" ($0 authorization). Purchases need a checkout config with a plan containing the real price so Apple Pay shows the actual amount. See the section above for the full fix.
Setup-mode checkouts don't pass setup_future_usage: "off_session". This blocks Apple Pay in card-saving mode and may cause off-session renewal failures.
finalizeWhopCard polls 60×2s = 2 min. If the webhook is delayed, card onboarding silently fails with no recovery path for the user.
transact() uses exponential backoff capped at ~26s. Slow payments are marked as failed even when they succeed later via webhook.
Refund remoteId uses a timestamp suffix. Rapid duplicate webhooks within the same ms can create duplicate refund records.
Whop processing fee is hardcoded as 3% + $0.50 in transaction.rules.ts. If Whop’s fee schedule changes, payout calculations will silently drift.
Every change maps to a specific file in your BA_V2 repo. No new modules, no new services.
| File | Change | Priority |
|---|---|---|
graph/src/card/payment_processors/whop.ts |
NEW METHOD createWhopPurchaseCheckout — checkout with plan for real purchases |
Critical |
graph/src/card/resolvers/ |
NEW MUTATION createWhopPurchaseCheckout — GraphQL entry point |
Critical |
web/src/components/ (purchase modals) |
Use new mutation + render Whop embed with session ID for purchases | Critical |
graph/src/webhooks/whop.controller.ts |
Handle payment.succeeded for purchase fulfillment via metadata |
Critical |
graph/src/transaction/transaction.service.ts |
NEW METHOD fulfillCheckoutPurchase — create Transaction from webhook |
Critical |
graph/src/card/payment_processors/whop.ts |
Add setup_future_usage: "off_session" to setup-mode checkout |
High |
graph/src/card/payment_processors/whop.ts |
Fix finalizeWhopCard — exponential backoff + pending state |
Medium |
graph/src/webhooks/whop.controller.ts |
Fix refund idempotency — use Whop refund ID | Low |
graph/src/transaction/transaction.rules.ts |
Make fee schedule configurable (env vars or cached API lookup) | Low |
Side-by-side: how your checkout flow works today vs. after these changes.
mode: "setup" ($0)setupFutureUsage flagplan (real price)setup_future_usage: "off_session" on bothWhere Apple Pay fits in your existing Whop checkout flow. Orange is new/changed, green stays unchanged.
User (Safari/iOS) | v Next.js 14 Frontend (web/) | |--- "Add a card" (settings) ----> createWhopCheckoutSession | mode: "setup" ($0) [existing] | |--- "Buy / Tip / Subscribe" ----> createWhopPurchaseCheckout [NEW] | plan: { initial_price: $9.99 } | Apple Pay shows real amount | v NestJS GraphQL API (graph/) | v WhopPaymentProcessor (processor ID 7) | |-- POST /checkout_configurations (with plan for purchases) |-- POST /checkout_configurations (setup mode for card saving) |-- POST /payments (off-session renewals with saved card) | v Whop API | v POST /webhooks/whop/payment | |-- payment.succeeded → fulfillCheckoutPurchase() [NEW] |-- setup_intent.succeeded → upsert Card |-- refund.created / updated | v PostgreSQL (Prisma) — Transaction, Card, FeedSubscription [Stripe (ID 1) + NMI (ID 6) stay as parallel processors — unchanged]
Apple Pay live in 3 days. Checkout hardening in parallel. All existing processors stay live.
Nearly everything. These are surgical changes to your existing Whop integration.