Skip to content

fix(api): map billing errors to actionable codes instead of internal - #1851

Merged
whoAbhishekSah merged 5 commits into
fix/billing-services-stripe-errorsfrom
fix/billing-handler-error-mapper
Aug 5, 2026
Merged

fix(api): map billing errors to actionable codes instead of internal#1851
whoAbhishekSah merged 5 commits into
fix/billing-services-stripe-errorsfrom
fix/billing-handler-error-mapper

Conversation

@whoAbhishekSah

Copy link
Copy Markdown
Member

What

Adds one shared fallback, mapBillingError, and uses it in place of the ~87 bare connect.CodeInternal wraps across the billing handlers:

error code message
billing.ErrProviderResourceMissing failed_precondition billing account is no longer linked to the payment provider
billing.ErrPaymentFailed failed_precondition payment failed: <Stripe's message>
billing.ErrProviderUnavailable unavailable billing provider is unavailable, retry later
subscription.ErrSubscriptionOnProviderNotFound failed_precondition subscription no longer exists on the billing provider
subscription.ErrPhaseIsUpdating failed_precondition phase is in the middle of a change, please try again later
customer.ErrExistingAccountWithPendingDues failed_precondition existing account with pending dues found
anything else internal unchanged

The existing specific mappings in the handlers (errors.Is ladders for not-found, invalid-argument, and so on) are untouched; only the final fallback changes. The rewrite was done with gofmt -r, so every call keeps its context string.

Why

internal should mean "a bug in Frontier". Today it also covers ordinary account states the caller can fix — the clearest case being GetUpcomingInvoice returning internal forever when the org's Stripe customer was deleted (#1835). With this change the caller learns whose move it is: fix the account, retry later, or report a bug.

Fixes #1836. Stack: translator (#1849) → services (#1850) → this PR.

🤖 Generated with Claude Code

@vercel

vercel Bot commented Aug 5, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
frontier Ready Ready Preview Aug 5, 2026 11:41am

@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 31cd9698-a92b-4ede-98ed-8659b52cb96a

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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.

@coveralls

coveralls commented Aug 5, 2026

Copy link
Copy Markdown

Coverage Report for CI Build 30980108222

Coverage increased (+0.04%) to 47.604%

Details

  • Coverage increased (+0.04%) from the base build.
  • Patch coverage: 61 uncovered changes across 10 files (65 of 126 lines covered, 51.59%).
  • No coverage regressions found.

Uncovered Changes

File Changed Covered %
internal/api/v1beta1connect/billing_customer.go 22 1 4.55%
internal/api/v1beta1connect/billing_product.go 20 9 45.0%
internal/api/v1beta1connect/billing_subscription.go 7 0 0.0%
internal/api/v1beta1connect/billing_invoice.go 10 4 40.0%
internal/api/v1beta1connect/billing_usage.go 9 3 33.33%
internal/api/v1beta1connect/billing_checkout.go 11 6 54.55%
billing/checkout/service.go 2 0 0.0%
internal/api/v1beta1connect/billing_check.go 5 4 80.0%
internal/api/v1beta1connect/billing_errors.go 32 31 96.88%
internal/api/v1beta1connect/billing_plan.go 7 6 85.71%
Total (11 files) 126 65 51.59%

Coverage Regressions

No coverage regressions found.


Coverage Stats

Coverage Status
Relevant Lines: 39413
Covered Lines: 18762
Line Coverage: 47.6%
Coverage Strength: 15.4 hits per line

💛 - Coveralls

@rohilsurana rohilsurana left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Reviewed with the rest of the stack. The mapping table is sensible, the mechanical rewrite kept every context string (spot checked), and the test table is thorough: redaction shapes, test-mode ids, and caller-supplied coupon codes are all covered. Promoting already subscribed to the plan to a sentinel with already_exists is a nice touch, and the UpdateProduct refactor keeps the ErrInvalidDetail to invalid_argument path intact.

One real concern about what ends up in server logs, and two smaller points, all inline.

Also checked BillingWebhookCallback: it now answers the webhook sender with 412/503 instead of 500 for provider problems. Stripe retries on any non-2xx, so retry behavior does not change.

Comment thread internal/api/v1beta1connect/billing_errors.go Outdated
Comment thread internal/api/v1beta1connect/billing_errors.go Outdated
Comment thread internal/api/v1beta1connect/billing_errors.go Outdated

@rohilsurana rohilsurana left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM overall. Please look at the logging comment on mapBillingError before merge.

whoAbhishekSah and others added 5 commits August 5, 2026 17:09
Billing handlers used a bare CodeInternal for every unrecognized
error, so account-state problems looked like Frontier bugs to the
caller. All billing handlers now fall back to a shared mapBillingError:

- provider record missing -> failed_precondition, "billing account is
  no longer linked to the payment provider"
- payment failures -> failed_precondition with the provider's message
- provider rate limits and outages -> unavailable
- plan change in progress, pending dues, subscription gone on the
  provider -> failed_precondition
- everything else -> internal, as before

Fixes #1836.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- pass the provider's message through for resource-missing errors so a
  missing coupon or payment method isn't reported as an unlinked account
- map customer not-found to not_found instead of internal
- name the already-subscribed checkout error and map it to already_exists
- map product and feature not-found to not_found

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Provider messages passed to the caller keep their text but hide
provider-generated object ids: a deleted customer reads as "No such
customer: 'cus_*****'". Caller-supplied values like coupon codes don't
match the id shape and stay intact.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
cs_test_/cs_live_ checkout session ids escaped the mask because of
the mode infix, and charge (ch_) ids were not in the prefix list.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Review fixes on the error mapper:

- mapped errors are logged with full detail (handler context, provider
  request id) before the clean error goes to the caller — the caller
  error is all the logger interceptor sees, so without this the org and
  method context would vanish from server logs for exactly these cases
- the id mask now matches the general shape of provider ids instead of
  an allowlist of prefixes, so an object type we haven't listed cannot
  leak its id
- the resource-missing fallback uses the same message as the translated
  path instead of a second text that guessed the missing object

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@whoAbhishekSah
whoAbhishekSah force-pushed the fix/billing-handler-error-mapper branch from 25d5aa8 to e947ba0 Compare August 5, 2026 11:40
@whoAbhishekSah
whoAbhishekSah merged commit 5c7721f into main Aug 5, 2026
13 checks passed
@whoAbhishekSah
whoAbhishekSah deleted the fix/billing-handler-error-mapper branch August 5, 2026 11:50
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.

Billing APIs return "internal" for errors the caller can act on

3 participants