mirror of
https://github.com/bitwarden/server.git
synced 2025-05-22 20:11:04 -05:00
Merge branch 'main' into billing/PM-20485-PM-20486/plan-adapter-missing-mappings
This commit is contained in:
commit
9d8ca6b47e
4
.github/workflows/build.yml
vendored
4
.github/workflows/build.yml
vendored
@ -636,7 +636,9 @@ jobs:
|
|||||||
|
|
||||||
setup-ephemeral-environment:
|
setup-ephemeral-environment:
|
||||||
name: Setup Ephemeral Environment
|
name: Setup Ephemeral Environment
|
||||||
needs: build-docker
|
needs:
|
||||||
|
- build-artifacts
|
||||||
|
- build-docker
|
||||||
if: |
|
if: |
|
||||||
needs.build-artifacts.outputs.has_secrets == 'true'
|
needs.build-artifacts.outputs.has_secrets == 'true'
|
||||||
&& github.event_name == 'pull_request'
|
&& github.event_name == 'pull_request'
|
||||||
|
@ -2,13 +2,11 @@
|
|||||||
using Bit.Api.AdminConsole.Models.Request.Organizations;
|
using Bit.Api.AdminConsole.Models.Request.Organizations;
|
||||||
using Bit.Api.AdminConsole.Models.Response.Organizations;
|
using Bit.Api.AdminConsole.Models.Response.Organizations;
|
||||||
using Bit.Api.Models.Response;
|
using Bit.Api.Models.Response;
|
||||||
using Bit.Core;
|
|
||||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationDomains.Interfaces;
|
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationDomains.Interfaces;
|
||||||
using Bit.Core.Context;
|
using Bit.Core.Context;
|
||||||
using Bit.Core.Entities;
|
using Bit.Core.Entities;
|
||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Exceptions;
|
||||||
using Bit.Core.Repositories;
|
using Bit.Core.Repositories;
|
||||||
using Bit.Core.Utilities;
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
@ -137,7 +135,6 @@ public class OrganizationDomainController : Controller
|
|||||||
|
|
||||||
[AllowAnonymous]
|
[AllowAnonymous]
|
||||||
[HttpPost("domain/sso/verified")]
|
[HttpPost("domain/sso/verified")]
|
||||||
[RequireFeature(FeatureFlagKeys.VerifiedSsoDomainEndpoint)]
|
|
||||||
public async Task<VerifiedOrganizationDomainSsoDetailsResponseModel> GetVerifiedOrgDomainSsoDetailsAsync(
|
public async Task<VerifiedOrganizationDomainSsoDetailsResponseModel> GetVerifiedOrgDomainSsoDetailsAsync(
|
||||||
[FromBody] OrganizationDomainSsoDetailsRequestModel model)
|
[FromBody] OrganizationDomainSsoDetailsRequestModel model)
|
||||||
{
|
{
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
using Bit.Api.Billing.Models.Requests;
|
using Bit.Api.Billing.Models.Requests;
|
||||||
using Bit.Api.Billing.Models.Responses;
|
using Bit.Api.Billing.Models.Responses;
|
||||||
|
using Bit.Commercial.Core.Billing;
|
||||||
using Bit.Core;
|
using Bit.Core;
|
||||||
using Bit.Core.AdminConsole.Repositories;
|
using Bit.Core.AdminConsole.Repositories;
|
||||||
using Bit.Core.Billing.Models;
|
using Bit.Core.Billing.Models;
|
||||||
@ -148,13 +149,33 @@ public class ProviderBillingController(
|
|||||||
|
|
||||||
var providerPlans = await providerPlanRepository.GetByProviderId(provider.Id);
|
var providerPlans = await providerPlanRepository.GetByProviderId(provider.Id);
|
||||||
|
|
||||||
|
var getProviderPriceFromStripe = featureService.IsEnabled(FeatureFlagKeys.PM21383_GetProviderPriceFromStripe);
|
||||||
|
|
||||||
var configuredProviderPlans = await Task.WhenAll(providerPlans.Select(async providerPlan =>
|
var configuredProviderPlans = await Task.WhenAll(providerPlans.Select(async providerPlan =>
|
||||||
{
|
{
|
||||||
var plan = await pricingClient.GetPlanOrThrow(providerPlan.PlanType);
|
var plan = await pricingClient.GetPlanOrThrow(providerPlan.PlanType);
|
||||||
|
|
||||||
|
decimal unitAmount;
|
||||||
|
|
||||||
|
if (getProviderPriceFromStripe)
|
||||||
|
{
|
||||||
|
var priceId = ProviderPriceAdapter.GetPriceId(provider, subscription, plan.Type);
|
||||||
|
var price = await stripeAdapter.PriceGetAsync(priceId);
|
||||||
|
|
||||||
|
unitAmount = price.UnitAmountDecimal.HasValue
|
||||||
|
? price.UnitAmountDecimal.Value / 100M
|
||||||
|
: plan.PasswordManager.ProviderPortalSeatPrice;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
unitAmount = plan.PasswordManager.ProviderPortalSeatPrice;
|
||||||
|
}
|
||||||
|
|
||||||
return new ConfiguredProviderPlan(
|
return new ConfiguredProviderPlan(
|
||||||
providerPlan.Id,
|
providerPlan.Id,
|
||||||
providerPlan.ProviderId,
|
providerPlan.ProviderId,
|
||||||
plan,
|
plan,
|
||||||
|
unitAmount,
|
||||||
providerPlan.SeatMinimum ?? 0,
|
providerPlan.SeatMinimum ?? 0,
|
||||||
providerPlan.PurchasedSeats ?? 0,
|
providerPlan.PurchasedSeats ?? 0,
|
||||||
providerPlan.AllocatedSeats ?? 0);
|
providerPlan.AllocatedSeats ?? 0);
|
||||||
|
@ -35,7 +35,7 @@ public record ProviderSubscriptionResponse(
|
|||||||
.Select(providerPlan =>
|
.Select(providerPlan =>
|
||||||
{
|
{
|
||||||
var plan = providerPlan.Plan;
|
var plan = providerPlan.Plan;
|
||||||
var cost = (providerPlan.SeatMinimum + providerPlan.PurchasedSeats) * plan.PasswordManager.ProviderPortalSeatPrice;
|
var cost = (providerPlan.SeatMinimum + providerPlan.PurchasedSeats) * providerPlan.Price;
|
||||||
var cadence = plan.IsAnnual ? _annualCadence : _monthlyCadence;
|
var cadence = plan.IsAnnual ? _annualCadence : _monthlyCadence;
|
||||||
return new ProviderPlanResponse(
|
return new ProviderPlanResponse(
|
||||||
plan.Name,
|
plan.Name,
|
||||||
|
@ -6,6 +6,7 @@ public record ConfiguredProviderPlan(
|
|||||||
Guid Id,
|
Guid Id,
|
||||||
Guid ProviderId,
|
Guid ProviderId,
|
||||||
Plan Plan,
|
Plan Plan,
|
||||||
|
decimal Price,
|
||||||
int SeatMinimum,
|
int SeatMinimum,
|
||||||
int PurchasedSeats,
|
int PurchasedSeats,
|
||||||
int AssignedSeats);
|
int AssignedSeats);
|
||||||
|
@ -150,6 +150,7 @@ public static class FeatureFlagKeys
|
|||||||
public const string UseOrganizationWarningsService = "use-organization-warnings-service";
|
public const string UseOrganizationWarningsService = "use-organization-warnings-service";
|
||||||
public const string PM20322_AllowTrialLength0 = "pm-20322-allow-trial-length-0";
|
public const string PM20322_AllowTrialLength0 = "pm-20322-allow-trial-length-0";
|
||||||
public const string PM21092_SetNonUSBusinessUseToReverseCharge = "pm-21092-set-non-us-business-use-to-reverse-charge";
|
public const string PM21092_SetNonUSBusinessUseToReverseCharge = "pm-21092-set-non-us-business-use-to-reverse-charge";
|
||||||
|
public const string PM21383_GetProviderPriceFromStripe = "pm-21383-get-provider-price-from-stripe";
|
||||||
|
|
||||||
/* Data Insights and Reporting Team */
|
/* Data Insights and Reporting Team */
|
||||||
public const string RiskInsightsCriticalApplication = "pm-14466-risk-insights-critical-application";
|
public const string RiskInsightsCriticalApplication = "pm-14466-risk-insights-critical-application";
|
||||||
|
@ -57,4 +57,5 @@ public interface IStripeAdapter
|
|||||||
Task<SetupIntent> SetupIntentGet(string id, SetupIntentGetOptions options = null);
|
Task<SetupIntent> SetupIntentGet(string id, SetupIntentGetOptions options = null);
|
||||||
Task SetupIntentVerifyMicroDeposit(string id, SetupIntentVerifyMicrodepositsOptions options);
|
Task SetupIntentVerifyMicroDeposit(string id, SetupIntentVerifyMicrodepositsOptions options);
|
||||||
Task<List<Stripe.TestHelpers.TestClock>> TestClockListAsync();
|
Task<List<Stripe.TestHelpers.TestClock>> TestClockListAsync();
|
||||||
|
Task<Price> PriceGetAsync(string id, PriceGetOptions options = null);
|
||||||
}
|
}
|
||||||
|
@ -283,4 +283,7 @@ public class StripeAdapter : IStripeAdapter
|
|||||||
}
|
}
|
||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Task<Price> PriceGetAsync(string id, PriceGetOptions options = null)
|
||||||
|
=> _priceService.GetAsync(id, options);
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
using Bit.Api.Billing.Controllers;
|
using Bit.Api.Billing.Controllers;
|
||||||
using Bit.Api.Billing.Models.Requests;
|
using Bit.Api.Billing.Models.Requests;
|
||||||
using Bit.Api.Billing.Models.Responses;
|
using Bit.Api.Billing.Models.Responses;
|
||||||
|
using Bit.Commercial.Core.Billing;
|
||||||
|
using Bit.Core;
|
||||||
using Bit.Core.AdminConsole.Entities.Provider;
|
using Bit.Core.AdminConsole.Entities.Provider;
|
||||||
using Bit.Core.AdminConsole.Enums.Provider;
|
using Bit.Core.AdminConsole.Enums.Provider;
|
||||||
using Bit.Core.AdminConsole.Repositories;
|
using Bit.Core.AdminConsole.Repositories;
|
||||||
@ -285,6 +287,19 @@ public class ProviderBillingControllerTests
|
|||||||
Discount = new Discount { Coupon = new Coupon { PercentOff = 10 } },
|
Discount = new Discount { Coupon = new Coupon { PercentOff = 10 } },
|
||||||
TaxIds = new StripeList<TaxId> { Data = [new TaxId { Value = "123456789" }] }
|
TaxIds = new StripeList<TaxId> { Data = [new TaxId { Value = "123456789" }] }
|
||||||
},
|
},
|
||||||
|
Items = new StripeList<SubscriptionItem>
|
||||||
|
{
|
||||||
|
Data = [
|
||||||
|
new SubscriptionItem
|
||||||
|
{
|
||||||
|
Price = new Price { Id = ProviderPriceAdapter.MSP.Active.Enterprise }
|
||||||
|
},
|
||||||
|
new SubscriptionItem
|
||||||
|
{
|
||||||
|
Price = new Price { Id = ProviderPriceAdapter.MSP.Active.Teams }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
Status = "unpaid",
|
Status = "unpaid",
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -330,11 +345,21 @@ public class ProviderBillingControllerTests
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
sutProvider.GetDependency<IFeatureService>().IsEnabled(FeatureFlagKeys.PM21383_GetProviderPriceFromStripe)
|
||||||
|
.Returns(true);
|
||||||
|
|
||||||
sutProvider.GetDependency<IProviderPlanRepository>().GetByProviderId(provider.Id).Returns(providerPlans);
|
sutProvider.GetDependency<IProviderPlanRepository>().GetByProviderId(provider.Id).Returns(providerPlans);
|
||||||
|
|
||||||
foreach (var providerPlan in providerPlans)
|
foreach (var providerPlan in providerPlans)
|
||||||
{
|
{
|
||||||
sutProvider.GetDependency<IPricingClient>().GetPlanOrThrow(providerPlan.PlanType).Returns(StaticStore.GetPlan(providerPlan.PlanType));
|
var plan = StaticStore.GetPlan(providerPlan.PlanType);
|
||||||
|
sutProvider.GetDependency<IPricingClient>().GetPlanOrThrow(providerPlan.PlanType).Returns(plan);
|
||||||
|
var priceId = ProviderPriceAdapter.GetPriceId(provider, subscription, providerPlan.PlanType);
|
||||||
|
sutProvider.GetDependency<IStripeAdapter>().PriceGetAsync(priceId)
|
||||||
|
.Returns(new Price
|
||||||
|
{
|
||||||
|
UnitAmountDecimal = plan.PasswordManager.ProviderPortalSeatPrice * 100
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
var result = await sutProvider.Sut.GetSubscriptionAsync(provider.Id);
|
var result = await sutProvider.Sut.GetSubscriptionAsync(provider.Id);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user