From c3d73236e07e7fc85c71df3e699aceb2660d1437 Mon Sep 17 00:00:00 2001 From: Andrew Camilleri Date: Fri, 4 May 2018 16:15:34 +0200 Subject: [PATCH 01/49] start work on payment tolerance feature --- BTCPayServer/Controllers/InvoiceController.cs | 1 + BTCPayServer/Controllers/StoresController.cs | 2 ++ BTCPayServer/Data/StoreData.cs | 5 ++++ .../InvoiceNotificationManager.cs | 1 + BTCPayServer/HostedServices/InvoiceWatcher.cs | 23 ++++++++++++++++--- BTCPayServer/Models/InvoiceResponse.cs | 2 +- .../Models/StoreViewModels/StoreViewModel.cs | 8 +++++++ .../Services/Invoices/InvoiceEntity.cs | 1 + BTCPayServer/Views/Stores/UpdateStore.cshtml | 5 ++++ 9 files changed, 44 insertions(+), 4 deletions(-) diff --git a/BTCPayServer/Controllers/InvoiceController.cs b/BTCPayServer/Controllers/InvoiceController.cs index 96299651b..21f2fa83a 100644 --- a/BTCPayServer/Controllers/InvoiceController.cs +++ b/BTCPayServer/Controllers/InvoiceController.cs @@ -98,6 +98,7 @@ namespace BTCPayServer.Controllers entity.ExtendedNotifications = invoice.ExtendedNotifications; entity.NotificationURL = notificationUri?.AbsoluteUri; entity.BuyerInformation = Map(invoice); + entity.PaymentTolerance = storeBlob.PaymentTolerance; //Another way of passing buyer info to support FillBuyerInfo(invoice.Buyer, entity.BuyerInformation); if (entity?.BuyerInformation?.BuyerEmail != null) diff --git a/BTCPayServer/Controllers/StoresController.cs b/BTCPayServer/Controllers/StoresController.cs index 6a22d7f6d..2c4b42a60 100644 --- a/BTCPayServer/Controllers/StoresController.cs +++ b/BTCPayServer/Controllers/StoresController.cs @@ -431,6 +431,7 @@ namespace BTCPayServer.Controllers vm.MonitoringExpiration = storeBlob.MonitoringExpiration; vm.InvoiceExpiration = storeBlob.InvoiceExpiration; vm.LightningDescriptionTemplate = storeBlob.LightningDescriptionTemplate; + vm.PaymentTolerance = storeBlob.PaymentTolerance; return View(vm); } @@ -496,6 +497,7 @@ namespace BTCPayServer.Controllers blob.MonitoringExpiration = model.MonitoringExpiration; blob.InvoiceExpiration = model.InvoiceExpiration; blob.LightningDescriptionTemplate = model.LightningDescriptionTemplate ?? string.Empty; + blob.PaymentTolerance = model.PaymentTolerance; if (StoreData.SetStoreBlob(blob)) { diff --git a/BTCPayServer/Data/StoreData.cs b/BTCPayServer/Data/StoreData.cs index 30c86ad4e..9fa4224a2 100644 --- a/BTCPayServer/Data/StoreData.cs +++ b/BTCPayServer/Data/StoreData.cs @@ -247,6 +247,7 @@ namespace BTCPayServer.Data { InvoiceExpiration = 15; MonitoringExpiration = 60; + PaymentTolerance = 0; RequiresRefundEmail = true; } public bool NetworkFeeDisabled @@ -326,6 +327,10 @@ namespace BTCPayServer.Data } } + [DefaultValue(0)] + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)] + public double PaymentTolerance { get; set; } + public BTCPayServer.Rating.RateRules GetRateRules(BTCPayNetworkProvider networkProvider) { if (!RateScripting || diff --git a/BTCPayServer/HostedServices/InvoiceNotificationManager.cs b/BTCPayServer/HostedServices/InvoiceNotificationManager.cs index 86f24d7d4..25da15e71 100644 --- a/BTCPayServer/HostedServices/InvoiceNotificationManager.cs +++ b/BTCPayServer/HostedServices/InvoiceNotificationManager.cs @@ -312,6 +312,7 @@ namespace BTCPayServer.HostedServices { if (e.Name == "invoice_expired" || e.Name == "invoice_paidInFull" || + e.Name == "invoice_paidWithinTolerance" || e.Name == "invoice_failedToConfirm" || e.Name == "invoice_markedInvalid" || e.Name == "invoice_failedToConfirm" || diff --git a/BTCPayServer/HostedServices/InvoiceWatcher.cs b/BTCPayServer/HostedServices/InvoiceWatcher.cs index 5aef0a738..d745de69e 100644 --- a/BTCPayServer/HostedServices/InvoiceWatcher.cs +++ b/BTCPayServer/HostedServices/InvoiceWatcher.cs @@ -96,11 +96,28 @@ namespace BTCPayServer.HostedServices } } - if (accounting.Paid < accounting.TotalDue && invoice.GetPayments().Count != 0 && invoice.ExceptionStatus != "paidPartial") + if (accounting.Paid < accounting.TotalDue && invoice.GetPayments().Count != 0) { - invoice.ExceptionStatus = "paidPartial"; - context.MarkDirty(); + if (invoice.PaymentTolerance > 0) + { + var tolerantAmount = (accounting.TotalDue.Satoshi * (invoice.PaymentTolerance / 100)); + var minimumTotalDue = accounting.TotalDue.Satoshi - tolerantAmount; + if (accounting.Paid.Satoshi >= minimumTotalDue) + { + context.Events.Add(new InvoiceEvent(invoice, 1003, "invoide_paidWithinTolerance")); + invoice.ExceptionStatus = "paidWithinTolerance"; + invoice.Status = "paid"; + } + } + + if (invoice.ExceptionStatus != "paidPartial") + { + invoice.ExceptionStatus = "paidPartial"; + context.MarkDirty(); + } } + + } // Just make sure RBF did not cancelled a payment diff --git a/BTCPayServer/Models/InvoiceResponse.cs b/BTCPayServer/Models/InvoiceResponse.cs index 4ff6f21ed..33fd3d951 100644 --- a/BTCPayServer/Models/InvoiceResponse.cs +++ b/BTCPayServer/Models/InvoiceResponse.cs @@ -178,7 +178,7 @@ namespace BTCPayServer.Models } //"exceptionStatus":false - //Can be `paidPartial`, `paidOver`, or false + //Can be `paidPartial`, `paidOver`, `paidWithinTolerance` or false [JsonProperty("exceptionStatus")] public JToken ExceptionStatus { diff --git a/BTCPayServer/Models/StoreViewModels/StoreViewModel.cs b/BTCPayServer/Models/StoreViewModels/StoreViewModel.cs index ee4fbf318..c2fadcdd3 100644 --- a/BTCPayServer/Models/StoreViewModels/StoreViewModel.cs +++ b/BTCPayServer/Models/StoreViewModels/StoreViewModel.cs @@ -85,5 +85,13 @@ namespace BTCPayServer.Models.StoreViewModels { get; set; } = new List(); + + [Display(Name = "Consider the invoice paid even if the paid amount is ... % less than expected")] + [Range(0, 100)] + public double PaymentTolerance + { + get; + set; + } } } diff --git a/BTCPayServer/Services/Invoices/InvoiceEntity.cs b/BTCPayServer/Services/Invoices/InvoiceEntity.cs index 0468de9f8..e30181706 100644 --- a/BTCPayServer/Services/Invoices/InvoiceEntity.cs +++ b/BTCPayServer/Services/Invoices/InvoiceEntity.cs @@ -314,6 +314,7 @@ namespace BTCPayServer.Services.Invoices } public bool ExtendedNotifications { get; set; } public List Events { get; internal set; } + public double PaymentTolerance { get; set; } public bool IsExpired() { diff --git a/BTCPayServer/Views/Stores/UpdateStore.cshtml b/BTCPayServer/Views/Stores/UpdateStore.cshtml index 83e4b60d9..cf5d2ef12 100644 --- a/BTCPayServer/Views/Stores/UpdateStore.cshtml +++ b/BTCPayServer/Views/Stores/UpdateStore.cshtml @@ -44,6 +44,11 @@ +
+ + + +
+ From 9afc143801e83e8c3cca8151e1a2b813755d7d7b Mon Sep 17 00:00:00 2001 From: "nicolas.dorier" Date: Fri, 11 May 2018 22:38:31 +0900 Subject: [PATCH 44/49] Use decimals and fix invoices --- BTCPayServer.Tests/UnitTest1.cs | 40 +++++++++---------- .../Controllers/AppsController.PointOfSale.cs | 6 +-- .../InvoiceNotificationManager.cs | 2 +- BTCPayServer/Hosting/BTCpayMiddleware.cs | 5 ++- BTCPayServer/Models/InvoiceResponse.cs | 4 +- .../InvoicingModels/CreateInvoiceModel.cs | 2 +- .../Services/Invoices/InvoiceEntity.cs | 4 +- 7 files changed, 32 insertions(+), 31 deletions(-) diff --git a/BTCPayServer.Tests/UnitTest1.cs b/BTCPayServer.Tests/UnitTest1.cs index 630a7c3af..f82a4bc0a 100644 --- a/BTCPayServer.Tests/UnitTest1.cs +++ b/BTCPayServer.Tests/UnitTest1.cs @@ -276,7 +276,7 @@ namespace BTCPayServer.Tests var invoice = user.BitPay.CreateInvoice(new Invoice() { Buyer = new Buyer() { email = "test@fwf.com" }, - Price = 5000.0, + Price = 5000.0m, Currency = "USD", PosData = "posData", OrderId = "orderId", @@ -308,7 +308,7 @@ namespace BTCPayServer.Tests var invoice = user.BitPay.CreateInvoice(new Invoice() { Buyer = new Buyer() { email = "test@fwf.com" }, - Price = 5000.0, + Price = 5000.0m, Currency = "USD", PosData = "posData", OrderId = "orderId", @@ -440,7 +440,7 @@ namespace BTCPayServer.Tests var invoice = user.BitPay.CreateInvoice(new Invoice() { - Price = 0.01, + Price = 0.01m, Currency = "USD", PosData = "posData", OrderId = "orderId", @@ -473,7 +473,7 @@ namespace BTCPayServer.Tests var invoice = user.BitPay.CreateInvoice(new Invoice() { - Price = 0.01, + Price = 0.01m, Currency = "USD", PosData = "posData", OrderId = "orderId", @@ -501,7 +501,7 @@ namespace BTCPayServer.Tests await Task.Delay(TimeSpan.FromSeconds(RandomUtils.GetUInt32() % 5)); var invoice = await user.BitPay.CreateInvoiceAsync(new Invoice() { - Price = 0.01, + Price = 0.01m, Currency = "USD", PosData = "posData", OrderId = "orderId", @@ -554,7 +554,7 @@ namespace BTCPayServer.Tests acc.RegisterDerivationScheme("BTC"); var invoice = acc.BitPay.CreateInvoice(new Invoice() { - Price = 5.0, + Price = 5.0m, Currency = "USD", PosData = "posData", OrderId = "orderId", @@ -656,7 +656,7 @@ namespace BTCPayServer.Tests user.RegisterDerivationScheme("BTC"); var invoice = user.BitPay.CreateInvoice(new Invoice() { - Price = 5000.0, + Price = 5000.0m, Currency = "USD" }, Facade.Merchant); var payment1 = invoice.BtcDue + Money.Coins(0.0001m); @@ -756,7 +756,7 @@ namespace BTCPayServer.Tests message.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", Encoders.Base64.EncodeData(Encoders.ASCII.DecodeData(apiKey))); var invoice = new Invoice() { - Price = 5000.0, + Price = 5000.0m, Currency = "USD" }; message.Content = new StringContent(JsonConvert.SerializeObject(invoice), Encoding.UTF8, "application/json"); @@ -798,7 +798,7 @@ namespace BTCPayServer.Tests storeController.Rates(vm).Wait(); var invoice2 = user.BitPay.CreateInvoice(new Invoice() { - Price = 5000.0, + Price = 5000.0m, Currency = "USD", PosData = "posData", OrderId = "orderId", @@ -822,7 +822,7 @@ namespace BTCPayServer.Tests // First we try payment with a merchant having only BTC var invoice1 = user.BitPay.CreateInvoice(new Invoice() { - Price = 5000.0, + Price = 5000.0m, Currency = "USD", PosData = "posData", OrderId = "orderId", @@ -840,7 +840,7 @@ namespace BTCPayServer.Tests var invoice2 = user.BitPay.CreateInvoice(new Invoice() { - Price = 5000.0, + Price = 5000.0m, Currency = "USD", PosData = "posData", OrderId = "orderId", @@ -896,7 +896,7 @@ namespace BTCPayServer.Tests // Despite it is called BitcoinAddress it should be LTC because BTC is not available Assert.Null(invoice.BitcoinAddress); - Assert.NotEqual(1.0, invoice.Rate); + Assert.NotEqual(1.0m, invoice.Rate); Assert.NotEqual(invoice.BtcDue, invoice.CryptoInfo[0].Due); // Should be BTC rate cashCow.SendToAddress(invoiceAddress, invoice.CryptoInfo[0].Due); @@ -983,7 +983,7 @@ namespace BTCPayServer.Tests // First we try payment with a merchant having only BTC var invoice = user.BitPay.CreateInvoice(new Invoice() { - Price = 5000.0, + Price = 5000.0m, Currency = "USD", PosData = "posData", OrderId = "orderId", @@ -1015,7 +1015,7 @@ namespace BTCPayServer.Tests user.RegisterDerivationScheme("LTC"); invoice = user.BitPay.CreateInvoice(new Invoice() { - Price = 5000.0, + Price = 5000.0m, Currency = "USD", PosData = "posData", OrderId = "orderId", @@ -1137,7 +1137,7 @@ namespace BTCPayServer.Tests var invoice = user.BitPay.CreateInvoice(new Invoice() { - Price = 1.5, + Price = 1.5m, Currency = "USD", PosData = "posData", OrderId = "orderId", @@ -1150,7 +1150,7 @@ namespace BTCPayServer.Tests invoice = user.BitPay.CreateInvoice(new Invoice() { - Price = 5.5, + Price = 5.5m, Currency = "USD", PosData = "posData", OrderId = "orderId", @@ -1199,7 +1199,7 @@ namespace BTCPayServer.Tests Assert.Equal("$5.00", vmview.Items[0].Price.Formatted); Assert.IsType(apps.ViewPointOfSale(appId, 0, "orange").Result); var invoice = user.BitPay.GetInvoices().First(); - Assert.Equal(10.00, invoice.Price); + Assert.Equal(10.00m, invoice.Price); Assert.Equal("CAD", invoice.Currency); Assert.Equal("orange", invoice.ItemDesc); } @@ -1250,7 +1250,7 @@ namespace BTCPayServer.Tests user.RegisterDerivationScheme("BTC"); var invoice = user.BitPay.CreateInvoice(new Invoice() { - Price = 5000.0, + Price = 5000.0m, Currency = "USD", PosData = "posData", OrderId = "orderId", @@ -1355,12 +1355,12 @@ namespace BTCPayServer.Tests { var localInvoice = user.BitPay.GetInvoice(invoice.Id, Facade.Merchant); Assert.Equal("complete", localInvoice.Status); - Assert.NotEqual(0.0, localInvoice.Rate); + Assert.NotEqual(0.0m, localInvoice.Rate); }); invoice = user.BitPay.CreateInvoice(new Invoice() { - Price = 5000.0, + Price = 5000.0m, Currency = "USD", PosData = "posData", OrderId = "orderId", diff --git a/BTCPayServer/Controllers/AppsController.PointOfSale.cs b/BTCPayServer/Controllers/AppsController.PointOfSale.cs index 76b0278d1..27290739b 100644 --- a/BTCPayServer/Controllers/AppsController.PointOfSale.cs +++ b/BTCPayServer/Controllers/AppsController.PointOfSale.cs @@ -163,7 +163,7 @@ namespace BTCPayServer.Controllers [HttpPost] [Route("{appId}/pos")] [IgnoreAntiforgeryToken] - public async Task ViewPointOfSale(string appId, double amount, string choiceKey) + public async Task ViewPointOfSale(string appId, decimal amount, string choiceKey) { var app = await GetApp(appId, AppType.PointOfSale); if (string.IsNullOrEmpty(choiceKey) && amount <= 0) @@ -178,7 +178,7 @@ namespace BTCPayServer.Controllers return RedirectToAction(nameof(ViewPointOfSale), new { appId = appId }); } string title = null; - double price = 0.0; + var price = 0.0m; if (!string.IsNullOrEmpty(choiceKey)) { var choices = Parse(settings.Template, settings.Currency); @@ -186,7 +186,7 @@ namespace BTCPayServer.Controllers if (choice == null) return NotFound(); title = choice.Title; - price = (double)choice.Price.Value; + price = choice.Price.Value; } else { diff --git a/BTCPayServer/HostedServices/InvoiceNotificationManager.cs b/BTCPayServer/HostedServices/InvoiceNotificationManager.cs index 9845b2d53..0a2d9a057 100644 --- a/BTCPayServer/HostedServices/InvoiceNotificationManager.cs +++ b/BTCPayServer/HostedServices/InvoiceNotificationManager.cs @@ -207,7 +207,7 @@ namespace BTCPayServer.HostedServices if (btcCryptoInfo != null) { #pragma warning disable CS0618 - notification.Rate = (double)dto.Rate; + notification.Rate = dto.Rate; notification.Url = dto.Url; notification.BTCDue = dto.BTCDue; notification.BTCPaid = dto.BTCPaid; diff --git a/BTCPayServer/Hosting/BTCpayMiddleware.cs b/BTCPayServer/Hosting/BTCpayMiddleware.cs index bb80f20aa..a74c8d723 100644 --- a/BTCPayServer/Hosting/BTCpayMiddleware.cs +++ b/BTCPayServer/Hosting/BTCpayMiddleware.cs @@ -79,12 +79,13 @@ namespace BTCPayServer.Hosting if (!httpContext.Request.Path.HasValue) return false; + var isJson = (httpContext.Request.ContentType ?? string.Empty).StartsWith("application/json", StringComparison.OrdinalIgnoreCase); var path = httpContext.Request.Path.Value; if ( bitpayAuth && path == "/invoices" && httpContext.Request.Method == "POST" && - (httpContext.Request.ContentType ?? string.Empty).StartsWith("application/json", StringComparison.OrdinalIgnoreCase)) + isJson) return true; if ( @@ -96,7 +97,7 @@ namespace BTCPayServer.Hosting if ( path.StartsWith("/invoices/", StringComparison.OrdinalIgnoreCase) && httpContext.Request.Method == "GET" && - (httpContext.Request.ContentType ?? string.Empty).StartsWith("application/json", StringComparison.OrdinalIgnoreCase)) + (isJson || httpContext.Request.Query.ContainsKey("token"))) return true; if (path.Equals("/rates", StringComparison.OrdinalIgnoreCase) && diff --git a/BTCPayServer/Models/InvoiceResponse.cs b/BTCPayServer/Models/InvoiceResponse.cs index 4ff6f21ed..fd3bab95c 100644 --- a/BTCPayServer/Models/InvoiceResponse.cs +++ b/BTCPayServer/Models/InvoiceResponse.cs @@ -79,7 +79,7 @@ namespace BTCPayServer.Models //"price":5 [JsonProperty("price")] - public double Price + public decimal Price { get; set; } @@ -94,7 +94,7 @@ namespace BTCPayServer.Models //"exRates":{"USD":4320.02} [JsonProperty("exRates")] [Obsolete("Use CryptoInfo.ExRates instead")] - public Dictionary ExRates + public Dictionary ExRates { get; set; } diff --git a/BTCPayServer/Models/InvoicingModels/CreateInvoiceModel.cs b/BTCPayServer/Models/InvoicingModels/CreateInvoiceModel.cs index 39b77c8dc..9ac99d667 100644 --- a/BTCPayServer/Models/InvoicingModels/CreateInvoiceModel.cs +++ b/BTCPayServer/Models/InvoicingModels/CreateInvoiceModel.cs @@ -14,7 +14,7 @@ namespace BTCPayServer.Models.InvoicingModels Currency = "USD"; } [Required] - public double? Amount + public decimal? Amount { get; set; } diff --git a/BTCPayServer/Services/Invoices/InvoiceEntity.cs b/BTCPayServer/Services/Invoices/InvoiceEntity.cs index 6655971f9..1142e792d 100644 --- a/BTCPayServer/Services/Invoices/InvoiceEntity.cs +++ b/BTCPayServer/Services/Invoices/InvoiceEntity.cs @@ -358,9 +358,9 @@ namespace BTCPayServer.Services.Invoices cryptoInfo.CryptoPaid = accounting.CryptoPaid.ToString(); cryptoInfo.Address = info.GetPaymentMethodDetails()?.GetPaymentDestination(); - cryptoInfo.ExRates = new Dictionary + cryptoInfo.ExRates = new Dictionary { - { ProductInformation.Currency, (double)cryptoInfo.Rate } + { ProductInformation.Currency, cryptoInfo.Rate } }; var paymentId = info.GetId(); var scheme = info.Network.UriScheme; From 4afb0acc8429d99337297a239fbeb192420cac0c Mon Sep 17 00:00:00 2001 From: "nicolas.dorier" Date: Fri, 11 May 2018 22:41:11 +0900 Subject: [PATCH 45/49] does not generate antiforgery --- BTCPayServer/Views/Apps/ViewPointOfSale.cshtml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/BTCPayServer/Views/Apps/ViewPointOfSale.cshtml b/BTCPayServer/Views/Apps/ViewPointOfSale.cshtml index da90f297c..a4445ca83 100644 --- a/BTCPayServer/Views/Apps/ViewPointOfSale.cshtml +++ b/BTCPayServer/Views/Apps/ViewPointOfSale.cshtml @@ -19,7 +19,7 @@

@Model.Title

-
+
@for(int i = 0; i < Model.Items.Length; i++) { @@ -36,7 +36,7 @@ {
- +
From 70a6bd6a01b58e1472f40bae8d8e73da742c6fd0 Mon Sep 17 00:00:00 2001 From: "nicolas.dorier" Date: Fri, 11 May 2018 22:42:29 +0900 Subject: [PATCH 46/49] bump --- BTCPayServer/BTCPayServer.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BTCPayServer/BTCPayServer.csproj b/BTCPayServer/BTCPayServer.csproj index b651b7232..652595bf7 100644 --- a/BTCPayServer/BTCPayServer.csproj +++ b/BTCPayServer/BTCPayServer.csproj @@ -2,7 +2,7 @@ Exe netcoreapp2.1 - 1.0.2.13 + 1.0.2.14 NU1701,CA1816,CA1308,CA1810,CA2208 From af3dee95dec9edcf23d97385089e29a57382641a Mon Sep 17 00:00:00 2001 From: "nicolas.dorier" Date: Fri, 11 May 2018 23:31:50 +0900 Subject: [PATCH 47/49] round up rates sent back by the RateProviderFactory --- BTCPayServer.Tests/UnitTest1.cs | 2 +- BTCPayServer/Rating/RateRules.cs | 11 +++++++++++ .../Services/Rates/BTCPayRateProviderFactory.cs | 11 ++++++++++- 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/BTCPayServer.Tests/UnitTest1.cs b/BTCPayServer.Tests/UnitTest1.cs index f82a4bc0a..2bbcaba46 100644 --- a/BTCPayServer.Tests/UnitTest1.cs +++ b/BTCPayServer.Tests/UnitTest1.cs @@ -1454,7 +1454,7 @@ namespace BTCPayServer.Tests private static BTCPayRateProviderFactory CreateBTCPayRateFactory(BTCPayNetworkProvider provider) { - return new BTCPayRateProviderFactory(new MemoryCacheOptions() { ExpirationScanFrequency = TimeSpan.FromSeconds(1.0) }, provider, new CoinAverageSettings()); + return new BTCPayRateProviderFactory(new MemoryCacheOptions() { ExpirationScanFrequency = TimeSpan.FromSeconds(1.0) }, provider, null, new CoinAverageSettings()); } [Fact] diff --git a/BTCPayServer/Rating/RateRules.cs b/BTCPayServer/Rating/RateRules.cs index 617a24447..d4f72618f 100644 --- a/BTCPayServer/Rating/RateRules.cs +++ b/BTCPayServer/Rating/RateRules.cs @@ -417,10 +417,21 @@ namespace BTCPayServer.Rating public RateRule(RateRules parent, CurrencyPair currencyPair, SyntaxNode candidate) { + _CurrencyPair = currencyPair; flatten = new FlattenExpressionRewriter(parent, currencyPair); this.expression = flatten.Visit(candidate); } + + private readonly CurrencyPair _CurrencyPair; + public CurrencyPair CurrencyPair + { + get + { + return _CurrencyPair; + } + } + public ExchangeRates ExchangeRates { get diff --git a/BTCPayServer/Services/Rates/BTCPayRateProviderFactory.cs b/BTCPayServer/Services/Rates/BTCPayRateProviderFactory.cs index 91104b3ba..b115d1d30 100644 --- a/BTCPayServer/Services/Rates/BTCPayRateProviderFactory.cs +++ b/BTCPayServer/Services/Rates/BTCPayRateProviderFactory.cs @@ -35,7 +35,7 @@ namespace BTCPayServer.Services.Rates } IMemoryCache _Cache; private IOptions _CacheOptions; - + CurrencyNameTable _CurrencyTable; public IMemoryCache Cache { get @@ -46,10 +46,12 @@ namespace BTCPayServer.Services.Rates CoinAverageSettings _CoinAverageSettings; public BTCPayRateProviderFactory(IOptions cacheOptions, BTCPayNetworkProvider btcpayNetworkProvider, + CurrencyNameTable currencyTable, CoinAverageSettings coinAverageSettings) { if (cacheOptions == null) throw new ArgumentNullException(nameof(cacheOptions)); + _CurrencyTable = currencyTable; _CoinAverageSettings = coinAverageSettings; _Cache = new MemoryCache(cacheOptions); _CacheOptions = cacheOptions; @@ -161,6 +163,13 @@ namespace BTCPayServer.Services.Rates } rateRule.Reevaluate(); result.Value = rateRule.Value; + + var currencyData = _CurrencyTable?.GetCurrencyData(rateRule.CurrencyPair.Right); + if(currencyData != null && result.Value.HasValue) + { + result.Value = decimal.Round(result.Value.Value, currencyData.Divisibility, MidpointRounding.AwayFromZero); + } + result.Errors = rateRule.Errors; result.EvaluatedRule = rateRule.ToString(true); result.Rule = rateRule.ToString(false); From 355989c278a7210c808cd4d41a904369daca5eb4 Mon Sep 17 00:00:00 2001 From: "nicolas.dorier" Date: Fri, 11 May 2018 23:34:42 +0900 Subject: [PATCH 48/49] bump --- BTCPayServer/BTCPayServer.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BTCPayServer/BTCPayServer.csproj b/BTCPayServer/BTCPayServer.csproj index 652595bf7..3c9101e9f 100644 --- a/BTCPayServer/BTCPayServer.csproj +++ b/BTCPayServer/BTCPayServer.csproj @@ -2,7 +2,7 @@ Exe netcoreapp2.1 - 1.0.2.14 + 1.0.2.15 NU1701,CA1816,CA1308,CA1810,CA2208 From 786d129452e756c39a1085a303842b780b8807a9 Mon Sep 17 00:00:00 2001 From: "nicolas.dorier" Date: Sat, 12 May 2018 00:14:39 +0900 Subject: [PATCH 49/49] Make sure to not freeze if ligthning does not respond --- BTCPayServer/BTCPayServer.csproj | 2 +- .../Lightning/LightningLikePaymentHandler.cs | 78 +++++++++++-------- 2 files changed, 45 insertions(+), 35 deletions(-) diff --git a/BTCPayServer/BTCPayServer.csproj b/BTCPayServer/BTCPayServer.csproj index 3c9101e9f..61a2a615b 100644 --- a/BTCPayServer/BTCPayServer.csproj +++ b/BTCPayServer/BTCPayServer.csproj @@ -2,7 +2,7 @@ Exe netcoreapp2.1 - 1.0.2.15 + 1.0.2.16 NU1701,CA1816,CA1308,CA1810,CA2208 diff --git a/BTCPayServer/Payments/Lightning/LightningLikePaymentHandler.cs b/BTCPayServer/Payments/Lightning/LightningLikePaymentHandler.cs index e3e3a6361..76fc5cb18 100644 --- a/BTCPayServer/Payments/Lightning/LightningLikePaymentHandler.cs +++ b/BTCPayServer/Payments/Lightning/LightningLikePaymentHandler.cs @@ -36,17 +36,25 @@ namespace BTCPayServer.Payments.Lightning expiry = TimeSpan.FromSeconds(1); LightningInvoice lightningInvoice = null; - try + + string description = storeBlob.LightningDescriptionTemplate; + description = description.Replace("{StoreName}", store.StoreName ?? "", StringComparison.OrdinalIgnoreCase) + .Replace("{ItemDescription}", invoice.ProductInformation.ItemDesc ?? "", StringComparison.OrdinalIgnoreCase) + .Replace("{OrderId}", invoice.OrderId ?? "", StringComparison.OrdinalIgnoreCase); + using (var cts = new CancellationTokenSource(5000)) { - string description = storeBlob.LightningDescriptionTemplate; - description = description.Replace("{StoreName}", store.StoreName ?? "", StringComparison.OrdinalIgnoreCase) - .Replace("{ItemDescription}", invoice.ProductInformation.ItemDesc ?? "", StringComparison.OrdinalIgnoreCase) - .Replace("{OrderId}", invoice.OrderId ?? "", StringComparison.OrdinalIgnoreCase); - lightningInvoice = await client.CreateInvoice(new LightMoney(due, LightMoneyUnit.BTC), description, expiry); - } - catch (Exception ex) - { - throw new PaymentMethodUnavailableException($"Impossible to create lightning invoice ({ex.Message})", ex); + try + { + lightningInvoice = await client.CreateInvoice(new LightMoney(due, LightMoneyUnit.BTC), description, expiry, cts.Token); + } + catch (OperationCanceledException) when (cts.IsCancellationRequested) + { + throw new PaymentMethodUnavailableException($"The lightning node did not replied in a timely maner"); + } + catch (Exception ex) + { + throw new PaymentMethodUnavailableException($"Impossible to create lightning invoice ({ex.Message})", ex); + } } var nodeInfo = await test; return new LightningLikePaymentMethodDetails() @@ -62,34 +70,36 @@ namespace BTCPayServer.Payments.Lightning if (!_Dashboard.IsFullySynched(network.CryptoCode, out var summary)) throw new PaymentMethodUnavailableException($"Full node not available"); - var cts = new CancellationTokenSource(5000); - var client = _LightningClientFactory.CreateClient(supportedPaymentMethod, network); - LightningNodeInformation info = null; - try + using (var cts = new CancellationTokenSource(5000)) { - info = await client.GetInfo(cts.Token); - } - catch (OperationCanceledException) when (cts.IsCancellationRequested) - { - throw new PaymentMethodUnavailableException($"The lightning node did not replied in a timely maner"); - } - catch (Exception ex) - { - throw new PaymentMethodUnavailableException($"Error while connecting to the API ({ex.Message})"); - } + var client = _LightningClientFactory.CreateClient(supportedPaymentMethod, network); + LightningNodeInformation info = null; + try + { + info = await client.GetInfo(cts.Token); + } + catch (OperationCanceledException) when (cts.IsCancellationRequested) + { + throw new PaymentMethodUnavailableException($"The lightning node did not replied in a timely maner"); + } + catch (Exception ex) + { + throw new PaymentMethodUnavailableException($"Error while connecting to the API ({ex.Message})"); + } - if (info.Address == null) - { - throw new PaymentMethodUnavailableException($"No lightning node public address has been configured"); - } + if (info.Address == null) + { + throw new PaymentMethodUnavailableException($"No lightning node public address has been configured"); + } - var blocksGap = Math.Abs(info.BlockHeight - summary.Status.ChainHeight); - if (blocksGap > 10) - { - throw new PaymentMethodUnavailableException($"The lightning is not synched ({blocksGap} blocks)"); - } + var blocksGap = Math.Abs(info.BlockHeight - summary.Status.ChainHeight); + if (blocksGap > 10) + { + throw new PaymentMethodUnavailableException($"The lightning is not synched ({blocksGap} blocks)"); + } - return new NodeInfo(info.NodeId, info.Address, info.P2PPort); + return new NodeInfo(info.NodeId, info.Address, info.P2PPort); + } } public async Task TestConnection(NodeInfo nodeInfo, CancellationToken cancellation)