using Bit.Core.AdminConsole.Entities; using Bit.Core.Billing.Caches; using Bit.Core.Billing.Constants; using Bit.Core.Billing.Payment.Queries; using Bit.Core.Billing.Services; using Bit.Core.Services; using Bit.Core.Test.Billing.Extensions; using Braintree; using Microsoft.Extensions.Logging; using NSubstitute; using Stripe; using Xunit; using Customer = Stripe.Customer; using PaymentMethod = Stripe.PaymentMethod; namespace Bit.Core.Test.Billing.Payment.Queries; using static StripeConstants; public class GetPaymentMethodQueryTests { private readonly IBraintreeGateway _braintreeGateway = Substitute.For(); private readonly ISetupIntentCache _setupIntentCache = Substitute.For(); private readonly IStripeAdapter _stripeAdapter = Substitute.For(); private readonly ISubscriberService _subscriberService = Substitute.For(); private readonly GetPaymentMethodQuery _query; public GetPaymentMethodQueryTests() { _query = new GetPaymentMethodQuery( _braintreeGateway, Substitute.For>(), _setupIntentCache, _stripeAdapter, _subscriberService); } [Fact] public async Task Run_NoPaymentMethod_ReturnsNull() { var organization = new Organization { Id = Guid.NewGuid() }; var customer = new Customer { InvoiceSettings = new CustomerInvoiceSettings(), Metadata = new Dictionary() }; _subscriberService.GetCustomer(organization, Arg.Is(options => options.HasExpansions("default_source", "invoice_settings.default_payment_method"))).Returns(customer); var maskedPaymentMethod = await _query.Run(organization); Assert.Null(maskedPaymentMethod); } [Fact] public async Task Run_BankAccount_FromPaymentMethod_ReturnsMaskedBankAccount() { var organization = new Organization { Id = Guid.NewGuid() }; var customer = new Customer { InvoiceSettings = new CustomerInvoiceSettings { DefaultPaymentMethod = new PaymentMethod { Type = "us_bank_account", UsBankAccount = new PaymentMethodUsBankAccount { BankName = "Chase", Last4 = "9999" } } }, Metadata = new Dictionary() }; _subscriberService.GetCustomer(organization, Arg.Is(options => options.HasExpansions("default_source", "invoice_settings.default_payment_method"))).Returns(customer); var maskedPaymentMethod = await _query.Run(organization); Assert.NotNull(maskedPaymentMethod); Assert.True(maskedPaymentMethod.IsT0); var maskedBankAccount = maskedPaymentMethod.AsT0; Assert.Equal("Chase", maskedBankAccount.BankName); Assert.Equal("9999", maskedBankAccount.Last4); Assert.True(maskedBankAccount.Verified); } [Fact] public async Task Run_BankAccount_FromSource_ReturnsMaskedBankAccount() { var organization = new Organization { Id = Guid.NewGuid() }; var customer = new Customer { DefaultSource = new BankAccount { BankName = "Chase", Last4 = "9999", Status = "verified" }, InvoiceSettings = new CustomerInvoiceSettings(), Metadata = new Dictionary() }; _subscriberService.GetCustomer(organization, Arg.Is(options => options.HasExpansions("default_source", "invoice_settings.default_payment_method"))).Returns(customer); var maskedPaymentMethod = await _query.Run(organization); Assert.NotNull(maskedPaymentMethod); Assert.True(maskedPaymentMethod.IsT0); var maskedBankAccount = maskedPaymentMethod.AsT0; Assert.Equal("Chase", maskedBankAccount.BankName); Assert.Equal("9999", maskedBankAccount.Last4); Assert.True(maskedBankAccount.Verified); } [Fact] public async Task Run_BankAccount_FromSetupIntent_ReturnsMaskedBankAccount() { var organization = new Organization { Id = Guid.NewGuid() }; var customer = new Customer { InvoiceSettings = new CustomerInvoiceSettings(), Metadata = new Dictionary() }; _subscriberService.GetCustomer(organization, Arg.Is(options => options.HasExpansions("default_source", "invoice_settings.default_payment_method"))).Returns(customer); _setupIntentCache.Get(organization.Id).Returns("seti_123"); _stripeAdapter .SetupIntentGet("seti_123", Arg.Is(options => options.HasExpansions("payment_method"))).Returns( new SetupIntent { PaymentMethod = new PaymentMethod { Type = "us_bank_account", UsBankAccount = new PaymentMethodUsBankAccount { BankName = "Chase", Last4 = "9999" } }, NextAction = new SetupIntentNextAction { VerifyWithMicrodeposits = new SetupIntentNextActionVerifyWithMicrodeposits() }, Status = "requires_action" }); var maskedPaymentMethod = await _query.Run(organization); Assert.NotNull(maskedPaymentMethod); Assert.True(maskedPaymentMethod.IsT0); var maskedBankAccount = maskedPaymentMethod.AsT0; Assert.Equal("Chase", maskedBankAccount.BankName); Assert.Equal("9999", maskedBankAccount.Last4); Assert.False(maskedBankAccount.Verified); } [Fact] public async Task Run_Card_FromPaymentMethod_ReturnsMaskedCard() { var organization = new Organization { Id = Guid.NewGuid() }; var customer = new Customer { InvoiceSettings = new CustomerInvoiceSettings { DefaultPaymentMethod = new PaymentMethod { Type = "card", Card = new PaymentMethodCard { Brand = "visa", Last4 = "9999", ExpMonth = 1, ExpYear = 2028 } } }, Metadata = new Dictionary() }; _subscriberService.GetCustomer(organization, Arg.Is(options => options.HasExpansions("default_source", "invoice_settings.default_payment_method"))).Returns(customer); var maskedPaymentMethod = await _query.Run(organization); Assert.NotNull(maskedPaymentMethod); Assert.True(maskedPaymentMethod.IsT1); var maskedCard = maskedPaymentMethod.AsT1; Assert.Equal("visa", maskedCard.Brand); Assert.Equal("9999", maskedCard.Last4); Assert.Equal("01/2028", maskedCard.Expiration); } [Fact] public async Task Run_Card_FromSource_ReturnsMaskedCard() { var organization = new Organization { Id = Guid.NewGuid() }; var customer = new Customer { DefaultSource = new Card { Brand = "visa", Last4 = "9999", ExpMonth = 1, ExpYear = 2028 }, InvoiceSettings = new CustomerInvoiceSettings(), Metadata = new Dictionary() }; _subscriberService.GetCustomer(organization, Arg.Is(options => options.HasExpansions("default_source", "invoice_settings.default_payment_method"))).Returns(customer); var maskedPaymentMethod = await _query.Run(organization); Assert.NotNull(maskedPaymentMethod); Assert.True(maskedPaymentMethod.IsT1); var maskedCard = maskedPaymentMethod.AsT1; Assert.Equal("visa", maskedCard.Brand); Assert.Equal("9999", maskedCard.Last4); Assert.Equal("01/2028", maskedCard.Expiration); } [Fact] public async Task Run_Card_FromSourceCard_ReturnsMaskedCard() { var organization = new Organization { Id = Guid.NewGuid() }; var customer = new Customer { DefaultSource = new Source { Card = new SourceCard { Brand = "Visa", Last4 = "9999", ExpMonth = 1, ExpYear = 2028 } }, InvoiceSettings = new CustomerInvoiceSettings(), Metadata = new Dictionary() }; _subscriberService.GetCustomer(organization, Arg.Is(options => options.HasExpansions("default_source", "invoice_settings.default_payment_method"))).Returns(customer); var maskedPaymentMethod = await _query.Run(organization); Assert.NotNull(maskedPaymentMethod); Assert.True(maskedPaymentMethod.IsT1); var maskedCard = maskedPaymentMethod.AsT1; Assert.Equal("visa", maskedCard.Brand); Assert.Equal("9999", maskedCard.Last4); Assert.Equal("01/2028", maskedCard.Expiration); } [Fact] public async Task Run_PayPalAccount_ReturnsMaskedPayPalAccount() { var organization = new Organization { Id = Guid.NewGuid() }; var customer = new Customer { Metadata = new Dictionary { [MetadataKeys.BraintreeCustomerId] = "braintree_customer_id" } }; _subscriberService.GetCustomer(organization, Arg.Is(options => options.HasExpansions("default_source", "invoice_settings.default_payment_method"))).Returns(customer); var customerGateway = Substitute.For(); var braintreeCustomer = Substitute.For(); var payPalAccount = Substitute.For(); payPalAccount.Email.Returns("user@gmail.com"); payPalAccount.IsDefault.Returns(true); braintreeCustomer.PaymentMethods.Returns([payPalAccount]); customerGateway.FindAsync("braintree_customer_id").Returns(braintreeCustomer); _braintreeGateway.Customer.Returns(customerGateway); var maskedPaymentMethod = await _query.Run(organization); Assert.NotNull(maskedPaymentMethod); Assert.True(maskedPaymentMethod.IsT2); var maskedPayPalAccount = maskedPaymentMethod.AsT2; Assert.Equal("user@gmail.com", maskedPayPalAccount.Email); } }