Skip to content

Add a dedicated Invoices tab for all purchases - #2436

Open
richiemcilroy wants to merge 5 commits into
mainfrom
codex/unified-invoices
Open

richiemcilroy wants to merge 5 commits into
mainfrom
codex/unified-invoices

Conversation

@richiemcilroy

@richiemcilroy richiemcilroy commented Oct 9, 2026 •

Copy link
Copy Markdown
Member

Customers currently have to look in Security for SSO invoices, while desktop license invoices are absent from the dashboard. Add an Invoices tab and account-menu entry covering Pro, desktop and self-hosted licenses, developer credits, and owner-managed SSO subscriptions. Remove the SSO invoice card from Billing & Members so it no longer pushes the member list down.

The page shows paid status, PDF downloads, and historical receipts, with pagination, retryable failures, and a deadline that closes stalled license-database reads. Each request resolves its billing sources from the signed-in account; SSO history remains subscription-scoped and owner-only. License purchases require a verified email, including verified Google and Apple identities already stored by the authentication adapter. Each server instance caps license reads at four concurrent connections and invoice requests at 30 per minute per account, with bounded counter storage. No payment or subscription mutations are introduced.

Validation: 143 focused tests passed, web TypeScript build and Next route generation passed, and Biome passed on all 25 changed files. The production license lookup was verified over TLS using a dedicated read-only credential.

Deployment: LICENSE_DATABASE_URL is configured for production only. Preview environments without a license reader show a retryable license-history warning; existing account and SSO sources remain usable. No schema migrations.

RetriggerConfidence Score: 5/5

The PR appears safe to merge; the stalled license-query issue is fixed and no new blocking issue was found.

Summary

Adds a dedicated Invoices page for account purchases, licenses, developer credits, and owner-managed SSO subscriptions.

  • Customers can find invoices and receipts on one page.
  • Verified purchase emails unlock desktop and self-hosted invoice history.

Reviews (2) · Last reviewed commit: "fix: limit concurrent invoice billing re..." · Reviewed by Greptile

Comment on lines +13 to +15
const [rows] = await connection.execute<
(RowDataPacket & { stripeId: string })[]
>(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 License reads stall all invoices

getLicenseCustomerIds limits connection setup to five seconds, but the query has no deadline. If the license database connects and then stops answering, every invoice request waits on it before loading any purchases, including Pro and SSO. Add a deadline for the read and close stalled connections so the license warning can appear while other purchases remain usable.

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/database/billing/license-customers.ts
Line: 13-15

Comment:
**License reads stall all invoices**

`getLicenseCustomerIds` limits connection setup to five seconds, but the query has no deadline. If the license database connects and then stops answering, every invoice request waits on it before loading any purchases, including Pro and SSO. Add a deadline for the read and close stalled connections so the license warning can appear while other purchases remain usable.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

@superagent-security superagent-security 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.

Superagent found 1 security concern(s).

export async function getLicenseCustomerIds(email: string) {
const uri = serverEnv().LICENSE_DATABASE_URL;
if (!uri) throw new Error("License billing is unavailable.");
const connection = await createConnection({

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: Invoice actions allow authenticated users to create unbounded license-database connections

Each verified user request opens a fresh license-DB connection; repeated concurrent invoice actions have no rate or concurrency limit.

Rate-limit invoice actions and cap concurrent license-DB connections with a shared pool or semaphore.

AI prompt
Check if this security scanner issue is valid. If so, understand the root cause and fix it. If appropriate, update or add tests. Keep the change focused and preserve intended behavior.

<file name="packages/database/billing/license-customers.ts">
<violation number="1" location="packages/database/billing/license-customers.ts:7">
<priority>P2</priority>
<title>Invoice actions allow authenticated users to create unbounded license-database connections</title>
<evidence>The new `getLicenseCustomerIds` function opens a fresh MySQL TLS connection for each call (`createConnection` at line 7), executes an email-bound query, and only destroys the connection after the query finishes (lines 14–31). The new invoice server actions call this function during source discovery for verified-email users (`apps/web/actions/billing/invoices.ts:77–91`); both initial history and pagination invoke source discovery (`:199–225`). A signed-in Cap user with a verified email can repeatedly invoke these actions, including concurrently, causing a new connection and query against the configured `LICENSE_DATABASE_URL` for each request. The five-second connect/query deadlines and connection cleanup bound an individual request but do not cap concurrent or repeated requests; these actions apply no rate limit or shared connection/concurrency bound. This adds user-triggerable load on the license database, which was not reachable through the base user-facing invoice path.</evidence>
<recommendation>Add an enforced per-user and/or per-IP rate limit to both invoice history actions, and bound concurrent license-database connections with a shared pool or semaphore. Keep the per-query timeout and cleanup as secondary safeguards.</recommendation>
</violation>
</file>

@richiemcilroy

Copy link
Copy Markdown
Member Author

hey @greptileai please review the pr

@vercel

vercel Bot commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

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

Project Deployment Actions Updated
cap-status Ready Ready Preview Oct 20, 2026 8:19pm UTC

@richiemcilroy

Copy link
Copy Markdown
Member Author

@greptileai review

@capy-ai capy-ai 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.

Capy found no issues.

View 1 other finding in Capy.

Open in Capy

This branch was successfully deployed

3 active (1 outdated) deployments
Preview – cap-web — a0f08648 Deployed Oct 20, 2026 by vercel[bot]
Preview – cap-status — a0f08648 Deployed Oct 20, 2026 by vercel[bot]
Preview — fe9d989d Deployed Oct 9, 2026 by vercel[bot]
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