mirror of
https://github.com/bitwarden/server.git
synced 2024-11-21 12:05:42 +01:00
Add additional return properties ti providerSubscriptionResponse (#4159)
Signed-off-by: Cy Okeke <cokeke@bitwarden.com>
This commit is contained in:
parent
97b3f3e7ee
commit
fef34d845f
@ -1,4 +1,5 @@
|
||||
using Bit.Core.AdminConsole.Entities;
|
||||
using Bit.Core;
|
||||
using Bit.Core.AdminConsole.Entities;
|
||||
using Bit.Core.AdminConsole.Entities.Provider;
|
||||
using Bit.Core.AdminConsole.Enums.Provider;
|
||||
using Bit.Core.AdminConsole.Repositories;
|
||||
@ -30,7 +31,8 @@ public class ProviderBillingService(
|
||||
IProviderPlanRepository providerPlanRepository,
|
||||
IProviderRepository providerRepository,
|
||||
IStripeAdapter stripeAdapter,
|
||||
ISubscriberService subscriberService) : IProviderBillingService
|
||||
ISubscriberService subscriberService,
|
||||
IFeatureService featureService) : IProviderBillingService
|
||||
{
|
||||
public async Task AssignSeatsToClientOrganization(
|
||||
Provider provider,
|
||||
@ -248,6 +250,18 @@ public class ProviderBillingService(
|
||||
return null;
|
||||
}
|
||||
|
||||
DateTime? subscriptionSuspensionDate = null;
|
||||
DateTime? subscriptionUnpaidPeriodEndDate = null;
|
||||
if (featureService.IsEnabled(FeatureFlagKeys.AC1795_UpdatedSubscriptionStatusSection))
|
||||
{
|
||||
var (suspensionDate, unpaidPeriodEndDate) = await paymentService.GetSuspensionDateAsync(subscription);
|
||||
if (suspensionDate.HasValue && unpaidPeriodEndDate.HasValue)
|
||||
{
|
||||
subscriptionSuspensionDate = suspensionDate;
|
||||
subscriptionUnpaidPeriodEndDate = unpaidPeriodEndDate;
|
||||
}
|
||||
}
|
||||
|
||||
var providerPlans = await providerPlanRepository.GetByProviderId(provider.Id);
|
||||
|
||||
var configuredProviderPlans = providerPlans
|
||||
@ -257,7 +271,9 @@ public class ProviderBillingService(
|
||||
|
||||
return new ConsolidatedBillingSubscriptionDTO(
|
||||
configuredProviderPlans,
|
||||
subscription);
|
||||
subscription,
|
||||
subscriptionSuspensionDate,
|
||||
subscriptionUnpaidPeriodEndDate);
|
||||
}
|
||||
|
||||
public async Task ScaleSeats(
|
||||
|
@ -7,6 +7,10 @@ public record ConsolidatedBillingSubscriptionResponse(
|
||||
string Status,
|
||||
DateTime CurrentPeriodEndDate,
|
||||
decimal? DiscountPercentage,
|
||||
string CollectionMethod,
|
||||
DateTime? UnpaidPeriodEndDate,
|
||||
int? GracePeriod,
|
||||
DateTime? SuspensionDate,
|
||||
IEnumerable<ProviderPlanResponse> Plans)
|
||||
{
|
||||
private const string _annualCadence = "Annual";
|
||||
@ -15,7 +19,7 @@ public record ConsolidatedBillingSubscriptionResponse(
|
||||
public static ConsolidatedBillingSubscriptionResponse From(
|
||||
ConsolidatedBillingSubscriptionDTO consolidatedBillingSubscription)
|
||||
{
|
||||
var (providerPlans, subscription) = consolidatedBillingSubscription;
|
||||
var (providerPlans, subscription, suspensionDate, unpaidPeriodEndDate) = consolidatedBillingSubscription;
|
||||
|
||||
var providerPlansDTO = providerPlans
|
||||
.Select(providerPlan =>
|
||||
@ -31,11 +35,15 @@ public record ConsolidatedBillingSubscriptionResponse(
|
||||
cost,
|
||||
cadence);
|
||||
});
|
||||
|
||||
var gracePeriod = subscription.CollectionMethod == "charge_automatically" ? 14 : 30;
|
||||
return new ConsolidatedBillingSubscriptionResponse(
|
||||
subscription.Status,
|
||||
subscription.CurrentPeriodEnd,
|
||||
subscription.Customer?.Discount?.Coupon?.PercentOff,
|
||||
subscription.CollectionMethod,
|
||||
unpaidPeriodEndDate,
|
||||
gracePeriod,
|
||||
suspensionDate,
|
||||
providerPlansDTO);
|
||||
}
|
||||
}
|
||||
|
@ -4,4 +4,6 @@ namespace Bit.Core.Billing.Models;
|
||||
|
||||
public record ConsolidatedBillingSubscriptionDTO(
|
||||
List<ConfiguredProviderPlanDTO> ProviderPlans,
|
||||
Subscription Subscription);
|
||||
Subscription Subscription,
|
||||
DateTime? SuspensionDate,
|
||||
DateTime? UnpaidPeriodEndDate);
|
||||
|
@ -55,4 +55,5 @@ public interface IPaymentService
|
||||
int additionalServiceAccount);
|
||||
Task<bool> RisksSubscriptionFailure(Organization organization);
|
||||
Task<bool> HasSecretsManagerStandalone(Organization organization);
|
||||
Task<(DateTime?, DateTime?)> GetSuspensionDateAsync(Stripe.Subscription subscription);
|
||||
}
|
||||
|
@ -1820,6 +1820,47 @@ public class StripePaymentService : IPaymentService
|
||||
return customer?.Discount?.Coupon?.Id == SecretsManagerStandaloneDiscountId;
|
||||
}
|
||||
|
||||
public async Task<(DateTime?, DateTime?)> GetSuspensionDateAsync(Subscription subscription)
|
||||
{
|
||||
if (subscription.Status is not "past_due" && subscription.Status is not "unpaid")
|
||||
{
|
||||
return (null, null);
|
||||
}
|
||||
|
||||
var openInvoices = await _stripeAdapter.InvoiceSearchAsync(new InvoiceSearchOptions
|
||||
{
|
||||
Query = $"subscription:'{subscription.Id}' status:'open'"
|
||||
});
|
||||
|
||||
if (openInvoices.Count == 0)
|
||||
{
|
||||
return (null, null);
|
||||
}
|
||||
|
||||
var currentDate = subscription.TestClock?.FrozenTime ?? DateTime.UtcNow;
|
||||
|
||||
switch (subscription.CollectionMethod)
|
||||
{
|
||||
case "charge_automatically":
|
||||
{
|
||||
var firstOverdueInvoice = openInvoices
|
||||
.Where(invoice => invoice.PeriodEnd < currentDate && invoice.Attempted)
|
||||
.MinBy(invoice => invoice.Created);
|
||||
|
||||
return (firstOverdueInvoice?.Created.AddDays(14), firstOverdueInvoice?.PeriodEnd);
|
||||
}
|
||||
case "send_invoice":
|
||||
{
|
||||
var firstOverdueInvoice = openInvoices
|
||||
.Where(invoice => invoice.DueDate < currentDate)
|
||||
.MinBy(invoice => invoice.Created);
|
||||
|
||||
return (firstOverdueInvoice?.DueDate?.AddDays(30), firstOverdueInvoice?.PeriodEnd);
|
||||
}
|
||||
default: return (null, null);
|
||||
}
|
||||
}
|
||||
|
||||
private PaymentMethod GetLatestCardPaymentMethod(string customerId)
|
||||
{
|
||||
var cardPaymentMethods = _stripeAdapter.PaymentMethodListAutoPaging(
|
||||
@ -1962,45 +2003,4 @@ public class StripePaymentService : IPaymentService
|
||||
? subscriberName
|
||||
: subscriberName[..30];
|
||||
}
|
||||
|
||||
private async Task<(DateTime?, DateTime?)> GetSuspensionDateAsync(Subscription subscription)
|
||||
{
|
||||
if (subscription.Status is not "past_due" && subscription.Status is not "unpaid")
|
||||
{
|
||||
return (null, null);
|
||||
}
|
||||
|
||||
var openInvoices = await _stripeAdapter.InvoiceSearchAsync(new InvoiceSearchOptions
|
||||
{
|
||||
Query = $"subscription:'{subscription.Id}' status:'open'"
|
||||
});
|
||||
|
||||
if (openInvoices.Count == 0)
|
||||
{
|
||||
return (null, null);
|
||||
}
|
||||
|
||||
var currentDate = subscription.TestClock?.FrozenTime ?? DateTime.UtcNow;
|
||||
|
||||
switch (subscription.CollectionMethod)
|
||||
{
|
||||
case "charge_automatically":
|
||||
{
|
||||
var firstOverdueInvoice = openInvoices
|
||||
.Where(invoice => invoice.PeriodEnd < currentDate && invoice.Attempted)
|
||||
.MinBy(invoice => invoice.Created);
|
||||
|
||||
return (firstOverdueInvoice?.Created.AddDays(14), firstOverdueInvoice?.PeriodEnd);
|
||||
}
|
||||
case "send_invoice":
|
||||
{
|
||||
var firstOverdueInvoice = openInvoices
|
||||
.Where(invoice => invoice.DueDate < currentDate)
|
||||
.MinBy(invoice => invoice.Created);
|
||||
|
||||
return (firstOverdueInvoice?.DueDate?.AddDays(30), firstOverdueInvoice?.PeriodEnd);
|
||||
}
|
||||
default: return (null, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -282,9 +282,14 @@ public class ProviderBillingControllerTests
|
||||
Customer = new Customer { Discount = new Discount { Coupon = new Coupon { PercentOff = 10 } } }
|
||||
};
|
||||
|
||||
DateTime? SuspensionDate = new DateTime();
|
||||
DateTime? UnpaidPeriodEndDate = new DateTime();
|
||||
var gracePeriod = 30;
|
||||
var consolidatedBillingSubscription = new ConsolidatedBillingSubscriptionDTO(
|
||||
configuredProviderPlans,
|
||||
subscription);
|
||||
subscription,
|
||||
SuspensionDate,
|
||||
UnpaidPeriodEndDate);
|
||||
|
||||
sutProvider.GetDependency<IProviderBillingService>().GetConsolidatedBillingSubscription(provider)
|
||||
.Returns(consolidatedBillingSubscription);
|
||||
@ -298,6 +303,10 @@ public class ProviderBillingControllerTests
|
||||
Assert.Equal(response.Status, subscription.Status);
|
||||
Assert.Equal(response.CurrentPeriodEndDate, subscription.CurrentPeriodEnd);
|
||||
Assert.Equal(response.DiscountPercentage, subscription.Customer!.Discount!.Coupon!.PercentOff);
|
||||
Assert.Equal(response.CollectionMethod, subscription.CollectionMethod);
|
||||
Assert.Equal(response.UnpaidPeriodEndDate, UnpaidPeriodEndDate);
|
||||
Assert.Equal(response.GracePeriod, gracePeriod);
|
||||
Assert.Equal(response.SuspensionDate, SuspensionDate);
|
||||
|
||||
var teamsPlan = StaticStore.GetPlan(PlanType.TeamsMonthly);
|
||||
var providerTeamsPlan = response.Plans.FirstOrDefault(plan => plan.PlanName == teamsPlan.Name);
|
||||
|
Loading…
Reference in New Issue
Block a user