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:
parent
a34ca4700d
commit
abb1751bfe
@ -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; }
|
||||||
|
@ -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; }
|
||||||
|
@ -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_"))
|
||||||
{
|
{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user