Skip to content

Promote dev to main (billing UI debt fix + upstream sync + reddit broker e2e spec) - #62

Merged
JOY (JOY) merged 2 commits into
mainfrom
dev
Sep 25, 2026
Merged

JOY (JOY) merged 2 commits into
mainfrom
dev

Conversation

@JOY

@JOY JOY (JOY) commented Sep 25, 2026 •

Copy link
Copy Markdown

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

  • 🚀 New feature
  • 🐛 Bugfix
  • 🧹 Refactor
  • ⚡ Performance
  • 📚 Documentation
  • ⚙️ CI / Configuration

🔍 Changes Walkthrough

File Summary of Changes
apps/frontend/src/components/billing/first.billing.component.tsx Added useToaster import and hook. Refactored startDosCheckout to check response.ok before processing the JSON payload. Added error toast notifications if the subscription request fails.
apps/frontend/src/components/billing/main.billing.component.tsx 1. Error Handling: Refactored updatePayment, moveToCheckout (reactivate/cancel/subscribe) to check response.ok and display specific error messages via toast instead of assuming success.
2. UI Logic: Introduced showPortalAndCancel logic 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
Loading

… 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

@gemini-code-assist gemini-code-assist Bot 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.

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'

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The fallback error message is hardcoded. To support internationalization (i18n), please wrap the string in the t() translation function.

Suggested change
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');

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The fallback error message is hardcoded. Please wrap it in the t() translation function to support internationalization.

Suggested change
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');

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The fallback error message is hardcoded. Please wrap it in the t() translation function to support internationalization.

Suggested change
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');

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The fallback error message is hardcoded. Please wrap it in the t() translation function to support internationalization.

Suggested change
toast.show(message || 'Could not cancel the subscription');
toast.show(message || t('could_not_cancel_subscription', 'Could not cancel the subscription'));

// 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');

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The fallback error message is hardcoded. Please wrap it in the t() translation function to support internationalization.

Suggested change
toast.show(message || 'Subscription update failed, please try again later');
toast.show(message || t('subscription_update_failed', 'Subscription update failed, please try again later'));

@JOY
JOY (JOY) merged commit ff9157e into main Sep 25, 2026
22 checks passed
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.

1 participant