mirror of
https://github.com/bitwarden/server.git
synced 2025-07-06 18:42:49 -05:00
[PM-7452] Handle PayPal for premium users (#4835)
* Add PremiumUserSale * Add PremiumUserBillingService * Integrate into UserService behind FF * Update invoice.created handler to bill newly created PayPal customers * Run dotnet format
This commit is contained in:
@ -34,11 +34,9 @@ public class OrganizationBillingService(
|
||||
{
|
||||
var (organization, customerSetup, subscriptionSetup) = sale;
|
||||
|
||||
List<string> expand = ["tax"];
|
||||
|
||||
var customer = customerSetup != null
|
||||
? await CreateCustomerAsync(organization, customerSetup, expand)
|
||||
: await subscriberService.GetCustomerOrThrow(organization, new CustomerGetOptions { Expand = expand });
|
||||
var customer = string.IsNullOrEmpty(organization.GatewayCustomerId) && customerSetup != null
|
||||
? await CreateCustomerAsync(organization, customerSetup)
|
||||
: await subscriberService.GetCustomerOrThrow(organization, new CustomerGetOptions { Expand = ["tax"] });
|
||||
|
||||
var subscription = await CreateSubscriptionAsync(organization.Id, customer, subscriptionSetup);
|
||||
|
||||
@ -111,31 +109,31 @@ public class OrganizationBillingService(
|
||||
|
||||
private async Task<Customer> CreateCustomerAsync(
|
||||
Organization organization,
|
||||
CustomerSetup customerSetup,
|
||||
List<string>? expand = null)
|
||||
CustomerSetup customerSetup)
|
||||
{
|
||||
var organizationDisplayName = organization.DisplayName();
|
||||
var displayName = organization.DisplayName();
|
||||
|
||||
var customerCreateOptions = new CustomerCreateOptions
|
||||
{
|
||||
Coupon = customerSetup.Coupon,
|
||||
Description = organization.DisplayBusinessName(),
|
||||
Email = organization.BillingEmail,
|
||||
Expand = expand,
|
||||
Expand = ["tax"],
|
||||
InvoiceSettings = new CustomerInvoiceSettingsOptions
|
||||
{
|
||||
CustomFields = [
|
||||
new CustomerInvoiceSettingsCustomFieldOptions
|
||||
{
|
||||
Name = organization.SubscriberType(),
|
||||
Value = organizationDisplayName.Length <= 30
|
||||
? organizationDisplayName
|
||||
: organizationDisplayName[..30]
|
||||
Value = displayName.Length <= 30
|
||||
? displayName
|
||||
: displayName[..30]
|
||||
}]
|
||||
},
|
||||
Metadata = new Dictionary<string, string>
|
||||
{
|
||||
{ "region", globalSettings.BaseServiceUri.CloudRegion }
|
||||
["organizationId"] = organization.Id.ToString(),
|
||||
["region"] = globalSettings.BaseServiceUri.CloudRegion
|
||||
}
|
||||
};
|
||||
|
||||
@ -174,46 +172,41 @@ public class OrganizationBillingService(
|
||||
};
|
||||
customerCreateOptions.TaxIdData = taxIdData;
|
||||
|
||||
var (type, token) = customerSetup.TokenizedPaymentSource;
|
||||
var (paymentMethodType, paymentMethodToken) = customerSetup.TokenizedPaymentSource;
|
||||
|
||||
// ReSharper disable once SwitchStatementHandlesSomeKnownEnumValuesWithDefault
|
||||
switch (type)
|
||||
switch (paymentMethodType)
|
||||
{
|
||||
case PaymentMethodType.BankAccount:
|
||||
{
|
||||
var setupIntent =
|
||||
(await stripeAdapter.SetupIntentList(new SetupIntentListOptions { PaymentMethod = token }))
|
||||
(await stripeAdapter.SetupIntentList(new SetupIntentListOptions { PaymentMethod = paymentMethodToken }))
|
||||
.FirstOrDefault();
|
||||
|
||||
if (setupIntent == null)
|
||||
{
|
||||
logger.LogError("Cannot create customer for organization ({OrganizationID}) without a setup intent for their bank account", organization.Id);
|
||||
|
||||
throw new BillingException();
|
||||
}
|
||||
|
||||
await setupIntentCache.Set(organization.Id, setupIntent.Id);
|
||||
|
||||
break;
|
||||
}
|
||||
case PaymentMethodType.Card:
|
||||
{
|
||||
customerCreateOptions.PaymentMethod = token;
|
||||
customerCreateOptions.InvoiceSettings.DefaultPaymentMethod = token;
|
||||
customerCreateOptions.PaymentMethod = paymentMethodToken;
|
||||
customerCreateOptions.InvoiceSettings.DefaultPaymentMethod = paymentMethodToken;
|
||||
break;
|
||||
}
|
||||
case PaymentMethodType.PayPal:
|
||||
{
|
||||
braintreeCustomerId = await subscriberService.CreateBraintreeCustomer(organization, token);
|
||||
|
||||
braintreeCustomerId = await subscriberService.CreateBraintreeCustomer(organization, paymentMethodToken);
|
||||
customerCreateOptions.Metadata[BraintreeCustomerIdKey] = braintreeCustomerId;
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
logger.LogError("Cannot create customer for organization ({OrganizationID}) using payment method type ({PaymentMethodType}) as it is not supported", organization.Id, type.ToString());
|
||||
|
||||
logger.LogError("Cannot create customer for organization ({OrganizationID}) using payment method type ({PaymentMethodType}) as it is not supported", organization.Id, paymentMethodType.ToString());
|
||||
throw new BillingException();
|
||||
}
|
||||
}
|
||||
@ -227,7 +220,6 @@ public class OrganizationBillingService(
|
||||
StripeConstants.ErrorCodes.CustomerTaxLocationInvalid)
|
||||
{
|
||||
await Revert();
|
||||
|
||||
throw new BadRequestException(
|
||||
"Your location wasn't recognized. Please ensure your country and postal code are valid.");
|
||||
}
|
||||
@ -235,7 +227,6 @@ public class OrganizationBillingService(
|
||||
StripeConstants.ErrorCodes.TaxIdInvalid)
|
||||
{
|
||||
await Revert();
|
||||
|
||||
throw new BadRequestException(
|
||||
"Your tax ID wasn't recognized for your selected country. Please ensure your country and tax ID are valid.");
|
||||
}
|
||||
@ -257,7 +248,7 @@ public class OrganizationBillingService(
|
||||
await setupIntentCache.Remove(organization.Id);
|
||||
break;
|
||||
}
|
||||
case PaymentMethodType.PayPal:
|
||||
case PaymentMethodType.PayPal when !string.IsNullOrEmpty(braintreeCustomerId):
|
||||
{
|
||||
await braintreeGateway.Customer.DeleteAsync(braintreeCustomerId);
|
||||
break;
|
||||
|
Reference in New Issue
Block a user