Repository navigation
fix: expose discounted Stripe billed amount per period - #819
Conversation
There was a problem hiding this comment.
Important
The new invoice-preview dependency can prevent account-deletion jobs from stopping Stripe renewal, leaving subscriptions able to charge after deletion is requested.
Reviewed changes: Reviewed the billed-amount addition, Stripe preview semantics, billing response mapping, OpenAPI updates, lifecycle callers, and existing client compatibility.
- Stripe amount lookup: Previews the requested subscription's next invoice and maps
AmountDue, preserving zero and treatinginvoice_upcoming_noneas absence. - Response contract: Appends nullable
billedAmountPerPeriodwithout changing the existing plan price or other shipped fields. - API documentation: Declares the billing response DTO and adds its schemas to both OpenAPI documents.
- Behavior tests: Covers mapping, missing previews, provider failures, retries, and cancellation; all 73 focused tests passed. A separate temporary unit reproducer confirmed the cancellation regression described inline and was removed afterward.
gpt-6.1-sol | 𝕏
|
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes: Reviewed d5dd870c since the prior Pullfrog review, checked the resolved discussion against the current code, and found no remaining blocking issues.
- Isolated invoice previews: Removed the preview dependency from subscription-state reads used by deletion cancellation, restoration and owed-cancellation jobs.
- Refined recurring amounts: Calculated the recurring amount from subscription-line subtotals minus Stripe-calculated discounts, excluding prorations, one-time charges and customer balance.
- Preserved billing availability: Returned a nullable amount for unavailable or unusable previews while retaining retries and caller cancellation propagation; skipped preview calls for unsupported subscription states.
- Protected provider boundaries: Rejected Play subscribers with historical Stripe identifiers and preserved the append-only response contract for existing clients.
- Verified regressions: Passed all 85 focused infrastructure tests and 34 focused application tests, with zero failures or skipped tests.
gpt-6.1-sol | 𝕏
…e sub-habits and referral Pro days (#746) (#829) * fix: preserve yearly streak gap repairs through recalculation (#818) * fix: preserve yearly streak gap repairs through recalculation * fix: isolate yearly streak repair history window (cherry picked from commit 90c5320) * fix: expose discounted Stripe billed amount per period (#819) * fix: expose discounted Stripe billed amount per period * fix: isolate optional recurring Stripe billing preview (cherry picked from commit 0460a54) * Make sub-habits free across every creation path (ORB-231) (#824) * feat: make sub-habits available to every plan (ORB-231) * test: track free sub-habits in migration model expectations * fix: preserve Pro slip alerts when creating free sub-habits (cherry picked from commit c9d22a6) * Grant atomic referral Pro days (ORB-22) (#823) * feat: grant atomic referral Pro days (ORB-22) * fix: preserve referral transaction and billing context * fix: preserve bonus Pro access across provider changes (cherry picked from commit 7b4e9af) * test: retain combined migration assertions after main carry * fix: include one-time coupons in billed amount preview (#830) (cherry picked from commit 377eca5)
* fix: expose discounted Stripe billed amount per period * fix: isolate optional recurring Stripe billing preview (cherry picked from commit 0460a54)




Closes thomasluizon/orbit-tickets#385
Couponed subscribers need the Stripe charge amount alongside the existing plan price. Billing details now append optional, nullable
billedAmountPerPeriod, in minor units of the subscription currency.amountPerPeriodretains its existing meaning.StripeBillingServicepreviews the next invoice for the requested subscription and maps Stripe'sAmountDue. Stripe owns discounts, duration, rounding, taxes and credits.invoice_upcoming_noneyields a null billed amount while preserving the subscription. Other preview failures retain the billing-unavailable response, transient failures retry, and caller cancellation propagates.The value passes through
BillingSubscriptionDetails,GetBillingDetailsQueryHandler, andBillingDetailsResponse. The billing controller now declares its response DTO because its generated success response previously had no schema. Both OpenAPI documents include the optional field. The architecture map was regenerated locally. Behavior tests exercise the real service and query handler with SDK subscription, referral coupon and invoice objects; existing service and deletion tests mock the added invoice call.The paired UI ticket thomasluizon/orbit-tickets#386 consumes this field after the API deploys.
Stripe interface evidence
The project and restored package use Stripe.net 52.4.2. Its installed
~/.nuget/packages/stripe.net/52.4.2/stripe.net.nuspecidentifies source commitf4f1c8ce704825d7d7f00b4c9d848a2d028757eb. I downloaded that exact source revision and read the declarations before using the members:Task<Invoice>from/v1/invoices/create_previewand accepts options, request options and a cancellation token.Subscription,PreviewMode,DiscountsandSubscriptionDetails. OnlySubscriptionis supplied, so Stripe inherits applicable discounts.longfinal charge amount and the currency field. These shapes also appear in the installedlib/net9.0/Stripe.net.xmldocumentation.Subscription,SubscriptionItem,Price,PriceRecurring,StripeList<T>,Discount,DiscountSourceandCouponentity sources, includingDiscount.Source.Coupon,PercentOffandDuration.The installed 52.3.0 package named in the ticket identifies commit
f4cd0d3e3e8fdab648b61711bafdcd6e897a3ec7; I also checked itsInvoiceService.CreatePreviewAsync,InvoiceCreatePreviewOptions.SubscriptionandInvoice.AmountDuesource declarations. The dependency remains at the version already selected by the repository.To reproduce the source inspection, read the installed package's nuspec repository commit, download
https://codeload.github.com/stripe/stripe-dotnet/tar.gz/<commit>, and inspect the paths linked above. No live customer records or Stripe credentials are needed.Assumptions
recurringpreview, which excludes one-time coupons and does not support cancellations, trials or pauses. The preview reference documents these differences.BilledAmountPerPeriod; rejected renaming the append-only contract.AmountDue, which includes unrelated billing adjustments.Test evidence
dotnet test tests/Orbit.Infrastructure.Tests --filter FullyQualifiedName~StripeBillingServiceTestspassed all 43 unchanged tests with the defect present.dotnet test tests/Orbit.Application.Tests --filter FullyQualifiedName~GetBillingDetailsQueryHandlerMappingTestspassed all 4 unchanged mapping tests with the defect present.dotnet test tests/Orbit.Infrastructure.Tests --filter FullyQualifiedName~StripeBillingDetailsTestscompiled and failed all 11 strengthened behavior cases.Handle_MapsStripeInvoiceAmountWithoutChangingPlanPriceand the no-invoice cases failed becauseBilledAmountPerPeriodwas absent; failure, retry and cancellation cases failed because the preview was never called.dotnet test tests/Orbit.Infrastructure.Tests --no-build --filter 'FullyQualifiedName~StripeBillingDetailsTests|FullyQualifiedName~StripeBillingServiceTests|FullyQualifiedName~DeletionCancellationPersistenceTests'passed all 73 cases, including all 11 new cases. The unchanged application mapping tests also passed all 4 cases.dotnet build Orbit.slnx: zero errors.dotnet test tests/Orbit.Domain.Tests --no-build: 767 passed, exit 0.dotnet test tests/Orbit.Application.Tests --no-build: 5,224 passed, exit 0.dotnet test tests/Orbit.Infrastructure.Tests --no-build: 3,782 passed, exit 0.dotnet test tests/Orbit.Analyzers.Tests --no-build: 40 passed, exit 0.oasdiff breaking origin/main:src/Orbit.Api/<document> src/Orbit.Api/<document> --fail-on ERRwith no breaking changes.node tools/arch-map.mjscompleted. Generated architecture files remain local.dotnet test tests/Orbit.Infrastructure.Tests --filter FullyQualifiedName~StripeBillingDetailsTests --no-restorepassed all 11 existing tests with the defects present.dotnet test tests/Orbit.Infrastructure.Tests --no-restore --filter FullyQualifiedName~StripeBillingDetailsTestsfailed 15 tests, including the strengthened availability and recurring-amount regressions.GetSubscriptionDetails_DoesNotRequestInvoicePreviewseparately failed before the fix because it observed one preview call.dotnet test tests/Orbit.Application.Tests --no-restore --filter FullyQualifiedName~GetBillingDetailsQueryHandlerTests.dotnet test tests/Orbit.Infrastructure.Tests --no-build --filter FullyQualifiedName~StripeBillingDetailsTests(23 tests) anddotnet test tests/Orbit.Application.Tests --no-build --filter FullyQualifiedName~GetBillingDetailsQueryHandler(9 tests).dotnet build Orbit.slnx: exit 0, zero errors.dotnet test tests/Orbit.Domain.Tests(767),dotnet test tests/Orbit.Application.Tests(5,226), anddotnet test tests/Orbit.Infrastructure.Tests(3,794).oasdiff breaking origin/main:src/Orbit.Api/openapi.json src/Orbit.Api/openapi.json --fail-on ERR: exit 0, no breaking changes.Stripe evidence: the installed dependency is 52.4.2, confirmed through its nuspec and resolved assets. Inspected its pinned source and cross-checked the relevant entity/service members against 52.3.0. The implementation reads preview options, invoice currency/lines, pagination, recurring parent details, line subtotal, and Stripe-calculated discount amounts. Installed SDK source, 52.3.0 source.
Checked Stripe’s current references on 2026-10-20. Recurring previews exclude one-time billing effects and do not support trials, cancellations, or pauses. Those cases return null. Preview reference.
Confirmed
error.codeparsing in the installed SDK andinvoice_upcoming_nonein Stripe’s reference. Its HTTP status remains unconfirmed; the fixture leaves it unset. SDK error parser, Error reference.Manual steps
The production API release of the go-live runbook carries this change; the run verifies
billedAmountPerPeriodonGET /api/subscriptions/billingafter it, before the paired UI ticket thomasluizon/orbit-tickets#386 ships.No new environment variables, secrets, dashboard settings, migrations, or backfills are required.