mirror of
https://github.com/bitwarden/server
synced 2026-02-25 17:03:22 +00:00
[PM-31040] Replace ISetupIntentCache with customer-based approach (#6954)
* docs(billing): add design document for replacing SetupIntent cache * docs(billing): add implementation plan for replacing SetupIntent cache * feat(db): add gateway lookup stored procedures for Organization, Provider, and User * feat(db): add gateway lookup indexes to Organization, Provider, and User table definitions * chore(db): add SQL Server migration for gateway lookup indexes and stored procedures * feat(repos): add gateway lookup methods to IOrganizationRepository and Dapper implementation * feat(repos): add gateway lookup methods to IProviderRepository and Dapper implementation * feat(repos): add gateway lookup methods to IUserRepository and Dapper implementation * feat(repos): add EF OrganizationRepository gateway lookup methods and index configuration * feat(repos): add EF ProviderRepository gateway lookup methods and index configuration * feat(repos): add EF UserRepository gateway lookup methods and index configuration * chore(db): add EF migrations for gateway lookup indexes * refactor(billing): update SetupIntentSucceededHandler to use repository instead of cache * refactor(billing): simplify StripeEventService by expanding customer on SetupIntent * refactor(billing): query Stripe for SetupIntents by customer ID in GetPaymentMethodQuery * refactor(billing): query Stripe for SetupIntents by customer ID in HasPaymentMethodQuery * refactor(billing): update OrganizationBillingService to set customer on SetupIntent * refactor(billing): update ProviderBillingService to set customer on SetupIntent and query by customer * refactor(billing): update UpdatePaymentMethodCommand to set customer on SetupIntent * refactor(billing): remove bank account support from CreatePremiumCloudHostedSubscriptionCommand * refactor(billing): remove OrganizationBillingService.UpdatePaymentMethod dead code * refactor(billing): remove ProviderBillingService.UpdatePaymentMethod * refactor(billing): remove PremiumUserBillingService.UpdatePaymentMethod and UserService.ReplacePaymentMethodAsync * refactor(billing): remove SubscriberService.UpdatePaymentSource and related dead code * refactor(billing): update SubscriberService.GetPaymentSourceAsync to query Stripe by customer ID Add Task 15a to plan - this was a missed requirement for updating GetPaymentSourceAsync which still used the cache. * refactor(billing): complete removal of PremiumUserBillingService.Finalize and UserService.SignUpPremiumAsync * refactor(billing): remove ISetupIntentCache and SetupIntentDistributedCache * chore: remove temporary planning documents * chore: run dotnet format * fix(billing): add MaxLength(50) to Provider gateway ID properties * chore(db): add EF migrations for Provider gateway column lengths * chore: run dotnet format * chore: rename SQL migration for chronological order
This commit is contained in:
@@ -15,13 +15,10 @@ using Bit.Core.Auth.Enums;
|
||||
using Bit.Core.Auth.UserFeatures.TwoFactorAuth.Interfaces;
|
||||
using Bit.Core.Billing.Licenses;
|
||||
using Bit.Core.Billing.Licenses.Extensions;
|
||||
using Bit.Core.Billing.Models;
|
||||
using Bit.Core.Billing.Models.Business;
|
||||
using Bit.Core.Billing.Models.Sales;
|
||||
using Bit.Core.Billing.Premium.Queries;
|
||||
using Bit.Core.Billing.Pricing;
|
||||
using Bit.Core.Billing.Services;
|
||||
using Bit.Core.Billing.Tax.Models;
|
||||
using Bit.Core.Context;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
@@ -68,7 +65,6 @@ public class UserService : UserManager<User>, IUserService
|
||||
private readonly IProviderUserRepository _providerUserRepository;
|
||||
private readonly IStripeSyncService _stripeSyncService;
|
||||
private readonly IFeatureService _featureService;
|
||||
private readonly IPremiumUserBillingService _premiumUserBillingService;
|
||||
private readonly IRevokeNonCompliantOrganizationUserCommand _revokeNonCompliantOrganizationUserCommand;
|
||||
private readonly ITwoFactorIsEnabledQuery _twoFactorIsEnabledQuery;
|
||||
private readonly IDistributedCache _distributedCache;
|
||||
@@ -105,7 +101,6 @@ public class UserService : UserManager<User>, IUserService
|
||||
IProviderUserRepository providerUserRepository,
|
||||
IStripeSyncService stripeSyncService,
|
||||
IFeatureService featureService,
|
||||
IPremiumUserBillingService premiumUserBillingService,
|
||||
IRevokeNonCompliantOrganizationUserCommand revokeNonCompliantOrganizationUserCommand,
|
||||
ITwoFactorIsEnabledQuery twoFactorIsEnabledQuery,
|
||||
IDistributedCache distributedCache,
|
||||
@@ -146,7 +141,6 @@ public class UserService : UserManager<User>, IUserService
|
||||
_providerUserRepository = providerUserRepository;
|
||||
_stripeSyncService = stripeSyncService;
|
||||
_featureService = featureService;
|
||||
_premiumUserBillingService = premiumUserBillingService;
|
||||
_revokeNonCompliantOrganizationUserCommand = revokeNonCompliantOrganizationUserCommand;
|
||||
_twoFactorIsEnabledQuery = twoFactorIsEnabledQuery;
|
||||
_distributedCache = distributedCache;
|
||||
@@ -742,78 +736,6 @@ public class UserService : UserManager<User>, IUserService
|
||||
return true;
|
||||
}
|
||||
|
||||
public async Task<Tuple<bool, string>> SignUpPremiumAsync(User user, string paymentToken,
|
||||
PaymentMethodType paymentMethodType, short additionalStorageGb, UserLicense license,
|
||||
TaxInfo taxInfo)
|
||||
{
|
||||
if (user.Premium)
|
||||
{
|
||||
throw new BadRequestException("Already a premium user.");
|
||||
}
|
||||
|
||||
if (additionalStorageGb < 0)
|
||||
{
|
||||
throw new BadRequestException("You can't subtract storage!");
|
||||
}
|
||||
|
||||
string paymentIntentClientSecret = null;
|
||||
IStripePaymentService paymentService = null;
|
||||
if (_globalSettings.SelfHosted)
|
||||
{
|
||||
if (license == null || !_licenseService.VerifyLicense(license))
|
||||
{
|
||||
throw new BadRequestException("Invalid license.");
|
||||
}
|
||||
|
||||
var claimsPrincipal = _licenseService.GetClaimsPrincipalFromLicense(license);
|
||||
|
||||
if (!license.CanUse(user, claimsPrincipal, out var exceptionMessage))
|
||||
{
|
||||
throw new BadRequestException(exceptionMessage);
|
||||
}
|
||||
|
||||
var dir = $"{_globalSettings.LicenseDirectory}/user";
|
||||
Directory.CreateDirectory(dir);
|
||||
using var fs = File.OpenWrite(Path.Combine(dir, $"{user.Id}.json"));
|
||||
await JsonSerializer.SerializeAsync(fs, license, JsonHelpers.Indented);
|
||||
}
|
||||
else
|
||||
{
|
||||
var sale = PremiumUserSale.From(user, paymentMethodType, paymentToken, taxInfo, additionalStorageGb);
|
||||
await _premiumUserBillingService.Finalize(sale);
|
||||
}
|
||||
|
||||
user.Premium = true;
|
||||
user.RevisionDate = DateTime.UtcNow;
|
||||
|
||||
if (_globalSettings.SelfHosted)
|
||||
{
|
||||
user.MaxStorageGb = Constants.SelfHostedMaxStorageGb;
|
||||
user.LicenseKey = license.LicenseKey;
|
||||
user.PremiumExpirationDate = license.Expires;
|
||||
}
|
||||
else
|
||||
{
|
||||
user.LicenseKey = CoreHelpers.SecureRandomString(20);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await SaveUserAsync(user);
|
||||
await _pushService.PushSyncVaultAsync(user.Id);
|
||||
}
|
||||
catch when (!_globalSettings.SelfHosted)
|
||||
{
|
||||
await paymentService.CancelAndRecoverChargesAsync(user);
|
||||
throw;
|
||||
}
|
||||
|
||||
|
||||
|
||||
return new Tuple<bool, string>(string.IsNullOrWhiteSpace(paymentIntentClientSecret),
|
||||
paymentIntentClientSecret);
|
||||
}
|
||||
|
||||
public async Task UpdateLicenseAsync(User user, UserLicense license)
|
||||
{
|
||||
if (!_globalSettings.SelfHosted)
|
||||
@@ -883,20 +805,6 @@ public class UserService : UserManager<User>, IUserService
|
||||
return secret;
|
||||
}
|
||||
|
||||
public async Task ReplacePaymentMethodAsync(User user, string paymentToken, PaymentMethodType paymentMethodType, TaxInfo taxInfo)
|
||||
{
|
||||
if (paymentToken.StartsWith("btok_"))
|
||||
{
|
||||
throw new BadRequestException("Invalid token.");
|
||||
}
|
||||
|
||||
var tokenizedPaymentSource = new TokenizedPaymentSource(paymentMethodType, paymentToken);
|
||||
var taxInformation = TaxInformation.From(taxInfo);
|
||||
|
||||
await _premiumUserBillingService.UpdatePaymentMethod(user, tokenizedPaymentSource, taxInformation);
|
||||
await SaveUserAsync(user);
|
||||
}
|
||||
|
||||
public async Task CancelPremiumAsync(User user, bool? endOfPeriod = null)
|
||||
{
|
||||
var eop = endOfPeriod.GetValueOrDefault(true);
|
||||
|
||||
Reference in New Issue
Block a user