mirror of
https://github.com/bitwarden/server.git
synced 2024-12-01 13:43:23 +01:00
inject stripepaymentservice
This commit is contained in:
parent
a97a6216d7
commit
d568b86e1e
@ -28,6 +28,7 @@ namespace Bit.Api.Controllers
|
|||||||
private readonly ICipherService _cipherService;
|
private readonly ICipherService _cipherService;
|
||||||
private readonly IOrganizationUserRepository _organizationUserRepository;
|
private readonly IOrganizationUserRepository _organizationUserRepository;
|
||||||
private readonly ILicensingService _licenseService;
|
private readonly ILicensingService _licenseService;
|
||||||
|
private readonly IPaymentService _paymentService;
|
||||||
private readonly GlobalSettings _globalSettings;
|
private readonly GlobalSettings _globalSettings;
|
||||||
|
|
||||||
public AccountsController(
|
public AccountsController(
|
||||||
@ -38,6 +39,7 @@ namespace Bit.Api.Controllers
|
|||||||
ICipherService cipherService,
|
ICipherService cipherService,
|
||||||
IOrganizationUserRepository organizationUserRepository,
|
IOrganizationUserRepository organizationUserRepository,
|
||||||
ILicensingService licenseService,
|
ILicensingService licenseService,
|
||||||
|
IPaymentService paymentService,
|
||||||
GlobalSettings globalSettings)
|
GlobalSettings globalSettings)
|
||||||
{
|
{
|
||||||
_userService = userService;
|
_userService = userService;
|
||||||
@ -47,6 +49,7 @@ namespace Bit.Api.Controllers
|
|||||||
_cipherService = cipherService;
|
_cipherService = cipherService;
|
||||||
_organizationUserRepository = organizationUserRepository;
|
_organizationUserRepository = organizationUserRepository;
|
||||||
_licenseService = licenseService;
|
_licenseService = licenseService;
|
||||||
|
_paymentService = paymentService;
|
||||||
_globalSettings = globalSettings;
|
_globalSettings = globalSettings;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -476,8 +479,7 @@ namespace Bit.Api.Controllers
|
|||||||
|
|
||||||
if(!_globalSettings.SelfHosted && user.Gateway != null)
|
if(!_globalSettings.SelfHosted && user.Gateway != null)
|
||||||
{
|
{
|
||||||
var paymentService = user.GetPaymentService(_globalSettings);
|
var billingInfo = await _paymentService.GetBillingAsync(user);
|
||||||
var billingInfo = await paymentService.GetBillingAsync(user);
|
|
||||||
var license = await _userService.GenerateLicenseAsync(user, billingInfo);
|
var license = await _userService.GenerateLicenseAsync(user, billingInfo);
|
||||||
return new BillingResponseModel(user, billingInfo, license);
|
return new BillingResponseModel(user, billingInfo, license);
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,7 @@ namespace Bit.Api.Controllers
|
|||||||
private readonly IOrganizationUserRepository _organizationUserRepository;
|
private readonly IOrganizationUserRepository _organizationUserRepository;
|
||||||
private readonly IOrganizationService _organizationService;
|
private readonly IOrganizationService _organizationService;
|
||||||
private readonly IUserService _userService;
|
private readonly IUserService _userService;
|
||||||
|
private readonly IPaymentService _paymentService;
|
||||||
private readonly CurrentContext _currentContext;
|
private readonly CurrentContext _currentContext;
|
||||||
private readonly GlobalSettings _globalSettings;
|
private readonly GlobalSettings _globalSettings;
|
||||||
|
|
||||||
@ -32,6 +33,7 @@ namespace Bit.Api.Controllers
|
|||||||
IOrganizationUserRepository organizationUserRepository,
|
IOrganizationUserRepository organizationUserRepository,
|
||||||
IOrganizationService organizationService,
|
IOrganizationService organizationService,
|
||||||
IUserService userService,
|
IUserService userService,
|
||||||
|
IPaymentService paymentService,
|
||||||
CurrentContext currentContext,
|
CurrentContext currentContext,
|
||||||
GlobalSettings globalSettings)
|
GlobalSettings globalSettings)
|
||||||
{
|
{
|
||||||
@ -39,6 +41,7 @@ namespace Bit.Api.Controllers
|
|||||||
_organizationUserRepository = organizationUserRepository;
|
_organizationUserRepository = organizationUserRepository;
|
||||||
_organizationService = organizationService;
|
_organizationService = organizationService;
|
||||||
_userService = userService;
|
_userService = userService;
|
||||||
|
_paymentService = paymentService;
|
||||||
_currentContext = currentContext;
|
_currentContext = currentContext;
|
||||||
_globalSettings = globalSettings;
|
_globalSettings = globalSettings;
|
||||||
}
|
}
|
||||||
@ -78,8 +81,7 @@ namespace Bit.Api.Controllers
|
|||||||
|
|
||||||
if(!_globalSettings.SelfHosted && organization.Gateway != null)
|
if(!_globalSettings.SelfHosted && organization.Gateway != null)
|
||||||
{
|
{
|
||||||
var paymentService = new StripePaymentService(_globalSettings);
|
var billingInfo = await _paymentService.GetBillingAsync(organization);
|
||||||
var billingInfo = await paymentService.GetBillingAsync(organization);
|
|
||||||
if(billingInfo == null)
|
if(billingInfo == null)
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
|
@ -17,12 +17,14 @@ namespace Bit.Billing.Jobs
|
|||||||
private readonly GlobalSettings _globalSettings;
|
private readonly GlobalSettings _globalSettings;
|
||||||
private readonly IUserRepository _userRepository;
|
private readonly IUserRepository _userRepository;
|
||||||
private readonly IMailService _mailService;
|
private readonly IMailService _mailService;
|
||||||
|
private readonly IPaymentService _paymentService;
|
||||||
|
|
||||||
public PremiumRenewalRemindersJob(
|
public PremiumRenewalRemindersJob(
|
||||||
IOptions<BillingSettings> billingSettings,
|
IOptions<BillingSettings> billingSettings,
|
||||||
GlobalSettings globalSettings,
|
GlobalSettings globalSettings,
|
||||||
IUserRepository userRepository,
|
IUserRepository userRepository,
|
||||||
IMailService mailService,
|
IMailService mailService,
|
||||||
|
IPaymentService paymentService,
|
||||||
ILogger<PremiumRenewalRemindersJob> logger)
|
ILogger<PremiumRenewalRemindersJob> logger)
|
||||||
: base(logger)
|
: base(logger)
|
||||||
{
|
{
|
||||||
@ -30,6 +32,7 @@ namespace Bit.Billing.Jobs
|
|||||||
_globalSettings = globalSettings;
|
_globalSettings = globalSettings;
|
||||||
_userRepository = userRepository;
|
_userRepository = userRepository;
|
||||||
_mailService = mailService;
|
_mailService = mailService;
|
||||||
|
_paymentService = paymentService;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async override Task ExecuteJobAsync(IJobExecutionContext context)
|
protected async override Task ExecuteJobAsync(IJobExecutionContext context)
|
||||||
@ -37,8 +40,7 @@ namespace Bit.Billing.Jobs
|
|||||||
var users = await _userRepository.GetManyByPremiumRenewalAsync();
|
var users = await _userRepository.GetManyByPremiumRenewalAsync();
|
||||||
foreach(var user in users)
|
foreach(var user in users)
|
||||||
{
|
{
|
||||||
var paymentService = user.GetPaymentService(_globalSettings);
|
var upcomingInvoice = await _paymentService.GetUpcomingInvoiceAsync(user);
|
||||||
var upcomingInvoice = await paymentService.GetUpcomingInvoiceAsync(user);
|
|
||||||
if(upcomingInvoice?.Date != null)
|
if(upcomingInvoice?.Date != null)
|
||||||
{
|
{
|
||||||
var items = new List<string> { "1 × Premium Membership (Annually)" };
|
var items = new List<string> { "1 × Premium Membership (Annually)" };
|
||||||
|
@ -15,6 +15,5 @@ namespace Bit.Core.Models.Table
|
|||||||
string BraintreeCustomerIdPrefix();
|
string BraintreeCustomerIdPrefix();
|
||||||
string BraintreeIdField();
|
string BraintreeIdField();
|
||||||
string GatewayIdField();
|
string GatewayIdField();
|
||||||
IPaymentService GetPaymentService(GlobalSettings globalSettings);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -99,29 +99,6 @@ namespace Bit.Core.Models.Table
|
|||||||
return maxStorageBytes - Storage.Value;
|
return maxStorageBytes - Storage.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IPaymentService GetPaymentService(GlobalSettings globalSettings)
|
|
||||||
{
|
|
||||||
if(Gateway == null)
|
|
||||||
{
|
|
||||||
throw new BadRequestException("No gateway.");
|
|
||||||
}
|
|
||||||
|
|
||||||
IPaymentService paymentService = null;
|
|
||||||
switch(Gateway)
|
|
||||||
{
|
|
||||||
case GatewayType.Stripe:
|
|
||||||
paymentService = new StripePaymentService(globalSettings);
|
|
||||||
break;
|
|
||||||
case GatewayType.Braintree:
|
|
||||||
paymentService = new BraintreePaymentService(globalSettings);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new NotSupportedException("Unsupported gateway.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return paymentService;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Dictionary<TwoFactorProviderType, TwoFactorProvider> GetTwoFactorProviders()
|
public Dictionary<TwoFactorProviderType, TwoFactorProvider> GetTwoFactorProviders()
|
||||||
{
|
{
|
||||||
if(string.IsNullOrWhiteSpace(TwoFactorProviders))
|
if(string.IsNullOrWhiteSpace(TwoFactorProviders))
|
||||||
|
@ -148,29 +148,6 @@ namespace Bit.Core.Models.Table
|
|||||||
return maxStorageBytes - Storage.Value;
|
return maxStorageBytes - Storage.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IPaymentService GetPaymentService(GlobalSettings globalSettings)
|
|
||||||
{
|
|
||||||
if(Gateway == null)
|
|
||||||
{
|
|
||||||
throw new BadRequestException("No gateway.");
|
|
||||||
}
|
|
||||||
|
|
||||||
IPaymentService paymentService = null;
|
|
||||||
switch(Gateway)
|
|
||||||
{
|
|
||||||
case GatewayType.Stripe:
|
|
||||||
paymentService = new StripePaymentService(globalSettings);
|
|
||||||
break;
|
|
||||||
case GatewayType.Braintree:
|
|
||||||
paymentService = new BraintreePaymentService(globalSettings);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new NotSupportedException("Unsupported gateway.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return paymentService;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IdentityUser ToIdentityUser(bool twoFactorEnabled)
|
public IdentityUser ToIdentityUser(bool twoFactorEnabled)
|
||||||
{
|
{
|
||||||
return new IdentityUser
|
return new IdentityUser
|
||||||
|
@ -8,6 +8,8 @@ namespace Bit.Core.Services
|
|||||||
public interface IPaymentService
|
public interface IPaymentService
|
||||||
{
|
{
|
||||||
Task CancelAndRecoverChargesAsync(ISubscriber subscriber);
|
Task CancelAndRecoverChargesAsync(ISubscriber subscriber);
|
||||||
|
Task PurchaseOrganizationAsync(Organization org, PaymentMethodType paymentMethodType, string paymentToken,
|
||||||
|
Models.StaticStore.Plan plan, short additionalStorageGb, short additionalSeats, bool premiumAccessAddon);
|
||||||
Task PurchasePremiumAsync(User user, PaymentMethodType paymentMethodType, string paymentToken,
|
Task PurchasePremiumAsync(User user, PaymentMethodType paymentMethodType, string paymentToken,
|
||||||
short additionalStorageGb);
|
short additionalStorageGb);
|
||||||
Task AdjustStorageAsync(IStorableSubscriber storableSubscriber, int additionalStorage, string storagePlanId);
|
Task AdjustStorageAsync(IStorableSubscriber storableSubscriber, int additionalStorage, string storagePlanId);
|
||||||
|
@ -1,386 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Bit.Core.Models.Table;
|
|
||||||
using Braintree;
|
|
||||||
using Bit.Core.Exceptions;
|
|
||||||
using Bit.Core.Models.Business;
|
|
||||||
using Bit.Core.Enums;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Bit.Core.Services
|
|
||||||
{
|
|
||||||
public class BraintreePaymentService : IPaymentService
|
|
||||||
{
|
|
||||||
private const string PremiumPlanId = "premium-annually";
|
|
||||||
private const string StoragePlanId = "storage-gb-annually";
|
|
||||||
private readonly BraintreeGateway _gateway;
|
|
||||||
|
|
||||||
public BraintreePaymentService(
|
|
||||||
GlobalSettings globalSettings)
|
|
||||||
{
|
|
||||||
_gateway = new BraintreeGateway
|
|
||||||
{
|
|
||||||
Environment = globalSettings.Braintree.Production ?
|
|
||||||
Braintree.Environment.PRODUCTION : Braintree.Environment.SANDBOX,
|
|
||||||
MerchantId = globalSettings.Braintree.MerchantId,
|
|
||||||
PublicKey = globalSettings.Braintree.PublicKey,
|
|
||||||
PrivateKey = globalSettings.Braintree.PrivateKey
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task AdjustStorageAsync(IStorableSubscriber storableSubscriber, int additionalStorage, string storagePlanId)
|
|
||||||
{
|
|
||||||
var sub = await _gateway.Subscription.FindAsync(storableSubscriber.GatewaySubscriptionId);
|
|
||||||
if(sub == null)
|
|
||||||
{
|
|
||||||
throw new GatewayException("Subscription was not found.");
|
|
||||||
}
|
|
||||||
|
|
||||||
var req = new SubscriptionRequest
|
|
||||||
{
|
|
||||||
AddOns = new AddOnsRequest(),
|
|
||||||
Options = new SubscriptionOptionsRequest
|
|
||||||
{
|
|
||||||
ProrateCharges = true,
|
|
||||||
RevertSubscriptionOnProrationFailure = true
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var storageItem = sub.AddOns?.FirstOrDefault(a => a.Id == storagePlanId);
|
|
||||||
if(additionalStorage > 0 && storageItem == null)
|
|
||||||
{
|
|
||||||
req.AddOns.Add = new AddAddOnRequest[]
|
|
||||||
{
|
|
||||||
new AddAddOnRequest
|
|
||||||
{
|
|
||||||
InheritedFromId = storagePlanId,
|
|
||||||
Quantity = additionalStorage,
|
|
||||||
NeverExpires = true
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else if(additionalStorage > 0 && storageItem != null)
|
|
||||||
{
|
|
||||||
req.AddOns.Update = new UpdateAddOnRequest[]
|
|
||||||
{
|
|
||||||
new UpdateAddOnRequest
|
|
||||||
{
|
|
||||||
ExistingId = storageItem.Id,
|
|
||||||
Quantity = additionalStorage,
|
|
||||||
NeverExpires = true
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else if(additionalStorage == 0 && storageItem != null)
|
|
||||||
{
|
|
||||||
req.AddOns.Remove = new string[] { storageItem.Id };
|
|
||||||
}
|
|
||||||
|
|
||||||
var result = await _gateway.Subscription.UpdateAsync(sub.Id, req);
|
|
||||||
if(!result.IsSuccess())
|
|
||||||
{
|
|
||||||
throw new GatewayException("Failed to adjust storage.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task CancelAndRecoverChargesAsync(ISubscriber subscriber)
|
|
||||||
{
|
|
||||||
if(!string.IsNullOrWhiteSpace(subscriber.GatewaySubscriptionId))
|
|
||||||
{
|
|
||||||
await _gateway.Subscription.CancelAsync(subscriber.GatewaySubscriptionId);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(string.IsNullOrWhiteSpace(subscriber.GatewayCustomerId))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var transactionRequest = new TransactionSearchRequest().CustomerId.Is(subscriber.GatewayCustomerId);
|
|
||||||
var transactions = _gateway.Transaction.Search(transactionRequest);
|
|
||||||
|
|
||||||
if((transactions?.MaximumCount ?? 0) > 0)
|
|
||||||
{
|
|
||||||
var txs = transactions.Cast<Braintree.Transaction>().Where(c => c.RefundedTransactionId == null);
|
|
||||||
foreach(var transaction in txs)
|
|
||||||
{
|
|
||||||
await _gateway.Transaction.RefundAsync(transaction.Id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await _gateway.Customer.DeleteAsync(subscriber.GatewayCustomerId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task CancelSubscriptionAsync(ISubscriber subscriber, bool endOfPeriod = false)
|
|
||||||
{
|
|
||||||
if(subscriber == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(subscriber));
|
|
||||||
}
|
|
||||||
|
|
||||||
if(string.IsNullOrWhiteSpace(subscriber.GatewaySubscriptionId))
|
|
||||||
{
|
|
||||||
throw new GatewayException("No subscription.");
|
|
||||||
}
|
|
||||||
|
|
||||||
var sub = await _gateway.Subscription.FindAsync(subscriber.GatewaySubscriptionId);
|
|
||||||
if(sub == null)
|
|
||||||
{
|
|
||||||
throw new GatewayException("Subscription was not found.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if(sub.Status == SubscriptionStatus.CANCELED || sub.Status == SubscriptionStatus.EXPIRED ||
|
|
||||||
!sub.NeverExpires.GetValueOrDefault())
|
|
||||||
{
|
|
||||||
throw new GatewayException("Subscription is already canceled.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if(endOfPeriod)
|
|
||||||
{
|
|
||||||
var req = new SubscriptionRequest
|
|
||||||
{
|
|
||||||
NeverExpires = false,
|
|
||||||
NumberOfBillingCycles = sub.CurrentBillingCycle
|
|
||||||
};
|
|
||||||
|
|
||||||
var result = await _gateway.Subscription.UpdateAsync(subscriber.GatewaySubscriptionId, req);
|
|
||||||
if(!result.IsSuccess())
|
|
||||||
{
|
|
||||||
throw new GatewayException("Unable to cancel subscription.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var result = await _gateway.Subscription.CancelAsync(subscriber.GatewaySubscriptionId);
|
|
||||||
if(!result.IsSuccess())
|
|
||||||
{
|
|
||||||
throw new GatewayException("Unable to cancel subscription.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<BillingInfo.BillingInvoice> GetUpcomingInvoiceAsync(ISubscriber subscriber)
|
|
||||||
{
|
|
||||||
if(!string.IsNullOrWhiteSpace(subscriber.GatewaySubscriptionId))
|
|
||||||
{
|
|
||||||
var sub = await _gateway.Subscription.FindAsync(subscriber.GatewaySubscriptionId);
|
|
||||||
if(sub != null)
|
|
||||||
{
|
|
||||||
var cancelAtEndDate = !sub.NeverExpires.GetValueOrDefault();
|
|
||||||
var canceled = sub.Status == SubscriptionStatus.CANCELED;
|
|
||||||
if(!canceled && !cancelAtEndDate && sub.NextBillingDate.HasValue)
|
|
||||||
{
|
|
||||||
return new BillingInfo.BillingInvoice(sub);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<BillingInfo> GetBillingAsync(ISubscriber subscriber)
|
|
||||||
{
|
|
||||||
var billingInfo = new BillingInfo();
|
|
||||||
if(!string.IsNullOrWhiteSpace(subscriber.GatewayCustomerId))
|
|
||||||
{
|
|
||||||
var customer = await _gateway.Customer.FindAsync(subscriber.GatewayCustomerId);
|
|
||||||
if(customer != null)
|
|
||||||
{
|
|
||||||
if(customer.DefaultPaymentMethod != null)
|
|
||||||
{
|
|
||||||
billingInfo.PaymentSource = new BillingInfo.BillingSource(customer.DefaultPaymentMethod);
|
|
||||||
}
|
|
||||||
|
|
||||||
var transactionRequest = new TransactionSearchRequest().CustomerId.Is(customer.Id);
|
|
||||||
var transactions = _gateway.Transaction.Search(transactionRequest);
|
|
||||||
billingInfo.Charges = transactions?.Cast<Braintree.Transaction>()
|
|
||||||
.OrderByDescending(t => t.CreatedAt).Select(t => new BillingInfo.BillingCharge(t));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!string.IsNullOrWhiteSpace(subscriber.GatewaySubscriptionId))
|
|
||||||
{
|
|
||||||
var sub = await _gateway.Subscription.FindAsync(subscriber.GatewaySubscriptionId);
|
|
||||||
if(sub != null)
|
|
||||||
{
|
|
||||||
var plans = await _gateway.Plan.AllAsync();
|
|
||||||
var plan = plans?.FirstOrDefault(p => p.Id == sub.PlanId);
|
|
||||||
billingInfo.Subscription = new BillingInfo.BillingSubscription(sub, plan);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!billingInfo.Subscription.Cancelled && !billingInfo.Subscription.CancelAtEndDate &&
|
|
||||||
sub.NextBillingDate.HasValue)
|
|
||||||
{
|
|
||||||
billingInfo.UpcomingInvoice = new BillingInfo.BillingInvoice(sub);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return billingInfo;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task PurchasePremiumAsync(User user, PaymentMethodType paymentMethodType, string paymentToken,
|
|
||||||
short additionalStorageGb)
|
|
||||||
{
|
|
||||||
var customerResult = await _gateway.Customer.CreateAsync(new CustomerRequest
|
|
||||||
{
|
|
||||||
PaymentMethodNonce = paymentToken,
|
|
||||||
Email = user.Email,
|
|
||||||
CustomFields = new Dictionary<string, string>
|
|
||||||
{
|
|
||||||
[user.BraintreeIdField()] = user.Id.ToString()
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if(!customerResult.IsSuccess() || customerResult.Target.PaymentMethods.Length == 0)
|
|
||||||
{
|
|
||||||
throw new GatewayException("Failed to create customer.");
|
|
||||||
}
|
|
||||||
|
|
||||||
var subId = user.BraintreeCustomerIdPrefix() + user.Id.ToString("N").ToLower() +
|
|
||||||
Utilities.CoreHelpers.RandomString(3, upper: false, numeric: false);
|
|
||||||
|
|
||||||
var subRequest = new SubscriptionRequest
|
|
||||||
{
|
|
||||||
Id = subId,
|
|
||||||
PaymentMethodToken = customerResult.Target.PaymentMethods[0].Token,
|
|
||||||
PlanId = PremiumPlanId
|
|
||||||
};
|
|
||||||
|
|
||||||
if(additionalStorageGb > 0)
|
|
||||||
{
|
|
||||||
subRequest.AddOns = new AddOnsRequest();
|
|
||||||
subRequest.AddOns.Add = new AddAddOnRequest[]
|
|
||||||
{
|
|
||||||
new AddAddOnRequest
|
|
||||||
{
|
|
||||||
InheritedFromId = StoragePlanId,
|
|
||||||
Quantity = additionalStorageGb
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
var subResult = await _gateway.Subscription.CreateAsync(subRequest);
|
|
||||||
|
|
||||||
if(!subResult.IsSuccess())
|
|
||||||
{
|
|
||||||
await _gateway.Customer.DeleteAsync(customerResult.Target.Id);
|
|
||||||
throw new GatewayException("Failed to create subscription.");
|
|
||||||
}
|
|
||||||
|
|
||||||
user.Gateway = Enums.GatewayType.Braintree;
|
|
||||||
user.GatewayCustomerId = customerResult.Target.Id;
|
|
||||||
user.GatewaySubscriptionId = subResult.Target.Id;
|
|
||||||
user.Premium = true;
|
|
||||||
user.PremiumExpirationDate = subResult.Target.BillingPeriodEndDate;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task ReinstateSubscriptionAsync(ISubscriber subscriber)
|
|
||||||
{
|
|
||||||
if(subscriber == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(subscriber));
|
|
||||||
}
|
|
||||||
|
|
||||||
if(string.IsNullOrWhiteSpace(subscriber.GatewaySubscriptionId))
|
|
||||||
{
|
|
||||||
throw new GatewayException("No subscription.");
|
|
||||||
}
|
|
||||||
|
|
||||||
var sub = await _gateway.Subscription.FindAsync(subscriber.GatewaySubscriptionId);
|
|
||||||
if(sub == null)
|
|
||||||
{
|
|
||||||
throw new GatewayException("Subscription was not found.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if(sub.Status != SubscriptionStatus.ACTIVE || sub.NeverExpires.GetValueOrDefault())
|
|
||||||
{
|
|
||||||
throw new GatewayException("Subscription is not marked for cancellation.");
|
|
||||||
}
|
|
||||||
|
|
||||||
var req = new SubscriptionRequest
|
|
||||||
{
|
|
||||||
NeverExpires = true,
|
|
||||||
NumberOfBillingCycles = null
|
|
||||||
};
|
|
||||||
|
|
||||||
var result = await _gateway.Subscription.UpdateAsync(subscriber.GatewaySubscriptionId, req);
|
|
||||||
if(!result.IsSuccess())
|
|
||||||
{
|
|
||||||
throw new GatewayException("Unable to reinstate subscription.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<bool> UpdatePaymentMethodAsync(ISubscriber subscriber, PaymentMethodType paymentMethodType,
|
|
||||||
string paymentToken)
|
|
||||||
{
|
|
||||||
if(subscriber == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(subscriber));
|
|
||||||
}
|
|
||||||
|
|
||||||
if(paymentMethodType != PaymentMethodType.PayPal)
|
|
||||||
{
|
|
||||||
throw new GatewayException("Payment method not allowed");
|
|
||||||
}
|
|
||||||
|
|
||||||
if(subscriber.Gateway.HasValue && subscriber.Gateway.Value != GatewayType.Braintree)
|
|
||||||
{
|
|
||||||
throw new GatewayException("Switching from one payment type to another is not supported. " +
|
|
||||||
"Contact us for assistance.");
|
|
||||||
}
|
|
||||||
|
|
||||||
var updatedSubscriber = false;
|
|
||||||
Customer customer = null;
|
|
||||||
|
|
||||||
if(!string.IsNullOrWhiteSpace(subscriber.GatewayCustomerId))
|
|
||||||
{
|
|
||||||
customer = await _gateway.Customer.FindAsync(subscriber.GatewayCustomerId);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(customer == null)
|
|
||||||
{
|
|
||||||
var result = await _gateway.Customer.CreateAsync(new CustomerRequest
|
|
||||||
{
|
|
||||||
Email = subscriber.BillingEmailAddress(),
|
|
||||||
PaymentMethodNonce = paymentToken,
|
|
||||||
CustomFields = new Dictionary<string, string>
|
|
||||||
{
|
|
||||||
[subscriber.BraintreeIdField()] = subscriber.Id.ToString()
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if(!result.IsSuccess())
|
|
||||||
{
|
|
||||||
throw new GatewayException("Cannot create customer.");
|
|
||||||
}
|
|
||||||
|
|
||||||
customer = result.Target;
|
|
||||||
subscriber.Gateway = Enums.GatewayType.Braintree;
|
|
||||||
subscriber.GatewayCustomerId = customer.Id;
|
|
||||||
updatedSubscriber = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if(customer.DefaultPaymentMethod != null)
|
|
||||||
{
|
|
||||||
var deleteResult = await _gateway.PaymentMethod.DeleteAsync(customer.DefaultPaymentMethod.Token);
|
|
||||||
if(!deleteResult.IsSuccess())
|
|
||||||
{
|
|
||||||
throw new GatewayException("Cannot delete old payment method.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var result = await _gateway.PaymentMethod.CreateAsync(new PaymentMethodRequest
|
|
||||||
{
|
|
||||||
PaymentMethodNonce = paymentToken,
|
|
||||||
CustomerId = customer.Id
|
|
||||||
});
|
|
||||||
if(!result.IsSuccess())
|
|
||||||
{
|
|
||||||
throw new GatewayException("Cannot add new payment method.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return updatedSubscriber;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -32,7 +32,7 @@ namespace Bit.Core.Services
|
|||||||
private readonly IEventService _eventService;
|
private readonly IEventService _eventService;
|
||||||
private readonly IInstallationRepository _installationRepository;
|
private readonly IInstallationRepository _installationRepository;
|
||||||
private readonly IApplicationCacheService _applicationCacheService;
|
private readonly IApplicationCacheService _applicationCacheService;
|
||||||
private readonly StripePaymentService _stripePaymentService;
|
private readonly IPaymentService _paymentService;
|
||||||
private readonly GlobalSettings _globalSettings;
|
private readonly GlobalSettings _globalSettings;
|
||||||
|
|
||||||
public OrganizationService(
|
public OrganizationService(
|
||||||
@ -50,6 +50,7 @@ namespace Bit.Core.Services
|
|||||||
IEventService eventService,
|
IEventService eventService,
|
||||||
IInstallationRepository installationRepository,
|
IInstallationRepository installationRepository,
|
||||||
IApplicationCacheService applicationCacheService,
|
IApplicationCacheService applicationCacheService,
|
||||||
|
IPaymentService paymentService,
|
||||||
GlobalSettings globalSettings)
|
GlobalSettings globalSettings)
|
||||||
{
|
{
|
||||||
_organizationRepository = organizationRepository;
|
_organizationRepository = organizationRepository;
|
||||||
@ -66,7 +67,7 @@ namespace Bit.Core.Services
|
|||||||
_eventService = eventService;
|
_eventService = eventService;
|
||||||
_installationRepository = installationRepository;
|
_installationRepository = installationRepository;
|
||||||
_applicationCacheService = applicationCacheService;
|
_applicationCacheService = applicationCacheService;
|
||||||
_stripePaymentService = new StripePaymentService(globalSettings);
|
_paymentService = paymentService;
|
||||||
_globalSettings = globalSettings;
|
_globalSettings = globalSettings;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,7 +93,7 @@ namespace Bit.Core.Services
|
|||||||
paymentMethodType = PaymentMethodType.PayPal;
|
paymentMethodType = PaymentMethodType.PayPal;
|
||||||
}
|
}
|
||||||
|
|
||||||
var updated = await _stripePaymentService.UpdatePaymentMethodAsync(organization,
|
var updated = await _paymentService.UpdatePaymentMethodAsync(organization,
|
||||||
paymentMethodType, paymentToken);
|
paymentMethodType, paymentToken);
|
||||||
if(updated)
|
if(updated)
|
||||||
{
|
{
|
||||||
@ -115,7 +116,7 @@ namespace Bit.Core.Services
|
|||||||
eop = false;
|
eop = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
await _stripePaymentService.CancelSubscriptionAsync(organization, eop);
|
await _paymentService.CancelSubscriptionAsync(organization, eop);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task ReinstateSubscriptionAsync(Guid organizationId)
|
public async Task ReinstateSubscriptionAsync(Guid organizationId)
|
||||||
@ -126,7 +127,7 @@ namespace Bit.Core.Services
|
|||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
await _stripePaymentService.ReinstateSubscriptionAsync(organization);
|
await _paymentService.ReinstateSubscriptionAsync(organization);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task UpgradePlanAsync(Guid organizationId, PlanType plan, int additionalSeats)
|
public async Task UpgradePlanAsync(Guid organizationId, PlanType plan, int additionalSeats)
|
||||||
@ -286,7 +287,7 @@ namespace Bit.Core.Services
|
|||||||
throw new BadRequestException("Plan does not allow additional storage.");
|
throw new BadRequestException("Plan does not allow additional storage.");
|
||||||
}
|
}
|
||||||
|
|
||||||
await BillingHelpers.AdjustStorageAsync(_stripePaymentService, organization, storageAdjustmentGb,
|
await BillingHelpers.AdjustStorageAsync(_paymentService, organization, storageAdjustmentGb,
|
||||||
plan.StripeStoragePlanId);
|
plan.StripeStoragePlanId);
|
||||||
await ReplaceAndUpdateCache(organization);
|
await ReplaceAndUpdateCache(organization);
|
||||||
}
|
}
|
||||||
@ -411,7 +412,7 @@ namespace Bit.Core.Services
|
|||||||
var invoicedNow = false;
|
var invoicedNow = false;
|
||||||
if(additionalSeats > 0)
|
if(additionalSeats > 0)
|
||||||
{
|
{
|
||||||
invoicedNow = await _stripePaymentService.PreviewUpcomingInvoiceAndPayAsync(
|
invoicedNow = await (_paymentService as StripePaymentService).PreviewUpcomingInvoiceAndPayAsync(
|
||||||
organization, plan.StripeSeatPlanId, subItemOptions, 500);
|
organization, plan.StripeSeatPlanId, subItemOptions, 500);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -561,7 +562,7 @@ namespace Bit.Core.Services
|
|||||||
paymentMethodType = PaymentMethodType.PayPal;
|
paymentMethodType = PaymentMethodType.PayPal;
|
||||||
}
|
}
|
||||||
|
|
||||||
await _stripePaymentService.PurchaseOrganizationAsync(organization, paymentMethodType,
|
await _paymentService.PurchaseOrganizationAsync(organization, paymentMethodType,
|
||||||
signup.PaymentToken, plan, signup.AdditionalStorageGb, signup.AdditionalSeats,
|
signup.PaymentToken, plan, signup.AdditionalStorageGb, signup.AdditionalSeats,
|
||||||
signup.PremiumAccessAddon);
|
signup.PremiumAccessAddon);
|
||||||
}
|
}
|
||||||
@ -676,7 +677,7 @@ namespace Bit.Core.Services
|
|||||||
{
|
{
|
||||||
if(withPayment)
|
if(withPayment)
|
||||||
{
|
{
|
||||||
await _stripePaymentService.CancelAndRecoverChargesAsync(organization);
|
await _paymentService.CancelAndRecoverChargesAsync(organization);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(organization.Id != default(Guid))
|
if(organization.Id != default(Guid))
|
||||||
@ -785,7 +786,7 @@ namespace Bit.Core.Services
|
|||||||
{
|
{
|
||||||
var eop = !organization.ExpirationDate.HasValue ||
|
var eop = !organization.ExpirationDate.HasValue ||
|
||||||
organization.ExpirationDate.Value >= DateTime.UtcNow;
|
organization.ExpirationDate.Value >= DateTime.UtcNow;
|
||||||
await _stripePaymentService.CancelSubscriptionAsync(organization, eop);
|
await _paymentService.CancelSubscriptionAsync(organization, eop);
|
||||||
}
|
}
|
||||||
catch(GatewayException) { }
|
catch(GatewayException) { }
|
||||||
}
|
}
|
||||||
@ -1206,8 +1207,7 @@ namespace Bit.Core.Services
|
|||||||
throw new BadRequestException("Invalid installation id");
|
throw new BadRequestException("Invalid installation id");
|
||||||
}
|
}
|
||||||
|
|
||||||
var paymentService = new StripePaymentService(_globalSettings);
|
var billingInfo = await _paymentService.GetBillingAsync(organization);
|
||||||
var billingInfo = await paymentService.GetBillingAsync(organization);
|
|
||||||
return new OrganizationLicense(organization, billingInfo, installationId, _licensingService);
|
return new OrganizationLicense(organization, billingInfo, installationId, _licensingService);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ using Bit.Core.Exceptions;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Bit.Core.Models.Business;
|
using Bit.Core.Models.Business;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
|
using Bit.Core.Repositories;
|
||||||
|
|
||||||
namespace Bit.Core.Services
|
namespace Bit.Core.Services
|
||||||
{
|
{
|
||||||
@ -15,9 +16,11 @@ namespace Bit.Core.Services
|
|||||||
private const string PremiumPlanId = "premium-annually";
|
private const string PremiumPlanId = "premium-annually";
|
||||||
private const string StoragePlanId = "storage-gb-annually";
|
private const string StoragePlanId = "storage-gb-annually";
|
||||||
|
|
||||||
|
private readonly ITransactionRepository _transactionRepository;
|
||||||
private readonly Braintree.BraintreeGateway _btGateway;
|
private readonly Braintree.BraintreeGateway _btGateway;
|
||||||
|
|
||||||
public StripePaymentService(
|
public StripePaymentService(
|
||||||
|
ITransactionRepository transactionRepository,
|
||||||
GlobalSettings globalSettings)
|
GlobalSettings globalSettings)
|
||||||
{
|
{
|
||||||
_btGateway = new Braintree.BraintreeGateway
|
_btGateway = new Braintree.BraintreeGateway
|
||||||
@ -28,6 +31,7 @@ namespace Bit.Core.Services
|
|||||||
PublicKey = globalSettings.Braintree.PublicKey,
|
PublicKey = globalSettings.Braintree.PublicKey,
|
||||||
PrivateKey = globalSettings.Braintree.PrivateKey
|
PrivateKey = globalSettings.Braintree.PrivateKey
|
||||||
};
|
};
|
||||||
|
_transactionRepository = transactionRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task PurchaseOrganizationAsync(Organization org, PaymentMethodType paymentMethodType,
|
public async Task PurchaseOrganizationAsync(Organization org, PaymentMethodType paymentMethodType,
|
||||||
@ -912,6 +916,22 @@ namespace Bit.Core.Services
|
|||||||
public async Task<BillingInfo> GetBillingAsync(ISubscriber subscriber)
|
public async Task<BillingInfo> GetBillingAsync(ISubscriber subscriber)
|
||||||
{
|
{
|
||||||
var billingInfo = new BillingInfo();
|
var billingInfo = new BillingInfo();
|
||||||
|
|
||||||
|
ICollection<Transaction> transactions = null;
|
||||||
|
if(subscriber is User)
|
||||||
|
{
|
||||||
|
transactions = await _transactionRepository.GetManyByUserIdAsync(subscriber.Id);
|
||||||
|
}
|
||||||
|
else if(subscriber is Organization)
|
||||||
|
{
|
||||||
|
transactions = await _transactionRepository.GetManyByOrganizationIdAsync(subscriber.Id);
|
||||||
|
}
|
||||||
|
if(transactions != null)
|
||||||
|
{
|
||||||
|
billingInfo.Transactions = transactions?.OrderByDescending(i => i.CreationDate)
|
||||||
|
.Select(t => new BillingInfo.BillingTransaction(t));
|
||||||
|
}
|
||||||
|
|
||||||
var customerService = new CustomerService();
|
var customerService = new CustomerService();
|
||||||
var subscriptionService = new SubscriptionService();
|
var subscriptionService = new SubscriptionService();
|
||||||
var chargeService = new ChargeService();
|
var chargeService = new ChargeService();
|
||||||
|
@ -42,6 +42,7 @@ namespace Bit.Core.Services
|
|||||||
private readonly ILicensingService _licenseService;
|
private readonly ILicensingService _licenseService;
|
||||||
private readonly IEventService _eventService;
|
private readonly IEventService _eventService;
|
||||||
private readonly IApplicationCacheService _applicationCacheService;
|
private readonly IApplicationCacheService _applicationCacheService;
|
||||||
|
private readonly IPaymentService _paymentService;
|
||||||
private readonly IDataProtector _organizationServiceDataProtector;
|
private readonly IDataProtector _organizationServiceDataProtector;
|
||||||
private readonly CurrentContext _currentContext;
|
private readonly CurrentContext _currentContext;
|
||||||
private readonly GlobalSettings _globalSettings;
|
private readonly GlobalSettings _globalSettings;
|
||||||
@ -67,6 +68,7 @@ namespace Bit.Core.Services
|
|||||||
IEventService eventService,
|
IEventService eventService,
|
||||||
IApplicationCacheService applicationCacheService,
|
IApplicationCacheService applicationCacheService,
|
||||||
IDataProtectionProvider dataProtectionProvider,
|
IDataProtectionProvider dataProtectionProvider,
|
||||||
|
IPaymentService paymentService,
|
||||||
CurrentContext currentContext,
|
CurrentContext currentContext,
|
||||||
GlobalSettings globalSettings)
|
GlobalSettings globalSettings)
|
||||||
: base(
|
: base(
|
||||||
@ -94,6 +96,7 @@ namespace Bit.Core.Services
|
|||||||
_licenseService = licenseService;
|
_licenseService = licenseService;
|
||||||
_eventService = eventService;
|
_eventService = eventService;
|
||||||
_applicationCacheService = applicationCacheService;
|
_applicationCacheService = applicationCacheService;
|
||||||
|
_paymentService = paymentService;
|
||||||
_organizationServiceDataProtector = dataProtectionProvider.CreateProtector(
|
_organizationServiceDataProtector = dataProtectionProvider.CreateProtector(
|
||||||
"OrganizationServiceDataProtector");
|
"OrganizationServiceDataProtector");
|
||||||
_currentContext = currentContext;
|
_currentContext = currentContext;
|
||||||
@ -717,7 +720,7 @@ namespace Bit.Core.Services
|
|||||||
paymentMethodType = PaymentMethodType.PayPal;
|
paymentMethodType = PaymentMethodType.PayPal;
|
||||||
}
|
}
|
||||||
|
|
||||||
await new StripePaymentService(_globalSettings).PurchasePremiumAsync(user, paymentMethodType,
|
await _paymentService.PurchasePremiumAsync(user, paymentMethodType,
|
||||||
paymentToken, additionalStorageGb);
|
paymentToken, additionalStorageGb);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -793,8 +796,7 @@ namespace Bit.Core.Services
|
|||||||
throw new BadRequestException("Not a premium user.");
|
throw new BadRequestException("Not a premium user.");
|
||||||
}
|
}
|
||||||
|
|
||||||
var paymentService = user.GetPaymentService(_globalSettings);
|
await BillingHelpers.AdjustStorageAsync(_paymentService, user, storageAdjustmentGb, StoragePlanId);
|
||||||
await BillingHelpers.AdjustStorageAsync(paymentService, user, storageAdjustmentGb, StoragePlanId);
|
|
||||||
await SaveUserAsync(user);
|
await SaveUserAsync(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -806,7 +808,6 @@ namespace Bit.Core.Services
|
|||||||
}
|
}
|
||||||
|
|
||||||
PaymentMethodType paymentMethodType;
|
PaymentMethodType paymentMethodType;
|
||||||
var paymentService = new StripePaymentService(_globalSettings);
|
|
||||||
if(paymentToken.StartsWith("tok_"))
|
if(paymentToken.StartsWith("tok_"))
|
||||||
{
|
{
|
||||||
paymentMethodType = PaymentMethodType.Card;
|
paymentMethodType = PaymentMethodType.Card;
|
||||||
@ -816,7 +817,7 @@ namespace Bit.Core.Services
|
|||||||
paymentMethodType = PaymentMethodType.PayPal;
|
paymentMethodType = PaymentMethodType.PayPal;
|
||||||
}
|
}
|
||||||
|
|
||||||
var updated = await paymentService.UpdatePaymentMethodAsync(user, paymentMethodType, paymentToken);
|
var updated = await _paymentService.UpdatePaymentMethodAsync(user, paymentMethodType, paymentToken);
|
||||||
if(updated)
|
if(updated)
|
||||||
{
|
{
|
||||||
await SaveUserAsync(user);
|
await SaveUserAsync(user);
|
||||||
@ -825,20 +826,18 @@ namespace Bit.Core.Services
|
|||||||
|
|
||||||
public async Task CancelPremiumAsync(User user, bool? endOfPeriod = null)
|
public async Task CancelPremiumAsync(User user, bool? endOfPeriod = null)
|
||||||
{
|
{
|
||||||
var paymentService = user.GetPaymentService(_globalSettings);
|
|
||||||
var eop = endOfPeriod.GetValueOrDefault(true);
|
var eop = endOfPeriod.GetValueOrDefault(true);
|
||||||
if(!endOfPeriod.HasValue && user.PremiumExpirationDate.HasValue &&
|
if(!endOfPeriod.HasValue && user.PremiumExpirationDate.HasValue &&
|
||||||
user.PremiumExpirationDate.Value < DateTime.UtcNow)
|
user.PremiumExpirationDate.Value < DateTime.UtcNow)
|
||||||
{
|
{
|
||||||
eop = false;
|
eop = false;
|
||||||
}
|
}
|
||||||
await paymentService.CancelSubscriptionAsync(user, eop);
|
await _paymentService.CancelSubscriptionAsync(user, eop);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task ReinstatePremiumAsync(User user)
|
public async Task ReinstatePremiumAsync(User user)
|
||||||
{
|
{
|
||||||
var paymentService = user.GetPaymentService(_globalSettings);
|
await _paymentService.ReinstateSubscriptionAsync(user);
|
||||||
await paymentService.ReinstateSubscriptionAsync(user);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task DisablePremiumAsync(Guid userId, DateTime? expirationDate)
|
public async Task DisablePremiumAsync(Guid userId, DateTime? expirationDate)
|
||||||
@ -878,8 +877,7 @@ namespace Bit.Core.Services
|
|||||||
|
|
||||||
if(billingInfo == null && user.Gateway != null)
|
if(billingInfo == null && user.Gateway != null)
|
||||||
{
|
{
|
||||||
var paymentService = user.GetPaymentService(_globalSettings);
|
billingInfo = await _paymentService.GetBillingAsync(user);
|
||||||
billingInfo = await paymentService.GetBillingAsync(user);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return billingInfo == null ? new UserLicense(user, _licenseService) :
|
return billingInfo == null ? new UserLicense(user, _licenseService) :
|
||||||
|
@ -78,6 +78,7 @@ namespace Bit.Core.Utilities
|
|||||||
|
|
||||||
public static void AddDefaultServices(this IServiceCollection services, GlobalSettings globalSettings)
|
public static void AddDefaultServices(this IServiceCollection services, GlobalSettings globalSettings)
|
||||||
{
|
{
|
||||||
|
services.AddSingleton<IPaymentService, StripePaymentService>();
|
||||||
services.AddSingleton<IMailService, HandlebarsMailService>();
|
services.AddSingleton<IMailService, HandlebarsMailService>();
|
||||||
services.AddSingleton<ILicensingService, LicensingService>();
|
services.AddSingleton<ILicensingService, LicensingService>();
|
||||||
services.AddSingleton<IApplicationCacheService, InMemoryApplicationCacheService>();
|
services.AddSingleton<IApplicationCacheService, InMemoryApplicationCacheService>();
|
||||||
|
Loading…
Reference in New Issue
Block a user