mirror of
https://github.com/bitwarden/server.git
synced 2025-07-01 16:12:49 -05:00
[PM-5548] Eliminate in-app purchase logic (#3640)
* Eliminate in-app purchase logic * Totally remove obsolete and unused properties / types * Remove unused enum values * Restore token update
This commit is contained in:
@ -1,132 +0,0 @@
|
||||
using System.Net.Http.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using Bit.Billing.Models;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.Settings;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Bit.Core.Services;
|
||||
|
||||
public class AppleIapService : IAppleIapService
|
||||
{
|
||||
private readonly HttpClient _httpClient = new HttpClient();
|
||||
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly IWebHostEnvironment _hostingEnvironment;
|
||||
private readonly IMetaDataRepository _metaDataRepository;
|
||||
private readonly ILogger<AppleIapService> _logger;
|
||||
|
||||
public AppleIapService(
|
||||
GlobalSettings globalSettings,
|
||||
IWebHostEnvironment hostingEnvironment,
|
||||
IMetaDataRepository metaDataRepository,
|
||||
ILogger<AppleIapService> logger)
|
||||
{
|
||||
_globalSettings = globalSettings;
|
||||
_hostingEnvironment = hostingEnvironment;
|
||||
_metaDataRepository = metaDataRepository;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task<AppleReceiptStatus> GetVerifiedReceiptStatusAsync(string receiptData)
|
||||
{
|
||||
var receiptStatus = await GetReceiptStatusAsync(receiptData);
|
||||
if (receiptStatus?.Status != 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
var validEnvironment = _globalSettings.AppleIap.AppInReview ||
|
||||
(!(_hostingEnvironment.IsProduction() || _hostingEnvironment.IsEnvironment("QA")) && receiptStatus.Environment == "Sandbox") ||
|
||||
((_hostingEnvironment.IsProduction() || _hostingEnvironment.IsEnvironment("QA")) && receiptStatus.Environment != "Sandbox");
|
||||
var validProductBundle = receiptStatus.Receipt.BundleId == "com.bitwarden.desktop" ||
|
||||
receiptStatus.Receipt.BundleId == "com.8bit.bitwarden";
|
||||
var validProduct = receiptStatus.LatestReceiptInfo.LastOrDefault()?.ProductId == "premium_annually";
|
||||
var validIds = receiptStatus.GetOriginalTransactionId() != null &&
|
||||
receiptStatus.GetLastTransactionId() != null;
|
||||
var validTransaction = receiptStatus.GetLastExpiresDate()
|
||||
.GetValueOrDefault(DateTime.MinValue) > DateTime.UtcNow;
|
||||
if (validEnvironment && validProductBundle && validProduct && validIds && validTransaction)
|
||||
{
|
||||
return receiptStatus;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public async Task SaveReceiptAsync(AppleReceiptStatus receiptStatus, Guid userId)
|
||||
{
|
||||
var originalTransactionId = receiptStatus.GetOriginalTransactionId();
|
||||
if (string.IsNullOrWhiteSpace(originalTransactionId))
|
||||
{
|
||||
throw new Exception("OriginalTransactionId is null");
|
||||
}
|
||||
await _metaDataRepository.UpsertAsync("AppleReceipt", originalTransactionId,
|
||||
new Dictionary<string, string>
|
||||
{
|
||||
["Data"] = receiptStatus.GetReceiptData(),
|
||||
["UserId"] = userId.ToString()
|
||||
});
|
||||
}
|
||||
|
||||
public async Task<Tuple<string, Guid?>> GetReceiptAsync(string originalTransactionId)
|
||||
{
|
||||
var receipt = await _metaDataRepository.GetAsync("AppleReceipt", originalTransactionId);
|
||||
if (receipt == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return new Tuple<string, Guid?>(receipt.ContainsKey("Data") ? receipt["Data"] : null,
|
||||
receipt.ContainsKey("UserId") ? new Guid(receipt["UserId"]) : (Guid?)null);
|
||||
}
|
||||
|
||||
// Internal for testing
|
||||
internal async Task<AppleReceiptStatus> GetReceiptStatusAsync(string receiptData, bool prod = true,
|
||||
int attempt = 0, AppleReceiptStatus lastReceiptStatus = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (attempt > 4)
|
||||
{
|
||||
throw new Exception(
|
||||
$"Failed verifying Apple IAP after too many attempts. Last attempt status: {lastReceiptStatus?.Status.ToString() ?? "null"}");
|
||||
}
|
||||
|
||||
var url = string.Format("https://{0}.itunes.apple.com/verifyReceipt", prod ? "buy" : "sandbox");
|
||||
|
||||
var response = await _httpClient.PostAsJsonAsync(url, new AppleVerifyReceiptRequestModel
|
||||
{
|
||||
ReceiptData = receiptData,
|
||||
Password = _globalSettings.AppleIap.Password
|
||||
});
|
||||
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
var receiptStatus = await response.Content.ReadFromJsonAsync<AppleReceiptStatus>();
|
||||
if (receiptStatus.Status == 21007)
|
||||
{
|
||||
return await GetReceiptStatusAsync(receiptData, false, attempt + 1, receiptStatus);
|
||||
}
|
||||
else if (receiptStatus.Status == 21005)
|
||||
{
|
||||
await Task.Delay(2000);
|
||||
return await GetReceiptStatusAsync(receiptData, prod, attempt + 1, receiptStatus);
|
||||
}
|
||||
return receiptStatus;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogWarning(e, "Error verifying Apple IAP receipt.");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public class AppleVerifyReceiptRequestModel
|
||||
{
|
||||
[JsonPropertyName("receipt-data")]
|
||||
public string ReceiptData { get; set; }
|
||||
[JsonPropertyName("password")]
|
||||
public string Password { get; set; }
|
||||
}
|
@ -1,5 +1,4 @@
|
||||
using Bit.Billing.Models;
|
||||
using Bit.Core.AdminConsole.Entities;
|
||||
using Bit.Core.AdminConsole.Entities;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Exceptions;
|
||||
@ -16,14 +15,11 @@ namespace Bit.Core.Services;
|
||||
public class StripePaymentService : IPaymentService
|
||||
{
|
||||
private const string PremiumPlanId = "premium-annually";
|
||||
private const string PremiumPlanAppleIapId = "premium-annually-appleiap";
|
||||
private const decimal PremiumPlanAppleIapPrice = 14.99M;
|
||||
private const string StoragePlanId = "storage-gb-annually";
|
||||
private const string ProviderDiscountId = "msp-discount-35";
|
||||
|
||||
private readonly ITransactionRepository _transactionRepository;
|
||||
private readonly IUserRepository _userRepository;
|
||||
private readonly IAppleIapService _appleIapService;
|
||||
private readonly ILogger<StripePaymentService> _logger;
|
||||
private readonly Braintree.IBraintreeGateway _btGateway;
|
||||
private readonly ITaxRateRepository _taxRateRepository;
|
||||
@ -33,7 +29,6 @@ public class StripePaymentService : IPaymentService
|
||||
public StripePaymentService(
|
||||
ITransactionRepository transactionRepository,
|
||||
IUserRepository userRepository,
|
||||
IAppleIapService appleIapService,
|
||||
ILogger<StripePaymentService> logger,
|
||||
ITaxRateRepository taxRateRepository,
|
||||
IStripeAdapter stripeAdapter,
|
||||
@ -42,7 +37,6 @@ public class StripePaymentService : IPaymentService
|
||||
{
|
||||
_transactionRepository = transactionRepository;
|
||||
_userRepository = userRepository;
|
||||
_appleIapService = appleIapService;
|
||||
_logger = logger;
|
||||
_taxRateRepository = taxRateRepository;
|
||||
_stripeAdapter = stripeAdapter;
|
||||
@ -345,21 +339,16 @@ public class StripePaymentService : IPaymentService
|
||||
{
|
||||
throw new BadRequestException("Your account does not have any credit available.");
|
||||
}
|
||||
if (paymentMethodType == PaymentMethodType.BankAccount || paymentMethodType == PaymentMethodType.GoogleInApp)
|
||||
if (paymentMethodType is PaymentMethodType.BankAccount)
|
||||
{
|
||||
throw new GatewayException("Payment method is not supported at this time.");
|
||||
}
|
||||
if ((paymentMethodType == PaymentMethodType.GoogleInApp ||
|
||||
paymentMethodType == PaymentMethodType.AppleInApp) && additionalStorageGb > 0)
|
||||
{
|
||||
throw new BadRequestException("You cannot add storage with this payment method.");
|
||||
}
|
||||
|
||||
var createdStripeCustomer = false;
|
||||
Stripe.Customer customer = null;
|
||||
Braintree.Customer braintreeCustomer = null;
|
||||
var stripePaymentMethod = paymentMethodType == PaymentMethodType.Card ||
|
||||
paymentMethodType == PaymentMethodType.BankAccount || paymentMethodType == PaymentMethodType.Credit;
|
||||
var stripePaymentMethod = paymentMethodType is PaymentMethodType.Card or PaymentMethodType.BankAccount
|
||||
or PaymentMethodType.Credit;
|
||||
|
||||
string stipeCustomerPaymentMethodId = null;
|
||||
string stipeCustomerSourceToken = null;
|
||||
@ -379,19 +368,9 @@ public class StripePaymentService : IPaymentService
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(paymentToken))
|
||||
{
|
||||
try
|
||||
{
|
||||
await UpdatePaymentMethodAsync(user, paymentMethodType, paymentToken, true, taxInfo);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
var message = e.Message.ToLowerInvariant();
|
||||
if (message.Contains("apple") || message.Contains("in-app"))
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
await UpdatePaymentMethodAsync(user, paymentMethodType, paymentToken, taxInfo);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
customer = await _stripeAdapter.CustomerGetAsync(user.GatewayCustomerId);
|
||||
@ -425,18 +404,6 @@ public class StripePaymentService : IPaymentService
|
||||
braintreeCustomer = customerResult.Target;
|
||||
stripeCustomerMetadata.Add("btCustomerId", braintreeCustomer.Id);
|
||||
}
|
||||
else if (paymentMethodType == PaymentMethodType.AppleInApp)
|
||||
{
|
||||
var verifiedReceiptStatus = await _appleIapService.GetVerifiedReceiptStatusAsync(paymentToken);
|
||||
if (verifiedReceiptStatus == null)
|
||||
{
|
||||
throw new GatewayException("Cannot verify apple in-app purchase.");
|
||||
}
|
||||
var receiptOriginalTransactionId = verifiedReceiptStatus.GetOriginalTransactionId();
|
||||
await VerifyAppleReceiptNotInUseAsync(receiptOriginalTransactionId, user);
|
||||
await _appleIapService.SaveReceiptAsync(verifiedReceiptStatus, user.Id);
|
||||
stripeCustomerMetadata.Add("appleReceipt", receiptOriginalTransactionId);
|
||||
}
|
||||
else if (!stripePaymentMethod)
|
||||
{
|
||||
throw new GatewayException("Payment method is not supported at this time.");
|
||||
@ -488,8 +455,8 @@ public class StripePaymentService : IPaymentService
|
||||
|
||||
subCreateOptions.Items.Add(new Stripe.SubscriptionItemOptions
|
||||
{
|
||||
Plan = paymentMethodType == PaymentMethodType.AppleInApp ? PremiumPlanAppleIapId : PremiumPlanId,
|
||||
Quantity = 1,
|
||||
Plan = PremiumPlanId,
|
||||
Quantity = 1
|
||||
});
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(taxInfo?.BillingAddressCountry)
|
||||
@ -547,7 +514,6 @@ public class StripePaymentService : IPaymentService
|
||||
{
|
||||
var addedCreditToStripeCustomer = false;
|
||||
Braintree.Transaction braintreeTransaction = null;
|
||||
Transaction appleTransaction = null;
|
||||
|
||||
var subInvoiceMetadata = new Dictionary<string, string>();
|
||||
Stripe.Subscription subscription = null;
|
||||
@ -564,39 +530,9 @@ public class StripePaymentService : IPaymentService
|
||||
|
||||
if (previewInvoice.AmountDue > 0)
|
||||
{
|
||||
var appleReceiptOrigTransactionId = customer.Metadata != null &&
|
||||
customer.Metadata.ContainsKey("appleReceipt") ? customer.Metadata["appleReceipt"] : null;
|
||||
var braintreeCustomerId = customer.Metadata != null &&
|
||||
customer.Metadata.ContainsKey("btCustomerId") ? customer.Metadata["btCustomerId"] : null;
|
||||
if (!string.IsNullOrWhiteSpace(appleReceiptOrigTransactionId))
|
||||
{
|
||||
if (!subscriber.IsUser())
|
||||
{
|
||||
throw new GatewayException("In-app purchase is only allowed for users.");
|
||||
}
|
||||
|
||||
var appleReceipt = await _appleIapService.GetReceiptAsync(
|
||||
appleReceiptOrigTransactionId);
|
||||
var verifiedAppleReceipt = await _appleIapService.GetVerifiedReceiptStatusAsync(
|
||||
appleReceipt.Item1);
|
||||
if (verifiedAppleReceipt == null)
|
||||
{
|
||||
throw new GatewayException("Failed to get Apple in-app purchase receipt data.");
|
||||
}
|
||||
subInvoiceMetadata.Add("appleReceipt", verifiedAppleReceipt.GetOriginalTransactionId());
|
||||
var lastTransactionId = verifiedAppleReceipt.GetLastTransactionId();
|
||||
subInvoiceMetadata.Add("appleReceiptTransactionId", lastTransactionId);
|
||||
var existingTransaction = await _transactionRepository.GetByGatewayIdAsync(
|
||||
GatewayType.AppStore, lastTransactionId);
|
||||
if (existingTransaction == null)
|
||||
{
|
||||
appleTransaction = verifiedAppleReceipt.BuildTransactionFromLastTransaction(
|
||||
PremiumPlanAppleIapPrice, subscriber.Id);
|
||||
appleTransaction.Type = TransactionType.Charge;
|
||||
await _transactionRepository.CreateAsync(appleTransaction);
|
||||
}
|
||||
}
|
||||
else if (!string.IsNullOrWhiteSpace(braintreeCustomerId))
|
||||
if (!string.IsNullOrWhiteSpace(braintreeCustomerId))
|
||||
{
|
||||
var btInvoiceAmount = (previewInvoice.AmountDue / 100M);
|
||||
var transactionResult = await _btGateway.Transaction.SaleAsync(
|
||||
@ -712,10 +648,6 @@ public class StripePaymentService : IPaymentService
|
||||
{
|
||||
await _btGateway.Customer.DeleteAsync(braintreeCustomer.Id);
|
||||
}
|
||||
if (appleTransaction != null)
|
||||
{
|
||||
await _transactionRepository.DeleteAsync(appleTransaction);
|
||||
}
|
||||
|
||||
if (e is Stripe.StripeException strEx &&
|
||||
(strEx.StripeError?.Message?.Contains("cannot be used because it is not verified") ?? false))
|
||||
@ -965,12 +897,6 @@ public class StripePaymentService : IPaymentService
|
||||
customerOptions.AddExpand("default_source");
|
||||
customerOptions.AddExpand("invoice_settings.default_payment_method");
|
||||
var customer = await _stripeAdapter.CustomerGetAsync(subscriber.GatewayCustomerId, customerOptions);
|
||||
var usingInAppPaymentMethod = customer.Metadata.ContainsKey("appleReceipt");
|
||||
if (usingInAppPaymentMethod)
|
||||
{
|
||||
throw new BadRequestException("Cannot perform this action with in-app purchase payment method. " +
|
||||
"Contact support.");
|
||||
}
|
||||
|
||||
string paymentIntentClientSecret = null;
|
||||
|
||||
@ -1128,8 +1054,7 @@ public class StripePaymentService : IPaymentService
|
||||
return paymentIntentClientSecret;
|
||||
}
|
||||
|
||||
public async Task CancelSubscriptionAsync(ISubscriber subscriber, bool endOfPeriod = false,
|
||||
bool skipInAppPurchaseCheck = false)
|
||||
public async Task CancelSubscriptionAsync(ISubscriber subscriber, bool endOfPeriod = false)
|
||||
{
|
||||
if (subscriber == null)
|
||||
{
|
||||
@ -1141,15 +1066,6 @@ public class StripePaymentService : IPaymentService
|
||||
throw new GatewayException("No subscription.");
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(subscriber.GatewayCustomerId) && !skipInAppPurchaseCheck)
|
||||
{
|
||||
var customer = await _stripeAdapter.CustomerGetAsync(subscriber.GatewayCustomerId);
|
||||
if (customer.Metadata.ContainsKey("appleReceipt"))
|
||||
{
|
||||
throw new BadRequestException("You are required to manage your subscription from the app store.");
|
||||
}
|
||||
}
|
||||
|
||||
var sub = await _stripeAdapter.SubscriptionGetAsync(subscriber.GatewaySubscriptionId);
|
||||
if (sub == null)
|
||||
{
|
||||
@ -1216,7 +1132,7 @@ public class StripePaymentService : IPaymentService
|
||||
}
|
||||
|
||||
public async Task<bool> UpdatePaymentMethodAsync(ISubscriber subscriber, PaymentMethodType paymentMethodType,
|
||||
string paymentToken, bool allowInAppPurchases = false, TaxInfo taxInfo = null)
|
||||
string paymentToken, TaxInfo taxInfo = null)
|
||||
{
|
||||
if (subscriber == null)
|
||||
{
|
||||
@ -1230,7 +1146,6 @@ public class StripePaymentService : IPaymentService
|
||||
}
|
||||
|
||||
var createdCustomer = false;
|
||||
AppleReceiptStatus appleReceiptStatus = null;
|
||||
Braintree.Customer braintreeCustomer = null;
|
||||
string stipeCustomerSourceToken = null;
|
||||
string stipeCustomerPaymentMethodId = null;
|
||||
@ -1238,23 +1153,10 @@ public class StripePaymentService : IPaymentService
|
||||
{
|
||||
{ "region", _globalSettings.BaseServiceUri.CloudRegion }
|
||||
};
|
||||
var stripePaymentMethod = paymentMethodType == PaymentMethodType.Card ||
|
||||
paymentMethodType == PaymentMethodType.BankAccount;
|
||||
var inAppPurchase = paymentMethodType == PaymentMethodType.AppleInApp ||
|
||||
paymentMethodType == PaymentMethodType.GoogleInApp;
|
||||
var stripePaymentMethod = paymentMethodType is PaymentMethodType.Card or PaymentMethodType.BankAccount;
|
||||
|
||||
Stripe.Customer customer = null;
|
||||
|
||||
if (!allowInAppPurchases && inAppPurchase)
|
||||
{
|
||||
throw new GatewayException("In-app purchase payment method is not allowed.");
|
||||
}
|
||||
|
||||
if (!subscriber.IsUser() && inAppPurchase)
|
||||
{
|
||||
throw new GatewayException("In-app purchase payment method is only allowed for users.");
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(subscriber.GatewayCustomerId))
|
||||
{
|
||||
var options = new Stripe.CustomerGetOptions();
|
||||
@ -1266,16 +1168,6 @@ public class StripePaymentService : IPaymentService
|
||||
}
|
||||
}
|
||||
|
||||
if (inAppPurchase && customer != null && customer.Balance != 0)
|
||||
{
|
||||
throw new GatewayException("Customer balance cannot exist when using in-app purchases.");
|
||||
}
|
||||
|
||||
if (!inAppPurchase && customer != null && stripeCustomerMetadata.ContainsKey("appleReceipt"))
|
||||
{
|
||||
throw new GatewayException("Cannot change from in-app payment method. Contact support.");
|
||||
}
|
||||
|
||||
var hadBtCustomer = stripeCustomerMetadata.ContainsKey("btCustomerId");
|
||||
if (stripePaymentMethod)
|
||||
{
|
||||
@ -1345,15 +1237,6 @@ public class StripePaymentService : IPaymentService
|
||||
braintreeCustomer = customerResult.Target;
|
||||
}
|
||||
}
|
||||
else if (paymentMethodType == PaymentMethodType.AppleInApp)
|
||||
{
|
||||
appleReceiptStatus = await _appleIapService.GetVerifiedReceiptStatusAsync(paymentToken);
|
||||
if (appleReceiptStatus == null)
|
||||
{
|
||||
throw new GatewayException("Cannot verify Apple in-app purchase.");
|
||||
}
|
||||
await VerifyAppleReceiptNotInUseAsync(appleReceiptStatus.GetOriginalTransactionId(), subscriber);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new GatewayException("Payment method is not supported at this time.");
|
||||
@ -1373,25 +1256,6 @@ public class StripePaymentService : IPaymentService
|
||||
stripeCustomerMetadata.Add("btCustomerId", braintreeCustomer.Id);
|
||||
}
|
||||
|
||||
if (appleReceiptStatus != null)
|
||||
{
|
||||
var originalTransactionId = appleReceiptStatus.GetOriginalTransactionId();
|
||||
if (stripeCustomerMetadata.ContainsKey("appleReceipt"))
|
||||
{
|
||||
if (originalTransactionId != stripeCustomerMetadata["appleReceipt"])
|
||||
{
|
||||
var nowSec = Utilities.CoreHelpers.ToEpocSeconds(DateTime.UtcNow);
|
||||
stripeCustomerMetadata.Add($"appleReceipt_{nowSec}", stripeCustomerMetadata["appleReceipt"]);
|
||||
}
|
||||
stripeCustomerMetadata["appleReceipt"] = originalTransactionId;
|
||||
}
|
||||
else
|
||||
{
|
||||
stripeCustomerMetadata.Add("appleReceipt", originalTransactionId);
|
||||
}
|
||||
await _appleIapService.SaveReceiptAsync(appleReceiptStatus, subscriber.Id);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (customer == null)
|
||||
@ -1595,11 +1459,6 @@ public class StripePaymentService : IPaymentService
|
||||
{
|
||||
subscriptionInfo.CustomerDiscount = new SubscriptionInfo.BillingCustomerDiscount(customer.Discount);
|
||||
}
|
||||
|
||||
if (subscriber.IsUser())
|
||||
{
|
||||
subscriptionInfo.UsingInAppPurchase = customer.Metadata.ContainsKey("appleReceipt");
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(subscriber.GatewaySubscriptionId))
|
||||
@ -1762,19 +1621,6 @@ public class StripePaymentService : IPaymentService
|
||||
return cardPaymentMethods.OrderByDescending(m => m.Created).FirstOrDefault();
|
||||
}
|
||||
|
||||
private async Task VerifyAppleReceiptNotInUseAsync(string receiptOriginalTransactionId, ISubscriber subscriber)
|
||||
{
|
||||
var existingReceipt = await _appleIapService.GetReceiptAsync(receiptOriginalTransactionId);
|
||||
if (existingReceipt != null && existingReceipt.Item2.HasValue && existingReceipt.Item2 != subscriber.Id)
|
||||
{
|
||||
var existingUser = await _userRepository.GetByIdAsync(existingReceipt.Item2.Value);
|
||||
if (existingUser != null)
|
||||
{
|
||||
throw new GatewayException("Apple receipt already in use by another user.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private decimal GetBillingBalance(Stripe.Customer customer)
|
||||
{
|
||||
return customer != null ? customer.Balance / 100M : default;
|
||||
@ -1787,14 +1633,6 @@ public class StripePaymentService : IPaymentService
|
||||
return null;
|
||||
}
|
||||
|
||||
if (customer.Metadata?.ContainsKey("appleReceipt") ?? false)
|
||||
{
|
||||
return new BillingInfo.BillingSource
|
||||
{
|
||||
Type = PaymentMethodType.AppleInApp
|
||||
};
|
||||
}
|
||||
|
||||
if (customer.Metadata?.ContainsKey("btCustomerId") ?? false)
|
||||
{
|
||||
try
|
||||
|
@ -255,7 +255,7 @@ public class UserService : UserManager<User>, IUserService, IDisposable
|
||||
{
|
||||
try
|
||||
{
|
||||
await CancelPremiumAsync(user, null, true);
|
||||
await CancelPremiumAsync(user);
|
||||
}
|
||||
catch (GatewayException) { }
|
||||
}
|
||||
@ -973,12 +973,6 @@ public class UserService : UserManager<User>, IUserService, IDisposable
|
||||
throw new BadRequestException("You can't subtract storage!");
|
||||
}
|
||||
|
||||
if ((paymentMethodType == PaymentMethodType.GoogleInApp ||
|
||||
paymentMethodType == PaymentMethodType.AppleInApp) && additionalStorageGb > 0)
|
||||
{
|
||||
throw new BadRequestException("You cannot add storage with this payment method.");
|
||||
}
|
||||
|
||||
string paymentIntentClientSecret = null;
|
||||
IPaymentService paymentService = null;
|
||||
if (_globalSettings.SelfHosted)
|
||||
@ -1039,29 +1033,6 @@ public class UserService : UserManager<User>, IUserService, IDisposable
|
||||
paymentIntentClientSecret);
|
||||
}
|
||||
|
||||
public async Task IapCheckAsync(User user, PaymentMethodType paymentMethodType)
|
||||
{
|
||||
if (paymentMethodType != PaymentMethodType.AppleInApp)
|
||||
{
|
||||
throw new BadRequestException("Payment method not supported for in-app purchases.");
|
||||
}
|
||||
|
||||
if (user.Premium)
|
||||
{
|
||||
throw new BadRequestException("Already a premium user.");
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(user.GatewayCustomerId))
|
||||
{
|
||||
var customerService = new Stripe.CustomerService();
|
||||
var customer = await customerService.GetAsync(user.GatewayCustomerId);
|
||||
if (customer != null && customer.Balance != 0)
|
||||
{
|
||||
throw new BadRequestException("Customer balance cannot exist when using in-app purchases.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async Task UpdateLicenseAsync(User user, UserLicense license)
|
||||
{
|
||||
if (!_globalSettings.SelfHosted)
|
||||
@ -1136,7 +1107,7 @@ public class UserService : UserManager<User>, IUserService, IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
public async Task CancelPremiumAsync(User user, bool? endOfPeriod = null, bool accountDelete = false)
|
||||
public async Task CancelPremiumAsync(User user, bool? endOfPeriod = null)
|
||||
{
|
||||
var eop = endOfPeriod.GetValueOrDefault(true);
|
||||
if (!endOfPeriod.HasValue && user.PremiumExpirationDate.HasValue &&
|
||||
@ -1144,11 +1115,11 @@ public class UserService : UserManager<User>, IUserService, IDisposable
|
||||
{
|
||||
eop = false;
|
||||
}
|
||||
await _paymentService.CancelSubscriptionAsync(user, eop, accountDelete);
|
||||
await _paymentService.CancelSubscriptionAsync(user, eop);
|
||||
await _referenceEventService.RaiseEventAsync(
|
||||
new ReferenceEvent(ReferenceEventType.CancelSubscription, user, _currentContext)
|
||||
{
|
||||
EndOfPeriod = eop,
|
||||
EndOfPeriod = eop
|
||||
});
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user