From 65a88df8f058b3f4473a88b457132d6903aad15a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 31 May 2026 23:49:44 +0000 Subject: [PATCH 1/3] Initial plan From bec41b7b0e89c6862addec925a71f0f818128652 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 31 May 2026 23:56:37 +0000 Subject: [PATCH 2/3] Align Stripe plan changes to monthly billing anchor Co-authored-by: niemyjski <1020579+niemyjski@users.noreply.github.com> --- .../Controllers/OrganizationController.cs | 30 +++++++++++++++++-- .../OrganizationControllerTests.cs | 7 +++++ 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/src/Exceptionless.Web/Controllers/OrganizationController.cs b/src/Exceptionless.Web/Controllers/OrganizationController.cs index 7d11666508..52ef2b64bf 100644 --- a/src/Exceptionless.Web/Controllers/OrganizationController.cs +++ b/src/Exceptionless.Web/Controllers/OrganizationController.cs @@ -505,7 +505,15 @@ public async Task> ChangePlanAsync( var subscriptionOptions = new SubscriptionCreateOptions { Customer = customer.Id, - Items = [new SubscriptionItemOptions { Price = model.PlanId }] + Items = [new SubscriptionItemOptions { Price = model.PlanId }], + BillingCycleAnchorConfig = new SubscriptionBillingCycleAnchorConfigOptions + { + DayOfMonth = 1, + Hour = 0, + Minute = 0, + Second = 0 + }, + ProrationBehavior = "create_prorations" }; if (isPaymentMethod) @@ -522,8 +530,24 @@ public async Task> ChangePlanAsync( // Existing customer: update (or create) their Stripe subscription and optionally swap payment method. else { - var update = new SubscriptionUpdateOptions { Items = [] }; - var create = new SubscriptionCreateOptions { Customer = organization.StripeCustomerId, Items = [] }; + var update = new SubscriptionUpdateOptions + { + Items = [], + ProrationBehavior = "create_prorations" + }; + var create = new SubscriptionCreateOptions + { + Customer = organization.StripeCustomerId, + Items = [], + BillingCycleAnchorConfig = new SubscriptionBillingCycleAnchorConfigOptions + { + DayOfMonth = 1, + Hour = 0, + Minute = 0, + Second = 0 + }, + ProrationBehavior = "create_prorations" + }; bool cardUpdated = false; var customerUpdateOptions = new CustomerUpdateOptions { Description = organization.Name }; diff --git a/tests/Exceptionless.Tests/Controllers/OrganizationControllerTests.cs b/tests/Exceptionless.Tests/Controllers/OrganizationControllerTests.cs index 93c4faf491..aef7044078 100644 --- a/tests/Exceptionless.Tests/Controllers/OrganizationControllerTests.cs +++ b/tests/Exceptionless.Tests/Controllers/OrganizationControllerTests.cs @@ -1059,6 +1059,12 @@ public async Task ChangePlanAsync_NewCustomerCreatesStripeCustomerAndSubscriptio var item = Assert.Single(subscription.Items); Assert.Equal(_plans.SmallPlan.Id, item.Price); Assert.Equal("coupon_10", Assert.Single(subscription.Discounts).Coupon); + Assert.Equal("create_prorations", subscription.ProrationBehavior); + Assert.NotNull(subscription.BillingCycleAnchorConfig); + Assert.Equal(1, subscription.BillingCycleAnchorConfig.DayOfMonth); + Assert.Equal(0, subscription.BillingCycleAnchorConfig.Hour); + Assert.Equal(0, subscription.BillingCycleAnchorConfig.Minute); + Assert.Equal(0, subscription.BillingCycleAnchorConfig.Second); var organization = await _organizationRepository.GetByIdAsync(SampleDataService.FREE_ORG_ID); Assert.NotNull(organization); @@ -1112,6 +1118,7 @@ public async Task ChangePlanAsync_ExistingCustomerUpdatesPaymentMethodAndSubscri Assert.Equal("si_active", item.Id); Assert.Equal(_plans.SmallPlan.Id, item.Price); Assert.Equal("coupon_10", Assert.Single(updatedSubscription.Options.Discounts).Coupon); + Assert.Equal("create_prorations", updatedSubscription.Options.ProrationBehavior); Assert.Empty(StripeBillingClient.CreatedSubscriptionOptions); var organization = await _organizationRepository.GetByIdAsync(SampleDataService.FREE_ORG_ID); From 0f1cd7575a8e7a17be94f8fd337a349288bbd07d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 1 Jun 2026 00:00:12 +0000 Subject: [PATCH 3/3] Deduplicate monthly Stripe anchor configuration Co-authored-by: niemyjski <1020579+niemyjski@users.noreply.github.com> --- .../Controllers/OrganizationController.cs | 27 +++++++++---------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/src/Exceptionless.Web/Controllers/OrganizationController.cs b/src/Exceptionless.Web/Controllers/OrganizationController.cs index 52ef2b64bf..c20f6ad05f 100644 --- a/src/Exceptionless.Web/Controllers/OrganizationController.cs +++ b/src/Exceptionless.Web/Controllers/OrganizationController.cs @@ -506,13 +506,7 @@ public async Task> ChangePlanAsync( { Customer = customer.Id, Items = [new SubscriptionItemOptions { Price = model.PlanId }], - BillingCycleAnchorConfig = new SubscriptionBillingCycleAnchorConfigOptions - { - DayOfMonth = 1, - Hour = 0, - Minute = 0, - Second = 0 - }, + BillingCycleAnchorConfig = CreateMonthlyBillingCycleAnchorConfig(), ProrationBehavior = "create_prorations" }; @@ -539,13 +533,7 @@ public async Task> ChangePlanAsync( { Customer = organization.StripeCustomerId, Items = [], - BillingCycleAnchorConfig = new SubscriptionBillingCycleAnchorConfigOptions - { - DayOfMonth = 1, - Hour = 0, - Minute = 0, - Second = 0 - }, + BillingCycleAnchorConfig = CreateMonthlyBillingCycleAnchorConfig(), ProrationBehavior = "create_prorations" }; bool cardUpdated = false; @@ -634,6 +622,17 @@ await Task.WhenAll( return Ok(new ChangePlanResult { Success = true }); } + private static SubscriptionBillingCycleAnchorConfigOptions CreateMonthlyBillingCycleAnchorConfig() + { + return new SubscriptionBillingCycleAnchorConfigOptions + { + DayOfMonth = 1, + Hour = 0, + Minute = 0, + Second = 0 + }; + } + /// /// Add user ///