From a97a6216d7bb06e50f2f10cfbfa7f1ed0b554f64 Mon Sep 17 00:00:00 2001 From: Kyle Spearrin Date: Fri, 8 Feb 2019 23:24:48 -0500 Subject: [PATCH] return invoices and transactions on billing api --- src/Core/Enums/GatewayType.cs | 2 +- .../Api/Response/BillingResponseModel.cs | 64 ++++++++++++------- .../Api/Response/OrganizationResponseModel.cs | 10 +-- src/Core/Models/Business/BillingInfo.cs | 63 ++++++++++++++++++ .../Implementations/StripePaymentService.cs | 8 +++ 5 files changed, 119 insertions(+), 28 deletions(-) diff --git a/src/Core/Enums/GatewayType.cs b/src/Core/Enums/GatewayType.cs index a8ceff36d0..bb0e88f365 100644 --- a/src/Core/Enums/GatewayType.cs +++ b/src/Core/Enums/GatewayType.cs @@ -15,6 +15,6 @@ namespace Bit.Core.Enums [Display(Name = "Coinbase")] Coinbase = 4, [Display(Name = "PayPal")] - PayPal = 1, + PayPal = 5, } } diff --git a/src/Core/Models/Api/Response/BillingResponseModel.cs b/src/Core/Models/Api/Response/BillingResponseModel.cs index b9d44a057f..efc2bda096 100644 --- a/src/Core/Models/Api/Response/BillingResponseModel.cs +++ b/src/Core/Models/Api/Response/BillingResponseModel.cs @@ -15,8 +15,9 @@ namespace Bit.Core.Models.Api CreditAmount = billing.CreditAmount; PaymentSource = billing.PaymentSource != null ? new BillingSource(billing.PaymentSource) : null; Subscription = billing.Subscription != null ? new BillingSubscription(billing.Subscription) : null; - Charges = billing.Charges.Select(c => new BillingCharge(c)); - UpcomingInvoice = billing.UpcomingInvoice != null ? new BillingInvoice(billing.UpcomingInvoice) : null; + Transactions = billing.Transactions?.Select(t => new BillingTransaction(t)); + Invoices = billing.Invoices?.Select(i => new BillingInvoice(i)); + UpcomingInvoice = billing.UpcomingInvoice != null ? new BillingInvoiceInfo(billing.UpcomingInvoice) : null; StorageName = user.Storage.HasValue ? Utilities.CoreHelpers.ReadableBytesSize(user.Storage.Value) : null; StorageGb = user.Storage.HasValue ? Math.Round(user.Storage.Value / 1073741824D, 2) : 0; // 1 GB MaxStorageGb = user.MaxStorageGb; @@ -44,8 +45,9 @@ namespace Bit.Core.Models.Api public short? MaxStorageGb { get; set; } public BillingSource PaymentSource { get; set; } public BillingSubscription Subscription { get; set; } - public BillingInvoice UpcomingInvoice { get; set; } - public IEnumerable Charges { get; set; } + public BillingInvoiceInfo UpcomingInvoice { get; set; } + public IEnumerable Invoices { get; set; } + public IEnumerable Transactions { get; set; } public UserLicense License { get; set; } public DateTime? Expiration { get; set; } } @@ -111,9 +113,9 @@ namespace Bit.Core.Models.Api } } - public class BillingInvoice + public class BillingInvoiceInfo { - public BillingInvoice(BillingInfo.BillingInvoice inv) + public BillingInvoiceInfo(BillingInfo.BillingInvoice inv) { Amount = inv.Amount; Date = inv.Date; @@ -123,28 +125,44 @@ namespace Bit.Core.Models.Api public DateTime? Date { get; set; } } - public class BillingCharge + public class BillingInvoice : BillingInvoiceInfo { - public BillingCharge(BillingInfo.BillingCharge charge) + public BillingInvoice(BillingInfo.BillingInvoice2 inv) + : base(inv) { - Amount = charge.Amount; - RefundedAmount = charge.RefundedAmount; - PaymentSource = charge.PaymentSource != null ? new BillingSource(charge.PaymentSource) : null; - CreatedDate = charge.CreatedDate; - FailureMessage = charge.FailureMessage; - Refunded = charge.Refunded; - Status = charge.Status; - InvoiceId = charge.InvoiceId; + Url = inv.Url; + PdfUrl = inv.PdfUrl; + Number = inv.Number; + Paid = inv.Paid; + } + + public string Url { get; set; } + public string PdfUrl { get; set; } + public string Number { get; set; } + public bool Paid { get; set; } + } + + public class BillingTransaction + { + public BillingTransaction(BillingInfo.BillingTransaction transaction) + { + CreatedDate = transaction.CreatedDate; + Amount = transaction.Amount; + Refunded = transaction.Refunded; + RefundedAmount = transaction.RefundedAmount; + PartiallyRefunded = transaction.PartiallyRefunded; + Type = transaction.Type; + PaymentMethodType = transaction.PaymentMethodType; + Details = transaction.Details; } public DateTime CreatedDate { get; set; } public decimal Amount { get; set; } - public BillingSource PaymentSource { get; set; } - public string Status { get; set; } - public string FailureMessage { get; set; } - public bool Refunded { get; set; } - public bool PartiallyRefunded => !Refunded && RefundedAmount > 0; - public decimal RefundedAmount { get; set; } - public string InvoiceId { get; set; } + public bool? Refunded { get; set; } + public bool? PartiallyRefunded { get; set; } + public decimal? RefundedAmount { get; set; } + public TransactionType Type { get; set; } + public PaymentMethodType? PaymentMethodType { get; set; } + public string Details { get; set; } } } diff --git a/src/Core/Models/Api/Response/OrganizationResponseModel.cs b/src/Core/Models/Api/Response/OrganizationResponseModel.cs index 1c26401b29..03623f3a0c 100644 --- a/src/Core/Models/Api/Response/OrganizationResponseModel.cs +++ b/src/Core/Models/Api/Response/OrganizationResponseModel.cs @@ -67,8 +67,9 @@ namespace Bit.Core.Models.Api { PaymentSource = billing.PaymentSource != null ? new BillingSource(billing.PaymentSource) : null; Subscription = billing.Subscription != null ? new BillingSubscription(billing.Subscription) : null; - Charges = billing.Charges.Select(c => new BillingCharge(c)); - UpcomingInvoice = billing.UpcomingInvoice != null ? new BillingInvoice(billing.UpcomingInvoice) : null; + Transactions = billing.Transactions?.Select(t => new BillingTransaction(t)); + Invoices = billing.Invoices?.Select(i => new BillingInvoice(i)); + UpcomingInvoice = billing.UpcomingInvoice != null ? new BillingInvoiceInfo(billing.UpcomingInvoice) : null; StorageName = organization.Storage.HasValue ? Utilities.CoreHelpers.ReadableBytesSize(organization.Storage.Value) : null; StorageGb = organization.Storage.HasValue ? Math.Round(organization.Storage.Value / 1073741824D) : 0; // 1 GB @@ -88,8 +89,9 @@ namespace Bit.Core.Models.Api public double? StorageGb { get; set; } public BillingSource PaymentSource { get; set; } public BillingSubscription Subscription { get; set; } - public BillingInvoice UpcomingInvoice { get; set; } - public IEnumerable Charges { get; set; } + public BillingInvoiceInfo UpcomingInvoice { get; set; } + public IEnumerable Invoices { get; set; } + public IEnumerable Transactions { get; set; } public DateTime? Expiration { get; set; } } } diff --git a/src/Core/Models/Business/BillingInfo.cs b/src/Core/Models/Business/BillingInfo.cs index ebfbadca09..ac4c6c0672 100644 --- a/src/Core/Models/Business/BillingInfo.cs +++ b/src/Core/Models/Business/BillingInfo.cs @@ -1,4 +1,5 @@ using Bit.Core.Enums; +using Bit.Core.Models.Table; using Stripe; using System; using System.Collections.Generic; @@ -13,6 +14,8 @@ namespace Bit.Core.Models.Business public BillingSubscription Subscription { get; set; } public BillingInvoice UpcomingInvoice { get; set; } public IEnumerable Charges { get; set; } = new List(); + public IEnumerable Invoices { get; set; } = new List(); + public IEnumerable Transactions { get; set; } = new List(); public class BillingSource { @@ -193,6 +196,8 @@ namespace Bit.Core.Models.Business public class BillingInvoice { + public BillingInvoice() { } + public BillingInvoice(Invoice inv) { Amount = inv.AmountDue / 100M; @@ -263,5 +268,63 @@ namespace Bit.Core.Models.Business public decimal RefundedAmount { get; set; } public string InvoiceId { get; set; } } + + public class BillingTransaction + { + public BillingTransaction(Transaction transaction) + { + CreatedDate = transaction.CreationDate; + Refunded = transaction.Refunded; + Type = transaction.Type; + PaymentMethodType = transaction.PaymentMethodType; + Details = transaction.Details; + + if(transaction.RefundedAmount.HasValue) + { + RefundedAmount = Math.Abs(transaction.RefundedAmount.Value); + } + switch(transaction.Type) + { + case TransactionType.Charge: + case TransactionType.Credit: + case TransactionType.PromotionalCredit: + case TransactionType.ReferralCredit: + Amount = -1 * Math.Abs(transaction.Amount); + break; + case TransactionType.Refund: + Amount = Math.Abs(transaction.Amount); + break; + default: + break; + } + } + + public DateTime CreatedDate { get; set; } + public decimal Amount { get; set; } + public bool? Refunded { get; set; } + public bool? PartiallyRefunded => !Refunded.GetValueOrDefault() && RefundedAmount.GetValueOrDefault() > 0; + public decimal? RefundedAmount { get; set; } + public TransactionType Type { get; set; } + public PaymentMethodType? PaymentMethodType { get; set; } + public string Details { get; set; } + } + + public class BillingInvoice2 : BillingInvoice + { + public BillingInvoice2(Invoice inv) + { + Url = inv.HostedInvoiceUrl; + PdfUrl = inv.InvoicePdf; + Number = inv.Number; + Paid = inv.Paid; + Amount = inv.Total / 100M; + Date = inv.Date.Value; + } + + public string Url { get; set; } + public string PdfUrl { get; set; } + public string Number { get; set; } + public bool Paid { get; set; } + } } } diff --git a/src/Core/Services/Implementations/StripePaymentService.cs b/src/Core/Services/Implementations/StripePaymentService.cs index 501deb46fe..64f9e1a2e0 100644 --- a/src/Core/Services/Implementations/StripePaymentService.cs +++ b/src/Core/Services/Implementations/StripePaymentService.cs @@ -958,6 +958,14 @@ namespace Bit.Core.Services }); billingInfo.Charges = charges?.Data?.OrderByDescending(c => c.Created) .Select(c => new BillingInfo.BillingCharge(c)); + + var invoices = await invoiceService.ListAsync(new InvoiceListOptions + { + CustomerId = customer.Id, + Limit = 20 + }); + billingInfo.Invoices = invoices?.Data?.OrderByDescending(i => i.Date) + .Select(i => new BillingInfo.BillingInvoice2(i)); } }