mirror of
https://github.com/bitwarden/server.git
synced 2025-04-08 14:38:15 -05:00
rework paypal payment to use customer balance
This commit is contained in:
parent
abb1751bfe
commit
d236bdd408
@ -32,14 +32,22 @@ namespace Bit.Core.Services
|
|||||||
public async Task PurchasePremiumAsync(User user, PaymentMethodType paymentMethodType, string paymentToken,
|
public async Task PurchasePremiumAsync(User user, PaymentMethodType paymentMethodType, string paymentToken,
|
||||||
short additionalStorageGb)
|
short additionalStorageGb)
|
||||||
{
|
{
|
||||||
|
var invoiceService = new InvoiceService();
|
||||||
|
var customerService = new CustomerService();
|
||||||
|
|
||||||
|
Braintree.Transaction braintreeTransaction = null;
|
||||||
Braintree.Customer braintreeCustomer = null;
|
Braintree.Customer braintreeCustomer = null;
|
||||||
Billing? stripeSubscriptionBilling = null;
|
|
||||||
string stipeCustomerSourceToken = null;
|
string stipeCustomerSourceToken = null;
|
||||||
var stripeCustomerMetadata = new Dictionary<string, string>();
|
var stripeCustomerMetadata = new Dictionary<string, string>();
|
||||||
|
var stripePaymentMethod = paymentMethodType == PaymentMethodType.Card ||
|
||||||
|
paymentMethodType == PaymentMethodType.BankAccount;
|
||||||
|
|
||||||
if(paymentMethodType == PaymentMethodType.PayPal)
|
if(stripePaymentMethod)
|
||||||
|
{
|
||||||
|
stipeCustomerSourceToken = paymentToken;
|
||||||
|
}
|
||||||
|
else if(paymentMethodType == PaymentMethodType.PayPal)
|
||||||
{
|
{
|
||||||
stripeSubscriptionBilling = Billing.SendInvoice;
|
|
||||||
var randomSuffix = Utilities.CoreHelpers.RandomString(3, upper: false, numeric: false);
|
var randomSuffix = Utilities.CoreHelpers.RandomString(3, upper: false, numeric: false);
|
||||||
var customerResult = await _btGateway.Customer.CreateAsync(new Braintree.CustomerRequest
|
var customerResult = await _btGateway.Customer.CreateAsync(new Braintree.CustomerRequest
|
||||||
{
|
{
|
||||||
@ -56,12 +64,7 @@ namespace Bit.Core.Services
|
|||||||
braintreeCustomer = customerResult.Target;
|
braintreeCustomer = customerResult.Target;
|
||||||
stripeCustomerMetadata.Add("btCustomerId", braintreeCustomer.Id);
|
stripeCustomerMetadata.Add("btCustomerId", braintreeCustomer.Id);
|
||||||
}
|
}
|
||||||
else if(paymentMethodType == PaymentMethodType.Card || paymentMethodType == PaymentMethodType.BankAccount)
|
|
||||||
{
|
|
||||||
stipeCustomerSourceToken = paymentToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
var customerService = new CustomerService();
|
|
||||||
var customer = await customerService.CreateAsync(new CustomerCreateOptions
|
var customer = await customerService.CreateAsync(new CustomerCreateOptions
|
||||||
{
|
{
|
||||||
Description = user.Name,
|
Description = user.Name,
|
||||||
@ -69,13 +72,11 @@ namespace Bit.Core.Services
|
|||||||
SourceToken = stipeCustomerSourceToken,
|
SourceToken = stipeCustomerSourceToken,
|
||||||
Metadata = stripeCustomerMetadata
|
Metadata = stripeCustomerMetadata
|
||||||
});
|
});
|
||||||
|
|
||||||
var subCreateOptions = new SubscriptionCreateOptions
|
var subCreateOptions = new SubscriptionCreateOptions
|
||||||
{
|
{
|
||||||
CustomerId = customer.Id,
|
CustomerId = customer.Id,
|
||||||
Items = new List<SubscriptionItemOption>(),
|
Items = new List<SubscriptionItemOption>(),
|
||||||
Billing = stripeSubscriptionBilling,
|
|
||||||
DaysUntilDue = stripeSubscriptionBilling != null ? 1 : (long?)null,
|
|
||||||
Metadata = new Dictionary<string, string>
|
Metadata = new Dictionary<string, string>
|
||||||
{
|
{
|
||||||
["userId"] = user.Id.ToString()
|
["userId"] = user.Id.ToString()
|
||||||
@ -85,7 +86,7 @@ namespace Bit.Core.Services
|
|||||||
subCreateOptions.Items.Add(new SubscriptionItemOption
|
subCreateOptions.Items.Add(new SubscriptionItemOption
|
||||||
{
|
{
|
||||||
PlanId = PremiumPlanId,
|
PlanId = PremiumPlanId,
|
||||||
Quantity = 1
|
Quantity = 1,
|
||||||
});
|
});
|
||||||
|
|
||||||
if(additionalStorageGb > 0)
|
if(additionalStorageGb > 0)
|
||||||
@ -97,82 +98,78 @@ namespace Bit.Core.Services
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var subInvoiceMetadata = new Dictionary<string, string>();
|
||||||
Subscription subscription = null;
|
Subscription subscription = null;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var subscriptionService = new SubscriptionService();
|
if(!stripePaymentMethod)
|
||||||
subscription = await subscriptionService.CreateAsync(subCreateOptions);
|
|
||||||
|
|
||||||
if(stripeSubscriptionBilling == Billing.SendInvoice)
|
|
||||||
{
|
{
|
||||||
var invoicePayOptions = new InvoicePayOptions();
|
var previewInvoice = await invoiceService.UpcomingAsync(new UpcomingInvoiceOptions
|
||||||
var invoiceService = new InvoiceService();
|
|
||||||
var invoices = await invoiceService.ListAsync(new InvoiceListOptions
|
|
||||||
{
|
{
|
||||||
SubscriptionId = subscription.Id
|
CustomerId = customer.Id,
|
||||||
|
SubscriptionItems = ToInvoiceSubscriptionItemOptions(subCreateOptions.Items)
|
||||||
});
|
});
|
||||||
|
|
||||||
var invoice = invoices?.FirstOrDefault(i => i.AmountDue > 0);
|
await customerService.UpdateAsync(customer.Id, new CustomerUpdateOptions
|
||||||
if(invoice == null)
|
|
||||||
{
|
{
|
||||||
throw new GatewayException("Invoice not found.");
|
AccountBalance = -1 * previewInvoice.AmountDue
|
||||||
}
|
});
|
||||||
|
|
||||||
if(braintreeCustomer != null)
|
if(braintreeCustomer != null)
|
||||||
{
|
{
|
||||||
invoicePayOptions.PaidOutOfBand = true;
|
var btInvoiceAmount = (previewInvoice.AmountDue / 100M);
|
||||||
Braintree.Transaction braintreeTransaction = null;
|
var transactionResult = await _btGateway.Transaction.SaleAsync(
|
||||||
try
|
new Braintree.TransactionRequest
|
||||||
{
|
|
||||||
var btInvoiceAmount = (invoice.AmountDue / 100M);
|
|
||||||
var transactionResult = await _btGateway.Transaction.SaleAsync(
|
|
||||||
new Braintree.TransactionRequest
|
|
||||||
{
|
|
||||||
Amount = btInvoiceAmount,
|
|
||||||
CustomerId = braintreeCustomer.Id,
|
|
||||||
Options = new Braintree.TransactionOptionsRequest { SubmitForSettlement = true }
|
|
||||||
});
|
|
||||||
|
|
||||||
if(!transactionResult.IsSuccess())
|
|
||||||
{
|
{
|
||||||
throw new GatewayException("Failed to charge PayPal customer.");
|
Amount = btInvoiceAmount,
|
||||||
}
|
CustomerId = braintreeCustomer.Id,
|
||||||
|
Options = new Braintree.TransactionOptionsRequest { SubmitForSettlement = true }
|
||||||
braintreeTransaction = transactionResult.Target;
|
|
||||||
if(transactionResult.Target.Amount != btInvoiceAmount)
|
|
||||||
{
|
|
||||||
throw new GatewayException("PayPal charge mismatch.");
|
|
||||||
}
|
|
||||||
|
|
||||||
await invoiceService.UpdateAsync(invoice.Id, new InvoiceUpdateOptions
|
|
||||||
{
|
|
||||||
Metadata = new Dictionary<string, string>
|
|
||||||
{
|
|
||||||
["btTransactionId"] = braintreeTransaction.Id,
|
|
||||||
["btPayPalTransactionId"] = braintreeTransaction.PayPalDetails.AuthorizationId
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
|
||||||
catch(Exception e)
|
if(!transactionResult.IsSuccess())
|
||||||
{
|
{
|
||||||
if(braintreeTransaction != null)
|
throw new GatewayException("Failed to charge PayPal customer.");
|
||||||
{
|
|
||||||
await _btGateway.Transaction.RefundAsync(braintreeTransaction.Id);
|
|
||||||
}
|
|
||||||
throw e;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
subInvoiceMetadata.Add("btTransactionId", braintreeTransaction.Id);
|
||||||
|
subInvoiceMetadata.Add("btPayPalTransactionId",
|
||||||
|
braintreeTransaction.PayPalDetails.AuthorizationId);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw new GatewayException("No payment was able to be collected.");
|
throw new GatewayException("No payment was able to be collected.");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
await invoiceService.PayAsync(invoice.Id, invoicePayOptions);
|
var subscriptionService = new SubscriptionService();
|
||||||
|
subscription = await subscriptionService.CreateAsync(subCreateOptions);
|
||||||
|
|
||||||
|
if(!stripePaymentMethod && subInvoiceMetadata.Any())
|
||||||
|
{
|
||||||
|
var invoices = await invoiceService.ListAsync(new InvoiceListOptions
|
||||||
|
{
|
||||||
|
SubscriptionId = subscription.Id
|
||||||
|
});
|
||||||
|
|
||||||
|
var invoice = invoices?.FirstOrDefault();
|
||||||
|
if(invoice == null)
|
||||||
|
{
|
||||||
|
throw new GatewayException("Invoice not found.");
|
||||||
|
}
|
||||||
|
|
||||||
|
await invoiceService.UpdateAsync(invoice.Id, new InvoiceUpdateOptions
|
||||||
|
{
|
||||||
|
Metadata = subInvoiceMetadata
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch(Exception e)
|
catch(Exception e)
|
||||||
{
|
{
|
||||||
await customerService.DeleteAsync(customer.Id);
|
await customerService.DeleteAsync(customer.Id);
|
||||||
|
if(braintreeTransaction != null)
|
||||||
|
{
|
||||||
|
await _btGateway.Transaction.RefundAsync(braintreeTransaction.Id);
|
||||||
|
}
|
||||||
if(braintreeCustomer != null)
|
if(braintreeCustomer != null)
|
||||||
{
|
{
|
||||||
await _btGateway.Customer.DeleteAsync(braintreeCustomer.Id);
|
await _btGateway.Customer.DeleteAsync(braintreeCustomer.Id);
|
||||||
@ -187,6 +184,16 @@ namespace Bit.Core.Services
|
|||||||
user.PremiumExpirationDate = subscription.CurrentPeriodEnd;
|
user.PremiumExpirationDate = subscription.CurrentPeriodEnd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<InvoiceSubscriptionItemOptions> ToInvoiceSubscriptionItemOptions(
|
||||||
|
List<SubscriptionItemOption> subItemOptions)
|
||||||
|
{
|
||||||
|
return subItemOptions.Select(si => new InvoiceSubscriptionItemOptions
|
||||||
|
{
|
||||||
|
PlanId = si.PlanId,
|
||||||
|
Quantity = si.Quantity
|
||||||
|
}).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
public async Task AdjustStorageAsync(IStorableSubscriber storableSubscriber, int additionalStorage,
|
public async Task AdjustStorageAsync(IStorableSubscriber storableSubscriber, int additionalStorage,
|
||||||
string storagePlanId)
|
string storagePlanId)
|
||||||
{
|
{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user