diff --git a/src/Exceptionless.Web/Controllers/OrganizationController.cs b/src/Exceptionless.Web/Controllers/OrganizationController.cs index 7d11666508..c20f6ad05f 100644 --- a/src/Exceptionless.Web/Controllers/OrganizationController.cs +++ b/src/Exceptionless.Web/Controllers/OrganizationController.cs @@ -505,7 +505,9 @@ public async Task> ChangePlanAsync( var subscriptionOptions = new SubscriptionCreateOptions { Customer = customer.Id, - Items = [new SubscriptionItemOptions { Price = model.PlanId }] + Items = [new SubscriptionItemOptions { Price = model.PlanId }], + BillingCycleAnchorConfig = CreateMonthlyBillingCycleAnchorConfig(), + ProrationBehavior = "create_prorations" }; if (isPaymentMethod) @@ -522,8 +524,18 @@ 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 = CreateMonthlyBillingCycleAnchorConfig(), + ProrationBehavior = "create_prorations" + }; bool cardUpdated = false; var customerUpdateOptions = new CustomerUpdateOptions { Description = organization.Name }; @@ -610,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 /// 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);