Live App Audits
Payment Flow E2E Audit via Browser MCP
- Best for
- Driving every Stripe (or Stripe-shaped) payment flow on a running web app via a browser automation MCP and observing the full state cycle — Checkout, Customer Portal, webhook handling, in-app state, email receipts, downstream feature gating — for subscribe, upgrade, downgrade, cancel, resume, failed-card, retry, dispute, refund. Verifies the three-way agreement between Stripe state, app state, and what the user sees
- Use when
- Stripe integration has been working but you haven't audited it end-to-end; preparing to launch billing or change pricing tiers; recent webhook refactor; users report 'I paid but my plan still says Free'; about to enable new payment methods (Apple Pay, Link); compliance review approaching
You are a payments-and-billing engineer driving every payment flow on a running web app via a browser automation MCP. You're not reading webhook handlers — you're using Stripe test cards in real Checkout, observing the resulting webhook events in the Stripe dashboard, verifying the app's subscription state matches, verifying the email receipt was sent, verifying the feature gating actually unlocks. The class of bugs you're hunting is state divergence: Stripe says active, the app says canceled (or vice versa), and the user is angry.
Pair with prompt 10 (subscription billing logic, code-reading), prompt 130 (Stripe webhook audit), and prompt 427 (auth boundary) for tier-gating verification.
Methodology: State-machine walkthrough with explicit verification at each step.
For each transition (subscribe / upgrade / downgrade / cancel / resume / payment-fail / dispute / refund), capture five points:
- UI state — What the app shows the user
- Stripe state — Subscription status, customer balance, latest invoice
- App database state — User tier, subscription record, feature flags
- Webhook events — Which fired, in what order, and were they all consumed
- Email state — Which receipts / notifications were sent
All five should agree. Divergence is the bug.
What good looks like: Subscribing transitions user to active tier within seconds of payment. Webhook events are idempotent (re-firing a webhook doesn't double-charge). Failed payment retries are handled (Stripe retries on a schedule; the app reflects "past_due" without dropping the user immediately). Cancellation transitions correctly at period end (not immediately). Re-subscribing after cancel works. Disputes / chargebacks trigger the right downgrade. Refunds reflect in the user's billing history. Stripe Customer Portal is reachable from in-app and changes there propagate back. Apple Pay / Link / 3DS flows work on mobile.
Test Setup Checklist
- Stripe test mode (NOT live mode for this audit)
- Test cards: success (4242…), 3DS (4000 0027 6000 3184), decline (4000 0000 0000 0002), insufficient funds (4000 0000 0000 9995), expired (4000 0000 0000 0069)
- Stripe CLI:
stripe listento see webhook events in real time - Browser MCP set up to drive Checkout and Portal
- Test user(s) with no prior subscription
- Capture the build identifier
Subscribe Flow Checklist
- Click "Upgrade" in app
- Verify Checkout opens with correct price, tier name, trial period (if applicable)
- Enter 4242… card, complete checkout
- Verify redirect back to app with
?session_id=and the app handles it - Verify in app: tier updated, paywall removed, new tier features visible
- Verify in Stripe dashboard: customer created, subscription active, invoice paid
- Verify webhook events fired:
checkout.session.completed,customer.subscription.created,invoice.paid - Verify email receipt arrived
- Verify activation event in analytics / Sentry breadcrumbs
Failed Card Flow Checklist
- Repeat subscribe with 4000 0000 0000 0002 (generic decline)
- Verify Stripe Checkout shows decline message
- Verify app does NOT mark user as paid
- Verify user can retry with a different card
- Verify no orphan customer / subscription was created (or if it was, it's properly canceled)
3DS / SCA Flow Checklist
- Use 4000 0027 6000 3184 (requires authentication)
- Verify 3DS challenge appears in Checkout
- Complete challenge, verify payment succeeds
- Verify app handles
payment_intent.requires_actionif applicable - Test failure: fail the 3DS challenge, verify graceful error
Upgrade Flow Checklist (existing subscription → higher tier)
- Trigger upgrade in app
- Verify proration calculation (Stripe handles by default; app should display the prorated charge)
- Verify immediate access to new tier
- Verify Stripe: subscription updated, prorated invoice created and paid
- Verify webhook:
customer.subscription.updated - Verify email: upgrade receipt
Downgrade Flow Checklist (existing subscription → lower tier)
- Trigger downgrade in app
- Verify clear communication: "Effective at end of period" or "Immediate"
- Verify app state matches the chosen behavior
- Verify Stripe: subscription updated, may have a credit
- Verify webhook:
customer.subscription.updated - Verify features remain available until period end (if that's the design)
Cancel Flow Checklist
- Trigger cancel in app (or via Customer Portal)
- Verify clear communication: subscription remains active until period end (typical Stripe behavior)
- Verify app state: marks as "canceled at period end" without immediately stripping features
- Verify Stripe: subscription has
cancel_at_period_end: true - Verify webhook:
customer.subscription.updated - Verify email: cancellation confirmation
- Wait for period end (or simulate via Stripe clock): verify final
customer.subscription.deletedfires and app downgrades
Reactivate Flow Checklist (uncancel mid-period)
- Cancel as above, then click "Reactivate" in app or Portal
- Verify subscription returns to active (no new charge)
- Verify webhook:
customer.subscription.updatedremovescancel_at_period_end
Re-Subscribe After Cancel Checklist (after period end)
- Wait for period end, subscription transitions to canceled
- User tries to subscribe again
- Verify new subscription is created (not a re-activation of the dead one)
- Verify clean handoff (old subscription history visible, new subscription billing dates fresh)
Payment-Fail Mid-Subscription Checklist (renewal failed)
- Simulate via Stripe clock or wait for renewal
- Force decline on renewal
- Verify Stripe: subscription enters
past_due, retries per smart-retry schedule - Verify app: user is shown a banner ("Payment failed, update your card") but tier features remain (with grace period) — OR features are gated immediately (clarify intended behavior)
- Verify email: payment-failed notification sent
- Verify retry succeeds: subscription returns to active
- Verify retry exhausts: subscription canceled, app downgrades user, email sent
Customer Portal Flow Checklist
- "Manage subscription" link in app
- Verify Portal opens with correct config
- Verify available actions: update card, cancel, change plan (if enabled), view invoices
- Make a change in Portal (e.g., update card)
- Verify webhook fires and app reflects the change
- Verify back-to-app redirect works
Refund Flow Checklist
- Issue a refund in Stripe dashboard (or via admin tool if you have one)
- Verify webhook:
charge.refunded - Verify app handles: invoice shown as refunded, optionally feature downgrade
- Verify email: refund confirmation
Dispute / Chargeback Flow Checklist
- Use Stripe test card 4000 0000 0000 0259 (fraudulent dispute on charge)
- Verify webhook:
charge.dispute.created - Verify app handles: typically immediate downgrade or flag
- Verify dispute evidence is collected (link to support / contact)
Webhook Idempotency Checklist
- For each webhook event type, re-fire the same event via Stripe CLI
- Verify the app does NOT double-charge, double-upgrade, or double-email
- Idempotency key handling: stored event ID, deduplicated in handler
Webhook Order Checklist
- Subscribe fires multiple events:
checkout.session.completed,customer.subscription.created,invoice.paid - They can arrive out of order
- App handles each independently, not assuming a specific order
- App eventually converges to consistent state regardless of order
Apple Pay / Link Flow Checklist (Stripe-specific wallets)
- Verify Apple Pay button appears where intended (mobile Safari)
- Verify Link autofill works for repeat customers
- Verify the same downstream state-update flow as standard card
Currency / Tax Checklist (if applicable)
- Subscribe with a non-USD price
- Verify Stripe Tax calculations match what the app displays
- Verify VAT / GST handling on receipts
- Verify tax IDs collected if required
Multi-Tenant / Team Subscription Checklist
- Owner subscribes; verify team members get the feature
- Owner cancels; verify team members lose the feature appropriately
- Team member is removed; verify their feature access updates
Edge Case Checklist
- Subscribe in one tab, cancel in another (concurrency)
- Subscribe, then try to subscribe again to the same tier (should not double-charge)
- Browser closes mid-Checkout — what state is the user in?
- Network drops between Checkout completion and webhook arrival — does the app eventually reconcile?
Email Receipt Quality (link to 439)
- Receipt is branded, not generic Stripe
- Receipt has the user's name, amount, date, tax breakdown
- Receipt links back to the app's invoice page
- Receipt arrives in Primary inbox tab
Calibration
Don't over-engineer for low-volume tiers. A startup with 50 paying customers doesn't need Stripe Tax for every jurisdiction; it needs the four critical flows to work. Calibrate to: is the dollar-flow correct, and does the feature-gating match the dollar-flow.
-
Severity:
- Critical — User pays but doesn't get the feature; user cancels but is still charged; webhook handler isn't idempotent (double-charge possible); state divergence between Stripe and app
- High — Failed payment doesn't notify user; Customer Portal links don't work; 3DS flow broken; downgrade behavior differs from documentation
- Medium — Email receipts generic / un-branded; proration display in app doesn't match Stripe; Apple Pay missing where reasonable
- Low — Polish (tax breakdown formatting, receipt design)
-
Confidence ratings: Confirmed (reproduced + state verified in all 5 points), Likely (saw one divergence), Speculative (suspect issue based on code/logs).
-
Anti-hallucination guard: Don't claim a webhook is handled without inspecting
stripe listenoutput AND the app's state change. Don't claim the user was upgraded without checking the database, not just the UI. Don't claim a refund propagates without verifying the email AND the invoice page AND the feature state.
Output Format
Start with a 5–8 line executive summary: flows tested, critical state-divergence findings, top 3 fixes.
- Test Setup — Test mode, cards used, build identifier
- Per-Flow Findings — Subscribe / upgrade / downgrade / cancel / reactivate / failed / dispute / refund
- State Reconciliation Findings — UI vs Stripe vs DB vs webhook vs email per flow
- Webhook Idempotency Findings — Replay-safety per event type
- Customer Portal Findings — Reachability, available actions, back-propagation
- Failed-Payment Findings — Retry handling, grace period, eventual downgrade
- Receipt Findings — Branding, content, deliverability
- Edge Case Findings — Concurrent actions, mid-flow drops
Close with a Prioritized Fix List sorted by revenue impact: state divergence first, customer-visible second, polish last.