1
0
mirror of https://github.com/bitwarden/server.git synced 2024-12-30 18:17:36 +01:00

attempt to pay invoices via braintree

This commit is contained in:
Kyle Spearrin 2019-02-03 00:00:21 -05:00
parent 856e331ef3
commit 9e14af3223

View File

@ -1,4 +1,5 @@
using Bit.Core.Enums;
using Bit.Core;
using Bit.Core.Enums;
using Bit.Core.Models.Table;
using Bit.Core.Repositories;
using Bit.Core.Services;
@ -24,8 +25,10 @@ namespace Bit.Billing.Controllers
private readonly ITransactionRepository _transactionRepository;
private readonly IUserService _userService;
private readonly IMailService _mailService;
private readonly Braintree.BraintreeGateway _btGateway;
public StripeController(
GlobalSettings globalSettings,
IOptions<BillingSettings> billingSettings,
IHostingEnvironment hostingEnvironment,
IOrganizationService organizationService,
@ -41,6 +44,15 @@ namespace Bit.Billing.Controllers
_transactionRepository = transactionRepository;
_userService = userService;
_mailService = mailService;
_btGateway = new Braintree.BraintreeGateway
{
Environment = globalSettings.Braintree.Production ?
Braintree.Environment.PRODUCTION : Braintree.Environment.SANDBOX,
MerchantId = globalSettings.Braintree.MerchantId,
PublicKey = globalSettings.Braintree.PublicKey,
PrivateKey = globalSettings.Braintree.PrivateKey
};
}
[HttpPost("webhook")]
@ -74,8 +86,7 @@ namespace Bit.Billing.Controllers
if(subDeleted || subUpdated)
{
var subscription = parsedEvent.Data.Object as Subscription;
if(subscription == null)
if(!(parsedEvent.Data.Object is Subscription subscription))
{
throw new Exception("Subscription is null.");
}
@ -117,8 +128,7 @@ namespace Bit.Billing.Controllers
}
else if(parsedEvent.Type.Equals("invoice.upcoming"))
{
var invoice = parsedEvent.Data.Object as Invoice;
if(invoice == null)
if(!(parsedEvent.Data.Object is Invoice invoice))
{
throw new Exception("Invoice is null.");
}
@ -160,8 +170,7 @@ namespace Bit.Billing.Controllers
}
else if(parsedEvent.Type.Equals("charge.succeeded"))
{
var charge = parsedEvent.Data.Object as Charge;
if(charge == null)
if(!(parsedEvent.Data.Object is Charge charge))
{
throw new Exception("Charge is null.");
}
@ -224,8 +233,7 @@ namespace Bit.Billing.Controllers
}
else if(parsedEvent.Type.Equals("charge.refunded"))
{
var charge = parsedEvent.Data.Object as Charge;
if(charge == null)
if(!(parsedEvent.Data.Object is Charge charge))
{
throw new Exception("Charge is null.");
}
@ -273,6 +281,30 @@ namespace Bit.Billing.Controllers
}
}
}
else if(parsedEvent.Type.Equals("invoice.payment_failed"))
{
if(!(parsedEvent.Data.Object is Invoice invoice))
{
throw new Exception("Invoice is null.");
}
if(invoice.AttemptCount > 1 && UnpaidAutoChargeInvoiceForSubscriptionCycle(invoice))
{
await AttemptToPayInvoiceWithBraintreeAsync(invoice);
}
}
else if(parsedEvent.Type.Equals("invoice.created"))
{
if(!(parsedEvent.Data.Object is Invoice invoice))
{
throw new Exception("Invoice is null.");
}
if(UnpaidAutoChargeInvoiceForSubscriptionCycle(invoice))
{
await AttemptToPayInvoiceWithBraintreeAsync(invoice);
}
}
return new OkResult();
}
@ -328,5 +360,70 @@ namespace Bit.Billing.Controllers
return false;
}
}
private async Task<bool> AttemptToPayInvoiceWithBraintreeAsync(Invoice invoice)
{
var customerService = new CustomerService();
var customer = await customerService.GetAsync(invoice.CustomerId);
if(!customer?.Metadata?.ContainsKey("btCustomerId") ?? true)
{
return false;
}
var subscriptionService = new SubscriptionService();
var subscription = await subscriptionService.GetAsync(invoice.SubscriptionId);
var ids = GetIdsFromMetaData(subscription?.Metadata);
if(!ids.Item1.HasValue && !ids.Item2.HasValue)
{
return false;
}
var btObjIdField = ids.Item1.HasValue ? "organization_id" : "user_id";
var btObjId = ids.Item1 ?? ids.Item2.Value;
var btInvoiceAmount = (invoice.AmountDue / 100M);
var transactionResult = await _btGateway.Transaction.SaleAsync(
new Braintree.TransactionRequest
{
Amount = btInvoiceAmount,
CustomerId = customer.Metadata["btCustomerId"],
Options = new Braintree.TransactionOptionsRequest
{
SubmitForSettlement = true,
PayPal = new Braintree.TransactionOptionsPayPalRequest
{
CustomField = $"{btObjIdField}:{btObjId}"
}
},
CustomFields = new Dictionary<string, string>
{
[btObjIdField] = btObjId.ToString()
}
});
if(!transactionResult.IsSuccess())
{
return false;
}
var invoiceService = new InvoiceService();
await invoiceService.UpdateAsync(invoice.Id, new InvoiceUpdateOptions
{
Metadata = new Dictionary<string, string>
{
["btTransactionId"] = transactionResult.Target.Id,
["btPayPalTransactionId"] =
transactionResult.Target.PayPalDetails?.AuthorizationId
}
});
await invoiceService.PayAsync(invoice.Id, new InvoicePayOptions { PaidOutOfBand = true });
return true;
}
private bool UnpaidAutoChargeInvoiceForSubscriptionCycle(Invoice invoice)
{
return invoice.AmountDue > 0 && !invoice.Paid && invoice.Billing == Stripe.Billing.ChargeAutomatically &&
invoice.BillingReason == "subscription_cycle" && invoice.SubscriptionId != null;
}
}
}