mirror of
https://github.com/bitwarden/server
synced 2026-02-26 01:13:35 +00:00
fix(billing): return null subscription when resource_missing (#7068)
This commit is contained in:
@@ -31,7 +31,7 @@ public abstract class BaseBillingCommand<T>(
|
||||
{
|
||||
return await function();
|
||||
}
|
||||
catch (StripeException stripeException) when (ErrorCodes.Get().Contains(stripeException.StripeError.Code))
|
||||
catch (StripeException stripeException) when (ErrorCodes.InputErrors().Contains(stripeException.StripeError.Code))
|
||||
{
|
||||
return stripeException.StripeError.Code switch
|
||||
{
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
using System.Reflection;
|
||||
|
||||
namespace Bit.Core.Billing.Constants;
|
||||
namespace Bit.Core.Billing.Constants;
|
||||
|
||||
public static class StripeConstants
|
||||
{
|
||||
@@ -51,14 +49,18 @@ public static class StripeConstants
|
||||
public const string PaymentMethodMicroDepositVerificationAttemptsExceeded = "payment_method_microdeposit_verification_attempts_exceeded";
|
||||
public const string PaymentMethodMicroDepositVerificationDescriptorCodeMismatch = "payment_method_microdeposit_verification_descriptor_code_mismatch";
|
||||
public const string PaymentMethodMicroDepositVerificationTimeout = "payment_method_microdeposit_verification_timeout";
|
||||
public const string ResourceMissing = "resource_missing";
|
||||
public const string TaxIdInvalid = "tax_id_invalid";
|
||||
|
||||
public static string[] Get() =>
|
||||
typeof(ErrorCodes)
|
||||
.GetFields(BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy)
|
||||
.Where(fi => fi is { IsLiteral: true, IsInitOnly: false } && fi.FieldType == typeof(string))
|
||||
.Select(fi => (string)fi.GetValue(null)!)
|
||||
.ToArray();
|
||||
public static string[] InputErrors() =>
|
||||
[
|
||||
CustomerTaxLocationInvalid,
|
||||
InvoiceUpcomingNone,
|
||||
PaymentMethodMicroDepositVerificationAttemptsExceeded,
|
||||
PaymentMethodMicroDepositVerificationDescriptorCodeMismatch,
|
||||
PaymentMethodMicroDepositVerificationTimeout,
|
||||
TaxIdInvalid
|
||||
];
|
||||
}
|
||||
|
||||
public static class InvoiceStatus
|
||||
|
||||
@@ -46,16 +46,12 @@ public class GetBitwardenSubscriptionQuery(
|
||||
return null;
|
||||
}
|
||||
|
||||
var subscription = await stripeAdapter.GetSubscriptionAsync(user.GatewaySubscriptionId, new SubscriptionGetOptions
|
||||
var subscription = await FetchSubscriptionAsync(user);
|
||||
|
||||
if (subscription == null)
|
||||
{
|
||||
Expand =
|
||||
[
|
||||
"customer.discount.coupon.applies_to",
|
||||
"discounts.coupon.applies_to",
|
||||
"items.data.price.product",
|
||||
"test_clock"
|
||||
]
|
||||
});
|
||||
return null;
|
||||
}
|
||||
|
||||
var cart = await GetPremiumCartAsync(subscription);
|
||||
|
||||
@@ -248,5 +244,27 @@ public class GetBitwardenSubscriptionQuery(
|
||||
return (cartLevel.FirstOrDefault(), productLevel);
|
||||
}
|
||||
|
||||
private async Task<Subscription?> FetchSubscriptionAsync(User user)
|
||||
{
|
||||
try
|
||||
{
|
||||
return await stripeAdapter.GetSubscriptionAsync(user.GatewaySubscriptionId, new SubscriptionGetOptions
|
||||
{
|
||||
Expand =
|
||||
[
|
||||
"customer.discount.coupon.applies_to",
|
||||
"discounts.coupon.applies_to",
|
||||
"items.data.price.product",
|
||||
"test_clock"
|
||||
]
|
||||
});
|
||||
}
|
||||
catch (StripeException stripeException) when (stripeException.StripeError?.Code == ErrorCodes.ResourceMissing)
|
||||
{
|
||||
logger.LogError("Subscription ({SubscriptionID}) for User ({UserID}) was not found", user.GatewaySubscriptionId, user.Id);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
@@ -55,6 +55,30 @@ public class GetBitwardenSubscriptionQueryTests
|
||||
await _stripeAdapter.DidNotReceive().GetSubscriptionAsync(Arg.Any<string>(), Arg.Any<SubscriptionGetOptions>());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Run_StripeSubscriptionNotFound_ReturnsNull()
|
||||
{
|
||||
var user = CreateUser();
|
||||
|
||||
_stripeAdapter.GetSubscriptionAsync(user.GatewaySubscriptionId, Arg.Any<SubscriptionGetOptions>())
|
||||
.ThrowsAsync(new StripeException { StripeError = new StripeError { Code = ErrorCodes.ResourceMissing } });
|
||||
|
||||
var result = await _query.Run(user);
|
||||
|
||||
Assert.Null(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Run_StripeExceptionNotResourceMissing_Throws()
|
||||
{
|
||||
var user = CreateUser();
|
||||
|
||||
_stripeAdapter.GetSubscriptionAsync(user.GatewaySubscriptionId, Arg.Any<SubscriptionGetOptions>())
|
||||
.ThrowsAsync(new StripeException { StripeError = new StripeError { Code = "api_error" } });
|
||||
|
||||
await Assert.ThrowsAsync<StripeException>(() => _query.Run(user));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Run_IncompleteStatus_ReturnsBitwardenSubscriptionWithSuspension()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user