Conversation
… by plan - /billing/subscribe and /billing/cancel responses are now checked with resp.ok: a 4xx (no dos.me plan row, stripe error, no_stripe_subscription) used to fall through to the success branch, toast 'Subscription updated successfully' and mutate the tier optimistically (fake success) - Update Payment no longer navigates to /undefined when the portal call fails: it shows the error message instead - Update Payment and Cancel subscription buttons are hidden under shared dos billing for FREE and legacy tiers (ULTIMATE) where the endpoints can only answer 409 no_stripe_subscription - first.billing checkout (DOS view) gets the same resp.ok gate with an error toast instead of failing silently
fix(billing): surface real billing errors, guard portal, gate buttons by plan
There was a problem hiding this comment.
Code Review
This pull request improves error handling and user feedback during billing actions (subscribing, updating payment, and cancelling) in both FirstBillingComponent and MainBillingComponent. It also introduces a conditional check to hide the portal and cancel buttons for free and legacy tiers. The review feedback suggests wrapping several hardcoded fallback error messages in the t() translation function to support internationalization (i18n).
| const result = await response.json().catch(() => ({})); | ||
| if (!response.ok) { | ||
| toaster.show( | ||
| result.message || 'Subscription update failed, please try again later' |
There was a problem hiding this comment.
The fallback error message is hardcoded. To support internationalization (i18n), please wrap the string in the t() translation function.
| result.message || 'Subscription update failed, please try again later' | |
| result.message || t('billing_subscription_update_failed', 'Subscription update failed, please try again later') |
| const response = await fetch('/billing/portal'); | ||
| const { portal, message } = await response.json().catch(() => ({})); | ||
| if (!response.ok || !portal) { | ||
| toast.show(message || 'Payment portal is not available for this account'); |
There was a problem hiding this comment.
The fallback error message is hardcoded. Please wrap it in the t() translation function to support internationalization.
| toast.show(message || 'Payment portal is not available for this account'); | |
| toast.show(message || t('payment_portal_not_available', 'Payment portal is not available for this account')); |
| .catch(() => ({})); | ||
| if (!response.ok) { | ||
| setLoading(false); | ||
| toast.show(message || 'Could not reactivate the subscription'); |
There was a problem hiding this comment.
The fallback error message is hardcoded. Please wrap it in the t() translation function to support internationalization.
| toast.show(message || 'Could not reactivate the subscription'); | |
| toast.show(message || t('could_not_reactivate_subscription', 'Could not reactivate the subscription')); |
| .catch(() => ({})); | ||
| if (!response.ok) { | ||
| setLoading(false); | ||
| toast.show(message || 'Could not cancel the subscription'); |
There was a problem hiding this comment.
| // to the success branch and toast "Subscription updated" + mutate | ||
| // the tier optimistically - surface the real failure instead. | ||
| setLoading(false); | ||
| toast.show(message || 'Subscription update failed, please try again later'); |
There was a problem hiding this comment.
The fallback error message is hardcoded. Please wrap it in the t() translation function to support internationalization.
| toast.show(message || 'Subscription update failed, please try again later'); | |
| toast.show(message || t('subscription_update_failed', 'Subscription update failed, please try again later')); |
Promotion for prod deploy: #61 (billing UI error surfacing + portal guard + plan-gated buttons), #59 (upstream sync 2026-09-22), #58 (reddit broker e2e spec). Same reviewed diffs, no new commits.
📌 TL;DR
This PR enhances the billing components by adding robust error handling for API responses (specifically for subscription, cancellation, and portal actions) and conditionally hides payment management buttons for users without a valid dos.me-managed subscription.
🎯 Type of Change
🔍 Changes Walkthrough
apps/frontend/src/components/billing/first.billing.component.tsxuseToasterimport and hook. RefactoredstartDosCheckoutto checkresponse.okbefore processing the JSON payload. Added error toast notifications if the subscription request fails.apps/frontend/src/components/billing/main.billing.component.tsxupdatePayment,moveToCheckout(reactivate/cancel/subscribe) to checkresponse.okand display specific error messages via toast instead of assuming success.2. UI Logic: Introduced
showPortalAndCancellogic to hide "Update Payment" and "Cancel" buttons for FREE or legacy tiers (e.g., comped ULTIMATE) where dos.me actions would return 409 errors.3. Safety: Added
.catch(() => ({}))to JSON parsing to prevent crashes on non-JSON error responses.📊 Architectural Flow
sequenceDiagram participant User participant Frontend as Billing Component participant API as Billing API participant Toaster as Toaster Service User->>Frontend: Trigger Action (Subscribe/Cancel/Portal) Frontend->>API: Fetch Request (POST/GET) alt Response OK API-->>Frontend: 200 OK + JSON Data Frontend->>Frontend: Process Data (Navigate/Update State) else Response Error (4xx/5xx) API-->>Frontend: Error Status + JSON Message Frontend->>Toaster: Show Error Message Toaster-->>User: Display Toast Frontend->>Frontend: Abort Action (Return) end