diff --git a/BTCPayServer.Tests/UnitTest1.cs b/BTCPayServer.Tests/UnitTest1.cs index 5aee1d360..b5e3df28a 100644 --- a/BTCPayServer.Tests/UnitTest1.cs +++ b/BTCPayServer.Tests/UnitTest1.cs @@ -913,6 +913,11 @@ namespace BTCPayServer.Tests Assert.Single(invoice.CryptoInfo); Assert.Equal("LTC", invoice.CryptoInfo[0].CryptoCode); + Assert.True(invoice.PaymentCodes.ContainsKey("LTC")); + Assert.True(invoice.SupportedTransactionCurrencies.ContainsKey("LTC")); + Assert.True(invoice.SupportedTransactionCurrencies["LTC"].Enabled); + Assert.True(invoice.PaymentSubtotals.ContainsKey("LTC")); + Assert.True(invoice.PaymentTotals.ContainsKey("LTC")); var cashCow = tester.LTCExplorerNode; var invoiceAddress = BitcoinAddress.Create(invoice.CryptoInfo[0].Address, cashCow.Network); var firstPayment = Money.Coins(0.1m); @@ -1047,6 +1052,16 @@ namespace BTCPayServer.Tests Assert.Single(checkout.AvailableCryptos); Assert.Equal("BTC", checkout.CryptoCode); + Assert.Single(invoice.PaymentCodes); + Assert.Single(invoice.SupportedTransactionCurrencies); + Assert.Single(invoice.SupportedTransactionCurrencies); + Assert.Single(invoice.PaymentSubtotals); + Assert.Single(invoice.PaymentTotals); + Assert.True(invoice.PaymentCodes.ContainsKey("BTC")); + Assert.True(invoice.SupportedTransactionCurrencies.ContainsKey("BTC")); + Assert.True(invoice.SupportedTransactionCurrencies["BTC"].Enabled); + Assert.True(invoice.PaymentSubtotals.ContainsKey("BTC")); + Assert.True(invoice.PaymentTotals.ContainsKey("BTC")); ////////////////////// // Retry now with LTC enabled @@ -1095,6 +1110,18 @@ namespace BTCPayServer.Tests checkout = (Models.InvoicingModels.PaymentModel)((JsonResult)controller.GetStatus(invoice.Id, "LTC").GetAwaiter().GetResult()).Value; Assert.Equal(2, checkout.AvailableCryptos.Count); Assert.Equal("LTC", checkout.CryptoCode); + + + Assert.Equal(2, invoice.PaymentCodes.Count()); + Assert.Equal(2, invoice.SupportedTransactionCurrencies.Count()); + Assert.Equal(2, invoice.SupportedTransactionCurrencies.Count()); + Assert.Equal(2, invoice.PaymentSubtotals.Count()); + Assert.Equal(2, invoice.PaymentTotals.Count()); + Assert.True(invoice.PaymentCodes.ContainsKey("LTC")); + Assert.True(invoice.SupportedTransactionCurrencies.ContainsKey("LTC")); + Assert.True(invoice.SupportedTransactionCurrencies["LTC"].Enabled); + Assert.True(invoice.PaymentSubtotals.ContainsKey("LTC")); + Assert.True(invoice.PaymentTotals.ContainsKey("LTC")); } } diff --git a/BTCPayServer/HostedServices/InvoiceNotificationManager.cs b/BTCPayServer/HostedServices/InvoiceNotificationManager.cs index 0a2d9a057..6605e11c8 100644 --- a/BTCPayServer/HostedServices/InvoiceNotificationManager.cs +++ b/BTCPayServer/HostedServices/InvoiceNotificationManager.cs @@ -198,7 +198,11 @@ namespace BTCPayServer.HostedServices PosData = dto.PosData, Price = dto.Price, Status = dto.Status, - BuyerFields = invoice.RefundMail == null ? null : new Newtonsoft.Json.Linq.JObject() { new JProperty("buyerEmail", invoice.RefundMail) } + BuyerFields = invoice.RefundMail == null ? null : new Newtonsoft.Json.Linq.JObject() { new JProperty("buyerEmail", invoice.RefundMail) }, + PaymentSubtotals = dto.PaymentSubtotals, + PaymentTotals = dto.PaymentTotals, + AmountPaid = dto.AmountPaid, + ExchangeRates = dto.ExchangeRates }; // We keep backward compatibility with bitpay by passing BTC info to the notification diff --git a/BTCPayServer/Models/InvoiceResponse.cs b/BTCPayServer/Models/InvoiceResponse.cs index fd3bab95c..c7ad99dff 100644 --- a/BTCPayServer/Models/InvoiceResponse.cs +++ b/BTCPayServer/Models/InvoiceResponse.cs @@ -224,6 +224,29 @@ namespace BTCPayServer.Models { get; set; } + + [JsonProperty("paymentSubtotals")] + public Dictionary PaymentSubtotals { get; set; } + + [JsonProperty("paymentTotals")] + public Dictionary PaymentTotals { get; set; } + + [JsonProperty("amountPaid")] + public long AmountPaid { get; set; } + + [JsonProperty("minerFees")] + public long MinerFees { get; set; } + + [JsonProperty("exchangeRates")] + public Dictionary> ExchangeRates{ get; set; } + + [JsonProperty("supportedTransactionCurrencies")] + public Dictionary SupportedTransactionCurrencies { get; set; } + + [JsonProperty("addresses")] + public Dictionary Addresses { get; set; } + [JsonProperty("paymentCodes")] + public Dictionary PaymentCodes{get; set;} } public class Flags { @@ -233,4 +256,5 @@ namespace BTCPayServer.Models get; set; } } + } diff --git a/BTCPayServer/Services/Invoices/InvoiceEntity.cs b/BTCPayServer/Services/Invoices/InvoiceEntity.cs index 1142e792d..c2264e275 100644 --- a/BTCPayServer/Services/Invoices/InvoiceEntity.cs +++ b/BTCPayServer/Services/Invoices/InvoiceEntity.cs @@ -12,6 +12,7 @@ using NBXplorer.Models; using NBXplorer; using NBXplorer.DerivationStrategy; using BTCPayServer.Payments; +using NBitpayClient; namespace BTCPayServer.Services.Invoices { @@ -336,19 +337,35 @@ namespace BTCPayServer.Services.Invoices ExpirationTime = ExpirationTime, Status = Status, Currency = ProductInformation.Currency, - Flags = new Flags() { Refundable = Refundable } + Flags = new Flags() { Refundable = Refundable }, + + PaymentSubtotals = new Dictionary(), + PaymentTotals= new Dictionary(), + SupportedTransactionCurrencies = new Dictionary(), + Addresses = new Dictionary(), + PaymentCodes = new Dictionary(), + ExchangeRates = new Dictionary>() }; dto.Url = ServerUrl.WithTrailingSlash() + $"invoice?id=" + Id; dto.CryptoInfo = new List(); foreach (var info in this.GetPaymentMethods(networkProvider)) { + var accounting = info.Calculate(); var cryptoInfo = new NBitpayClient.InvoiceCryptoInfo(); - cryptoInfo.CryptoCode = info.GetId().CryptoCode; + var subtotalPrice = accounting.TotalDue - accounting.NetworkFee; + var cryptoCode = info.GetId().CryptoCode; + var address = info.GetPaymentMethodDetails()?.GetPaymentDestination(); + var exrates = new Dictionary + { + { ProductInformation.Currency, cryptoInfo.Rate } + }; + + cryptoInfo.CryptoCode = cryptoCode; cryptoInfo.PaymentType = info.GetId().PaymentType.ToString(); cryptoInfo.Rate = info.Rate; - cryptoInfo.Price = (accounting.TotalDue - accounting.NetworkFee).ToString(); + cryptoInfo.Price = subtotalPrice.ToString(); cryptoInfo.Due = accounting.Due.ToString(); cryptoInfo.Paid = accounting.Paid.ToString(); @@ -357,11 +374,9 @@ namespace BTCPayServer.Services.Invoices cryptoInfo.TxCount = accounting.TxCount; cryptoInfo.CryptoPaid = accounting.CryptoPaid.ToString(); - cryptoInfo.Address = info.GetPaymentMethodDetails()?.GetPaymentDestination(); - cryptoInfo.ExRates = new Dictionary - { - { ProductInformation.Currency, cryptoInfo.Rate } - }; + cryptoInfo.Address = address; + + cryptoInfo.ExRates = exrates; var paymentId = info.GetId(); var scheme = info.Network.UriScheme; cryptoInfo.Url = ServerUrl.WithTrailingSlash() + $"i/{paymentId}/{Id}"; @@ -396,10 +411,22 @@ namespace BTCPayServer.Services.Invoices dto.BTCDue = cryptoInfo.Due; dto.PaymentUrls = cryptoInfo.PaymentUrls; } + #pragma warning restore CS0618 dto.CryptoInfo.Add(cryptoInfo); + + dto.PaymentSubtotals.Add(cryptoCode, subtotalPrice.Satoshi); + dto.PaymentTotals.Add(cryptoCode, accounting.TotalDue.Satoshi); + dto.SupportedTransactionCurrencies.Add(cryptoCode, new InvoiceSupportedTransactionCurrency() + { + Enabled = true + }); + dto.Addresses.Add(cryptoCode, address); + dto.ExchangeRates.Add(cryptoCode, exrates); } + //dto.AmountPaid dto.MinerFees & dto.TransactionCurrency are not supported by btcpayserver as we have multi currency payment support per invoice + Populate(ProductInformation, dto); Populate(BuyerInformation, dto);