1
0
mirror of https://github.com/bitwarden/server.git synced 2025-05-07 12:42:24 -05:00

stripe invoice handling. return credit amount.

This commit is contained in:
Kyle Spearrin 2019-01-29 17:44:31 -05:00
parent a34ca4700d
commit abb1751bfe
3 changed files with 96 additions and 43 deletions

View File

@ -12,6 +12,7 @@ namespace Bit.Core.Models.Api
public BillingResponseModel(User user, BillingInfo billing, UserLicense license) public BillingResponseModel(User user, BillingInfo billing, UserLicense license)
: base("billing") : base("billing")
{ {
CreditAmount = billing.CreditAmount;
PaymentSource = billing.PaymentSource != null ? new BillingSource(billing.PaymentSource) : null; PaymentSource = billing.PaymentSource != null ? new BillingSource(billing.PaymentSource) : null;
Subscription = billing.Subscription != null ? new BillingSubscription(billing.Subscription) : null; Subscription = billing.Subscription != null ? new BillingSubscription(billing.Subscription) : null;
Charges = billing.Charges.Select(c => new BillingCharge(c)); Charges = billing.Charges.Select(c => new BillingCharge(c));
@ -37,6 +38,7 @@ namespace Bit.Core.Models.Api
} }
} }
public decimal CreditAmount { get; set; }
public string StorageName { get; set; } public string StorageName { get; set; }
public double? StorageGb { get; set; } public double? StorageGb { get; set; }
public short? MaxStorageGb { get; set; } public short? MaxStorageGb { get; set; }

View File

