mirror of
https://github.com/bitwarden/server.git
synced 2024-11-22 12:15:36 +01:00
Merge pull request #782 from bitwarden/feature/tax-info-collection
Combined tax updates with other operations
This commit is contained in:
commit
61b15c55d0
@ -468,13 +468,23 @@ namespace Bit.Api.Controllers
|
|||||||
license = await ApiHelpers.ReadJsonFileFromBody<UserLicense>(HttpContext, model.License);
|
license = await ApiHelpers.ReadJsonFileFromBody<UserLicense>(HttpContext, model.License);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!valid && !_globalSettings.SelfHosted && string.IsNullOrWhiteSpace(model.Country))
|
||||||
|
{
|
||||||
|
throw new BadRequestException("Country is required.");
|
||||||
|
}
|
||||||
|
|
||||||
if (!valid || (_globalSettings.SelfHosted && license == null))
|
if (!valid || (_globalSettings.SelfHosted && license == null))
|
||||||
{
|
{
|
||||||
throw new BadRequestException("Invalid license.");
|
throw new BadRequestException("Invalid license.");
|
||||||
}
|
}
|
||||||
|
|
||||||
var result = await _userService.SignUpPremiumAsync(user, model.PaymentToken,
|
var result = await _userService.SignUpPremiumAsync(user, model.PaymentToken,
|
||||||
model.PaymentMethodType.Value, model.AdditionalStorageGb.GetValueOrDefault(0), license);
|
model.PaymentMethodType.Value, model.AdditionalStorageGb.GetValueOrDefault(0), license,
|
||||||
|
new TaxInfo
|
||||||
|
{
|
||||||
|
BillingAddressCountry = model.Country,
|
||||||
|
BillingAddressPostalCode = model.PostalCode,
|
||||||
|
});
|
||||||
var profile = new ProfileResponseModel(user, null, await _userService.TwoFactorIsEnabledAsync(user));
|
var profile = new ProfileResponseModel(user, null, await _userService.TwoFactorIsEnabledAsync(user));
|
||||||
return new PaymentResponseModel
|
return new PaymentResponseModel
|
||||||
{
|
{
|
||||||
@ -534,7 +544,12 @@ namespace Bit.Api.Controllers
|
|||||||
throw new UnauthorizedAccessException();
|
throw new UnauthorizedAccessException();
|
||||||
}
|
}
|
||||||
|
|
||||||
await _userService.ReplacePaymentMethodAsync(user, model.PaymentToken, model.PaymentMethodType.Value);
|
await _userService.ReplacePaymentMethodAsync(user, model.PaymentToken, model.PaymentMethodType.Value,
|
||||||
|
new TaxInfo
|
||||||
|
{
|
||||||
|
BillingAddressCountry = model.Country,
|
||||||
|
BillingAddressPostalCode = model.PostalCode,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("storage")]
|
[HttpPost("storage")]
|
||||||
|
@ -210,7 +210,16 @@ namespace Bit.Api.Controllers
|
|||||||
}
|
}
|
||||||
|
|
||||||
await _organizationService.ReplacePaymentMethodAsync(orgIdGuid, model.PaymentToken,
|
await _organizationService.ReplacePaymentMethodAsync(orgIdGuid, model.PaymentToken,
|
||||||
model.PaymentMethodType.Value);
|
model.PaymentMethodType.Value, new TaxInfo
|
||||||
|
{
|
||||||
|
BillingAddressLine1 = model.Line1,
|
||||||
|
BillingAddressLine2 = model.Line2,
|
||||||
|
BillingAddressState = model.State,
|
||||||
|
BillingAddressCity = model.City,
|
||||||
|
BillingAddressPostalCode = model.PostalCode,
|
||||||
|
BillingAddressCountry = model.Country,
|
||||||
|
TaxIdNumber = model.TaxId,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("{id}/upgrade")]
|
[HttpPost("{id}/upgrade")]
|
||||||
|
@ -13,11 +13,17 @@ namespace Bit.Core.Models.Api
|
|||||||
[Range(0, 99)]
|
[Range(0, 99)]
|
||||||
public short? AdditionalStorageGb { get; set; }
|
public short? AdditionalStorageGb { get; set; }
|
||||||
public IFormFile License { get; set; }
|
public IFormFile License { get; set; }
|
||||||
|
public string Country { get; set; }
|
||||||
|
public string PostalCode { get; set; }
|
||||||
|
|
||||||
public bool Validate(GlobalSettings globalSettings)
|
public bool Validate(GlobalSettings globalSettings)
|
||||||
{
|
{
|
||||||
return (License == null && !globalSettings.SelfHosted) ||
|
if (!(License == null && !globalSettings.SelfHosted) ||
|
||||||
(License != null && globalSettings.SelfHosted);
|
(License != null && globalSettings.SelfHosted))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return globalSettings.SelfHosted || !string.IsNullOrWhiteSpace(Country);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
|
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
|
||||||
@ -27,6 +33,11 @@ namespace Bit.Core.Models.Api
|
|||||||
{
|
{
|
||||||
yield return new ValidationResult("Payment token or license is required.");
|
yield return new ValidationResult("Payment token or license is required.");
|
||||||
}
|
}
|
||||||
|
if (Country == "US" && string.IsNullOrWhiteSpace(PostalCode))
|
||||||
|
{
|
||||||
|
yield return new ValidationResult("Zip / postal code is required.",
|
||||||
|
new string[] { nameof(PostalCode) });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ using Bit.Core.Enums;
|
|||||||
|
|
||||||
namespace Bit.Core.Models.Api
|
namespace Bit.Core.Models.Api
|
||||||
{
|
{
|
||||||
public class PaymentRequestModel
|
public class PaymentRequestModel : OrganizationTaxInfoUpdateRequestModel
|
||||||
{
|
{
|
||||||
[Required]
|
[Required]
|
||||||
public PaymentMethodType? PaymentMethodType { get; set; }
|
public PaymentMethodType? PaymentMethodType { get; set; }
|
||||||
|
@ -10,7 +10,8 @@ namespace Bit.Core.Services
|
|||||||
{
|
{
|
||||||
public interface IOrganizationService
|
public interface IOrganizationService
|
||||||
{
|
{
|
||||||
Task ReplacePaymentMethodAsync(Guid organizationId, string paymentToken, PaymentMethodType paymentMethodType);
|
Task ReplacePaymentMethodAsync(Guid organizationId, string paymentToken, PaymentMethodType paymentMethodType,
|
||||||
|
TaxInfo taxInfo);
|
||||||
Task CancelSubscriptionAsync(Guid organizationId, bool? endOfPeriod = null);
|
Task CancelSubscriptionAsync(Guid organizationId, bool? endOfPeriod = null);
|
||||||
Task ReinstateSubscriptionAsync(Guid organizationId);
|
Task ReinstateSubscriptionAsync(Guid organizationId);
|
||||||
Task<Tuple<bool, string>> UpgradePlanAsync(Guid organizationId, OrganizationUpgrade upgrade);
|
Task<Tuple<bool, string>> UpgradePlanAsync(Guid organizationId, OrganizationUpgrade upgrade);
|
||||||
|
@ -14,13 +14,13 @@ namespace Bit.Core.Services
|
|||||||
Task<string> UpgradeFreeOrganizationAsync(Organization org, Models.StaticStore.Plan plan,
|
Task<string> UpgradeFreeOrganizationAsync(Organization org, Models.StaticStore.Plan plan,
|
||||||
short additionalStorageGb, short additionalSeats, bool premiumAccessAddon);
|
short additionalStorageGb, short additionalSeats, bool premiumAccessAddon);
|
||||||
Task<string> PurchasePremiumAsync(User user, PaymentMethodType paymentMethodType, string paymentToken,
|
Task<string> PurchasePremiumAsync(User user, PaymentMethodType paymentMethodType, string paymentToken,
|
||||||
short additionalStorageGb);
|
short additionalStorageGb, TaxInfo taxInfo);
|
||||||
Task<string> AdjustStorageAsync(IStorableSubscriber storableSubscriber, int additionalStorage, string storagePlanId);
|
Task<string> AdjustStorageAsync(IStorableSubscriber storableSubscriber, int additionalStorage, string storagePlanId);
|
||||||
Task CancelSubscriptionAsync(ISubscriber subscriber, bool endOfPeriod = false,
|
Task CancelSubscriptionAsync(ISubscriber subscriber, bool endOfPeriod = false,
|
||||||
bool skipInAppPurchaseCheck = false);
|
bool skipInAppPurchaseCheck = false);
|
||||||
Task ReinstateSubscriptionAsync(ISubscriber subscriber);
|
Task ReinstateSubscriptionAsync(ISubscriber subscriber);
|
||||||
Task<bool> UpdatePaymentMethodAsync(ISubscriber subscriber, PaymentMethodType paymentMethodType,
|
Task<bool> UpdatePaymentMethodAsync(ISubscriber subscriber, PaymentMethodType paymentMethodType,
|
||||||
string paymentToken, bool allowInAppPurchases = false);
|
string paymentToken, bool allowInAppPurchases = false, TaxInfo taxInfo = null);
|
||||||
Task<bool> CreditAccountAsync(ISubscriber subscriber, decimal creditAmount);
|
Task<bool> CreditAccountAsync(ISubscriber subscriber, decimal creditAmount);
|
||||||
Task<BillingInfo> GetBillingAsync(ISubscriber subscriber);
|
Task<BillingInfo> GetBillingAsync(ISubscriber subscriber);
|
||||||
Task<SubscriptionInfo> GetSubscriptionAsync(ISubscriber subscriber);
|
Task<SubscriptionInfo> GetSubscriptionAsync(ISubscriber subscriber);
|
||||||
|
@ -46,11 +46,12 @@ namespace Bit.Core.Services
|
|||||||
Task<IdentityResult> DeleteAsync(User user, string token);
|
Task<IdentityResult> DeleteAsync(User user, string token);
|
||||||
Task SendDeleteConfirmationAsync(string email);
|
Task SendDeleteConfirmationAsync(string email);
|
||||||
Task<Tuple<bool, string>> SignUpPremiumAsync(User user, string paymentToken,
|
Task<Tuple<bool, string>> SignUpPremiumAsync(User user, string paymentToken,
|
||||||
PaymentMethodType paymentMethodType, short additionalStorageGb, UserLicense license);
|
PaymentMethodType paymentMethodType, short additionalStorageGb, UserLicense license,
|
||||||
|
TaxInfo taxInfo);
|
||||||
Task IapCheckAsync(User user, PaymentMethodType paymentMethodType);
|
Task IapCheckAsync(User user, PaymentMethodType paymentMethodType);
|
||||||
Task UpdateLicenseAsync(User user, UserLicense license);
|
Task UpdateLicenseAsync(User user, UserLicense license);
|
||||||
Task<string> AdjustStorageAsync(User user, short storageAdjustmentGb);
|
Task<string> AdjustStorageAsync(User user, short storageAdjustmentGb);
|
||||||
Task ReplacePaymentMethodAsync(User user, string paymentToken, PaymentMethodType paymentMethodType);
|
Task ReplacePaymentMethodAsync(User user, string paymentToken, PaymentMethodType paymentMethodType, TaxInfo taxInfo);
|
||||||
Task CancelPremiumAsync(User user, bool? endOfPeriod = null, bool accountDelete = false);
|
Task CancelPremiumAsync(User user, bool? endOfPeriod = null, bool accountDelete = false);
|
||||||
Task ReinstatePremiumAsync(User user);
|
Task ReinstatePremiumAsync(User user);
|
||||||
Task EnablePremiumAsync(Guid userId, DateTime? expirationDate);
|
Task EnablePremiumAsync(Guid userId, DateTime? expirationDate);
|
||||||
|
@ -75,7 +75,7 @@ namespace Bit.Core.Services
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async Task ReplacePaymentMethodAsync(Guid organizationId, string paymentToken,
|
public async Task ReplacePaymentMethodAsync(Guid organizationId, string paymentToken,
|
||||||
PaymentMethodType paymentMethodType)
|
PaymentMethodType paymentMethodType, TaxInfo taxInfo)
|
||||||
{
|
{
|
||||||
var organization = await GetOrgById(organizationId);
|
var organization = await GetOrgById(organizationId);
|
||||||
if (organization == null)
|
if (organization == null)
|
||||||
@ -83,6 +83,7 @@ namespace Bit.Core.Services
|
|||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await _paymentService.SaveTaxInfoAsync(organization, taxInfo);
|
||||||
var updated = await _paymentService.UpdatePaymentMethodAsync(organization,
|
var updated = await _paymentService.UpdatePaymentMethodAsync(organization,
|
||||||
paymentMethodType, paymentToken);
|
paymentMethodType, paymentToken);
|
||||||
if (updated)
|
if (updated)
|
||||||
|
@ -345,7 +345,7 @@ namespace Bit.Core.Services
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async Task<string> PurchasePremiumAsync(User user, PaymentMethodType paymentMethodType,
|
public async Task<string> PurchasePremiumAsync(User user, PaymentMethodType paymentMethodType,
|
||||||
string paymentToken, short additionalStorageGb)
|
string paymentToken, short additionalStorageGb, TaxInfo taxInfo)
|
||||||
{
|
{
|
||||||
if (paymentMethodType != PaymentMethodType.Credit && string.IsNullOrWhiteSpace(paymentToken))
|
if (paymentMethodType != PaymentMethodType.Credit && string.IsNullOrWhiteSpace(paymentToken))
|
||||||
{
|
{
|
||||||
@ -393,7 +393,7 @@ namespace Bit.Core.Services
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await UpdatePaymentMethodAsync(user, paymentMethodType, paymentToken, true);
|
await UpdatePaymentMethodAsync(user, paymentMethodType, paymentToken, true, taxInfo);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
@ -463,7 +463,13 @@ namespace Bit.Core.Services
|
|||||||
InvoiceSettings = new CustomerInvoiceSettingsOptions
|
InvoiceSettings = new CustomerInvoiceSettingsOptions
|
||||||
{
|
{
|
||||||
DefaultPaymentMethod = stipeCustomerPaymentMethodId
|
DefaultPaymentMethod = stipeCustomerPaymentMethodId
|
||||||
}
|
},
|
||||||
|
Address = new AddressOptions
|
||||||
|
{
|
||||||
|
Line1 = string.Empty,
|
||||||
|
Country = taxInfo.BillingAddressCountry,
|
||||||
|
PostalCode = taxInfo.BillingAddressPostalCode,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
createdStripeCustomer = true;
|
createdStripeCustomer = true;
|
||||||
}
|
}
|
||||||
@ -1098,7 +1104,7 @@ namespace Bit.Core.Services
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> UpdatePaymentMethodAsync(ISubscriber subscriber, PaymentMethodType paymentMethodType,
|
public async Task<bool> UpdatePaymentMethodAsync(ISubscriber subscriber, PaymentMethodType paymentMethodType,
|
||||||
string paymentToken, bool allowInAppPurchases = false)
|
string paymentToken, bool allowInAppPurchases = false, TaxInfo taxInfo = null)
|
||||||
{
|
{
|
||||||
if (subscriber == null)
|
if (subscriber == null)
|
||||||
{
|
{
|
||||||
@ -1286,7 +1292,16 @@ namespace Bit.Core.Services
|
|||||||
InvoiceSettings = new CustomerInvoiceSettingsOptions
|
InvoiceSettings = new CustomerInvoiceSettingsOptions
|
||||||
{
|
{
|
||||||
DefaultPaymentMethod = stipeCustomerPaymentMethodId
|
DefaultPaymentMethod = stipeCustomerPaymentMethodId
|
||||||
}
|
},
|
||||||
|
Address = taxInfo == null ? null : new AddressOptions
|
||||||
|
{
|
||||||
|
Country = taxInfo.BillingAddressCountry,
|
||||||
|
PostalCode = taxInfo.BillingAddressPostalCode,
|
||||||
|
Line1 = taxInfo.BillingAddressLine1 ?? string.Empty,
|
||||||
|
Line2 = taxInfo.BillingAddressLine2,
|
||||||
|
City = taxInfo.BillingAddressCity,
|
||||||
|
State = taxInfo.BillingAddressState,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
subscriber.Gateway = GatewayType.Stripe;
|
subscriber.Gateway = GatewayType.Stripe;
|
||||||
@ -1345,7 +1360,16 @@ namespace Bit.Core.Services
|
|||||||
InvoiceSettings = new CustomerInvoiceSettingsOptions
|
InvoiceSettings = new CustomerInvoiceSettingsOptions
|
||||||
{
|
{
|
||||||
DefaultPaymentMethod = defaultPaymentMethodId
|
DefaultPaymentMethod = defaultPaymentMethodId
|
||||||
}
|
},
|
||||||
|
Address = taxInfo == null ? null : new AddressOptions
|
||||||
|
{
|
||||||
|
Country = taxInfo.BillingAddressCountry,
|
||||||
|
PostalCode = taxInfo.BillingAddressPostalCode,
|
||||||
|
Line1 = taxInfo.BillingAddressLine1 ?? string.Empty,
|
||||||
|
Line2 = taxInfo.BillingAddressLine2,
|
||||||
|
City = taxInfo.BillingAddressCity,
|
||||||
|
State = taxInfo.BillingAddressState,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -703,7 +703,8 @@ namespace Bit.Core.Services
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Tuple<bool, string>> SignUpPremiumAsync(User user, string paymentToken,
|
public async Task<Tuple<bool, string>> SignUpPremiumAsync(User user, string paymentToken,
|
||||||
PaymentMethodType paymentMethodType, short additionalStorageGb, UserLicense license)
|
PaymentMethodType paymentMethodType, short additionalStorageGb, UserLicense license,
|
||||||
|
TaxInfo taxInfo)
|
||||||
{
|
{
|
||||||
if (user.Premium)
|
if (user.Premium)
|
||||||
{
|
{
|
||||||
@ -742,7 +743,7 @@ namespace Bit.Core.Services
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
paymentIntentClientSecret = await _paymentService.PurchasePremiumAsync(user, paymentMethodType,
|
paymentIntentClientSecret = await _paymentService.PurchasePremiumAsync(user, paymentMethodType,
|
||||||
paymentToken, additionalStorageGb);
|
paymentToken, additionalStorageGb, taxInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
user.Premium = true;
|
user.Premium = true;
|
||||||
@ -844,14 +845,14 @@ namespace Bit.Core.Services
|
|||||||
return secret;
|
return secret;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task ReplacePaymentMethodAsync(User user, string paymentToken, PaymentMethodType paymentMethodType)
|
public async Task ReplacePaymentMethodAsync(User user, string paymentToken, PaymentMethodType paymentMethodType, TaxInfo taxInfo)
|
||||||
{
|
{
|
||||||
if (paymentToken.StartsWith("btok_"))
|
if (paymentToken.StartsWith("btok_"))
|
||||||
{
|
{
|
||||||
throw new BadRequestException("Invalid token.");
|
throw new BadRequestException("Invalid token.");
|
||||||
}
|
}
|
||||||
|
|
||||||
var updated = await _paymentService.UpdatePaymentMethodAsync(user, paymentMethodType, paymentToken);
|
var updated = await _paymentService.UpdatePaymentMethodAsync(user, paymentMethodType, paymentToken, taxInfo: taxInfo);
|
||||||
if (updated)
|
if (updated)
|
||||||
{
|
{
|
||||||
await SaveUserAsync(user);
|
await SaveUserAsync(user);
|
||||||
|
Loading…
Reference in New Issue
Block a user