mirror of
https://github.com/bitwarden/server.git
synced 2025-07-02 08:32:50 -05:00
[AC-1486] Feature: SM Billing (#3073)
* [AC-1423] Add AddonProduct and BitwardenProduct properties to BillingSubscriptionItem (#3037) * [AC-1423] Add AddonProduct and BitwardenProduct properties to BillingSubscriptionItem * [AC-1423] Add helper to StaticStore.cs to find a Plan by StripePlanId * [AC-1423] Use the helper method to set SubscriptionInfo.BitwardenProduct * Add SecretsManagerBilling feature flag to Constants * [AC 1409] Secrets Manager Subscription Stripe Integration (#3019) * [AC-1418] Add missing SecretsManagerPlan property to OrganizationResponseModel (#3055) * [AC 1460] Update Stripe Configuration (#3070) * [AC 1410] Secrets Manager subscription adjustment back-end changes (#3036) * Create UpgradeSecretsManagerSubscription command * [AC-1495] Extract UpgradePlanAsync into a command (#3081) * This is a pure lift & shift with no refactors * [AC-1503] Fix Stripe integration on organization upgrade (#3084) * Fix SM parameters not being passed to Stripe * [AC-1504] Allow SM max autoscale limits to be disabled (#3085) * [AC-1488] Changed SM Signup and Upgrade paths to set SmServiceAccounts to include the plan BaseServiceAccount (#3086) * [AC-1510] Enable access to Secrets Manager to Organization owner for new Subscription (#3089) * Revert changes to ReferenceEvent code (#3091) This will be done in AC-1481 * Add UsePasswordManager to sync data (#3114) * [AC-1522] Fix service account check on upgrading (#3111) * [AC-1521] Address checkmarx security feedback (#3124) * Reinstate target attribute but add noopener noreferrer * Update date on migration script --------- Co-authored-by: Shane Melton <smelton@bitwarden.com> Co-authored-by: Thomas Rittson <trittson@bitwarden.com> Co-authored-by: cyprain-okeke <108260115+cyprain-okeke@users.noreply.github.com> Co-authored-by: Thomas Rittson <31796059+eliykat@users.noreply.github.com> Co-authored-by: cyprain-okeke <cokeke@bitwarden.com> Co-authored-by: Rui Tomé <108268980+r-tome@users.noreply.github.com> Co-authored-by: Conner Turnbull <cturnbull@bitwarden.com> Co-authored-by: Rui Tome <rtome@bitwarden.com>
This commit is contained in:
@ -32,7 +32,7 @@ public class StripePaymentServiceTests
|
||||
public async void PurchaseOrganizationAsync_Invalid(PaymentMethodType paymentMethodType, SutProvider<StripePaymentService> sutProvider)
|
||||
{
|
||||
var exception = await Assert.ThrowsAsync<GatewayException>(
|
||||
() => sutProvider.Sut.PurchaseOrganizationAsync(null, paymentMethodType, null, null, 0, 0, false, null));
|
||||
() => sutProvider.Sut.PurchaseOrganizationAsync(null, paymentMethodType, null, null, 0, 0, false, null, false, -1, -1));
|
||||
|
||||
Assert.Equal("Payment method is not supported at this time.", exception.Message);
|
||||
}
|
||||
@ -40,7 +40,7 @@ public class StripePaymentServiceTests
|
||||
[Theory, BitAutoData]
|
||||
public async void PurchaseOrganizationAsync_Stripe_ProviderOrg_Coupon_Add(SutProvider<StripePaymentService> sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo, bool provider = true)
|
||||
{
|
||||
var plan = StaticStore.PasswordManagerPlans.First(p => p.Type == PlanType.EnterpriseAnnually);
|
||||
var plans = StaticStore.Plans.Where(p => p.Type == PlanType.EnterpriseAnnually).ToList();
|
||||
|
||||
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
|
||||
stripeAdapter.CustomerCreateAsync(default).ReturnsForAnyArgs(new Stripe.Customer
|
||||
@ -56,7 +56,7 @@ public class StripePaymentServiceTests
|
||||
.BaseServiceUri.CloudRegion
|
||||
.Returns("US");
|
||||
|
||||
var result = await sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.Card, paymentToken, plan, 0, 0, false, taxInfo, provider);
|
||||
var result = await sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.Card, paymentToken, plans, 0, 0, false, taxInfo, provider);
|
||||
|
||||
Assert.Null(result);
|
||||
Assert.Equal(GatewayType.Stripe, organization.Gateway);
|
||||
@ -91,10 +91,67 @@ public class StripePaymentServiceTests
|
||||
));
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async void PurchaseOrganizationAsync_SM_Stripe_ProviderOrg_Coupon_Add(SutProvider<StripePaymentService> sutProvider, Organization organization,
|
||||
string paymentToken, TaxInfo taxInfo, bool provider = true)
|
||||
{
|
||||
var plans = StaticStore.Plans.Where(p => p.Type == PlanType.EnterpriseAnnually).ToList();
|
||||
|
||||
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
|
||||
stripeAdapter.CustomerCreateAsync(default).ReturnsForAnyArgs(new Stripe.Customer
|
||||
{
|
||||
Id = "C-1",
|
||||
});
|
||||
stripeAdapter.SubscriptionCreateAsync(default).ReturnsForAnyArgs(new Stripe.Subscription
|
||||
{
|
||||
Id = "S-1",
|
||||
CurrentPeriodEnd = DateTime.Today.AddDays(10),
|
||||
|
||||
});
|
||||
sutProvider.GetDependency<IGlobalSettings>()
|
||||
.BaseServiceUri.CloudRegion
|
||||
.Returns("US");
|
||||
|
||||
var result = await sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.Card, paymentToken, plans, 1, 1,
|
||||
false, taxInfo, provider, 1, 1);
|
||||
|
||||
Assert.Null(result);
|
||||
Assert.Equal(GatewayType.Stripe, organization.Gateway);
|
||||
Assert.Equal("C-1", organization.GatewayCustomerId);
|
||||
Assert.Equal("S-1", organization.GatewaySubscriptionId);
|
||||
Assert.True(organization.Enabled);
|
||||
Assert.Equal(DateTime.Today.AddDays(10), organization.ExpirationDate);
|
||||
|
||||
await stripeAdapter.Received().CustomerCreateAsync(Arg.Is<Stripe.CustomerCreateOptions>(c =>
|
||||
c.Description == organization.BusinessName &&
|
||||
c.Email == organization.BillingEmail &&
|
||||
c.Source == paymentToken &&
|
||||
c.PaymentMethod == null &&
|
||||
c.Coupon == "msp-discount-35" &&
|
||||
c.Metadata.Count == 1 &&
|
||||
c.Metadata["region"] == "US" &&
|
||||
c.InvoiceSettings.DefaultPaymentMethod == null &&
|
||||
c.Address.Country == taxInfo.BillingAddressCountry &&
|
||||
c.Address.PostalCode == taxInfo.BillingAddressPostalCode &&
|
||||
c.Address.Line1 == taxInfo.BillingAddressLine1 &&
|
||||
c.Address.Line2 == taxInfo.BillingAddressLine2 &&
|
||||
c.Address.City == taxInfo.BillingAddressCity &&
|
||||
c.Address.State == taxInfo.BillingAddressState &&
|
||||
c.TaxIdData == null
|
||||
));
|
||||
|
||||
await stripeAdapter.Received().SubscriptionCreateAsync(Arg.Is<Stripe.SubscriptionCreateOptions>(s =>
|
||||
s.Customer == "C-1" &&
|
||||
s.Expand[0] == "latest_invoice.payment_intent" &&
|
||||
s.Metadata[organization.GatewayIdField()] == organization.Id.ToString() &&
|
||||
s.Items.Count == 4
|
||||
));
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async void PurchaseOrganizationAsync_Stripe(SutProvider<StripePaymentService> sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo)
|
||||
{
|
||||
var plan = StaticStore.PasswordManagerPlans.First(p => p.Type == PlanType.EnterpriseAnnually);
|
||||
var plans = StaticStore.Plans.Where(p => p.Type == PlanType.EnterpriseAnnually).ToList();
|
||||
|
||||
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
|
||||
stripeAdapter.CustomerCreateAsync(default).ReturnsForAnyArgs(new Stripe.Customer
|
||||
@ -110,7 +167,8 @@ public class StripePaymentServiceTests
|
||||
.BaseServiceUri.CloudRegion
|
||||
.Returns("US");
|
||||
|
||||
var result = await sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.Card, paymentToken, plan, 0, 0, false, taxInfo);
|
||||
var result = await sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.Card, paymentToken, plans, 0, 0
|
||||
, false, taxInfo, false, 8, 10);
|
||||
|
||||
Assert.Null(result);
|
||||
Assert.Equal(GatewayType.Stripe, organization.Gateway);
|
||||
@ -118,7 +176,6 @@ public class StripePaymentServiceTests
|
||||
Assert.Equal("S-1", organization.GatewaySubscriptionId);
|
||||
Assert.True(organization.Enabled);
|
||||
Assert.Equal(DateTime.Today.AddDays(10), organization.ExpirationDate);
|
||||
var res = organization.SubscriberName();
|
||||
await stripeAdapter.Received().CustomerCreateAsync(Arg.Is<Stripe.CustomerCreateOptions>(c =>
|
||||
c.Description == organization.BusinessName &&
|
||||
c.Email == organization.BillingEmail &&
|
||||
@ -143,14 +200,14 @@ public class StripePaymentServiceTests
|
||||
s.Customer == "C-1" &&
|
||||
s.Expand[0] == "latest_invoice.payment_intent" &&
|
||||
s.Metadata[organization.GatewayIdField()] == organization.Id.ToString() &&
|
||||
s.Items.Count == 0
|
||||
s.Items.Count == 2
|
||||
));
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async void PurchaseOrganizationAsync_Stripe_PM(SutProvider<StripePaymentService> sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo)
|
||||
{
|
||||
var plan = StaticStore.PasswordManagerPlans.First(p => p.Type == PlanType.EnterpriseAnnually);
|
||||
var plans = StaticStore.PasswordManagerPlans.Where(p => p.Type == PlanType.EnterpriseAnnually).ToList();
|
||||
paymentToken = "pm_" + paymentToken;
|
||||
|
||||
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
|
||||
@ -167,7 +224,7 @@ public class StripePaymentServiceTests
|
||||
.BaseServiceUri.CloudRegion
|
||||
.Returns("US");
|
||||
|
||||
var result = await sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.Card, paymentToken, plan, 0, 0, false, taxInfo);
|
||||
var result = await sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.Card, paymentToken, plans, 0, 0, false, taxInfo);
|
||||
|
||||
Assert.Null(result);
|
||||
Assert.Equal(GatewayType.Stripe, organization.Gateway);
|
||||
@ -207,7 +264,7 @@ public class StripePaymentServiceTests
|
||||
[Theory, BitAutoData]
|
||||
public async void PurchaseOrganizationAsync_Stripe_TaxRate(SutProvider<StripePaymentService> sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo)
|
||||
{
|
||||
var plan = StaticStore.PasswordManagerPlans.First(p => p.Type == PlanType.EnterpriseAnnually);
|
||||
var plans = StaticStore.Plans.Where(p => p.Type == PlanType.EnterpriseAnnually).ToList();
|
||||
|
||||
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
|
||||
stripeAdapter.CustomerCreateAsync(default).ReturnsForAnyArgs(new Stripe.Customer
|
||||
@ -223,7 +280,37 @@ public class StripePaymentServiceTests
|
||||
t.Country == taxInfo.BillingAddressCountry && t.PostalCode == taxInfo.BillingAddressPostalCode))
|
||||
.Returns(new List<TaxRate> { new() { Id = "T-1" } });
|
||||
|
||||
var result = await sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.Card, paymentToken, plan, 0, 0, false, taxInfo);
|
||||
var result = await sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.Card, paymentToken, plans, 0, 0, false, taxInfo);
|
||||
|
||||
Assert.Null(result);
|
||||
|
||||
await stripeAdapter.Received().SubscriptionCreateAsync(Arg.Is<Stripe.SubscriptionCreateOptions>(s =>
|
||||
s.DefaultTaxRates.Count == 1 &&
|
||||
s.DefaultTaxRates[0] == "T-1"
|
||||
));
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async void PurchaseOrganizationAsync_Stripe_TaxRate_SM(SutProvider<StripePaymentService> sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo)
|
||||
{
|
||||
var plans = StaticStore.Plans.Where(p => p.Type == PlanType.EnterpriseAnnually).ToList();
|
||||
|
||||
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
|
||||
stripeAdapter.CustomerCreateAsync(default).ReturnsForAnyArgs(new Stripe.Customer
|
||||
{
|
||||
Id = "C-1",
|
||||
});
|
||||
stripeAdapter.SubscriptionCreateAsync(default).ReturnsForAnyArgs(new Stripe.Subscription
|
||||
{
|
||||
Id = "S-1",
|
||||
CurrentPeriodEnd = DateTime.Today.AddDays(10),
|
||||
});
|
||||
sutProvider.GetDependency<ITaxRateRepository>().GetByLocationAsync(Arg.Is<TaxRate>(t =>
|
||||
t.Country == taxInfo.BillingAddressCountry && t.PostalCode == taxInfo.BillingAddressPostalCode))
|
||||
.Returns(new List<TaxRate> { new() { Id = "T-1" } });
|
||||
|
||||
var result = await sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.Card, paymentToken, plans, 2, 2,
|
||||
false, taxInfo, false, 2, 2);
|
||||
|
||||
Assert.Null(result);
|
||||
|
||||
@ -236,7 +323,7 @@ public class StripePaymentServiceTests
|
||||
[Theory, BitAutoData]
|
||||
public async void PurchaseOrganizationAsync_Stripe_Declined(SutProvider<StripePaymentService> sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo)
|
||||
{
|
||||
var plan = StaticStore.PasswordManagerPlans.First(p => p.Type == PlanType.EnterpriseAnnually);
|
||||
var plan = StaticStore.Plans.Where(p => p.Type == PlanType.EnterpriseAnnually).ToList();
|
||||
paymentToken = "pm_" + paymentToken;
|
||||
|
||||
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
|
||||
@ -266,10 +353,44 @@ public class StripePaymentServiceTests
|
||||
await stripeAdapter.Received(1).CustomerDeleteAsync("C-1");
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async void PurchaseOrganizationAsync_SM_Stripe_Declined(SutProvider<StripePaymentService> sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo)
|
||||
{
|
||||
var plan = StaticStore.Plans.Where(p => p.Type == PlanType.EnterpriseAnnually).ToList();
|
||||
paymentToken = "pm_" + paymentToken;
|
||||
|
||||
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
|
||||
stripeAdapter.CustomerCreateAsync(default).ReturnsForAnyArgs(new Stripe.Customer
|
||||
{
|
||||
Id = "C-1",
|
||||
});
|
||||
stripeAdapter.SubscriptionCreateAsync(default).ReturnsForAnyArgs(new Stripe.Subscription
|
||||
{
|
||||
Id = "S-1",
|
||||
CurrentPeriodEnd = DateTime.Today.AddDays(10),
|
||||
Status = "incomplete",
|
||||
LatestInvoice = new Stripe.Invoice
|
||||
{
|
||||
PaymentIntent = new Stripe.PaymentIntent
|
||||
{
|
||||
Status = "requires_payment_method",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
var exception = await Assert.ThrowsAsync<GatewayException>(
|
||||
() => sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.Card, paymentToken, plan,
|
||||
1, 12, false, taxInfo, false, 10, 10));
|
||||
|
||||
Assert.Equal("Payment method was declined.", exception.Message);
|
||||
|
||||
await stripeAdapter.Received(1).CustomerDeleteAsync("C-1");
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async void PurchaseOrganizationAsync_Stripe_RequiresAction(SutProvider<StripePaymentService> sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo)
|
||||
{
|
||||
var plan = StaticStore.PasswordManagerPlans.First(p => p.Type == PlanType.EnterpriseAnnually);
|
||||
var plans = StaticStore.Plans.Where(p => p.Type == PlanType.EnterpriseAnnually).ToList();
|
||||
|
||||
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
|
||||
stripeAdapter.CustomerCreateAsync(default).ReturnsForAnyArgs(new Stripe.Customer
|
||||
@ -291,7 +412,39 @@ public class StripePaymentServiceTests
|
||||
},
|
||||
});
|
||||
|
||||
var result = await sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.Card, paymentToken, plan, 0, 0, false, taxInfo);
|
||||
var result = await sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.Card, paymentToken, plans, 0, 0, false, taxInfo);
|
||||
|
||||
Assert.Equal("clientSecret", result);
|
||||
Assert.False(organization.Enabled);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async void PurchaseOrganizationAsync_SM_Stripe_RequiresAction(SutProvider<StripePaymentService> sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo)
|
||||
{
|
||||
var plans = StaticStore.Plans.Where(p => p.Type == PlanType.EnterpriseAnnually).ToList();
|
||||
|
||||
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
|
||||
stripeAdapter.CustomerCreateAsync(default).ReturnsForAnyArgs(new Stripe.Customer
|
||||
{
|
||||
Id = "C-1",
|
||||
});
|
||||
stripeAdapter.SubscriptionCreateAsync(default).ReturnsForAnyArgs(new Stripe.Subscription
|
||||
{
|
||||
Id = "S-1",
|
||||
CurrentPeriodEnd = DateTime.Today.AddDays(10),
|
||||
Status = "incomplete",
|
||||
LatestInvoice = new Stripe.Invoice
|
||||
{
|
||||
PaymentIntent = new Stripe.PaymentIntent
|
||||
{
|
||||
Status = "requires_action",
|
||||
ClientSecret = "clientSecret",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
var result = await sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.Card, paymentToken, plans,
|
||||
10, 10, false, taxInfo, false, 10, 10);
|
||||
|
||||
Assert.Equal("clientSecret", result);
|
||||
Assert.False(organization.Enabled);
|
||||
@ -300,7 +453,7 @@ public class StripePaymentServiceTests
|
||||
[Theory, BitAutoData]
|
||||
public async void PurchaseOrganizationAsync_Paypal(SutProvider<StripePaymentService> sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo)
|
||||
{
|
||||
var plan = StaticStore.PasswordManagerPlans.First(p => p.Type == PlanType.EnterpriseAnnually);
|
||||
var plans = StaticStore.Plans.Where(p => p.Type == PlanType.EnterpriseAnnually).ToList();
|
||||
|
||||
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
|
||||
stripeAdapter.CustomerCreateAsync(default).ReturnsForAnyArgs(new Stripe.Customer
|
||||
@ -327,7 +480,7 @@ public class StripePaymentServiceTests
|
||||
var braintreeGateway = sutProvider.GetDependency<IBraintreeGateway>();
|
||||
braintreeGateway.Customer.CreateAsync(default).ReturnsForAnyArgs(customerResult);
|
||||
|
||||
var result = await sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.PayPal, paymentToken, plan, 0, 0, false, taxInfo);
|
||||
var result = await sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.PayPal, paymentToken, plans, 0, 0, false, taxInfo);
|
||||
|
||||
Assert.Null(result);
|
||||
Assert.Equal(GatewayType.Stripe, organization.Gateway);
|
||||
@ -361,10 +514,85 @@ public class StripePaymentServiceTests
|
||||
));
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async void PurchaseOrganizationAsync_SM_Paypal(SutProvider<StripePaymentService> sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo)
|
||||
{
|
||||
var plans = StaticStore.Plans.Where(p => p.Type == PlanType.EnterpriseAnnually).ToList();
|
||||
var passwordManagerPlan = plans.Single(p => p.BitwardenProduct == BitwardenProductType.PasswordManager);
|
||||
var secretsManagerPlan = plans.Single(p => p.BitwardenProduct == BitwardenProductType.SecretsManager);
|
||||
|
||||
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
|
||||
stripeAdapter.CustomerCreateAsync(default).ReturnsForAnyArgs(new Stripe.Customer
|
||||
{
|
||||
Id = "C-1",
|
||||
});
|
||||
stripeAdapter.SubscriptionCreateAsync(default).ReturnsForAnyArgs(new Stripe.Subscription
|
||||
{
|
||||
Id = "S-1",
|
||||
CurrentPeriodEnd = DateTime.Today.AddDays(10),
|
||||
});
|
||||
|
||||
var customer = Substitute.For<Customer>();
|
||||
customer.Id.ReturnsForAnyArgs("Braintree-Id");
|
||||
customer.PaymentMethods.ReturnsForAnyArgs(new[] { Substitute.For<PaymentMethod>() });
|
||||
var customerResult = Substitute.For<Result<Customer>>();
|
||||
customerResult.IsSuccess().Returns(true);
|
||||
customerResult.Target.ReturnsForAnyArgs(customer);
|
||||
|
||||
var braintreeGateway = sutProvider.GetDependency<IBraintreeGateway>();
|
||||
braintreeGateway.Customer.CreateAsync(default).ReturnsForAnyArgs(customerResult);
|
||||
|
||||
sutProvider.GetDependency<IGlobalSettings>()
|
||||
.BaseServiceUri.CloudRegion
|
||||
.Returns("US");
|
||||
|
||||
var additionalStorage = (short)2;
|
||||
var additionalSeats = 10;
|
||||
var additionalSmSeats = 5;
|
||||
var additionalServiceAccounts = 20;
|
||||
var result = await sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.PayPal, paymentToken, plans,
|
||||
additionalStorage, additionalSeats, false, taxInfo, false, additionalSmSeats, additionalServiceAccounts);
|
||||
|
||||
Assert.Null(result);
|
||||
Assert.Equal(GatewayType.Stripe, organization.Gateway);
|
||||
Assert.Equal("C-1", organization.GatewayCustomerId);
|
||||
Assert.Equal("S-1", organization.GatewaySubscriptionId);
|
||||
Assert.True(organization.Enabled);
|
||||
Assert.Equal(DateTime.Today.AddDays(10), organization.ExpirationDate);
|
||||
|
||||
await stripeAdapter.Received().CustomerCreateAsync(Arg.Is<Stripe.CustomerCreateOptions>(c =>
|
||||
c.Description == organization.BusinessName &&
|
||||
c.Email == organization.BillingEmail &&
|
||||
c.PaymentMethod == null &&
|
||||
c.Metadata.Count == 2 &&
|
||||
c.Metadata["region"] == "US" &&
|
||||
c.Metadata["btCustomerId"] == "Braintree-Id" &&
|
||||
c.InvoiceSettings.DefaultPaymentMethod == null &&
|
||||
c.Address.Country == taxInfo.BillingAddressCountry &&
|
||||
c.Address.PostalCode == taxInfo.BillingAddressPostalCode &&
|
||||
c.Address.Line1 == taxInfo.BillingAddressLine1 &&
|
||||
c.Address.Line2 == taxInfo.BillingAddressLine2 &&
|
||||
c.Address.City == taxInfo.BillingAddressCity &&
|
||||
c.Address.State == taxInfo.BillingAddressState &&
|
||||
c.TaxIdData == null
|
||||
));
|
||||
|
||||
await stripeAdapter.Received().SubscriptionCreateAsync(Arg.Is<Stripe.SubscriptionCreateOptions>(s =>
|
||||
s.Customer == "C-1" &&
|
||||
s.Expand[0] == "latest_invoice.payment_intent" &&
|
||||
s.Metadata[organization.GatewayIdField()] == organization.Id.ToString() &&
|
||||
s.Items.Count == 4 &&
|
||||
s.Items.Count(i => i.Plan == passwordManagerPlan.StripeSeatPlanId && i.Quantity == additionalSeats) == 1 &&
|
||||
s.Items.Count(i => i.Plan == passwordManagerPlan.StripeStoragePlanId && i.Quantity == additionalStorage) == 1 &&
|
||||
s.Items.Count(i => i.Plan == secretsManagerPlan.StripeSeatPlanId && i.Quantity == additionalSmSeats) == 1 &&
|
||||
s.Items.Count(i => i.Plan == secretsManagerPlan.StripeServiceAccountPlanId && i.Quantity == additionalServiceAccounts) == 1
|
||||
));
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async void PurchaseOrganizationAsync_Paypal_FailedCreate(SutProvider<StripePaymentService> sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo)
|
||||
{
|
||||
var plan = StaticStore.PasswordManagerPlans.First(p => p.Type == PlanType.EnterpriseAnnually);
|
||||
var plans = StaticStore.Plans.Where(p => p.Type == PlanType.EnterpriseAnnually).ToList();
|
||||
|
||||
var customerResult = Substitute.For<Result<Customer>>();
|
||||
customerResult.IsSuccess().Returns(false);
|
||||
@ -373,7 +601,25 @@ public class StripePaymentServiceTests
|
||||
braintreeGateway.Customer.CreateAsync(default).ReturnsForAnyArgs(customerResult);
|
||||
|
||||
var exception = await Assert.ThrowsAsync<GatewayException>(
|
||||
() => sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.PayPal, paymentToken, plan, 0, 0, false, taxInfo));
|
||||
() => sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.PayPal, paymentToken, plans, 0, 0, false, taxInfo));
|
||||
|
||||
Assert.Equal("Failed to create PayPal customer record.", exception.Message);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async void PurchaseOrganizationAsync_SM_Paypal_FailedCreate(SutProvider<StripePaymentService> sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo)
|
||||
{
|
||||
var plans = StaticStore.Plans.Where(p => p.Type == PlanType.EnterpriseAnnually).ToList();
|
||||
|
||||
var customerResult = Substitute.For<Result<Customer>>();
|
||||
customerResult.IsSuccess().Returns(false);
|
||||
|
||||
var braintreeGateway = sutProvider.GetDependency<IBraintreeGateway>();
|
||||
braintreeGateway.Customer.CreateAsync(default).ReturnsForAnyArgs(customerResult);
|
||||
|
||||
var exception = await Assert.ThrowsAsync<GatewayException>(
|
||||
() => sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.PayPal, paymentToken, plans,
|
||||
1, 1, false, taxInfo, false, 8, 8));
|
||||
|
||||
Assert.Equal("Failed to create PayPal customer record.", exception.Message);
|
||||
}
|
||||
@ -381,7 +627,7 @@ public class StripePaymentServiceTests
|
||||
[Theory, BitAutoData]
|
||||
public async void PurchaseOrganizationAsync_PayPal_Declined(SutProvider<StripePaymentService> sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo)
|
||||
{
|
||||
var plan = StaticStore.PasswordManagerPlans.First(p => p.Type == PlanType.EnterpriseAnnually);
|
||||
var plans = StaticStore.Plans.Where(p => p.Type == PlanType.EnterpriseAnnually).ToList();
|
||||
paymentToken = "pm_" + paymentToken;
|
||||
|
||||
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
|
||||
@ -414,7 +660,7 @@ public class StripePaymentServiceTests
|
||||
braintreeGateway.Customer.CreateAsync(default).ReturnsForAnyArgs(customerResult);
|
||||
|
||||
var exception = await Assert.ThrowsAsync<GatewayException>(
|
||||
() => sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.PayPal, paymentToken, plan, 0, 0, false, taxInfo));
|
||||
() => sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.PayPal, paymentToken, plans, 0, 0, false, taxInfo));
|
||||
|
||||
Assert.Equal("Payment method was declined.", exception.Message);
|
||||
|
||||
@ -443,8 +689,55 @@ public class StripePaymentServiceTests
|
||||
});
|
||||
stripeAdapter.SubscriptionCreateAsync(default).ReturnsForAnyArgs(new Stripe.Subscription { });
|
||||
|
||||
var plan = StaticStore.PasswordManagerPlans.First(p => p.Type == PlanType.EnterpriseAnnually);
|
||||
var result = await sutProvider.Sut.UpgradeFreeOrganizationAsync(organization, plan, 0, 0, false, taxInfo);
|
||||
var plans = StaticStore.Plans.Where(p => p.Type == PlanType.EnterpriseAnnually).ToList();
|
||||
|
||||
var upgrade = new OrganizationUpgrade()
|
||||
{
|
||||
AdditionalStorageGb = 0,
|
||||
AdditionalSeats = 0,
|
||||
PremiumAccessAddon = false,
|
||||
TaxInfo = taxInfo,
|
||||
AdditionalSmSeats = 0,
|
||||
AdditionalServiceAccounts = 0
|
||||
};
|
||||
var result = await sutProvider.Sut.UpgradeFreeOrganizationAsync(organization, plans, upgrade);
|
||||
|
||||
Assert.Null(result);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async void UpgradeFreeOrganizationAsync_SM_Success(SutProvider<StripePaymentService> sutProvider,
|
||||
Organization organization, TaxInfo taxInfo)
|
||||
{
|
||||
organization.GatewaySubscriptionId = null;
|
||||
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
|
||||
stripeAdapter.CustomerGetAsync(default).ReturnsForAnyArgs(new Stripe.Customer
|
||||
{
|
||||
Id = "C-1",
|
||||
Metadata = new Dictionary<string, string>
|
||||
{
|
||||
{ "btCustomerId", "B-123" },
|
||||
}
|
||||
});
|
||||
stripeAdapter.InvoiceUpcomingAsync(default).ReturnsForAnyArgs(new Stripe.Invoice
|
||||
{
|
||||
PaymentIntent = new Stripe.PaymentIntent { Status = "requires_payment_method", },
|
||||
AmountDue = 0
|
||||
});
|
||||
stripeAdapter.SubscriptionCreateAsync(default).ReturnsForAnyArgs(new Stripe.Subscription { });
|
||||
|
||||
var upgrade = new OrganizationUpgrade()
|
||||
{
|
||||
AdditionalStorageGb = 1,
|
||||
AdditionalSeats = 10,
|
||||
PremiumAccessAddon = false,
|
||||
TaxInfo = taxInfo,
|
||||
AdditionalSmSeats = 5,
|
||||
AdditionalServiceAccounts = 50
|
||||
};
|
||||
|
||||
var plans = StaticStore.Plans.Where(p => p.Type == PlanType.EnterpriseAnnually).ToList();
|
||||
var result = await sutProvider.Sut.UpgradeFreeOrganizationAsync(organization, plans, upgrade);
|
||||
|
||||
Assert.Null(result);
|
||||
}
|
||||
|
Reference in New Issue
Block a user