feat(billing): alert customers when an invoice is overdue - #6845
Draft
macko911 wants to merge 8 commits into
Draft
Conversation
Contributor
There was a problem hiding this comment.
All reported issues were addressed across 14 files
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
macko911
force-pushed
the
matej/nan-6232-alert-customers-when-an-invoice-is-overdue
branch
from
August 12, 2026 15:54
d5342e7 to
e02ad69
Compare
Contributor
Preview Deploy
|
macko911
force-pushed
the
matej/nan-6232-alert-customers-when-an-invoice-is-overdue
branch
from
August 14, 2026 11:04
e02ad69 to
7f260a8
Compare
Contributor
Preview Deploys
|
Surface overdue Orb invoices in-app so a failed payment can't quietly lead to a service interruption. Adds a sidebar warning card and a banner at the top of the Billing & usage page, both linking to the Orb portal to edit the payment method. A new GET /plans/billing/overdue endpoint polls Orb (invoices.list) for issued, past-due, still-owed invoices. Non-paying plans (free / free-uncapped) short-circuit without hitting Orb, and the frontend hook is gated + cached (12h) so the always-rendered sidebar stays cheap.
Address review feedback: - Render the overdue banner even when the usage query fails, so a usage outage can't hide the payment warning. - Report Orb errors before returning 500 so failures are traceable. - Fetch the customer (portal URL) only when there's an overdue invoice. - Paginate all past-due issued invoices so a page of fully-credited ones can't hide a still-owed one. - Include the plan name in the query key so switching to a free plan in-session drops the stale overdue cache.
Verifying the overdue-invoice alerts previously meant flipping the account's plan in the database and patching the no-op billing client, since local dev has no Orb key and the alerts are gated on a paying plan. Adds a select to the existing Plan Override dev tool instead. The two variants differ only by whether a portal URL is returned, which is what decides if the "Edit payment method" CTA renders. The override also bypasses the paying-plan gate so the alerts can be previewed on a Free account, and it is part of the query key so toggling applies immediately rather than waiting out the 12h stale window.
The `ui/Alert` look has drifted from the design system's, so neither the sidebar card nor the Billing page banner matched the intended design. Hand-roll both to match `UsageLimitAlert`, the existing precedent for a sidebar alert, until `Alert` itself moves into the design system and the two can be folded back together. Both places now share one `OverdueInvoiceAlert` in components/patterns, replacing the sidebar-only `OverdueInvoiceCard` and the inline `Alert` on the Billing page, so the copy and styling live in one spot. Also drops the `StyledLink` warning variant, which was added for the old card and no longer has any users.
The design moved this alert to the danger status, and the typography it specifies (13px title over 12px body) never existed in the webapp's own scale, so both lines rendered at 12px. Bind to the danger tokens and to `type-text-regular-sm` / `-xs`, the composite classes generated from the Figma text styles the design actually uses — the sidebar's nav and dropdowns already use them, so `text-body-small-regular` (12px/16px) was the odd one out. Also swaps TriangleAlert for CircleAlert, matching how `UsageLimitAlert` already distinguishes its danger state from its warning one, and drops the underline the design doesn't have.
UsageLimitAlert carried the same drift the overdue alert just had: 12px for both lines where the design specifies a 13px title over a 12px body, plus an underline the design doesn't have. Move it onto the generated Figma text styles so the two alerts match when they stack in the sidebar footer, which they do on a Free plan with an unpaid invoice. Also records why the overdue alert has no dismiss button, since the design does show one.
Now that Alert lives in the design system, the overdue and usage-limit alerts drop their hand-rolled markup for it. The DS component already encodes the spec they were matching by hand: AlertTitle is Figma's text/regular/sm, AlertDescription text/regular/xs, and AlertButton the borderless pill with no underline, coloured from the alert's own status via --alert-link. Leaving off onDismiss keeps both non-dismissible. The sidebar keeps size="compact", matching the Figma sidebar alerts (Size=Compact). The Billing page banner takes size="wide" so its CTA sits inline on the right rather than wrapping under the text, which is what the extra width is for.
macko911
force-pushed
the
matej/nan-6232-alert-customers-when-an-invoice-is-overdue
branch
from
August 14, 2026 12:27
7f260a8 to
5a03226
Compare
macko911
changed the base branch from
master
to
matej/nan-6590-lift-alert-into-the-design-system
August 14, 2026 12:27
"View usage" routes to /team/billing#usage, an in-app route, but carried an ExternalLink icon implying it leaves the app. Use ArrowUpRight, as the "View log" CTA in AuthTab already does; alert CTAs that really do go off-site keep ExternalLink.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
When a customer has an overdue/unpaid Orb invoice, nothing surfaces it in-app, so a failed payment can quietly lead to a service interruption. No invoice-status read existed today — only the Orb portal URL was surfaced.
Solution
GET /api/v1/plans/billing/overdue, backed by a newbilling.getOverdueInvoices()(Orbinvoices.list, filtered to issued + past-due + still-owed viaisOverdueInvoice). Non-paying plans (free/free-uncapped) short-circuit without hitting Orb.useApiGetOverdueInvoiceshook is gated to paying plans and cached (12h stale time) so the always-rendered sidebar makes no request for free accounts.Fixes NAN-6232
Testing
isOverdueInvoicepredicate (issued + past due_date +amount_due > 0);ts-buildand lint pass./plans/billing/overduerequest.