Skip to content

fix: include one-time coupons in billed amount preview - #830

Merged
thomasluizon merged 1 commit into
mainfrom
thomasluizon/ticket-1581-billed-coupon
Oct 20, 2026
Merged

thomasluizon merged 1 commit into
mainfrom
thomasluizon/ticket-1581-billed-coupon

Conversation

@thomasluizon

Copy link
Copy Markdown
Owner

Fixes thomasluizon/orbit-tickets#1581

An unused 10% referral coupon now makes billedAmountPerPeriod 900 on a 1000-unit subscription. After consumption it returns to 1000, while amountPerPeriod remains the plan price.

src/Orbit.Infrastructure/Services/StripeBillingService.cs requests preview_mode=next so Stripe includes applicable one-time coupons. The existing filter still sums only non-prorated lines for the requested subscription, subtracts Stripe's line discounts, excludes one-time charges and customer balance, and returns null when a preview is unavailable or incomplete. The optional response field and its type are unchanged.

tests/Orbit.Infrastructure.Tests/Services/StripeBillingDetailsTests.cs covers once and forever coupons using the referral percentage from AppConstants, coupon consumption, no coupon, and customer credit and debit balances. Existing proration, one-time item, other-subscription, and unavailable-preview cases remain. The coupon fixture varies its response with preview mode, so it cannot give a one-time discount to a recurring preview.

External interface evidence

Read Stripe.net source at commit f4f1c8ce704825d7d7f00b4c9d848a2d028757eb, identified by the installed Stripe.net 52.4.2 package's stripe.net.nuspec repository entry. Reproduce by inspecting that package entry and opening these pinned source files:

Test evidence

Before editing the test or implementation:

dotnet test tests/Orbit.Infrastructure.Tests --filter FullyQualifiedName~Handle_ReferralDiscountChangesRecurringAmountWithoutChangingPlanPrice --logger 'console;verbosity=minimal'

Exit 0, 1 passed with the defect present. The original forever-only fixture missed the defect.

After strengthening the tests, with the implementation still using recurring mode:

dotnet test tests/Orbit.Infrastructure.Tests --filter FullyQualifiedName~StripeBillingDetailsTests --logger 'console;verbosity=minimal'

Exit 1, 3 failed and 24 passed. Handle_ReferralDiscountChangesRecurringAmountWithoutChangingPlanPrice(duration: "once") and Handle_ConsumedReferralCouponRestoresNormalBilledAmount expected 900 but received 1000. The forever case also rejected the recurring request mode. After changing the implementation to next mode, the same command exited 0 with all 27 passing.

Verification after the commit:

  • dotnet build Orbit.slnx: exit 0, zero errors. Existing dependency and obsolete API warnings remain. OpenAPI regeneration produced no tracked changes.
  • dotnet test tests/Orbit.Infrastructure.Tests --no-build --filter FullyQualifiedName~StripeBillingDetailsTests: exit 0, 27 passed.
  • dotnet test tests/Orbit.Domain.Tests --no-build: exit 0, 789 passed.
  • dotnet test tests/Orbit.Application.Tests --no-build: exit 0, 5313 passed.
  • dotnet test tests/Orbit.Infrastructure.Tests --no-build: exit 0, 3853 passed.
  • dotnet test Orbit.slnx --no-build: exit 0, all 9995 tests passed across the four test projects, including 40 analyzer tests.
  • Commit hooks passed formatting, dash, timeless text, root allowlist, and suppression checks.

@pullfrog pullfrog 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.

✅ No new issues found.

Reviewed changes Reviewed the complete billing-preview change and regression tests, verified Stripe's documented preview semantics, and checked the consumer schema; all 27 focused billing tests passed.

  • One-time coupon preview: Uses preview_mode=next so applicable one-time referral coupons affect BilledAmountPerPeriod, while preserving the plan price and existing subscription-line exclusions.
  • Regression coverage: Exercises once and forever coupons, restoration after coupon consumption, and independence from customer credit or debit balances, alongside the existing filtering and unavailable-preview cases.

Validation used mocked Stripe clients, not a live Stripe request.

Pullfrog  | View workflow run | Using gpt-6.1-sol | 𝕏

@sonarqubecloud

Copy link
Copy Markdown

@thomasluizon
thomasluizon merged commit 377eca5 into main Oct 20, 2026
22 checks passed
@thomasluizon
thomasluizon deleted the thomasluizon/ticket-1581-billed-coupon branch October 20, 2026 07:59
thomasluizon added a commit that referenced this pull request Oct 20, 2026
thomasluizon added a commit that referenced this pull request Oct 20, 2026
…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)
thomasluizon added a commit that referenced this pull request Oct 20, 2026
Carry the redesign-adapted fixes from a80cf56 for #818, #819, #823, #824 and #830 without replaying their main implementations. Preserve ordinary merge ancestry and the production keep-list for thomasluizon/orbit-tickets#1579.
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