mirror of
https://github.com/bitwarden/server.git
synced 2024-11-29 13:25:17 +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 IOrganizationUserRepository _organizationUserRepository;
|
||||
private readonly ILicensingService _licenseService;
|
||||
private readonly IPaymentService _paymentService;
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
|
||||
public AccountsController(
|
||||
@ -38,6 +39,7 @@ namespace Bit.Api.Controllers
|
||||
ICipherService cipherService,
|
||||
IOrganizationUserRepository organizationUserRepository,
|
||||
ILicensingService licenseService,
|
||||
IPaymentService paymentService,
|
||||
GlobalSettings globalSettings)
|
||||
{
|
||||
_userService = userService;
|
||||
@ -47,6 +49,7 @@ namespace Bit.Api.Controllers
|
||||
_cipherService = cipherService;
|
||||
_organizationUserRepository = organizationUserRepository;
|
||||
_licenseService = licenseService;
|
||||
_paymentService = paymentService;
|
||||
_globalSettings = globalSettings;
|
||||
}
|
||||
|
||||
@ -476,8 +479,7 @@ namespace Bit.Api.Controllers
|
||||
|
||||
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);
|
||||
return new BillingResponseModel(user, billingInfo, license);
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ namespace Bit.Api.Controllers
|
||||
private readonly IOrganizationUserRepository _organizationUserRepository;
|
||||
private readonly IOrganizationService _organizationService;
|
||||
private readonly IUserService _userService;
|
||||
private readonly IPaymentService _paymentService;
|
||||
private readonly CurrentContext _currentContext;
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
|
||||
@ -32,6 +33,7 @@ namespace Bit.Api.Controllers
|
||||
IOrganizationUserRepository organizationUserRepository,
|
||||
IOrganizationService organizationService,
|
||||
IUserService userService,
|
||||
IPaymentService paymentService,
|
||||
CurrentContext currentContext,
|
||||
GlobalSettings globalSettings)
|
||||
{
|
||||
@ -39,6 +41,7 @@ namespace Bit.Api.Controllers
|
||||
_organizationUserRepository = organizationUserRepository;
|
||||
_organizationService = organizationService;
|
||||
_userService = userService;
|
||||
_paymentService = paymentService;
|
||||
_currentContext = currentContext;
|
||||
_globalSettings = globalSettings;
|
||||
}
|
||||
@ -78,8 +81,7 @@ namespace Bit.Api.Controllers
|
||||
|
||||
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)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
|
@ -17,12 +17,14 @@ namespace Bit.Billing.Jobs
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly IUserRepository _userRepository;
|
||||
private readonly IMailService _mailService;
|
||||
private readonly IPaymentService _paymentService;
|
||||
|
||||
public PremiumRenewalRemindersJob(
|
||||
IOptions<BillingSettings> billingSettings,
|
||||
GlobalSettings globalSettings,
|
||||
IUserRepository userRepository,
|
||||
IMailService mailService,
|
||||
IPaymentService paymentService,
|
||||
ILogger<PremiumRenewalRemindersJob> logger)
|
||||
: base(logger)
|
||||
{
|
||||
@ -30,6 +32,7 @@ namespace Bit.Billing.Jobs
|
||||
_globalSettings = globalSettings;
|
||||
_userRepository = userRepository;
|
||||
_mailService = mailService;
|
||||
_paymentService = paymentService;
|
||||
}
|
||||
|
||||
protected async override Task ExecuteJobAsync(IJobExecutionContext context)
|
||||
@ -37,8 +40,7 @@ namespace Bit.Billing.Jobs
|
||||
var users = await _userRepository.GetManyByPremiumRenewalAsync();
|
||||
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)
|
||||
{
|
||||
var items = new List<string> { "1 × Premium Membership (Annually)" };
|
||||
|
@ -15,6 +15,5 @@ namespace Bit.Core.Models.Table
|
||||
string BraintreeCustomerIdPrefix();
|
||||
string BraintreeIdField();
|
||||
string GatewayIdField();
|
||||
IPaymentService GetPaymentService(GlobalSettings globalSettings);
|
||||
}
|
||||
}
|
||||
|
@ -99,29 +99,6 @@ namespace Bit.Core.Models.Table
|
||||
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()
|
||||
{
|
||||
if(string.IsNullOrWhiteSpace(TwoFactorProviders))
|
||||
|
@ -148,29 +148,6 @@ namespace Bit.Core.Models.Table
|
||||
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)
|
||||
{
|
||||
return new IdentityUser
|
||||
|
@ -8,6 +8,8 @@ namespace Bit.Core.Services
|
||||
public interface IPaymentService
|
||||
{
|
||||
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,
|
||||
short additionalStorageGb);
|
||||
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 IInstallationRepository _installationRepository;
|
||||
private readonly IApplicationCacheService _applicationCacheService;
|
||||
private readonly StripePaymentService _stripePaymentService;
|
||||
private readonly IPaymentService _paymentService;
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
|
||||
public OrganizationService(
|
||||
@ -50,6 +50,7 @@ namespace Bit.Core.Services
|
||||
IEventService eventService,
|
||||
IInstallationRepository installationRepository,
|
||||
IApplicationCacheService applicationCacheService,
|
||||
IPaymentService paymentService,
|
||||
GlobalSettings globalSettings)
|
||||
{
|
||||
_organizationRepository = organizationRepository;
|
||||
@ -66,7 +67,7 @@ namespace Bit.Core.Services
|
||||
_eventService = eventService;
|
||||
_installationRepository = installationRepository;
|
||||
_applicationCacheService = applicationCacheService;
|
||||
_stripePaymentService = new StripePaymentService(globalSettings);
|
||||
_paymentService = paymentService;
|
||||
_globalSettings = globalSettings;
|
||||
}
|
||||
|
||||
@ -92,7 +93,7 @@ namespace Bit.Core.Services
|
||||
paymentMethodType = PaymentMethodType.PayPal;
|
||||
}
|
||||
|
||||
var updated = await _stripePaymentService.UpdatePaymentMethodAsync(organization,
|
||||
var updated = await _paymentService.UpdatePaymentMethodAsync(organization,
|
||||
paymentMethodType, paymentToken);
|
||||
if(updated)
|
||||
{
|
||||
@ -115,7 +116,7 @@ namespace Bit.Core.Services
|
||||
eop = false;
|
||||
}
|
||||
|
||||
await _stripePaymentService.CancelSubscriptionAsync(organization, eop);
|
||||
await _paymentService.CancelSubscriptionAsync(organization, eop);
|
||||
}
|
||||
|
||||
public async Task ReinstateSubscriptionAsync(Guid organizationId)
|
||||
@ -126,7 +127,7 @@ namespace Bit.Core.Services
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
await _stripePaymentService.ReinstateSubscriptionAsync(organization);
|
||||
await _paymentService.ReinstateSubscriptionAsync(organization);
|
||||
}
|
||||
|
||||
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.");
|
||||
}
|
||||
|
||||
await BillingHelpers.AdjustStorageAsync(_stripePaymentService, organization, storageAdjustmentGb,
|
||||
await BillingHelpers.AdjustStorageAsync(_paymentService, organization, storageAdjustmentGb,
|
||||
plan.StripeStoragePlanId);
|
||||
await ReplaceAndUpdateCache(organization);
|
||||
}
|
||||
@ -411,7 +412,7 @@ namespace Bit.Core.Services
|
||||
var invoicedNow = false;
|
||||
if(additionalSeats > 0)
|
||||
{
|
||||
invoicedNow = await _stripePaymentService.PreviewUpcomingInvoiceAndPayAsync(
|
||||
invoicedNow = await (_paymentService as StripePaymentService).PreviewUpcomingInvoiceAndPayAsync(
|
||||
organization, plan.StripeSeatPlanId, subItemOptions, 500);
|
||||
}
|
||||
|
||||
@ -561,7 +562,7 @@ namespace Bit.Core.Services
|
||||
paymentMethodType = PaymentMethodType.PayPal;
|
||||
}
|
||||
|
||||
await _stripePaymentService.PurchaseOrganizationAsync(organization, paymentMethodType,
|
||||
await _paymentService.PurchaseOrganizationAsync(organization, paymentMethodType,
|
||||
signup.PaymentToken, plan, signup.AdditionalStorageGb, signup.AdditionalSeats,
|
||||
signup.PremiumAccessAddon);
|
||||
}
|
||||
@ -676,7 +677,7 @@ namespace Bit.Core.Services
|
||||
{
|
||||
if(withPayment)
|
||||
{
|
||||
await _stripePaymentService.CancelAndRecoverChargesAsync(organization);
|
||||
await _paymentService.CancelAndRecoverChargesAsync(organization);
|
||||
}
|
||||
|
||||
if(organization.Id != default(Guid))
|
||||
@ -785,7 +786,7 @@ namespace Bit.Core.Services
|
||||
{
|
||||
var eop = !organization.ExpirationDate.HasValue ||
|
||||
organization.ExpirationDate.Value >= DateTime.UtcNow;
|
||||
await _stripePaymentService.CancelSubscriptionAsync(organization, eop);
|
||||
await _paymentService.CancelSubscriptionAsync(organization, eop);
|
||||
}
|
||||
catch(GatewayException) { }
|
||||
}
|
||||
@ -1206,8 +1207,7 @@ namespace Bit.Core.Services
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,7 @@ using Bit.Core.Exceptions;
|
||||
using System.Linq;
|
||||
using Bit.Core.Models.Business;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Repositories;
|
||||
|
||||
namespace Bit.Core.Services
|
||||
{
|
||||
@ -15,9 +16,11 @@ namespace Bit.Core.Services
|
||||
private const string PremiumPlanId = "premium-annually";
|
||||
private const string StoragePlanId = "storage-gb-annually";
|
||||
|
||||
private readonly ITransactionRepository _transactionRepository;
|
||||
private readonly Braintree.BraintreeGateway _btGateway;
|
||||
|
||||
public StripePaymentService(
|
||||
ITransactionRepository transactionRepository,
|
||||
GlobalSettings globalSettings)
|
||||
{
|
||||
_btGateway = new Braintree.BraintreeGateway
|
||||
@ -28,6 +31,7 @@ namespace Bit.Core.Services
|
||||
PublicKey = globalSettings.Braintree.PublicKey,
|
||||
PrivateKey = globalSettings.Braintree.PrivateKey
|
||||
};
|
||||
_transactionRepository = transactionRepository;
|
||||
}
|
||||
|
||||
public async Task PurchaseOrganizationAsync(Organization org, PaymentMethodType paymentMethodType,
|
||||
@ -912,6 +916,22 @@ namespace Bit.Core.Services
|
||||
public async Task<BillingInfo> GetBillingAsync(ISubscriber subscriber)
|
||||
{
|
||||
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 subscriptionService = new SubscriptionService();
|
||||
var chargeService = new ChargeService();
|
||||
|
@ -42,6 +42,7 @@ namespace Bit.Core.Services
|
||||
private readonly ILicensingService _licenseService;
|
||||
private readonly IEventService _eventService;
|
||||
private readonly IApplicationCacheService _applicationCacheService;
|
||||
private readonly IPaymentService _paymentService;
|
||||
private readonly IDataProtector _organizationServiceDataProtector;
|
||||
private readonly CurrentContext _currentContext;
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
@ -67,6 +68,7 @@ namespace Bit.Core.Services
|
||||
IEventService eventService,
|
||||
IApplicationCacheService applicationCacheService,
|
||||
IDataProtectionProvider dataProtectionProvider,
|
||||
IPaymentService paymentService,
|
||||
CurrentContext currentContext,
|
||||
GlobalSettings globalSettings)
|
||||
: base(
|
||||
@ -94,6 +96,7 @@ namespace Bit.Core.Services
|
||||
_licenseService = licenseService;
|
||||
_eventService = eventService;
|
||||
_applicationCacheService = applicationCacheService;
|
||||
_paymentService = paymentService;
|
||||
_organizationServiceDataProtector = dataProtectionProvider.CreateProtector(
|
||||
"OrganizationServiceDataProtector");
|
||||
_currentContext = currentContext;
|
||||
@ -717,7 +720,7 @@ namespace Bit.Core.Services
|
||||
paymentMethodType = PaymentMethodType.PayPal;
|
||||
}
|
||||
|
||||
await new StripePaymentService(_globalSettings).PurchasePremiumAsync(user, paymentMethodType,
|
||||
await _paymentService.PurchasePremiumAsync(user, paymentMethodType,
|
||||
paymentToken, additionalStorageGb);
|
||||
}
|
||||
else
|
||||
@ -793,8 +796,7 @@ namespace Bit.Core.Services
|
||||
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);
|
||||
}
|
||||
|
||||
@ -806,7 +808,6 @@ namespace Bit.Core.Services
|
||||
}
|
||||
|
||||
PaymentMethodType paymentMethodType;
|
||||
var paymentService = new StripePaymentService(_globalSettings);
|
||||
if(paymentToken.StartsWith("tok_"))
|
||||
{
|
||||
paymentMethodType = PaymentMethodType.Card;
|
||||
@ -816,7 +817,7 @@ namespace Bit.Core.Services
|
||||
paymentMethodType = PaymentMethodType.PayPal;
|
||||
}
|
||||
|
||||
var updated = await paymentService.UpdatePaymentMethodAsync(user, paymentMethodType, paymentToken);
|
||||
var updated = await _paymentService.UpdatePaymentMethodAsync(user, paymentMethodType, paymentToken);
|
||||
if(updated)
|
||||
{
|
||||
await SaveUserAsync(user);
|
||||
@ -825,20 +826,18 @@ namespace Bit.Core.Services
|
||||
|
||||
public async Task CancelPremiumAsync(User user, bool? endOfPeriod = null)
|
||||
{
|
||||
var paymentService = user.GetPaymentService(_globalSettings);
|
||||
var eop = endOfPeriod.GetValueOrDefault(true);
|
||||
if(!endOfPeriod.HasValue && user.PremiumExpirationDate.HasValue &&
|
||||
user.PremiumExpirationDate.Value < DateTime.UtcNow)
|
||||
{
|
||||
eop = false;
|
||||
}
|
||||
await paymentService.CancelSubscriptionAsync(user, eop);
|
||||
await _paymentService.CancelSubscriptionAsync(user, eop);
|
||||
}
|
||||
|
||||
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)
|
||||
@ -878,8 +877,7 @@ namespace Bit.Core.Services
|
||||
|
||||
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) :
|
||||
|
@ -78,6 +78,7 @@ namespace Bit.Core.Utilities
|
||||
|
||||
public static void AddDefaultServices(this IServiceCollection services, GlobalSettings globalSettings)
|
||||
{
|
||||
services.AddSingleton<IPaymentService, StripePaymentService>();
|
||||
services.AddSingleton<IMailService, HandlebarsMailService>();
|
||||
services.AddSingleton<ILicensingService, LicensingService>();
|
||||
services.AddSingleton<IApplicationCacheService, InMemoryApplicationCacheService>();
|
||||
|
Loading…
Reference in New Issue
Block a user