Skip to content

fix(api): surface Stripe card errors as 402 - #3354

Merged
steebchen merged 1 commit into
mainfrom
handle-stripe-card-errors
Aug 1, 2026
Merged

steebchen merged 1 commit into
mainfrom
handle-stripe-card-errors

Conversation

@steebchen

@steebchen steebchen commented Aug 1, 2026 •

Copy link
Copy Markdown
Member

Problem

A Stripe card error during a DevPass tier change (e.g. incorrect_cvc, seen in production as a 500 on POST /dev-plans/change-tier) fell through to the generic catch: the API returned 500 "Failed to change dev plan tier", logged it as a fatal error, and the billing UI showed only a generic failure toast — the user never learned their CVC was wrong.

Fix

  • New getStripeCardErrorMessage() helper (apps/api/src/lib/stripe-card-error.ts): detects StripeCardError (any code — incorrect CVC, expired card, insufficient funds, plain decline) and returns Stripe's own user-facing message, which is designed to be shown to customers.
  • POST /dev-plans/change-tier and POST /chat-plans/change-tier now map any card error to a 402 whose message is Stripe's explanation plus an actionable hint (e.g. "Your card's security code is incorrect. Update your payment method and try again."), logged at warn instead of error. The previous branch only caught the card_declined code.
  • The Reset Pass purchase path (already 402) now propagates the specific card-error message too instead of a hardcoded generic one.

The billing UI already renders the API error message in the toast description, so no frontend change was needed.

Testing

  • New unit test: change-tier with a StripeCardError (incorrect_cvc) → 402 with the exact message, org/tier/credits untouched, upgrade lease released, no transaction row. All 20 dev-plans.spec.ts tests pass.
  • Full local e2e with the stack + stripe listen webhook forwarding against Stripe test mode:
    • Real lite subscription, default PM = pm_card_chargeCustomerFail (attaches OK, fails at charge time — same code path as the production incorrect_cvc): "Pay and upgrade" in the dashboard → API returned 402 "Your card was declined. Update your payment method and try again.", toast showed that message, API logged a single WARN, plan/lease/credits unchanged.
    • Switched the default PM back to a working visa: same upgrade succeeded end-to-end (plan → Pro, credits rollover 311.5, dev_plan_upgrade transaction recorded, invoice.payment_succeeded webhook delivered and processed).
  • pnpm format and full pnpm build pass.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • Improved payment error messages during plan upgrades and pass purchases.
    • Card-related payment failures now show Stripe’s specific explanation when available, with a clear fallback message.
    • Failed payments return an appropriate payment-required response without changing plan credits or creating transactions.

Card errors (incorrect CVC, expired card, insufficient funds, plain
declines) during a dev/chat plan tier change or Reset Pass purchase were
falling through to a generic 500 "Failed to change dev plan tier" logged
as a fatal error. Map any StripeCardError to a 402 carrying Stripe's own
user-facing message so the billing UI toast tells the user what to fix,
and log it at warn instead of error.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings August 1, 2026 12:44
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@coderabbitai

coderabbitai Bot commented Aug 1, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 271c286a-a0f1-4935-9bd4-07c16395f9f8

📥 Commits

Reviewing files that changed from the base of the PR and between 56e4458 and e636dbf.

📒 Files selected for processing (4)
  • apps/api/src/lib/stripe-card-error.ts
  • apps/api/src/routes/chat-plans.ts
  • apps/api/src/routes/dev-plans.spec.ts
  • apps/api/src/routes/dev-plans.ts

Walkthrough

Stripe card errors now return specific user-facing messages with HTTP 402 responses for chat-plan changes, DevPass tier changes, and Reset Pass purchases. A shared helper provides validation and fallback handling. Tests cover declined DevPass upgrades and state cleanup.

Changes

Stripe card error handling

Layer / File(s) Summary
Card error message contract
apps/api/src/lib/stripe-card-error.ts
Adds getStripeCardErrorMessage to detect Stripe card errors, return valid messages, and apply a declined-card fallback.
Payment route responses
apps/api/src/routes/chat-plans.ts, apps/api/src/routes/dev-plans.ts, apps/api/src/routes/dev-plans.spec.ts
Chat-plan and DevPass payment failures return HTTP 402 responses with specific card messages and payment guidance. DevPass tests verify unchanged organization state, lease release, and no transaction creation.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related PRs

Suggested reviewers: copilot, smakosh

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main API change: returning Stripe card errors as HTTP 402 responses.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch handle-stripe-card-errors

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Improves billing-related UX in the API by correctly classifying Stripe card failures (e.g., incorrect CVC, declines) as user-actionable payment errors rather than server faults, so clients can display Stripe’s customer-facing message.

Changes:

  • Added a shared helper to extract Stripe card-error messages (StripeCardError) for safe client display.
  • Updated DevPass and Chat plan tier-change routes to return 402 with Stripe’s message (plus actionable guidance) and log at warn.
  • Updated Reset Pass purchase flow to propagate the specific card-error message (still 402) instead of a generic decline message; added unit coverage for DevPass tier change.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.

File Description
apps/api/src/lib/stripe-card-error.ts New helper to detect StripeCardError and return a user-facing message.
apps/api/src/routes/dev-plans.ts Maps Stripe card errors to 402 with actionable messaging in tier change + Reset Pass purchase.
apps/api/src/routes/dev-plans.spec.ts Adds unit test ensuring tier-change card errors surface as 402 without mutating org state.
apps/api/src/routes/chat-plans.ts Applies the same 402 + Stripe-message handling for chat plan tier changes.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@steebchen
steebchen added this pull request to the merge queue Aug 1, 2026
Merged via the queue into main with commit 4b149d0 Aug 1, 2026
13 checks passed
@steebchen
steebchen deleted the handle-stripe-card-errors branch August 1, 2026 17:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants