Whop × BrandArmy

Apple Pay & Checkout Hardening

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.

Fix $0 Apple Pay purchases 5 additional issues to fix Whop already integrated No new dependencies

Start Here — Drop This Into Your Repo

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.”

Fix Apple Pay $0 purchase issue on BrandArmy's Whop checkout. Root cause: all checkout sessions use mode "setup" ($0 auth). Fix: for purchases (tips, post unlocks, subscriptions, shop items), create checkout configs with a plan containing the real price instead of setup mode. Add new createWhopPurchaseCheckout method in graph/src/card/payment_processors/whop.ts, new GraphQL mutation, update frontend purchase modals to use the new checkout flow, and handle payment.succeeded webhook for purchase fulfillment. Keep setup mode only for "add a card" in settings. Also fix: add setupFutureUsage "off_session", fix polling timeouts, refund idempotency, and configurable fee schedule. See CLAUDE.md for full implementation.

Fix: Apple Pay Shows $0 on Purchases

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.

Root cause: 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.

Current Flow (Broken for Apple Pay)

Step 1 User opens checkout
Step 2 Setup mode ($0)
Step 3 Apple Pay shows $0
Step 4 Card saved, POST /payments later

Apple Pay only appears during card setup ($0). The real charge is a server-side API call — no Apple Pay UI.

Fixed Flow (Real Amount in Apple Pay)

Step 1 User taps "Buy"
Step 2 Checkout with plan ($9.99)
Step 3 Apple Pay shows $9.99
Step 4 Webhook fulfills purchase

Create a checkout configuration with a plan containing the real price. Apple Pay sheet shows the actual amount. Card is also saved for future use via setupFutureUsage: "off_session".

Two checkout paths after the fix: (A) "Add a card" in settings → setup mode → $0 → card saved. (B) Purchase (tip, post unlock, subscription, shop item) → plan with real price → Apple Pay shows amount → payment processed immediately.

Integration Issues Found

We reviewed your codebase scan and found 5 issues that will cause problems at scale. All are fixable with surgical changes to existing files.

Critical Apple Pay Shows $0 on Purchases

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.

High Missing setupFutureUsage

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.

Medium Card Finalization Timeout

finalizeWhopCard polls 60×2s = 2 min. If the webhook is delayed, card onboarding silently fails with no recovery path for the user.

Medium Payment Polling Timeout

transact() uses exponential backoff capped at ~26s. Slow payments are marked as failed even when they succeed later via webhook.

Low Refund Deduplication

Refund remoteId uses a timestamp suffix. Rapid duplicate webhooks within the same ms can create duplicate refund records.

Low Hardcoded Fee Schedule

Whop processing fee is hardcoded as 3% + $0.50 in transaction.rules.ts. If Whop’s fee schedule changes, payout calculations will silently drift.

Files to Change

Every change maps to a specific file in your BA_V2 repo. No new modules, no new services.

FileChangePriority
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

What Changes

Side-by-side: how your checkout flow works today vs. after these changes.

Today

  • All checkouts use mode: "setup" ($0)
  • Apple Pay shows $0 on every purchase
  • Purchases charged via server-side API (no Apple Pay UI)
  • No setupFutureUsage flag
  • Silent failure on poll timeout
  • Timestamp-based refund dedup

After

  • Purchases use checkout with plan (real price)
  • Apple Pay shows actual amount ($9.99)
  • Card saving uses setup mode ($0 — correct)
  • setup_future_usage: "off_session" on both
  • PENDING state + Socket.io notification
  • Whop refund ID-based dedup

Architecture

Where 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]

Timeline

Apple Pay live in 3 days. Checkout hardening in parallel. All existing processors stay live.

Day 1
Build
  • Add Apple Pay verification file
  • Add setupFutureUsage to checkout config
  • Fix polling + refund idempotency
  • Add PENDING status to schema
Day 2
Deploy & Verify
  • Deploy to production
  • Register domain in Whop dashboard
  • Verify Apple Pay renders on Safari
  • Test card setup + off-session payment
Day 3
Live
  • Complete Apple Pay test payment
  • Verify subscription renewal with Apple Pay
  • Confirm webhook fallback works
  • Monitor for edge cases

What Stays the Same

Nearly everything. These are surgical changes to your existing Whop integration.

NestJS + GraphQL API
Next.js 14 frontend
PostgreSQL + Prisma
Stripe (processor ID 1)
NMI (processor ID 6)
Dwolla payouts
Bull job queues
Socket.io real-time
JWT authentication
Kount fraud detection
Transaction rule chain
No new dependencies
~ whop.ts (3 fixes)
~ whop.controller.ts (2 fixes)
~ transaction.rules.ts (fee config)
Bottom line: Your existing Whop integration, all three payment processors, Dwolla payouts, the entire transaction rule chain, and your frontend are untouched. Apple Pay adds one static file. The 5 fixes are all in 3 existing files.