mirror of
https://github.com/bitwarden/server.git
synced 2025-04-08 06:28:14 -05:00
payment intent/method support for incomplete status
This commit is contained in:
parent
efcf626999
commit
00e808d731
@ -443,7 +443,7 @@ namespace Bit.Api.Controllers
|
|||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("premium")]
|
[HttpPost("premium")]
|
||||||
public async Task<ProfileResponseModel> PostPremium(PremiumRequestModel model)
|
public async Task<PaymentResponseModel> PostPremium(PremiumRequestModel model)
|
||||||
{
|
{
|
||||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||||
if(user == null)
|
if(user == null)
|
||||||
@ -463,9 +463,15 @@ namespace Bit.Api.Controllers
|
|||||||
throw new BadRequestException("Invalid license.");
|
throw new BadRequestException("Invalid license.");
|
||||||
}
|
}
|
||||||
|
|
||||||
await _userService.SignUpPremiumAsync(user, model.PaymentToken, model.PaymentMethodType.Value,
|
var result = await _userService.SignUpPremiumAsync(user, model.PaymentToken,
|
||||||
model.AdditionalStorageGb.GetValueOrDefault(0), license);
|
model.PaymentMethodType.Value, model.AdditionalStorageGb.GetValueOrDefault(0), license);
|
||||||
return new ProfileResponseModel(user, null, await _userService.TwoFactorIsEnabledAsync(user));
|
var profile = new ProfileResponseModel(user, null, await _userService.TwoFactorIsEnabledAsync(user));
|
||||||
|
return new PaymentResponseModel
|
||||||
|
{
|
||||||
|
UserProfile = profile,
|
||||||
|
PaymentIntentClientSecret = result.Item2,
|
||||||
|
Success = result.Item1
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("billing")]
|
[HttpGet("billing")]
|
||||||
|
@ -215,7 +215,7 @@ namespace Bit.Api.Controllers
|
|||||||
|
|
||||||
[HttpPost("{id}/upgrade")]
|
[HttpPost("{id}/upgrade")]
|
||||||
[SelfHosted(NotSelfHostedOnly = true)]
|
[SelfHosted(NotSelfHostedOnly = true)]
|
||||||
public async Task PostUpgrade(string id, [FromBody]OrganizationUpgradeRequestModel model)
|
public async Task<PaymentResponseModel> PostUpgrade(string id, [FromBody]OrganizationUpgradeRequestModel model)
|
||||||
{
|
{
|
||||||
var orgIdGuid = new Guid(id);
|
var orgIdGuid = new Guid(id);
|
||||||
if(!_currentContext.OrganizationOwner(orgIdGuid))
|
if(!_currentContext.OrganizationOwner(orgIdGuid))
|
||||||
@ -223,7 +223,12 @@ namespace Bit.Api.Controllers
|
|||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
await _organizationService.UpgradePlanAsync(orgIdGuid, model.ToOrganizationUpgrade());
|
var result = await _organizationService.UpgradePlanAsync(orgIdGuid, model.ToOrganizationUpgrade());
|
||||||
|
return new PaymentResponseModel
|
||||||
|
{
|
||||||
|
Success = result.Item1,
|
||||||
|
PaymentIntentClientSecret = result.Item2
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("{id}/seat")]
|
[HttpPost("{id}/seat")]
|
||||||
|
@ -3,6 +3,7 @@ using Bit.Core.Enums;
|
|||||||
using Bit.Core.Models.Table;
|
using Bit.Core.Models.Table;
|
||||||
using Bit.Core.Repositories;
|
using Bit.Core.Repositories;
|
||||||
using Bit.Core.Services;
|
using Bit.Core.Services;
|
||||||
|
using Bit.Core.Utilities;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
@ -338,6 +339,39 @@ namespace Bit.Billing.Controllers
|
|||||||
_logger.LogWarning("Charge refund amount doesn't seem correct. " + charge.Id);
|
_logger.LogWarning("Charge refund amount doesn't seem correct. " + charge.Id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if(parsedEvent.Type.Equals("invoice.payment_succeeded"))
|
||||||
|
{
|
||||||
|
if(!(parsedEvent.Data.Object is Invoice invoice))
|
||||||
|
{
|
||||||
|
throw new Exception("Invoice is null. " + parsedEvent.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(invoice.Paid && invoice.BillingReason == "subscription_create")
|
||||||
|
{
|
||||||
|
var subscriptionService = new SubscriptionService();
|
||||||
|
var subscription = await subscriptionService.GetAsync(invoice.SubscriptionId);
|
||||||
|
if(subscription?.Status == "active")
|
||||||
|
{
|
||||||
|
var ids = GetIdsFromMetaData(subscription.Metadata);
|
||||||
|
// org
|
||||||
|
if(ids.Item1.HasValue)
|
||||||
|
{
|
||||||
|
if(subscription.Items.Any(i => StaticStore.Plans.Any(p => p.StripePlanId == i.Plan.Id)))
|
||||||
|
{
|
||||||
|
await _organizationService.EnableAsync(ids.Item1.Value, subscription.CurrentPeriodEnd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// user
|
||||||
|
else if(ids.Item2.HasValue)
|
||||||
|
{
|
||||||
|
if(subscription.Items.Any(i => i.Plan.Id == "premium-annually"))
|
||||||
|
{
|
||||||
|
await _userService.EnablePremiumAsync(ids.Item2.Value, subscription.CurrentPeriodEnd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
else if(parsedEvent.Type.Equals("invoice.payment_failed"))
|
else if(parsedEvent.Type.Equals("invoice.payment_failed"))
|
||||||
{
|
{
|
||||||
if(!(parsedEvent.Data.Object is Invoice invoice))
|
if(!(parsedEvent.Data.Object is Invoice invoice))
|
||||||
|
13
src/Core/Models/Api/Response/PaymentResponseModel.cs
Normal file
13
src/Core/Models/Api/Response/PaymentResponseModel.cs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
namespace Bit.Core.Models.Api
|
||||||
|
{
|
||||||
|
public class PaymentResponseModel : ResponseModel
|
||||||
|
{
|
||||||
|
public PaymentResponseModel()
|
||||||
|
: base("payment")
|
||||||
|
{ }
|
||||||
|
|
||||||
|
public ProfileResponseModel UserProfile { get; set; }
|
||||||
|
public string PaymentIntentClientSecret { get; set; }
|
||||||
|
public bool Success { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -13,7 +13,7 @@ namespace Bit.Core.Services
|
|||||||
Task ReplacePaymentMethodAsync(Guid organizationId, string paymentToken, PaymentMethodType paymentMethodType);
|
Task ReplacePaymentMethodAsync(Guid organizationId, string paymentToken, PaymentMethodType paymentMethodType);
|
||||||
Task CancelSubscriptionAsync(Guid organizationId, bool? endOfPeriod = null);
|
Task CancelSubscriptionAsync(Guid organizationId, bool? endOfPeriod = null);
|
||||||
Task ReinstateSubscriptionAsync(Guid organizationId);
|
Task ReinstateSubscriptionAsync(Guid organizationId);
|
||||||
Task UpgradePlanAsync(Guid organizationId, OrganizationUpgrade upgrade);
|
Task<Tuple<bool, string>> UpgradePlanAsync(Guid organizationId, OrganizationUpgrade upgrade);
|
||||||
Task AdjustStorageAsync(Guid organizationId, short storageAdjustmentGb);
|
Task AdjustStorageAsync(Guid organizationId, short storageAdjustmentGb);
|
||||||
Task AdjustSeatsAsync(Guid organizationId, int seatAdjustment);
|
Task AdjustSeatsAsync(Guid organizationId, int seatAdjustment);
|
||||||
Task VerifyBankAsync(Guid organizationId, int amount1, int amount2);
|
Task VerifyBankAsync(Guid organizationId, int amount1, int amount2);
|
||||||
@ -22,6 +22,7 @@ namespace Bit.Core.Services
|
|||||||
string ownerKey, string collectionName);
|
string ownerKey, string collectionName);
|
||||||
Task UpdateLicenseAsync(Guid organizationId, OrganizationLicense license);
|
Task UpdateLicenseAsync(Guid organizationId, OrganizationLicense license);
|
||||||
Task DeleteAsync(Organization organization);
|
Task DeleteAsync(Organization organization);
|
||||||
|
Task EnableAsync(Guid organizationId, DateTime? expirationDate);
|
||||||
Task DisableAsync(Guid organizationId, DateTime? expirationDate);
|
Task DisableAsync(Guid organizationId, DateTime? expirationDate);
|
||||||
Task UpdateExpirationDateAsync(Guid organizationId, DateTime? expirationDate);
|
Task UpdateExpirationDateAsync(Guid organizationId, DateTime? expirationDate);
|
||||||
Task EnableAsync(Guid organizationId);
|
Task EnableAsync(Guid organizationId);
|
||||||
|
@ -10,9 +10,9 @@ namespace Bit.Core.Services
|
|||||||
Task CancelAndRecoverChargesAsync(ISubscriber subscriber);
|
Task CancelAndRecoverChargesAsync(ISubscriber subscriber);
|
||||||
Task PurchaseOrganizationAsync(Organization org, PaymentMethodType paymentMethodType, string paymentToken,
|
Task PurchaseOrganizationAsync(Organization org, PaymentMethodType paymentMethodType, string paymentToken,
|
||||||
Models.StaticStore.Plan plan, short additionalStorageGb, short additionalSeats, bool premiumAccessAddon);
|
Models.StaticStore.Plan plan, short additionalStorageGb, short additionalSeats, bool premiumAccessAddon);
|
||||||
Task 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 PurchasePremiumAsync(User user, PaymentMethodType paymentMethodType, string paymentToken,
|
Task<string> 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);
|
||||||
Task CancelSubscriptionAsync(ISubscriber subscriber, bool endOfPeriod = false);
|
Task CancelSubscriptionAsync(ISubscriber subscriber, bool endOfPeriod = false);
|
||||||
|
@ -43,13 +43,15 @@ namespace Bit.Core.Services
|
|||||||
Task<IdentityResult> DeleteAsync(User user);
|
Task<IdentityResult> DeleteAsync(User user);
|
||||||
Task<IdentityResult> DeleteAsync(User user, string token);
|
Task<IdentityResult> DeleteAsync(User user, string token);
|
||||||
Task SendDeleteConfirmationAsync(string email);
|
Task SendDeleteConfirmationAsync(string email);
|
||||||
Task SignUpPremiumAsync(User user, string paymentToken, PaymentMethodType paymentMethodType,
|
Task<Tuple<bool, string>> SignUpPremiumAsync(User user, string paymentToken,
|
||||||
short additionalStorageGb, UserLicense license);
|
PaymentMethodType paymentMethodType, short additionalStorageGb, UserLicense license);
|
||||||
Task UpdateLicenseAsync(User user, UserLicense license);
|
Task UpdateLicenseAsync(User user, UserLicense license);
|
||||||
Task AdjustStorageAsync(User user, short storageAdjustmentGb);
|
Task AdjustStorageAsync(User user, short storageAdjustmentGb);
|
||||||
Task ReplacePaymentMethodAsync(User user, string paymentToken, PaymentMethodType paymentMethodType);
|
Task ReplacePaymentMethodAsync(User user, string paymentToken, PaymentMethodType paymentMethodType);
|
||||||
Task CancelPremiumAsync(User user, bool? endOfPeriod = null);
|
Task CancelPremiumAsync(User user, bool? endOfPeriod = null);
|
||||||
Task ReinstatePremiumAsync(User user);
|
Task ReinstatePremiumAsync(User user);
|
||||||
|
Task EnablePremiumAsync(Guid userId, DateTime? expirationDate);
|
||||||
|
Task EnablePremiumAsync(User user, DateTime? expirationDate);
|
||||||
Task DisablePremiumAsync(Guid userId, DateTime? expirationDate);
|
Task DisablePremiumAsync(Guid userId, DateTime? expirationDate);
|
||||||
Task DisablePremiumAsync(User user, DateTime? expirationDate);
|
Task DisablePremiumAsync(User user, DateTime? expirationDate);
|
||||||
Task UpdatePremiumExpirationAsync(Guid userId, DateTime? expirationDate);
|
Task UpdatePremiumExpirationAsync(Guid userId, DateTime? expirationDate);
|
||||||
|
@ -117,7 +117,7 @@ namespace Bit.Core.Services
|
|||||||
await _paymentService.ReinstateSubscriptionAsync(organization);
|
await _paymentService.ReinstateSubscriptionAsync(organization);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task UpgradePlanAsync(Guid organizationId, OrganizationUpgrade upgrade)
|
public async Task<Tuple<bool, string>> UpgradePlanAsync(Guid organizationId, OrganizationUpgrade upgrade)
|
||||||
{
|
{
|
||||||
var organization = await GetOrgById(organizationId);
|
var organization = await GetOrgById(organizationId);
|
||||||
if(organization == null)
|
if(organization == null)
|
||||||
@ -195,10 +195,13 @@ namespace Bit.Core.Services
|
|||||||
|
|
||||||
// TODO: Check storage?
|
// TODO: Check storage?
|
||||||
|
|
||||||
|
string paymentIntentClientSecret = null;
|
||||||
|
var success = true;
|
||||||
if(string.IsNullOrWhiteSpace(organization.GatewaySubscriptionId))
|
if(string.IsNullOrWhiteSpace(organization.GatewaySubscriptionId))
|
||||||
{
|
{
|
||||||
await _paymentService.UpgradeFreeOrganizationAsync(organization, newPlan,
|
paymentIntentClientSecret = await _paymentService.UpgradeFreeOrganizationAsync(organization, newPlan,
|
||||||
upgrade.AdditionalStorageGb, upgrade.AdditionalSeats, upgrade.PremiumAccessAddon);
|
upgrade.AdditionalStorageGb, upgrade.AdditionalSeats, upgrade.PremiumAccessAddon);
|
||||||
|
success = string.IsNullOrWhiteSpace(paymentIntentClientSecret);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -221,9 +224,10 @@ namespace Bit.Core.Services
|
|||||||
organization.SelfHost = newPlan.SelfHost;
|
organization.SelfHost = newPlan.SelfHost;
|
||||||
organization.UsersGetPremium = newPlan.UsersGetPremium || upgrade.PremiumAccessAddon;
|
organization.UsersGetPremium = newPlan.UsersGetPremium || upgrade.PremiumAccessAddon;
|
||||||
organization.Plan = newPlan.Name;
|
organization.Plan = newPlan.Name;
|
||||||
organization.Enabled = true;
|
organization.Enabled = success;
|
||||||
organization.RevisionDate = DateTime.UtcNow;
|
|
||||||
await ReplaceAndUpdateCache(organization);
|
await ReplaceAndUpdateCache(organization);
|
||||||
|
|
||||||
|
return new Tuple<bool, string>(success, paymentIntentClientSecret);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task AdjustStorageAsync(Guid organizationId, short storageAdjustmentGb)
|
public async Task AdjustStorageAsync(Guid organizationId, short storageAdjustmentGb)
|
||||||
@ -710,6 +714,18 @@ namespace Bit.Core.Services
|
|||||||
await _applicationCacheService.DeleteOrganizationAbilityAsync(organization.Id);
|
await _applicationCacheService.DeleteOrganizationAbilityAsync(organization.Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task EnableAsync(Guid organizationId, DateTime? expirationDate)
|
||||||
|
{
|
||||||
|
var org = await GetOrgById(organizationId);
|
||||||
|
if(org != null && !org.Enabled)
|
||||||
|
{
|
||||||
|
org.Enabled = true;
|
||||||
|
org.ExpirationDate = expirationDate;
|
||||||
|
org.RevisionDate = DateTime.UtcNow;
|
||||||
|
await ReplaceAndUpdateCache(org);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public async Task DisableAsync(Guid organizationId, DateTime? expirationDate)
|
public async Task DisableAsync(Guid organizationId, DateTime? expirationDate)
|
||||||
{
|
{
|
||||||
var org = await GetOrgById(organizationId);
|
var org = await GetOrgById(organizationId);
|
||||||
|
@ -161,7 +161,7 @@ namespace Bit.Core.Services
|
|||||||
org.ExpirationDate = subscription.CurrentPeriodEnd;
|
org.ExpirationDate = subscription.CurrentPeriodEnd;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task UpgradeFreeOrganizationAsync(Organization org, Models.StaticStore.Plan plan,
|
public async Task<string> UpgradeFreeOrganizationAsync(Organization org, Models.StaticStore.Plan plan,
|
||||||
short additionalStorageGb, short additionalSeats, bool premiumAccessAddon)
|
short additionalStorageGb, short additionalSeats, bool premiumAccessAddon)
|
||||||
{
|
{
|
||||||
if(!string.IsNullOrWhiteSpace(org.GatewaySubscriptionId))
|
if(!string.IsNullOrWhiteSpace(org.GatewaySubscriptionId))
|
||||||
@ -231,28 +231,53 @@ namespace Bit.Core.Services
|
|||||||
{
|
{
|
||||||
paymentMethodType = PaymentMethodType.PayPal;
|
paymentMethodType = PaymentMethodType.PayPal;
|
||||||
}
|
}
|
||||||
if(!hasBtCustomerId && customer.DefaultSource != null)
|
else
|
||||||
{
|
{
|
||||||
if(customer.DefaultSource is Card || customer.DefaultSource is SourceCard)
|
if(customer.DefaultSource != null)
|
||||||
{
|
{
|
||||||
paymentMethodType = PaymentMethodType.Card;
|
if(customer.DefaultSource is Card || customer.DefaultSource is SourceCard)
|
||||||
stripePaymentMethod = true;
|
{
|
||||||
|
paymentMethodType = PaymentMethodType.Card;
|
||||||
|
stripePaymentMethod = true;
|
||||||
|
}
|
||||||
|
else if(customer.DefaultSource is BankAccount || customer.DefaultSource is SourceAchDebit)
|
||||||
|
{
|
||||||
|
paymentMethodType = PaymentMethodType.BankAccount;
|
||||||
|
stripePaymentMethod = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if(customer.DefaultSource is BankAccount || customer.DefaultSource is SourceAchDebit)
|
else
|
||||||
{
|
{
|
||||||
paymentMethodType = PaymentMethodType.BankAccount;
|
var paymentMethod = GetDefaultCardPaymentMethod(customer.Id);
|
||||||
stripePaymentMethod = true;
|
if(paymentMethod != null)
|
||||||
|
{
|
||||||
|
paymentMethodType = PaymentMethodType.Card;
|
||||||
|
stripePaymentMethod = true;
|
||||||
|
subCreateOptions.DefaultPaymentMethodId = paymentMethod.Id;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var subscription = await ChargeForNewSubscriptionAsync(org, customer, false,
|
var subscription = await ChargeForNewSubscriptionAsync(org, customer, false,
|
||||||
stripePaymentMethod, paymentMethodType, subCreateOptions, null);
|
stripePaymentMethod, paymentMethodType, subCreateOptions, null);
|
||||||
org.GatewaySubscriptionId = subscription.Id;
|
org.GatewaySubscriptionId = subscription.Id;
|
||||||
org.ExpirationDate = subscription.CurrentPeriodEnd;
|
|
||||||
|
if(subscription.Status == "incomplete" &&
|
||||||
|
subscription.LatestInvoice?.PaymentIntent?.Status == "requires_action")
|
||||||
|
{
|
||||||
|
org.Enabled = false;
|
||||||
|
return subscription.LatestInvoice.PaymentIntent.ClientSecret;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
org.Enabled = true;
|
||||||
|
org.ExpirationDate = subscription.CurrentPeriodEnd;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task PurchasePremiumAsync(User user, PaymentMethodType paymentMethodType, string paymentToken,
|
public async Task<string> PurchasePremiumAsync(User user, PaymentMethodType paymentMethodType,
|
||||||
short additionalStorageGb)
|
string paymentToken, short additionalStorageGb)
|
||||||
{
|
{
|
||||||
if(paymentMethodType != PaymentMethodType.Credit && string.IsNullOrWhiteSpace(paymentToken))
|
if(paymentMethodType != PaymentMethodType.Credit && string.IsNullOrWhiteSpace(paymentToken))
|
||||||
{
|
{
|
||||||
@ -384,8 +409,18 @@ namespace Bit.Core.Services
|
|||||||
user.Gateway = GatewayType.Stripe;
|
user.Gateway = GatewayType.Stripe;
|
||||||
user.GatewayCustomerId = customer.Id;
|
user.GatewayCustomerId = customer.Id;
|
||||||
user.GatewaySubscriptionId = subscription.Id;
|
user.GatewaySubscriptionId = subscription.Id;
|
||||||
user.Premium = true;
|
|
||||||
user.PremiumExpirationDate = subscription.CurrentPeriodEnd;
|
if(subscription.Status == "incomplete" &&
|
||||||
|
subscription.LatestInvoice?.PaymentIntent?.Status == "requires_action")
|
||||||
|
{
|
||||||
|
return subscription.LatestInvoice.PaymentIntent.ClientSecret;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
user.Premium = true;
|
||||||
|
user.PremiumExpirationDate = subscription.CurrentPeriodEnd;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<Subscription> ChargeForNewSubscriptionAsync(ISubscriber subcriber, Customer customer,
|
private async Task<Subscription> ChargeForNewSubscriptionAsync(ISubscriber subcriber, Customer customer,
|
||||||
@ -481,10 +516,6 @@ namespace Bit.Core.Services
|
|||||||
await subscriptionService.CancelAsync(subscription.Id, new SubscriptionCancelOptions());
|
await subscriptionService.CancelAsync(subscription.Id, new SubscriptionCancelOptions());
|
||||||
throw new GatewayException("Payment method failed.");
|
throw new GatewayException("Payment method failed.");
|
||||||
}
|
}
|
||||||
else if(subscription.LatestInvoice.PaymentIntent.Status == "requires_action")
|
|
||||||
{
|
|
||||||
// Needs SCA. Send email? Should be handled by Stripe.
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!stripePaymentMethod && subInvoiceMetadata.Any())
|
if(!stripePaymentMethod && subInvoiceMetadata.Any())
|
||||||
@ -1237,10 +1268,7 @@ namespace Bit.Core.Services
|
|||||||
}
|
}
|
||||||
if(billingInfo.PaymentSource == null)
|
if(billingInfo.PaymentSource == null)
|
||||||
{
|
{
|
||||||
var paymentMethodService = new PaymentMethodService();
|
var paymentMethod = GetDefaultCardPaymentMethod(customer.Id);
|
||||||
var cardPaymentMethods = paymentMethodService.ListAutoPaging(
|
|
||||||
new PaymentMethodListOptions { CustomerId = customer.Id, Type = "card" });
|
|
||||||
var paymentMethod = cardPaymentMethods.OrderByDescending(m => m.Created).FirstOrDefault();
|
|
||||||
if(paymentMethod != null)
|
if(paymentMethod != null)
|
||||||
{
|
{
|
||||||
billingInfo.PaymentSource = new BillingInfo.BillingSource(paymentMethod);
|
billingInfo.PaymentSource = new BillingInfo.BillingSource(paymentMethod);
|
||||||
@ -1292,5 +1320,13 @@ namespace Bit.Core.Services
|
|||||||
|
|
||||||
return subscriptionInfo;
|
return subscriptionInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private PaymentMethod GetDefaultCardPaymentMethod(string customerId)
|
||||||
|
{
|
||||||
|
var paymentMethodService = new PaymentMethodService();
|
||||||
|
var cardPaymentMethods = paymentMethodService.ListAutoPaging(
|
||||||
|
new PaymentMethodListOptions { CustomerId = customerId, Type = "card" });
|
||||||
|
return cardPaymentMethods.OrderByDescending(m => m.Created).FirstOrDefault();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -679,8 +679,8 @@ namespace Bit.Core.Services
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task SignUpPremiumAsync(User user, string paymentToken, PaymentMethodType paymentMethodType,
|
public async Task<Tuple<bool, string>> SignUpPremiumAsync(User user, string paymentToken,
|
||||||
short additionalStorageGb, UserLicense license)
|
PaymentMethodType paymentMethodType, short additionalStorageGb, UserLicense license)
|
||||||
{
|
{
|
||||||
if(user.Premium)
|
if(user.Premium)
|
||||||
{
|
{
|
||||||
@ -692,6 +692,7 @@ namespace Bit.Core.Services
|
|||||||
throw new BadRequestException("You can't subtract storage!");
|
throw new BadRequestException("You can't subtract storage!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string paymentIntentClientSecret = null;
|
||||||
IPaymentService paymentService = null;
|
IPaymentService paymentService = null;
|
||||||
if(_globalSettings.SelfHosted)
|
if(_globalSettings.SelfHosted)
|
||||||
{
|
{
|
||||||
@ -711,7 +712,8 @@ namespace Bit.Core.Services
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
await _paymentService.PurchasePremiumAsync(user, paymentMethodType, paymentToken, additionalStorageGb);
|
paymentIntentClientSecret = await _paymentService.PurchasePremiumAsync(user, paymentMethodType,
|
||||||
|
paymentToken, additionalStorageGb);
|
||||||
}
|
}
|
||||||
|
|
||||||
user.Premium = true;
|
user.Premium = true;
|
||||||
@ -739,6 +741,8 @@ namespace Bit.Core.Services
|
|||||||
await paymentService.CancelAndRecoverChargesAsync(user);
|
await paymentService.CancelAndRecoverChargesAsync(user);
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
return new Tuple<bool, string>(string.IsNullOrWhiteSpace(paymentIntentClientSecret),
|
||||||
|
paymentIntentClientSecret);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task UpdateLicenseAsync(User user, UserLicense license)
|
public async Task UpdateLicenseAsync(User user, UserLicense license)
|
||||||
@ -816,6 +820,23 @@ namespace Bit.Core.Services
|
|||||||
await _paymentService.ReinstateSubscriptionAsync(user);
|
await _paymentService.ReinstateSubscriptionAsync(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task EnablePremiumAsync(Guid userId, DateTime? expirationDate)
|
||||||
|
{
|
||||||
|
var user = await _userRepository.GetByIdAsync(userId);
|
||||||
|
await EnablePremiumAsync(user, expirationDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task EnablePremiumAsync(User user, DateTime? expirationDate)
|
||||||
|
{
|
||||||
|
if(user != null && !user.Premium)
|
||||||
|
{
|
||||||
|
user.Premium = true;
|
||||||
|
user.PremiumExpirationDate = expirationDate;
|
||||||
|
user.RevisionDate = DateTime.UtcNow;
|
||||||
|
await _userRepository.ReplaceAsync(user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public async Task DisablePremiumAsync(Guid userId, DateTime? expirationDate)
|
public async Task DisablePremiumAsync(Guid userId, DateTime? expirationDate)
|
||||||
{
|
{
|
||||||
var user = await _userRepository.GetByIdAsync(userId);
|
var user = await _userRepository.GetByIdAsync(userId);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user