feat: Next generation Stripe billing integration#2161
feat: Next generation Stripe billing integration#2161
Conversation
- Upgrade Stripe.net to v50.4.1 and update the backend to support modern PaymentMethods while maintaining legacy token compatibility. - Implement a new billing feature in the Svelte UI with lazy-loaded Stripe integration and a functional plan change dialog. - Add TanStack Query hooks for fetching available plans and processing plan changes with coupon support.
985add7 to
ee79cb9
Compare
There was a problem hiding this comment.
Pull request overview
This PR introduces a “next generation” Stripe billing integration spanning backend Stripe.net v50 updates, new billing endpoints/DTO mapping, and a new Svelte 5 billing UI (Stripe provider + change-plan dialog) wired into organization/project usage and billing pages.
Changes:
- Upgrades Stripe.net usage on the backend (invoice status handling, discounts, subscription updates, PaymentMethod support).
- Adds a new Svelte billing feature module (
StripeProvider,ChangePlanDialog) and hooks it into usage/billing routes. - Extends org client API with plan query + change-plan mutation; updates invoice mapping/tests accordingly.
Reviewed changes
Copilot reviewed 18 out of 19 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/Exceptionless.Tests/Mapping/InvoiceMapperTests.cs | Updates Stripe invoice test data to use Status instead of Paid. |
| tests/Exceptionless.Tests/Controllers/TokenControllerTests.cs | Simplifies test comments (no functional change). |
| src/Exceptionless.Web/Mapping/InvoiceMapper.cs | Maps “paid” via Invoice.Status string comparison. |
| src/Exceptionless.Web/Controllers/OrganizationController.cs | Updates Stripe invoice retrieval/discount handling and modernizes change-plan flow to support PaymentMethods + Stripe.net 50 API changes. |
| src/Exceptionless.Web/ClientApp/src/routes/(app)/project/[projectId]/usage/+page.svelte | “Change plan” now navigates to the org billing page. |
| src/Exceptionless.Web/ClientApp/src/routes/(app)/organization/[organizationId]/usage/+page.svelte | “Change plan” now navigates to the org billing page. |
| src/Exceptionless.Web/ClientApp/src/routes/(app)/organization/[organizationId]/billing/+page.svelte | Uses new billing module ChangePlanDialog and passes loaded org data into it. |
| src/Exceptionless.Web/ClientApp/src/lib/features/organizations/components/dialogs/change-plan-dialog.svelte | Removes the legacy placeholder change-plan dialog component. |
| src/Exceptionless.Web/ClientApp/src/lib/features/organizations/api.svelte.ts | Adds getPlansQuery() and changePlanMutation() + related query keys/types. |
| src/Exceptionless.Web/ClientApp/src/lib/features/billing/stripe.svelte.ts | Adds Stripe context + lazy singleton loader utilities. |
| src/Exceptionless.Web/ClientApp/src/lib/features/billing/schemas.ts | Adds zod schema/types for the change-plan form. |
| src/Exceptionless.Web/ClientApp/src/lib/features/billing/models.ts | Adds billing model re-exports + local billing form/params types. |
| src/Exceptionless.Web/ClientApp/src/lib/features/billing/index.ts | Barrel exports for billing feature module. |
| src/Exceptionless.Web/ClientApp/src/lib/features/billing/constants.ts | Defines FREE_PLAN_ID. |
| src/Exceptionless.Web/ClientApp/src/lib/features/billing/components/stripe-provider.svelte | Adds Stripe Elements provider wrapper with loading/error states. |
| src/Exceptionless.Web/ClientApp/src/lib/features/billing/components/change-plan-dialog.svelte | Implements the plan change dialog UI including Stripe payment collection. |
| src/Exceptionless.Web/ClientApp/package.json | Adds Stripe JS + svelte-stripe dependencies. |
| src/Exceptionless.Web/ClientApp/package-lock.json | Locks Stripe JS + svelte-stripe dependencies. |
| src/Exceptionless.Web/ClientApp/STRIPE-INTEGRATION-PLAN.md | Adds the Stripe integration plan document. |
| src/Exceptionless.Core/Exceptionless.Core.csproj | Upgrades Stripe.net to 50.4.1. |
| .claude/agents/engineer.md | Adds guidance for rerunning flaky CI jobs via gh run rerun. |
Files not reviewed (1)
- src/Exceptionless.Web/ClientApp/package-lock.json: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
This PR introduces a “next generation” Stripe billing integration across the backend (Stripe.net 50.x) and the Svelte client, enabling plan selection/changes and invoice display with the updated Stripe API surface.
Changes:
- Upgrades Stripe.net to 50.4.1 and updates invoice/plan-change logic to use
Status,Price,Discounts, andPaymentMethodAPIs. - Adds a new client-side billing feature module (Stripe context/provider + change plan dialog) and wires it into usage/billing pages.
- Adds client API helpers for fetching plans and performing plan changes, with cache invalidation.
Reviewed changes
Copilot reviewed 18 out of 19 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/Exceptionless.Tests/Mapping/InvoiceMapperTests.cs | Updates invoice mapping tests to reflect Stripe Status replacing Paid. |
| tests/Exceptionless.Tests/Controllers/TokenControllerTests.cs | Simplifies test comments (no functional change). |
| src/Exceptionless.Web/Mapping/InvoiceMapper.cs | Switches paid logic to derive from Invoice.Status. |
| src/Exceptionless.Web/Controllers/OrganizationController.cs | Updates invoice retrieval and plan change flows for Stripe.net 50.x (Price/Discounts/PaymentMethod changes). |
| src/Exceptionless.Web/ClientApp/src/routes/(app)/project/[projectId]/usage/+page.svelte | Replaces placeholder “change plan” handler with navigation to billing page. |
| src/Exceptionless.Web/ClientApp/src/routes/(app)/organization/[organizationId]/usage/+page.svelte | Same as above for org usage page. |
| src/Exceptionless.Web/ClientApp/src/routes/(app)/organization/[organizationId]/billing/+page.svelte | Switches to the new billing module’s ChangePlanDialog and passes organization data. |
| src/Exceptionless.Web/ClientApp/src/lib/features/organizations/components/dialogs/change-plan-dialog.svelte | Removes the old placeholder change-plan dialog component. |
| src/Exceptionless.Web/ClientApp/src/lib/features/organizations/api.svelte.ts | Adds getPlansQuery + changePlanMutation and associated query keys/types. |
| src/Exceptionless.Web/ClientApp/src/lib/features/billing/stripe.svelte.ts | Adds Stripe context utilities and a singleton Stripe loader. |
| src/Exceptionless.Web/ClientApp/src/lib/features/billing/schemas.ts | Adds Zod schema for the change-plan form. |
| src/Exceptionless.Web/ClientApp/src/lib/features/billing/models.ts | Adds billing-focused types and re-exports generated API models. |
| src/Exceptionless.Web/ClientApp/src/lib/features/billing/index.ts | Exposes billing module public API (components/hooks/constants/types). |
| src/Exceptionless.Web/ClientApp/src/lib/features/billing/constants.ts | Introduces FREE_PLAN_ID. |
| src/Exceptionless.Web/ClientApp/src/lib/features/billing/components/stripe-provider.svelte | Adds a Stripe <Elements> provider and sets Stripe context for children. |
| src/Exceptionless.Web/ClientApp/src/lib/features/billing/components/change-plan-dialog.svelte | Implements the new plan change UX using Stripe PaymentElement + plans query + mutation. |
| src/Exceptionless.Web/ClientApp/package.json | Adds @stripe/stripe-js and svelte-stripe. |
| src/Exceptionless.Web/ClientApp/package-lock.json | Locks new Stripe dependencies. |
| src/Exceptionless.Core/Exceptionless.Core.csproj | Upgrades Stripe.net from 47.4.0 to 50.4.1. |
Files not reviewed (1)
- src/Exceptionless.Web/ClientApp/package-lock.json: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
This PR introduces a new Stripe-based billing UX in the Svelte app and updates the backend Stripe integration to newer Stripe.net APIs, alongside some infra/telemetry adjustments.
Changes:
- Adds new billing feature module on the frontend (Stripe provider + change-plan dialog) and wires it into usage/billing pages.
- Updates backend invoice/payment handling to Stripe.net 50.x patterns (invoice status, discounts, line item pricing/price lookup).
- Updates dependencies/config (Stripe.net upgrade, adds
@stripe/stripe-js+svelte-stripe, adjusts OpenTelemetry Prometheus wiring and deploy workflow conditions).
Reviewed changes
Copilot reviewed 25 out of 26 changed files in this pull request and generated 10 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/Exceptionless.Tests/Mapping/InvoiceMapperTests.cs | Updates invoice mapping tests to use Status instead of Paid. |
| tests/Exceptionless.Tests/Controllers/TokenControllerTests.cs | Simplifies test comments (no functional change). |
| src/Exceptionless.Web/Startup.cs | Removes Prometheus scraping endpoint middleware. |
| src/Exceptionless.Web/Mapping/InvoiceMapper.cs | Maps Paid from Invoice.Status == "paid". |
| src/Exceptionless.Web/Exceptionless.Web.csproj | Removes Prometheus exporter package reference; formatting changes. |
| src/Exceptionless.Web/Controllers/OrganizationController.cs | Updates invoice and plan-change flows for Stripe.net 50.x (prices/discounts/payment methods). |
| src/Exceptionless.Web/ClientApp/src/routes/(app)/project/[projectId]/usage/+page.svelte | Navigates to org billing page with changePlan=true. |
| src/Exceptionless.Web/ClientApp/src/routes/(app)/organization/[organizationId]/usage/+page.svelte | Navigates to org billing page with changePlan=true. |
| src/Exceptionless.Web/ClientApp/src/routes/(app)/organization/[organizationId]/billing/+page.svelte | Switches to new billing ChangePlanDialog and passes organization data. |
| src/Exceptionless.Web/ClientApp/src/lib/features/organizations/components/dialogs/change-plan-dialog.svelte | Deletes placeholder dialog (replaced by billing feature). |
| src/Exceptionless.Web/ClientApp/src/lib/features/organizations/api.svelte.ts | Adds plans query + change-plan mutation/query keys. |
| src/Exceptionless.Web/ClientApp/src/lib/features/billing/stripe.svelte.ts | Adds Stripe context + lazy loader utilities. |
| src/Exceptionless.Web/ClientApp/src/lib/features/billing/schemas.ts | Adds Zod schema for change-plan form. |
| src/Exceptionless.Web/ClientApp/src/lib/features/billing/models.ts | Adds billing types and form state types. |
| src/Exceptionless.Web/ClientApp/src/lib/features/billing/index.ts | Adds billing feature barrel exports. |
| src/Exceptionless.Web/ClientApp/src/lib/features/billing/constants.ts | Adds FREE_PLAN_ID constant. |
| src/Exceptionless.Web/ClientApp/src/lib/features/billing/components/stripe-provider.svelte | Adds Stripe Elements provider component. |
| src/Exceptionless.Web/ClientApp/src/lib/features/billing/components/change-plan-dialog.svelte | Adds new Stripe-backed change-plan dialog UI. |
| src/Exceptionless.Web/ClientApp/package.json | Adds Stripe JS dependencies. |
| src/Exceptionless.Web/ClientApp/package-lock.json | Locks Stripe JS dependencies. |
| src/Exceptionless.Web/ApmExtensions.cs | Removes Prometheus exporter from OpenTelemetry metrics pipeline. |
| src/Exceptionless.Job/Program.cs | Removes Prometheus scraping endpoint middleware from job host. |
| src/Exceptionless.Job/Exceptionless.Job.csproj | Removes Prometheus exporter package reference; formatting changes. |
| src/Exceptionless.Core/Exceptionless.Core.csproj | Upgrades Stripe.net from 47.4.0 to 50.4.1; formatting changes. |
| .vscode/settings.json | Updates workspace editor/TypeScript SDK settings. |
| .github/workflows/build.yaml | Updates deploy job condition to allow dev deploys from a configured PR branch. |
Files not reviewed (1)
- src/Exceptionless.Web/ClientApp/package-lock.json: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
This PR introduces a next-generation Stripe-based billing flow across the Svelte app and the organization API, updating both frontend plan-change UX and backend Stripe integration to newer Stripe.net APIs.
Changes:
- Adds a new Billing feature module (Stripe context/provider + ChangePlanDialog) and wires it into the organization billing page and usage pages.
- Updates backend Stripe integration (Stripe.net 50.x) for invoice/payment/plan handling and updates invoice mapping/tests accordingly.
- Updates build/ops tooling (Docker MinVer build arg, workflow tweaks) and removes Prometheus OpenTelemetry exporter/scraping.
Reviewed changes
Copilot reviewed 26 out of 27 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/Exceptionless.Tests/Mapping/InvoiceMapperTests.cs | Updates tests to reflect invoice Status -> paid mapping. |
| tests/Exceptionless.Tests/Controllers/TokenControllerTests.cs | Simplifies test comments (no behavior change). |
| src/Exceptionless.Web/Startup.cs | Removes OTEL Prometheus scraping endpoint middleware. |
| src/Exceptionless.Web/Mapping/InvoiceMapper.cs | Maps invoice “paid” from Stripe Status instead of Paid. |
| src/Exceptionless.Web/Exceptionless.Web.csproj | Removes Prometheus exporter package + formatting changes. |
| src/Exceptionless.Web/Controllers/OrganizationController.cs | Updates Stripe invoice/plan change logic for Stripe.net 50.x and PaymentMethod support. |
| src/Exceptionless.Web/ClientApp/src/routes/(app)/project/[projectId]/usage/+page.svelte | Implements navigation to billing page on “Change plan”. |
| src/Exceptionless.Web/ClientApp/src/routes/(app)/organization/[organizationId]/usage/+page.svelte | Implements navigation to billing page on “Change plan”. |
| src/Exceptionless.Web/ClientApp/src/routes/(app)/organization/[organizationId]/billing/+page.svelte | Switches to new billing ChangePlanDialog component and passes organization data. |
| src/Exceptionless.Web/ClientApp/src/lib/features/organizations/components/dialogs/change-plan-dialog.svelte | Removes placeholder dialog from organizations feature. |
| src/Exceptionless.Web/ClientApp/src/lib/features/organizations/api.svelte.ts | Adds plans query + change-plan mutation and query keys. |
| src/Exceptionless.Web/ClientApp/src/lib/features/billing/stripe.svelte.ts | Adds Stripe lazy-loader + context hooks. |
| src/Exceptionless.Web/ClientApp/src/lib/features/billing/schemas.ts | Adds Zod schema for change-plan form. |
| src/Exceptionless.Web/ClientApp/src/lib/features/billing/models.ts | Adds billing feature types and re-exports generated API types. |
| src/Exceptionless.Web/ClientApp/src/lib/features/billing/index.ts | Public entrypoint for billing feature exports. |
| src/Exceptionless.Web/ClientApp/src/lib/features/billing/constants.ts | Adds FREE plan id constant. |
| src/Exceptionless.Web/ClientApp/src/lib/features/billing/components/stripe-provider.svelte | Adds provider wrapping Stripe Elements + loading/error UI. |
| src/Exceptionless.Web/ClientApp/src/lib/features/billing/components/change-plan-dialog.svelte | Adds full Stripe Elements-based plan change dialog. |
| src/Exceptionless.Web/ClientApp/package.json | Adds Stripe JS dependencies. |
| src/Exceptionless.Web/ClientApp/package-lock.json | Locks Stripe JS dependencies. |
| src/Exceptionless.Web/ApmExtensions.cs | Removes Prometheus exporter wiring. |
| src/Exceptionless.Job/Program.cs | Removes OTEL Prometheus scraping endpoint middleware. |
| src/Exceptionless.Job/Exceptionless.Job.csproj | Removes Prometheus exporter package + formatting changes. |
| src/Exceptionless.Core/Exceptionless.Core.csproj | Upgrades Stripe.net to 50.4.1. |
| Dockerfile | Adds MinVerVersionOverride build arg passthrough. |
| .vscode/settings.json | Disables format-on-save and changes TypeScript SDK path setting key. |
| .github/workflows/build.yaml | Ensures checkout uses correct ref and passes MinVer build arg into Docker builds; expands deploy conditions. |
Files not reviewed (1)
- src/Exceptionless.Web/ClientApp/package-lock.json: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Stripe v9 requires either clientSecret OR mode when calling stripe.elements(). The previous code passed neither when no clientSecret was provided, causing PaymentElement to fail to render. Uses a discriminated union Props type to enforce mutual exclusivity of clientSecret and mode at compile time, matching Stripe's own type constraints. Defaults to mode='setup' for collecting payment methods for future use.
Fix two issues preventing the Stripe PaymentElement from rendering in the Change Plan dialog: 1. Missing currency in Stripe Elements options: Stripe.js v9+ requires a currency string when using mode: 'setup'. Added currency: 'usd' to the elements creation options. 2. svelte-stripe Svelte 5 incompatibility: svelte-stripe's <Elements> and <PaymentElement> components use $bindable()/onMount patterns that don't trigger Svelte 5 template re-renders from async callbacks. Replaced both components with an imperative approach that loads Stripe, creates elements, and mounts the PaymentElement directly to the DOM via onMount, bypassing Svelte's reactive template system entirely. Additionally fixed the Change Plan dialog not being scrollable by adding max-h-[90vh] and overflow-y-auto to the Dialog.Content wrapper. Changes: - stripe-provider.svelte: Rewritten to imperatively mount PaymentElement without svelte-stripe components or Svelte reactive conditionals - change-plan-dialog.svelte: Removed svelte-stripe PaymentElement import, updated StripeProvider usage (no longer takes children), added dialog scroll constraints, removed debug markup Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… response check - stripe-provider: use braces on if (disposed) guard blocks - billing page: rename _success param to success - api.svelte: remove redundant !response.ok throw (fetchclient handles this)
- Integrate UpgradeRequiredDialog into app layout via upgradeRequiredDialog store - Replace handleUpgradeRequired with showUpgradeDialogIfNeeded on all pages - Usage pages open billing dialog directly instead of navigating away - Add Stripe context helpers (loadStripeOnce, retry-on-null)
| /** | ||
| * Billing feature module - Stripe integration for plan management. | ||
| */ | ||
|
|
||
| // Components | ||
| export { default as ChangePlanDialog } from './components/change-plan-dialog.svelte'; | ||
|
|
||
| export { default as StripeProvider } from './components/stripe-provider.svelte'; | ||
|
|
||
| // Upgrade required handling | ||
| export { default as UpgradeRequiredDialog } from './components/upgrade-required-dialog.svelte'; | ||
|
|
||
| // Constants | ||
| export { FREE_PLAN_ID } from './constants'; | ||
| // Models | ||
| export type { BillingPlan, CardMode, ChangePlanFormState, ChangePlanRequest, ChangePlanResult } from './models'; | ||
|
|
||
| // Context and hooks |
There was a problem hiding this comment.
remove these stupid comments
| import { resolve } from '$app/paths'; | ||
| import Button from '$comp/ui/button/button.svelte'; | ||
| import * as DropdownMenu from '$comp/ui/dropdown-menu'; | ||
| import { handleUpgradeRequired } from '$features/billing'; |
There was a problem hiding this comment.
better name for this, also what about the message
| onMount(() => { | ||
| loadStripeOnce() | ||
| .then((stripe) => { | ||
| if (disposed) { | ||
| return; |
There was a problem hiding this comment.
does this follow best practices?
| } | ||
| return ctx; |
There was a problem hiding this comment.
| } | |
| return ctx; | |
| } | |
| return ctx; |
| if (!response.ok) { | ||
| throw response.problem; | ||
| } | ||
|
|
There was a problem hiding this comment.
I don't think this is needed
| ); | ||
| //await confirmUpgradePlan(message, tack.organization_id); | ||
| //await promoteToExternal(); | ||
| showUpgradeDialogIfNeeded(response.problem, stack.organization_id, () => promoteToExternal()); |
There was a problem hiding this comment.
what about the custom error message?
| if (clientResponse.problem) { | ||
| showUpgradeDialogIfNeeded(clientResponse.problem, organization.current, () => loadData()); | ||
| } |
There was a problem hiding this comment.
do we still need this if?
There was a problem hiding this comment.
Pull request overview
This PR introduces a “next generation” Stripe billing flow across the ASP.NET Core API and the Svelte 5 client, including plan selection, upgrade-required UX, and Stripe SDK upgrades.
Changes:
- Upgrade Stripe integration (Stripe.net v51, Stripe.js v9) and update invoice/plan mapping accordingly.
- Add new billing UI module in Svelte (plan change dialog, Stripe provider, upgrade-required dialog) and wire it into key app pages.
- Extend organization billing APIs/docs/tests (plans listing, change-plan JSON body + legacy query support, invoices).
Reviewed changes
Copilot reviewed 47 out of 53 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/http/organizations.http | Adds preferred JSON-body change-plan request + legacy query example. |
| tests/Exceptionless.Tests/Mapping/InvoiceMapperTests.cs | Updates tests to use Invoice.Status instead of removed Paid. |
| tests/Exceptionless.Tests/Controllers/TokenControllerTests.cs | Comment/structure tweaks in tests (arrange/act/assert). |
| tests/Exceptionless.Tests/Controllers/OrganizationControllerTests.cs | Adds integration coverage for plans + billing-disabled behaviors and downgrade checks. |
| tests/Exceptionless.Tests/Controllers/Data/openapi.json | Documents change-plan requestBody and adds ChangePlanRequest schema. |
| src/Exceptionless.Web/Program.cs | Adds optional appsettings.Local.yml to config sources. |
| src/Exceptionless.Web/Models/ChangePlanRequest.cs | Introduces request model for JSON body plan changes. |
| src/Exceptionless.Web/Mapping/InvoiceMapper.cs | Maps invoice “paid” from Status == paid. |
| src/Exceptionless.Web/Exceptionless.Web.csproj | Formatting-only change. |
| src/Exceptionless.Web/Controllers/OrganizationController.cs | Implements JSON body + legacy query support for change-plan; updates invoice retrieval for Stripe.net v51; adds plans endpoint behavior changes. |
| src/Exceptionless.Web/ClientApp/src/routes/+layout.svelte | Treats HTTP 426 as a non-retriable status in the layout’s status handling. |
| src/Exceptionless.Web/ClientApp/src/routes/(app)/stream/+page.svelte | Adds centralized 426 handling + typed ProblemDetails for stream page flows. |
| src/Exceptionless.Web/ClientApp/src/routes/(app)/project/add/+page.svelte | Adds upgrade-required handling for project creation failures. |
| src/Exceptionless.Web/ClientApp/src/routes/(app)/project/[projectId]/usage/+page.svelte | Implements “Change plan” navigation into new billing page. |
| src/Exceptionless.Web/ClientApp/src/routes/(app)/project/[projectId]/integrations/+page.svelte | Adds upgrade-required handling for webhook + Slack integration actions. |
| src/Exceptionless.Web/ClientApp/src/routes/(app)/organization/add/+page.svelte | Adds upgrade-required handling for org creation failures. |
| src/Exceptionless.Web/ClientApp/src/routes/(app)/organization/[organizationId]/users/+page.svelte | Adds upgrade-required handling for user invite failures. |
| src/Exceptionless.Web/ClientApp/src/routes/(app)/organization/[organizationId]/usage/+page.svelte | Implements “Change plan” navigation into new billing page. |
| src/Exceptionless.Web/ClientApp/src/routes/(app)/organization/[organizationId]/billing/+page.svelte | Switches billing page to new ChangePlanDialog from billing feature module; fixes Stripe invoice URL encoding. |
| src/Exceptionless.Web/ClientApp/src/routes/(app)/issues/+page.svelte | Adds centralized 426 handling + typed ProblemDetails for issues page. |
| src/Exceptionless.Web/ClientApp/src/routes/(app)/event/[eventId]/+page.svelte | Replaces TODO 426 handling with shared upgrade-required handler. |
| src/Exceptionless.Web/ClientApp/src/routes/(app)/account/notifications/+page.svelte | Adds proactive “show upgrade dialog” behavior for premium notifications. |
| src/Exceptionless.Web/ClientApp/src/routes/(app)/+page.svelte | Adds centralized 426 handling + typed ProblemDetails for events dashboard page. |
| src/Exceptionless.Web/ClientApp/src/routes/(app)/+layout.svelte | Mounts UpgradeRequiredDialog and passes premium-needed state into org notifications. |
| src/Exceptionless.Web/ClientApp/src/lib/generated/schemas.ts | Adds ChangePlanRequest schema; adjusts several generated schema shapes. |
| src/Exceptionless.Web/ClientApp/src/lib/generated/api.ts | Adds ChangePlanRequest type; adjusts several generated API types. |
| src/Exceptionless.Web/ClientApp/src/lib/features/stacks/components/stack-options-dropdown-menu.svelte | Uses shared upgrade-required handler for premium “promote external” action. |
| src/Exceptionless.Web/ClientApp/src/lib/features/projects/components/user-notification-settings-form.svelte | Allows upgrade callback to be sync or async. |
| src/Exceptionless.Web/ClientApp/src/lib/features/organizations/components/dialogs/change-plan-dialog.svelte | Removes legacy placeholder change-plan dialog component. |
| src/Exceptionless.Web/ClientApp/src/lib/features/organizations/api.svelte.ts | Adds plans query + change-plan mutation APIs + query keys. |
| src/Exceptionless.Web/ClientApp/src/lib/features/events/premium-filter.ts | Adds client-side filter analysis to detect premium-required fields. |
| src/Exceptionless.Web/ClientApp/src/lib/features/billing/upgrade-required.svelte.ts | Adds shared upgrade-required dialog state + helpers (426 handling + proactive prompts). |
| src/Exceptionless.Web/ClientApp/src/lib/features/billing/stripe.svelte.ts | Adds Stripe context + singleton Stripe loader. |
| src/Exceptionless.Web/ClientApp/src/lib/features/billing/schemas.ts | Adds billing form schema for change-plan dialog state. |
| src/Exceptionless.Web/ClientApp/src/lib/features/billing/models.ts | Adds billing-specific model types and re-exports generated billing types. |
| src/Exceptionless.Web/ClientApp/src/lib/features/billing/index.ts | Introduces billing feature module exports (components, helpers, Stripe context). |
| src/Exceptionless.Web/ClientApp/src/lib/features/billing/constants.ts | Adds FREE plan ID constant. |
| src/Exceptionless.Web/ClientApp/src/lib/features/billing/components/upgrade-required-dialog.svelte | Adds global upgrade-required confirmation dialog and launches change-plan dialog. |
| src/Exceptionless.Web/ClientApp/src/lib/features/billing/components/stripe-provider.svelte | Adds imperative Stripe Elements/PaymentElement provider to work around Svelte 5 reactivity issues. |
| src/Exceptionless.Web/ClientApp/src/lib/features/billing/components/change-plan-dialog.svelte | Adds full-featured Svelte change-plan dialog (plan tiers, coupons, PaymentElement, mutation wiring). |
| src/Exceptionless.Web/ClientApp/src/lib/features/billing/components/change-plan-dialog.stories.ts | Adds Storybook coverage for many dialog states. |
| src/Exceptionless.Web/ClientApp/src/lib/features/billing/components/change-plan-dialog-harness.svelte | Adds Storybook harness with query client seeding/mocks. |
| src/Exceptionless.Web/ClientApp/src/app.css | Adjusts --primary-foreground in light/dark themes. |
| src/Exceptionless.Web/ClientApp/package.json | Adds @stripe/stripe-js. |
| src/Exceptionless.Web/ClientApp/package-lock.json | Locks @stripe/stripe-js dependency. |
| src/Exceptionless.Web/ClientApp/.storybook/mocks/env.js | Provides a placeholder publishable key so billing stories can render. |
| src/Exceptionless.Web/ClientApp.angular/components/organization/organization-service.js | Updates legacy Angular changePlan to POST JSON body instead of query params. |
| src/Exceptionless.Web/ClientApp.angular/components/billing/change-plan-controller.js | Improves error message handling for plan change failures. |
| src/Exceptionless.Job/Exceptionless.Job.csproj | Formatting-only change. |
| src/Exceptionless.Core/Exceptionless.Core.csproj | Upgrades Stripe.net dependency to 51.1.0. |
| docs/billing-stripe-integration.md | Adds documentation for new billing integration architecture and endpoints. |
| .vscode/settings.json | Fixes VS Code TypeScript SDK setting key. |
| .github/hooks/follow-up.json | Adds repository hook configuration (command-based stop hook). |
| .aspire/settings.json | Formatting-only change. |
Files not reviewed (1)
- src/Exceptionless.Web/ClientApp/package-lock.json: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if (!value) { | ||
| state.retryCallback = undefined; | ||
| } | ||
| }, |
| function onUpgrade() { | ||
| upgradeRequiredDialog.open = false; | ||
|
|
||
| if (isStripeEnabled() && upgradeRequiredDialog.organizationId) { | ||
| showChangePlan = true; | ||
| } | ||
| } | ||
|
|
||
| async function onChangePlanClose(success: boolean) { | ||
| const retry = success ? upgradeRequiredDialog.retryCallback : undefined; | ||
| showChangePlan = false; | ||
|
|
||
| if (retry) { | ||
| await retry(); | ||
| } | ||
| } |
| } | ||
|
|
||
| function onCouponCancel() { | ||
| couponOpen = false; | ||
| couponInput = ''; | ||
| couponError = null; | ||
| } | ||
|
|
||
| function onCouponApply() { | ||
| const code = couponInput.trim(); | ||
| if (!code) { |
| **Body** (JSON, preferred): | ||
|
|
||
| | Field | Type | Description | | ||
| | --- | --- | --- | | ||
| | `plan_id` | string | Target plan ID (e.g., `EX_MEDIUM`, `EX_LARGE_YEARLY`) | | ||
| | `stripe_token` | string? | `pm_` PaymentMethod ID (Svelte) or `tok_` token (Angular) | | ||
| | `last4` | string? | Last 4 digits of card (display only) | | ||
| | `coupon_id` | string? | Stripe coupon code | | ||
|
|
| string planName = billingPlan?.Name ?? priceId; | ||
| string interval = priceId.EndsWith("_YEARLY", StringComparison.OrdinalIgnoreCase) ? "year" : "month"; | ||
| item.Description = $"Exceptionless - {planName} Plan ({line.Amount / 100.0m:c}/{interval})"; | ||
| } |
There was a problem hiding this comment.
should we log a warning here saying it's not found.
|
|
||
| var customer = await customerService.CreateAsync(createCustomer); | ||
|
|
||
| var subscriptionOptions = new SubscriptionCreateOptions |
There was a problem hiding this comment.
some comments explaining this would help in here.
There was a problem hiding this comment.
Pull request overview
Adds the next-generation Stripe billing flow across backend + Svelte UI, including plan selection, payment method capture via Stripe.js, upgrade-required UX, and Stripe.net v51 migration updates.
Changes:
- Added JSON-body (preferred) support for
POST /organizations/{id}/change-plan, while keeping legacy query-string support. - Implemented new Svelte billing UI (ChangePlanDialog, StripeProvider) and centralized “Upgrade Required (426)” handling across pages.
- Migrated Stripe invoice/plan handling to Stripe.net v51 semantics (status/discounts/price IDs) and updated related tests + OpenAPI artifacts.
Reviewed changes
Copilot reviewed 47 out of 53 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/http/organizations.http | Updates change-plan request examples (JSON body + legacy query). |
| tests/Exceptionless.Tests/Mapping/InvoiceMapperTests.cs | Adjusts tests for Stripe Invoice “paid” → Status. |
| tests/Exceptionless.Tests/Controllers/OrganizationControllerTests.cs | Adds coverage for billing plans endpoints and billing-disabled behaviors. |
| tests/Exceptionless.Tests/Controllers/Data/openapi.json | Documents change-plan request body + new schema. |
| src/Exceptionless.Web/Program.cs | Adds optional appsettings.Local.yml config source. |
| src/Exceptionless.Web/Models/ChangePlanRequest.cs | Introduces typed request model for JSON-body change-plan. |
| src/Exceptionless.Web/Mapping/InvoiceMapper.cs | Maps invoice “paid” from Status == "paid". |
| src/Exceptionless.Web/Exceptionless.Web.csproj | Formatting-only change. |
| src/Exceptionless.Web/Controllers/OrganizationController.cs | Updates invoice fetching + plan listing + change-plan endpoint to new Stripe.net v51 patterns and JSON-body support. |
| src/Exceptionless.Web/ClientApp/src/routes/+layout.svelte | Treats 426 as non-retriable to enable upgrade-required UX. |
| src/Exceptionless.Web/ClientApp/src/routes/(app)/stream/+page.svelte | Shows upgrade dialog on 426 and resets selection on errors. |
| src/Exceptionless.Web/ClientApp/src/routes/(app)/project/add/+page.svelte | Intercepts 426 to show upgrade dialog and supports retry. |
| src/Exceptionless.Web/ClientApp/src/routes/(app)/project/[projectId]/usage/+page.svelte | Replaces placeholder change-plan action with ChangePlanDialog. |
| src/Exceptionless.Web/ClientApp/src/routes/(app)/project/[projectId]/integrations/+page.svelte | Intercepts 426 on premium integration actions. |
| src/Exceptionless.Web/ClientApp/src/routes/(app)/organization/add/+page.svelte | Intercepts 426 to show upgrade dialog and supports retry. |
| src/Exceptionless.Web/ClientApp/src/routes/(app)/organization/[organizationId]/users/+page.svelte | Intercepts 426 on inviting users. |
| src/Exceptionless.Web/ClientApp/src/routes/(app)/organization/[organizationId]/usage/+page.svelte | Replaces placeholder change-plan action with ChangePlanDialog. |
| src/Exceptionless.Web/ClientApp/src/routes/(app)/organization/[organizationId]/billing/+page.svelte | Swaps legacy dialog for new ChangePlanDialog and improves invoice URL safety. |
| src/Exceptionless.Web/ClientApp/src/routes/(app)/issues/+page.svelte | Intercepts 426 responses and routes into upgrade dialog. |
| src/Exceptionless.Web/ClientApp/src/routes/(app)/event/[eventId]/+page.svelte | Uses centralized upgrade-required helper. |
| src/Exceptionless.Web/ClientApp/src/routes/(app)/account/notifications/+page.svelte | Adds proactive upgrade dialog trigger for premium notifications. |
| src/Exceptionless.Web/ClientApp/src/routes/(app)/+page.svelte | Intercepts 426 on event list load and handles errors consistently. |
| src/Exceptionless.Web/ClientApp/src/routes/(app)/+layout.svelte | Mounts UpgradeRequiredDialog and adds premium-filter awareness to notifications. |
| src/Exceptionless.Web/ClientApp/src/lib/generated/schemas.ts | Adds ChangePlanRequest schema; updates several generated schemas. |
| src/Exceptionless.Web/ClientApp/src/lib/generated/api.ts | Adds ChangePlanRequest type and updates several generated types. |
| src/Exceptionless.Web/ClientApp/src/lib/features/stacks/components/stack-options-dropdown-menu.svelte | Uses upgrade-required dialog for premium-only actions. |
| src/Exceptionless.Web/ClientApp/src/lib/features/shared/utils/formatters.ts | Adds shared currency formatter. |
| src/Exceptionless.Web/ClientApp/src/lib/features/projects/components/user-notification-settings-form.svelte | Loosens upgrade callback type to allow sync handler. |
| src/Exceptionless.Web/ClientApp/src/lib/features/organizations/components/dialogs/change-plan-dialog.svelte | Removes placeholder legacy dialog. |
| src/Exceptionless.Web/ClientApp/src/lib/features/organizations/api.svelte.ts | Adds plans query + change-plan mutation + query keys. |
| src/Exceptionless.Web/ClientApp/src/lib/features/events/premium-filter.ts | Adds client-side detection for premium filter usage. |
| src/Exceptionless.Web/ClientApp/src/lib/features/billing/upgrade-required.svelte.ts | Adds shared upgrade-required state + helpers. |
| src/Exceptionless.Web/ClientApp/src/lib/features/billing/stripe.svelte.ts | Adds Stripe context + lazy loader singleton. |
| src/Exceptionless.Web/ClientApp/src/lib/features/billing/schemas.ts | Adds billing form schema. |
| src/Exceptionless.Web/ClientApp/src/lib/features/billing/models.ts | Adds billing model re-exports and local types. |
| src/Exceptionless.Web/ClientApp/src/lib/features/billing/index.ts | Exposes billing public API (dialogs, helpers, stripe hooks). |
| src/Exceptionless.Web/ClientApp/src/lib/features/billing/constants.ts | Adds Free plan constant. |
| src/Exceptionless.Web/ClientApp/src/lib/features/billing/components/upgrade-required-dialog.svelte | Adds dialog to route 426 → upgrade CTA → ChangePlanDialog. |
| src/Exceptionless.Web/ClientApp/src/lib/features/billing/components/stripe-provider.svelte | Adds imperative Stripe PaymentElement loader/mounter (Svelte 5 workaround). |
| src/Exceptionless.Web/ClientApp/src/lib/features/billing/components/change-plan-dialog.svelte | Implements new plan selection + payment + coupon UI and submit logic. |
| src/Exceptionless.Web/ClientApp/src/lib/features/billing/components/change-plan-dialog.stories.ts | Adds Storybook coverage for billing dialog states. |
| src/Exceptionless.Web/ClientApp/src/lib/features/billing/components/change-plan-dialog-harness.svelte | Storybook harness for dialog + mocked query data. |
| src/Exceptionless.Web/ClientApp/src/app.css | Adjusts primary foreground color. |
| src/Exceptionless.Web/ClientApp/package.json | Adds @stripe/stripe-js. |
| src/Exceptionless.Web/ClientApp/package-lock.json | Locks @stripe/stripe-js@9.2.0. |
| src/Exceptionless.Web/ClientApp/.storybook/mocks/env.js | Adds mock Stripe publishable key for billing stories. |
| src/Exceptionless.Web/ClientApp.angular/components/organization/organization-service.js | Updates legacy Angular change-plan call to send JSON body. |
| src/Exceptionless.Web/ClientApp.angular/components/billing/change-plan-controller.js | Improves legacy error message extraction. |
| src/Exceptionless.Job/Exceptionless.Job.csproj | Formatting-only change. |
| src/Exceptionless.Core/Exceptionless.Core.csproj | Upgrades Stripe.net to v51.1.0. |
| docs/billing-stripe-integration.md | Adds Stripe integration architecture + migration notes. |
| .vscode/settings.json | Fixes TS SDK setting key. |
| .aspire/settings.json | Formatting-only change. |
Files not reviewed (1)
- src/Exceptionless.Web/ClientApp/package-lock.json: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| <A class="cursor-pointer" role="button" tabindex={0} onclick={() => (changePlanDialogOpen = true)}> | ||
| <span class="font-bold">{organizationQuery.data?.plan_name}</span> plan | ||
| </A> |
|
|
||
| {#if canChangePlan} | ||
| <A onclick={handleChangePlan}>Click here to change your plan or billing information.</A> | ||
| <A class="cursor-pointer" role="button" tabindex={0} onclick={() => (changePlanDialogOpen = true)}>Click here to change your plan or billing information.</A> |
| catch (StripeException ex) | ||
| { | ||
| _logger.LogCritical(ex, "Error getting invoice ({InvoiceId}): {Message}", id, ex.Message); | ||
| } |
| 1. **`Invoice.Paid` removed** → Use `String.Equals(invoice.Status, "paid", StringComparison.Ordinal)` | ||
| 2. **`Invoice.Discount` removed** → Use `Invoice.Discounts?.FirstOrDefault(d => d.Deleted is not true)?.Source?.Coupon` | ||
| 3. **`line.Plan` removed** → Use `line.Pricing?.PriceDetails?.PriceId` + `PriceService.GetAsync()` | ||
| 4. **`CustomerCreateOptions.Plan` deprecated** → Create subscription separately |
|
|
||
| 1. ~~**Coupon not applied for existing customers changing plans**~~ — Fixed. Coupons are now applied in all paths: new customer, existing customer updating subscription, and existing customer creating a new subscription. | ||
| 2. **Potential orphaned Stripe customers** — If subscription creation fails after customer creation, a retry would create a duplicate Stripe customer. Mitigated by the low likelihood of this failure path. | ||
| 3. **N+1 price fetches in invoice view** — Each unique price ID in an invoice makes a separate Stripe API call. Mitigated by a per-request cache (`priceCache`). Most invoices have 1-3 distinct prices. |
| <A class="cursor-pointer" role="button" tabindex={0} onclick={() => (changePlanDialogOpen = true)}> | ||
| <span class="font-bold">{organizationQuery.data?.plan_name}</span> plan | ||
| </A> |
|
|
||
| {#if canChangePlan} | ||
| <A onclick={handleChangePlan}>Click here to change your plan or billing information.</A> | ||
| <A class="cursor-pointer" role="button" tabindex={0} onclick={() => (changePlanDialogOpen = true)}>Click here to change your plan or billing information.</A> |
…nd plan-not-found warning
… drop unnecessary .Originals() on intermediate save
There was a problem hiding this comment.
Pull request overview
Introduces a “next generation” Stripe billing flow across the ASP.NET Core API and the Svelte 5 app, including plan selection/upgrade UX, server-side Stripe.net v51 changes, and supporting documentation/tests.
Changes:
- Adds a new Svelte billing UI (ChangePlanDialog, StripeProvider) plus a global “Upgrade required” (426) dialog.
- Extends/updates organization billing API behavior (plans/invoices/change-plan) and migrates Stripe.net usage to v51 semantics (invoice status/discounts, subscription item price IDs).
- Updates tests, OpenAPI snapshot, legacy Angular client calls, and adds Stripe integration documentation.
Reviewed changes
Copilot reviewed 47 out of 53 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/http/organizations.http | Updates change-plan HTTP examples (JSON body preferred + legacy query params). |
| tests/Exceptionless.Tests/Mapping/InvoiceMapperTests.cs | Updates invoice mapping tests to use Status instead of Paid. |
| tests/Exceptionless.Tests/Controllers/OrganizationControllerTests.cs | Adds/updates billing-related controller and BillingManager tests. |
| tests/Exceptionless.Tests/Controllers/Data/openapi.json | Updates OpenAPI snapshot for change-plan body + legacy query params. |
| src/Exceptionless.Web/Program.cs | Adds support for optional appsettings.Local.yml. |
| src/Exceptionless.Web/Models/ChangePlanRequest.cs | Introduces JSON body model for change-plan requests. |
| src/Exceptionless.Web/Mapping/InvoiceMapper.cs | Migrates invoice “paid” mapping to Status == "paid". |
| src/Exceptionless.Web/Exceptionless.Web.csproj | Formatting-only change. |
| src/Exceptionless.Web/Controllers/OrganizationController.cs | Implements billing endpoints updates, change-plan body+legacy support, Stripe.net v51 migrations. |
| src/Exceptionless.Web/ClientApp/src/routes/+layout.svelte | Treats 426 as a non-retryable status in route gating logic. |
| src/Exceptionless.Web/ClientApp/src/routes/(app)/stream/+page.svelte | Opens upgrade dialog on 426 responses and wires retry callback. |
| src/Exceptionless.Web/ClientApp/src/routes/(app)/project/add/+page.svelte | Shows upgrade dialog on 426 during project creation flow. |
| src/Exceptionless.Web/ClientApp/src/routes/(app)/project/[projectId]/usage/+page.svelte | Replaces placeholder “change plan” behavior with ChangePlanDialog. |
| src/Exceptionless.Web/ClientApp/src/routes/(app)/project/[projectId]/integrations/+page.svelte | Shows upgrade dialog on 426 during integrations mutations. |
| src/Exceptionless.Web/ClientApp/src/routes/(app)/organization/add/+page.svelte | Shows upgrade dialog on 426 during org creation flow. |
| src/Exceptionless.Web/ClientApp/src/routes/(app)/organization/[organizationId]/users/+page.svelte | Shows upgrade dialog on 426 during invite flow. |
| src/Exceptionless.Web/ClientApp/src/routes/(app)/organization/[organizationId]/usage/+page.svelte | Replaces placeholder “change plan” behavior with ChangePlanDialog. |
| src/Exceptionless.Web/ClientApp/src/routes/(app)/organization/[organizationId]/billing/+page.svelte | Switches billing page to new ChangePlanDialog component + safer invoice URL encoding. |
| src/Exceptionless.Web/ClientApp/src/routes/(app)/issues/+page.svelte | Opens upgrade dialog on 426 responses and clears selection state. |
| src/Exceptionless.Web/ClientApp/src/routes/(app)/event/[eventId]/+page.svelte | Uses shared upgrade-required handler instead of TODO placeholder. |
| src/Exceptionless.Web/ClientApp/src/routes/(app)/account/notifications/+page.svelte | Proactively opens upgrade dialog for premium notification settings. |
| src/Exceptionless.Web/ClientApp/src/routes/(app)/+page.svelte | Opens upgrade dialog on 426 responses and clears selection state. |
| src/Exceptionless.Web/ClientApp/src/routes/(app)/+layout.svelte | Mounts UpgradeRequiredDialog globally; adds client-side premium-filter detection. |
| src/Exceptionless.Web/ClientApp/src/lib/generated/schemas.ts | Regenerates Zod schemas (adds ChangePlanRequest, adjusts other schema shapes). |
| src/Exceptionless.Web/ClientApp/src/lib/generated/api.ts | Regenerates TS API types (adds ChangePlanRequest, adjusts other types). |
| src/Exceptionless.Web/ClientApp/src/lib/features/stacks/components/stack-options-dropdown-menu.svelte | Uses shared upgrade-required handler on 426 for “promote to external”. |
| src/Exceptionless.Web/ClientApp/src/lib/features/shared/utils/formatters.ts | Adds shared formatCurrency utility. |
| src/Exceptionless.Web/ClientApp/src/lib/features/projects/components/user-notification-settings-form.svelte | Allows upgrade callback to be sync or async. |
| src/Exceptionless.Web/ClientApp/src/lib/features/organizations/components/dialogs/change-plan-dialog.svelte | Removes legacy placeholder dialog component. |
| src/Exceptionless.Web/ClientApp/src/lib/features/organizations/api.svelte.ts | Adds plans query + change-plan mutation and invalidations. |
| src/Exceptionless.Web/ClientApp/src/lib/features/events/premium-filter.ts | Adds client-side detection for premium filter fields (kept in sync with backend). |
| src/Exceptionless.Web/ClientApp/src/lib/features/billing/upgrade-required.svelte.ts | Adds shared 426 detection + dialog state + retry callback wiring. |
| src/Exceptionless.Web/ClientApp/src/lib/features/billing/stripe.svelte.ts | Adds Stripe context + lazy singleton Stripe.js loader. |
| src/Exceptionless.Web/ClientApp/src/lib/features/billing/schemas.ts | Adds Zod schema for change-plan form state. |
| src/Exceptionless.Web/ClientApp/src/lib/features/billing/models.ts | Adds billing models and form-state types. |
| src/Exceptionless.Web/ClientApp/src/lib/features/billing/index.ts | Adds billing feature barrel exports. |
| src/Exceptionless.Web/ClientApp/src/lib/features/billing/constants.ts | Adds billing constants (FREE_PLAN_ID). |
| src/Exceptionless.Web/ClientApp/src/lib/features/billing/components/upgrade-required-dialog.svelte | Adds global upgrade-required dialog that can open ChangePlanDialog. |
| src/Exceptionless.Web/ClientApp/src/lib/features/billing/components/stripe-provider.svelte | Adds imperative Stripe Elements/PaymentElement mounting for Svelte 5. |
| src/Exceptionless.Web/ClientApp/src/lib/features/billing/components/change-plan-dialog.svelte | Adds the new “Manage subscription” dialog UI and Stripe PaymentElement flow. |
| src/Exceptionless.Web/ClientApp/src/lib/features/billing/components/change-plan-dialog.stories.ts | Adds Storybook coverage for many dialog states. |
| src/Exceptionless.Web/ClientApp/src/lib/features/billing/components/change-plan-dialog-harness.svelte | Adds Storybook harness with mocked query data. |
| src/Exceptionless.Web/ClientApp/src/app.css | Updates --primary-foreground values. |
| src/Exceptionless.Web/ClientApp/package.json | Adds @stripe/stripe-js dependency. |
| src/Exceptionless.Web/ClientApp/package-lock.json | Locks @stripe/stripe-js dependency. |
| src/Exceptionless.Web/ClientApp/.storybook/mocks/env.js | Ensures Stripe publishable key exists for Storybook billing stories. |
| src/Exceptionless.Web/ClientApp.angular/components/organization/organization-service.js | Updates legacy Angular change-plan call to send JSON body. |
| src/Exceptionless.Web/ClientApp.angular/components/billing/change-plan-controller.js | Improves legacy failure messaging to include ProblemDetails title/message. |
| src/Exceptionless.Job/Exceptionless.Job.csproj | Formatting-only change. |
| src/Exceptionless.Core/Exceptionless.Core.csproj | Updates Stripe.net package version (47.x → 51.1.0). |
| docs/billing-stripe-integration.md | Adds Stripe integration design/usage documentation. |
| .vscode/settings.json | Fixes VS Code TS SDK setting key. |
| .aspire/settings.json | Formatting-only change. |
Files not reviewed (1)
- src/Exceptionless.Web/ClientApp/package-lock.json: Language not supported
Comments suppressed due to low confidence (1)
src/Exceptionless.Web/Controllers/OrganizationController.cs:242
GetInvoiceAsyncswallowsStripeException/Exceptionand then falls through toreturn NotFound()becausestripeInvoicestays null. This will report a 404 for transient Stripe/API failures, which is misleading for clients and makes debugging harder. Consider returning an appropriate 5xx (or rethrowing) when the Stripe call fails, while still returning 404 only when the invoice truly doesn’t exist or the user can’t access it.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| function onUpgrade() { | ||
| upgradeRequiredDialog.open = false; | ||
|
|
||
| if (isStripeEnabled() && upgradeRequiredDialog.organizationId) { | ||
| showChangePlan = true; | ||
| } | ||
| } | ||
|
|
||
| async function onChangePlanClose(success: boolean) { | ||
| const retry = success ? upgradeRequiredDialog.retryCallback : undefined; | ||
| showChangePlan = false; | ||
|
|
||
| if (retry) { | ||
| await retry(); | ||
| } | ||
| } |
| @@ -112,7 +109,9 @@ | |||
| (<TimeAgo value={nextBillingDate} />). | |||
|
|
|||
| {#if canChangePlan} | |||
| <A onclick={handleChangePlan}>Click here to change your plan or billing information.</A> | |||
| <A class="cursor-pointer" role="button" tabindex={0} onclick={() => (changePlanDialogOpen = true)} | |||
| >Click here to change your plan or billing information.</A | |||
| @@ -140,7 +137,9 @@ | |||
| (<TimeAgo value={nextBillingDate} />). | |||
|
|
|||
| {#if canChangePlan} | |||
| <A onclick={handleChangePlan}>Click here to change your plan or billing information.</A> | |||
| <A class="cursor-pointer" role="button" tabindex={0} onclick={() => (changePlanDialogOpen = true)} | |||
| >Click here to change your plan or billing information.</A | |||
| 2. **Potential orphaned Stripe customers** — If subscription creation fails after customer creation, a retry would create a duplicate Stripe customer. Mitigated by the low likelihood of this failure path. | ||
| 3. **N+1 price fetches in invoice view** — Each unique price ID in an invoice makes a separate Stripe API call. Mitigated by a per-request cache (`priceCache`). Most invoices have 1-3 distinct prices. |
…use async/await in stripe-provider, remove unnecessary comments and guards
Summary
ChangePlanDialogandStripeProvidergetBilling,changePlan,getInvoicesChanges