mirror of
https://github.com/bitwarden/server
synced 2026-03-01 02:41:33 +00:00
[PM-29604] [PM-29605] [PM-29606] Support premium subscription page redesign (#6821)
* feat(get-subscription): Add EnumMemberJsonConverter * feat(get-subscription): Add BitwardenDiscount model * feat(get-subscription): Add Cart model * feat(get-subscription): Add Storage model * feat(get-subscription): Add BitwardenSubscription model * feat(get-subscription): Add DiscountExtensions * feat(get-subscription): Add error code to StripeConstants * feat(get-subscription): Add GetBitwardenSubscriptionQuery * feat(get-subscription): Expose GET /account/billing/vnext/subscription * feat(reinstate-subscription): Add ReinstateSubscriptionCommand * feat(reinstate-subscription): Expose POST /account/billing/vnext/subscription/reinstate * feat(pay-with-paypal-immediately): Add SubscriberId union * feat(pay-with-paypal-immediately): Add BraintreeService with PayInvoice method * feat(pay-with-paypal-immediately): Pay PayPal invoice immediately when starting premium subscription * feat(pay-with-paypal-immediately): Pay invoice with Braintree on invoice.created for subscription cycles only * fix(update-storage): Always invoice for premium storage update * fix(update-storage): Move endpoint to subscription path * docs: Note FF removal POIs * (format): Run dotnet format
This commit is contained in:
@@ -22,7 +22,7 @@ public class AccountsController(
|
||||
IFeatureService featureService,
|
||||
ILicensingService licensingService) : Controller
|
||||
{
|
||||
// TODO: Migrate to Query / AccountBillingVNextController as part of Premium -> Organization upgrade work.
|
||||
// TODO: Remove with deletion of pm-29594-update-individual-subscription-page
|
||||
[HttpGet("subscription")]
|
||||
public async Task<SubscriptionResponseModel> GetSubscriptionAsync(
|
||||
[FromServices] GlobalSettings globalSettings,
|
||||
@@ -61,7 +61,7 @@ public class AccountsController(
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Migrate to Command / AccountBillingVNextController as PUT /account/billing/vnext/subscription
|
||||
// TODO: Remove with deletion of pm-29594-update-individual-subscription-page
|
||||
[HttpPost("storage")]
|
||||
[SelfHosted(NotSelfHostedOnly = true)]
|
||||
public async Task<PaymentResponseModel> PostStorageAsync([FromBody] StorageRequestModel model)
|
||||
@@ -118,7 +118,7 @@ public class AccountsController(
|
||||
user.IsExpired());
|
||||
}
|
||||
|
||||
// TODO: Migrate to Command / AccountBillingVNextController as POST /account/billing/vnext/subscription/reinstate
|
||||
// TODO: Remove with deletion of pm-29594-update-individual-subscription-page
|
||||
[HttpPost("reinstate-premium")]
|
||||
[SelfHosted(NotSelfHostedOnly = true)]
|
||||
public async Task PostReinstateAsync()
|
||||
@@ -131,10 +131,4 @@ public class AccountsController(
|
||||
|
||||
await userService.ReinstatePremiumAsync(user);
|
||||
}
|
||||
|
||||
private async Task<IEnumerable<Guid>> GetOrganizationIdsClaimingUserAsync(Guid userId)
|
||||
{
|
||||
var organizationsClaimingUser = await userService.GetOrganizationsClaimingUserAsync(userId);
|
||||
return organizationsClaimingUser.Select(o => o.Id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@ using Bit.Core.Billing.Licenses.Queries;
|
||||
using Bit.Core.Billing.Payment.Commands;
|
||||
using Bit.Core.Billing.Payment.Queries;
|
||||
using Bit.Core.Billing.Premium.Commands;
|
||||
using Bit.Core.Billing.Subscriptions.Commands;
|
||||
using Bit.Core.Billing.Subscriptions.Queries;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Utilities;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
@@ -21,9 +23,11 @@ namespace Bit.Api.Billing.Controllers.VNext;
|
||||
public class AccountBillingVNextController(
|
||||
ICreateBitPayInvoiceForCreditCommand createBitPayInvoiceForCreditCommand,
|
||||
ICreatePremiumCloudHostedSubscriptionCommand createPremiumCloudHostedSubscriptionCommand,
|
||||
IGetBitwardenSubscriptionQuery getBitwardenSubscriptionQuery,
|
||||
IGetCreditQuery getCreditQuery,
|
||||
IGetPaymentMethodQuery getPaymentMethodQuery,
|
||||
IGetUserLicenseQuery getUserLicenseQuery,
|
||||
IReinstateSubscriptionCommand reinstateSubscriptionCommand,
|
||||
IUpdatePaymentMethodCommand updatePaymentMethodCommand,
|
||||
IUpdatePremiumStorageCommand updatePremiumStorageCommand,
|
||||
IUpgradePremiumToOrganizationCommand upgradePremiumToOrganizationCommand) : BaseBillingController
|
||||
@@ -91,10 +95,30 @@ public class AccountBillingVNextController(
|
||||
return TypedResults.Ok(response);
|
||||
}
|
||||
|
||||
[HttpPut("storage")]
|
||||
[HttpGet("subscription")]
|
||||
[RequireFeature(FeatureFlagKeys.PM29594_UpdateIndividualSubscriptionPage)]
|
||||
[InjectUser]
|
||||
public async Task<IResult> UpdateStorageAsync(
|
||||
public async Task<IResult> GetSubscriptionAsync(
|
||||
[BindNever] User user)
|
||||
{
|
||||
var subscription = await getBitwardenSubscriptionQuery.Run(user);
|
||||
return TypedResults.Ok(subscription);
|
||||
}
|
||||
|
||||
[HttpPost("subscription/reinstate")]
|
||||
[RequireFeature(FeatureFlagKeys.PM29594_UpdateIndividualSubscriptionPage)]
|
||||
[InjectUser]
|
||||
public async Task<IResult> ReinstateSubscriptionAsync(
|
||||
[BindNever] User user)
|
||||
{
|
||||
var result = await reinstateSubscriptionCommand.Run(user);
|
||||
return Handle(result);
|
||||
}
|
||||
|
||||
[HttpPut("subscription/storage")]
|
||||
[RequireFeature(FeatureFlagKeys.PM29594_UpdateIndividualSubscriptionPage)]
|
||||
[InjectUser]
|
||||
public async Task<IResult> UpdateSubscriptionStorageAsync(
|
||||
[BindNever] User user,
|
||||
[FromBody] StorageUpdateRequest request)
|
||||
{
|
||||
|
||||
@@ -13,7 +13,6 @@ public class StorageUpdateRequest : IValidatableObject
|
||||
/// Must be between 0 and the maximum allowed (minus base storage).
|
||||
/// </summary>
|
||||
[Required]
|
||||
[Range(0, 99)]
|
||||
public short AdditionalStorageGb { get; set; }
|
||||
|
||||
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
|
||||
@@ -22,14 +21,14 @@ public class StorageUpdateRequest : IValidatableObject
|
||||
{
|
||||
yield return new ValidationResult(
|
||||
"Additional storage cannot be negative.",
|
||||
new[] { nameof(AdditionalStorageGb) });
|
||||
[nameof(AdditionalStorageGb)]);
|
||||
}
|
||||
|
||||
if (AdditionalStorageGb > 99)
|
||||
{
|
||||
yield return new ValidationResult(
|
||||
"Maximum additional storage is 99 GB.",
|
||||
new[] { nameof(AdditionalStorageGb) });
|
||||
[nameof(AdditionalStorageGb)]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ using Bit.Core.Utilities;
|
||||
|
||||
namespace Bit.Api.Models.Response;
|
||||
|
||||
// TODO: Remove with deletion of pm-29594-update-individual-subscription-page
|
||||
public class SubscriptionResponseModel : ResponseModel
|
||||
{
|
||||
|
||||
|
||||
Reference in New Issue
Block a user