@ -8,6 +8,7 @@ namespace Bit.Core.Models.Business
{ {
public class BillingInfo public class BillingInfo
{ {
public decimal CreditAmount { get; set; }
public BillingSource PaymentSource { get; set; } public BillingSource PaymentSource { get; set; }
public BillingSubscription Subscription { get; set; } public BillingSubscription Subscription { get; set; }
public BillingInvoice UpcomingInvoice { get; set; } public BillingInvoice UpcomingInvoice { get; set; }

View File

@ -105,6 +105,7 @@ namespace Bit.Core.Services
if(stripeSubscriptionBilling == Billing.SendInvoice) if(stripeSubscriptionBilling == Billing.SendInvoice)
{ {
var invoicePayOptions = new InvoicePayOptions();
var invoiceService = new InvoiceService(); var invoiceService = new InvoiceService();
var invoices = await invoiceService.ListAsync(new InvoiceListOptions var invoices = await invoiceService.ListAsync(new InvoiceListOptions
{ {
@ -118,41 +119,55 @@ namespace Bit.Core.Services
} }
if(braintreeCustomer != null) if(braintreeCustomer != null)
{
invoicePayOptions.PaidOutOfBand = true;
Braintree.Transaction braintreeTransaction = null;
try
{ {
var btInvoiceAmount = (invoice.AmountDue / 100M); var btInvoiceAmount = (invoice.AmountDue / 100M);
var transactionResult = await _btGateway.Transaction.SaleAsync(new Braintree.TransactionRequest var transactionResult = await _btGateway.Transaction.SaleAsync(
new Braintree.TransactionRequest
{ {
Amount = btInvoiceAmount, Amount = btInvoiceAmount,
CustomerId = braintreeCustomer.Id CustomerId = braintreeCustomer.Id,
Options = new Braintree.TransactionOptionsRequest { SubmitForSettlement = true }
}); });
if(!transactionResult.IsSuccess() || transactionResult.Target.Amount != btInvoiceAmount) if(!transactionResult.IsSuccess())
{ {
throw new GatewayException("Failed to charge PayPal customer."); throw new GatewayException("Failed to charge PayPal customer.");
} }
var invoiceItemService = new InvoiceItemService(); braintreeTransaction = transactionResult.Target;
await invoiceItemService.CreateAsync(new InvoiceItemCreateOptions if(transactionResult.Target.Amount != btInvoiceAmount)
{
throw new GatewayException("PayPal charge mismatch.");
}
await invoiceService.UpdateAsync(invoice.Id, new InvoiceUpdateOptions
{ {
Currency = "USD",
CustomerId = customer.Id,
InvoiceId = invoice.Id,
Amount = -1 * invoice.AmountDue,
Description = $"PayPal Credit, Transaction ID " +
transactionResult.Target.PayPalDetails.AuthorizationId,
Metadata = new Dictionary<string, string> Metadata = new Dictionary<string, string>
{ {
["btTransactionId"] = transactionResult.Target.Id, ["btTransactionId"] = braintreeTransaction.Id,
["btPayPalTransactionId"] = transactionResult.Target.PayPalDetails.AuthorizationId ["btPayPalTransactionId"] = braintreeTransaction.PayPalDetails.AuthorizationId
} }
}); });
} }
catch(Exception e)
{
if(braintreeTransaction != null)
{
await _btGateway.Transaction.RefundAsync(braintreeTransaction.Id);
}
throw e;
}
}
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, new InvoicePayOptions { }); await invoiceService.PayAsync(invoice.Id, invoicePayOptions);
} }
} }
catch(Exception e) catch(Exception e)
@ -271,41 +286,65 @@ namespace Bit.Core.Services
SubscriptionId = subscriber.GatewaySubscriptionId SubscriptionId = subscriber.GatewaySubscriptionId
}); });
var invoicePayOptions = new InvoicePayOptions();
if(invoice.AmountDue > 0) if(invoice.AmountDue > 0)
{ {
var customerService = new CustomerService(); var customerService = new CustomerService();
var customer = await customerService.GetAsync(subscriber.GatewayCustomerId); var customer = await customerService.GetAsync(subscriber.GatewayCustomerId);
if(customer != null) if(customer != null)
{ {
Braintree.Transaction braintreeTransaction = null;
if(customer.Metadata.ContainsKey("btCustomerId")) if(customer.Metadata.ContainsKey("btCustomerId"))
{ {
var invoiceAmount = (invoice.AmountDue / 100M); invoicePayOptions.PaidOutOfBand = true;
try
{
var btInvoiceAmount = (invoice.AmountDue / 100M);
var transactionResult = await _btGateway.Transaction.SaleAsync( var transactionResult = await _btGateway.Transaction.SaleAsync(
new Braintree.TransactionRequest new Braintree.TransactionRequest
{ {
Amount = invoiceAmount, Amount = btInvoiceAmount,
CustomerId = customer.Metadata["btCustomerId"] CustomerId = customer.Metadata["btCustomerId"],
Options = new Braintree.TransactionOptionsRequest
{
SubmitForSettlement = true
}
}); });
if(!transactionResult.IsSuccess() || transactionResult.Target.Amount != invoiceAmount) if(!transactionResult.IsSuccess())
{ {
await invoiceService.UpdateAsync(invoice.Id, new InvoiceUpdateOptions
{
Closed = true
});
throw new GatewayException("Failed to charge PayPal customer."); throw new GatewayException("Failed to charge PayPal customer.");
} }
await customerService.UpdateAsync(customer.Id, new CustomerUpdateOptions braintreeTransaction = transactionResult.Target;
if(transactionResult.Target.Amount != btInvoiceAmount)
{ {
AccountBalance = customer.AccountBalance - invoice.AmountDue, throw new GatewayException("PayPal charge mismatch.");
Metadata = customer.Metadata }
await invoiceService.UpdateAsync(invoice.Id, new InvoiceUpdateOptions
{
Metadata = new Dictionary<string, string>
{
["btTransactionId"] = braintreeTransaction.Id,
["btPayPalTransactionId"] =
braintreeTransaction.PayPalDetails.AuthorizationId
}
}); });
} }
catch(Exception e)
{
if(braintreeTransaction != null)
{
await _btGateway.Transaction.RefundAsync(braintreeTransaction.Id);
}
throw e;
}
}
}
} }
await invoiceService.PayAsync(invoice.Id, new InvoicePayOptions()); await invoiceService.PayAsync(invoice.Id, invoicePayOptions);
}
} }
catch(StripeException) { } catch(StripeException) { }
} }
@ -502,7 +541,18 @@ namespace Bit.Core.Services
var customer = await customerService.GetAsync(subscriber.GatewayCustomerId); var customer = await customerService.GetAsync(subscriber.GatewayCustomerId);
if(customer != null) if(customer != null)
{ {
if(!string.IsNullOrWhiteSpace(customer.DefaultSourceId) && customer.Sources?.Data != null) billingInfo.CreditAmount = customer.AccountBalance / 100M;
if(customer.Metadata?.ContainsKey("btCustomerId") ?? false)
{
var braintreeCustomer = await _btGateway.Customer.FindAsync(customer.Metadata["btCustomerId"]);
if(braintreeCustomer?.DefaultPaymentMethod != null)
{
billingInfo.PaymentSource = new BillingInfo.BillingSource(
braintreeCustomer.DefaultPaymentMethod);
}
}
else if(!string.IsNullOrWhiteSpace(customer.DefaultSourceId) && customer.Sources?.Data != null)
{ {
if(customer.DefaultSourceId.StartsWith("card_") || customer.DefaultSourceId.StartsWith("ba_")) if(customer.DefaultSourceId.StartsWith("card_") || customer.DefaultSourceId.StartsWith("ba_"))
{ {