From 090da6cfb6ef52423df1003d7a4773fe544dc350 Mon Sep 17 00:00:00 2001 From: Nicolas Dorier Date: Mon, 24 Jan 2022 20:17:09 +0900 Subject: [PATCH] Add configurable BOLT11Expiration for refunds (Fix #3281) (#3341) * Add configurable BOLT11Expiration for refunds (Fix #3281) * Add BOLT11Expiration configuration in Payment --- .../JsonConverters/TimeSpanJsonConverter.cs | 11 ++++++++++ .../Models/CreatePullPaymentRequest.cs | 3 +++ .../Models/PullPaymentBaseData.cs | 3 +++ .../AltcoinTests/AltcoinTests.cs | 8 +++++++ BTCPayServer.Tests/FastTests.cs | 8 +++++++ BTCPayServer.Tests/GreenfieldAPITests.cs | 6 +++-- .../GreenfieldPullPaymentController.cs | 8 ++++++- .../Controllers/UIInvoiceController.UI.cs | 3 ++- .../Controllers/UIPullPaymentController.cs | 2 +- ...torePullPaymentsController.PullPayments.cs | 3 ++- .../Controllers/UIStoresController.cs | 4 +++- .../BitcoinLike/BitcoinLikePayoutHandler.cs | 9 ++++++-- BTCPayServer/Data/Payouts/IPayoutHandler.cs | 13 ++++++++++- .../LightningLikePayoutHandler.cs | 22 +++++++++++++------ .../UILightningLikePayoutController.cs | 2 +- .../Data/PullPayments/PullPaymentBlob.cs | 7 ++++++ BTCPayServer/Data/StoreBlob.cs | 5 +++++ .../PullPaymentHostedService.cs | 6 +++-- BTCPayServer/Hosting/MigrationStartupTask.cs | 2 +- .../StoreViewModels/PaymentViewModel.cs | 4 ++++ .../WalletViewModels/PullPaymentsModel.cs | 3 +++ .../UIStorePullPayments/NewPullPayment.cshtml | 19 ++++++++++++++++ BTCPayServer/Views/UIStores/Payment.cshtml | 9 +++++++- .../v1/swagger.template.pull-payments.json | 12 ++++++++++ 24 files changed, 150 insertions(+), 22 deletions(-) diff --git a/BTCPayServer.Client/JsonConverters/TimeSpanJsonConverter.cs b/BTCPayServer.Client/JsonConverters/TimeSpanJsonConverter.cs index b392655d4..fb12a1765 100644 --- a/BTCPayServer.Client/JsonConverters/TimeSpanJsonConverter.cs +++ b/BTCPayServer.Client/JsonConverters/TimeSpanJsonConverter.cs @@ -29,6 +29,17 @@ namespace BTCPayServer.Client.JsonConverters return TimeSpan.FromMinutes(value); } } + public class Days : TimeSpanJsonConverter + { + protected override long ToLong(TimeSpan value) + { + return (long)value.TotalDays; + } + protected override TimeSpan ToTimespan(long value) + { + return TimeSpan.FromDays(value); + } + } public override bool CanConvert(Type objectType) { return objectType == typeof(TimeSpan) || objectType == typeof(TimeSpan?); diff --git a/BTCPayServer.Client/Models/CreatePullPaymentRequest.cs b/BTCPayServer.Client/Models/CreatePullPaymentRequest.cs index 6cce582ca..3ef48c5ba 100644 --- a/BTCPayServer.Client/Models/CreatePullPaymentRequest.cs +++ b/BTCPayServer.Client/Models/CreatePullPaymentRequest.cs @@ -13,6 +13,9 @@ namespace BTCPayServer.Client.Models public string Currency { get; set; } [JsonConverter(typeof(TimeSpanJsonConverter.Seconds))] public TimeSpan? Period { get; set; } + [JsonConverter(typeof(TimeSpanJsonConverter.Days))] + [JsonProperty("BOLT11Expiration")] + public TimeSpan? BOLT11Expiration { get; set; } [JsonConverter(typeof(NBitcoin.JsonConverters.DateTimeToUnixTimeConverter))] public DateTimeOffset? ExpiresAt { get; set; } [JsonConverter(typeof(NBitcoin.JsonConverters.DateTimeToUnixTimeConverter))] diff --git a/BTCPayServer.Client/Models/PullPaymentBaseData.cs b/BTCPayServer.Client/Models/PullPaymentBaseData.cs index 371bdee9d..c4fc9f390 100644 --- a/BTCPayServer.Client/Models/PullPaymentBaseData.cs +++ b/BTCPayServer.Client/Models/PullPaymentBaseData.cs @@ -18,6 +18,9 @@ namespace BTCPayServer.Client.Models public decimal Amount { get; set; } [JsonConverter(typeof(TimeSpanJsonConverter.Seconds))] public TimeSpan? Period { get; set; } + [JsonConverter(typeof(TimeSpanJsonConverter.Days))] + [JsonProperty("BOLT11Expiration")] + public TimeSpan BOLT11Expiration { get; set; } public bool Archived { get; set; } public string ViewLink { get; set; } } diff --git a/BTCPayServer.Tests/AltcoinTests/AltcoinTests.cs b/BTCPayServer.Tests/AltcoinTests/AltcoinTests.cs index c83787d8c..6802b2fc1 100644 --- a/BTCPayServer.Tests/AltcoinTests/AltcoinTests.cs +++ b/BTCPayServer.Tests/AltcoinTests/AltcoinTests.cs @@ -386,6 +386,9 @@ namespace BTCPayServer.Tests // BTC crash by 50% s.Server.PayTester.ChangeRate("BTC_USD", new Rating.BidAsk(5000.0m / 2.0m, 5100.0m / 2.0m)); + s.GoToStore(StoreNavPages.Payment); + s.Driver.FindElement(By.Id("BOLT11Expiration")).Clear(); + s.Driver.FindElement(By.Id("BOLT11Expiration")).SendKeys("5" + Keys.Enter); s.GoToInvoice(invoice.Id); s.Driver.FindElement(By.Id("refundlink")).Click(); if (multiCurrency) @@ -408,6 +411,11 @@ namespace BTCPayServer.Tests s.GoToInvoice(invoice.Id); s.Driver.FindElement(By.Id("refundlink")).Click(); Assert.Contains("pull-payments", s.Driver.Url); + var client = await user.CreateClient(); + var ppid = s.Driver.Url.Split('/').Last(); + var pps = await client.GetPullPayments(user.StoreId); + var pp = Assert.Single(pps, p => p.Id == ppid); + Assert.Equal(TimeSpan.FromDays(5.0), pp.BOLT11Expiration); } [Fact(Timeout = TestTimeout)] diff --git a/BTCPayServer.Tests/FastTests.cs b/BTCPayServer.Tests/FastTests.cs index fda520e03..3f4980d69 100644 --- a/BTCPayServer.Tests/FastTests.cs +++ b/BTCPayServer.Tests/FastTests.cs @@ -1252,6 +1252,14 @@ namespace BTCPayServer.Tests Assert.Equal(cache.States[0].Rates[0].Pair, cache2.States[0].Rates[0].Pair); } + [Fact] + public void KitchenSinkTest() + { + var b = JsonConvert.DeserializeObject("{}"); + Assert.Equal(TimeSpan.FromDays(30.0), b.BOLT11Expiration); + var aaa = JsonConvert.SerializeObject(b); + } + [Fact] public void CanParseRateRules() { diff --git a/BTCPayServer.Tests/GreenfieldAPITests.cs b/BTCPayServer.Tests/GreenfieldAPITests.cs index a32c82b16..3e00be687 100644 --- a/BTCPayServer.Tests/GreenfieldAPITests.cs +++ b/BTCPayServer.Tests/GreenfieldAPITests.cs @@ -389,14 +389,15 @@ namespace BTCPayServer.Tests result = Assert.Single(pullPayments); VerifyResult(); - Thread.Sleep(1000); var test2 = await client.CreatePullPayment(storeId, new Client.Models.CreatePullPaymentRequest() { Name = "Test 2", Amount = 12.3m, Currency = "BTC", - PaymentMethods = new[] { "BTC" } + PaymentMethods = new[] { "BTC" }, + BOLT11Expiration = TimeSpan.FromDays(31.0) }); + Assert.Equal(TimeSpan.FromDays(31.0), test2.BOLT11Expiration); TestLogs.LogInformation("Can't archive without knowing the walletId"); var ex = await AssertAPIError("missing-permission", async () => await client.ArchivePullPayment("lol", result.Id)); @@ -405,6 +406,7 @@ namespace BTCPayServer.Tests await AssertAPIError("unauthenticated", async () => await unauthenticated.ArchivePullPayment(storeId, result.Id)); await client.ArchivePullPayment(storeId, result.Id); result = await unauthenticated.GetPullPayment(result.Id); + Assert.Equal(TimeSpan.FromDays(30.0), result.BOLT11Expiration); Assert.True(result.Archived); var pps = await client.GetPullPayments(storeId); result = Assert.Single(pps); diff --git a/BTCPayServer/Controllers/GreenField/GreenfieldPullPaymentController.cs b/BTCPayServer/Controllers/GreenField/GreenfieldPullPaymentController.cs index f504229e6..88fdf5119 100644 --- a/BTCPayServer/Controllers/GreenField/GreenfieldPullPaymentController.cs +++ b/BTCPayServer/Controllers/GreenField/GreenfieldPullPaymentController.cs @@ -99,6 +99,10 @@ namespace BTCPayServer.Controllers.Greenfield { ModelState.AddModelError(nameof(request.Period), $"The period should be positive"); } + if (request.BOLT11Expiration <= TimeSpan.Zero) + { + ModelState.AddModelError(nameof(request.BOLT11Expiration), $"The BOLT11 expiration should be positive"); + } PaymentMethodId?[]? paymentMethods = null; if (request.PaymentMethods is { } paymentMethodsStr) { @@ -127,6 +131,7 @@ namespace BTCPayServer.Controllers.Greenfield StartsAt = request.StartsAt, ExpiresAt = request.ExpiresAt, Period = request.Period, + BOLT11Expiration = request.BOLT11Expiration, Name = request.Name, Amount = request.Amount, Currency = request.Currency, @@ -150,6 +155,7 @@ namespace BTCPayServer.Controllers.Greenfield Currency = ppBlob.Currency, Period = ppBlob.Period, Archived = pp.Archived, + BOLT11Expiration = ppBlob.BOLT11Expiration, ViewLink = _linkGenerator.GetUriByAction( nameof(UIPullPaymentController.ViewPullPayment), "UIPullPayment", @@ -245,7 +251,7 @@ namespace BTCPayServer.Controllers.Greenfield if (pp is null) return PullPaymentNotFound(); var ppBlob = pp.GetBlob(); - var destination = await payoutHandler.ParseClaimDestination(paymentMethodId, request!.Destination, true); + var destination = await payoutHandler.ParseAndValidateClaimDestination(paymentMethodId, request!.Destination, ppBlob); if (destination.destination is null) { ModelState.AddModelError(nameof(request.Destination), destination.error ?? "The destination is invalid for the payment specified"); diff --git a/BTCPayServer/Controllers/UIInvoiceController.UI.cs b/BTCPayServer/Controllers/UIInvoiceController.UI.cs index e5321a8c9..339e122d9 100644 --- a/BTCPayServer/Controllers/UIInvoiceController.UI.cs +++ b/BTCPayServer/Controllers/UIInvoiceController.UI.cs @@ -285,7 +285,8 @@ namespace BTCPayServer.Controllers { Name = $"Refund {invoice.Id}", PaymentMethodIds = new[] { paymentMethodId }, - StoreId = invoice.StoreId + StoreId = invoice.StoreId, + BOLT11Expiration = store.GetStoreBlob().RefundBOLT11Expiration }; switch (model.SelectedRefundOption) { diff --git a/BTCPayServer/Controllers/UIPullPaymentController.cs b/BTCPayServer/Controllers/UIPullPaymentController.cs index 64c26e9be..4444cd83c 100644 --- a/BTCPayServer/Controllers/UIPullPaymentController.cs +++ b/BTCPayServer/Controllers/UIPullPaymentController.cs @@ -115,7 +115,7 @@ namespace BTCPayServer.Controllers ModelState.AddModelError(nameof(vm.SelectedPaymentMethod), $"Invalid destination with selected payment method"); return await ViewPullPayment(pullPaymentId); } - var destination = await payoutHandler?.ParseClaimDestination(paymentMethodId, vm.Destination, true); + var destination = await payoutHandler?.ParseAndValidateClaimDestination(paymentMethodId, vm.Destination, ppBlob); if (destination.destination is null) { ModelState.AddModelError(nameof(vm.Destination), destination.error ?? "Invalid destination with selected payment method"); diff --git a/BTCPayServer/Controllers/UIStorePullPaymentsController.PullPayments.cs b/BTCPayServer/Controllers/UIStorePullPaymentsController.PullPayments.cs index 519f7bdaf..d4232f468 100644 --- a/BTCPayServer/Controllers/UIStorePullPaymentsController.PullPayments.cs +++ b/BTCPayServer/Controllers/UIStorePullPaymentsController.PullPayments.cs @@ -134,7 +134,8 @@ namespace BTCPayServer.Controllers StoreId = storeId, PaymentMethodIds = selectedPaymentMethodIds, EmbeddedCSS = model.EmbeddedCSS, - CustomCSSLink = model.CustomCSSLink + CustomCSSLink = model.CustomCSSLink, + BOLT11Expiration = TimeSpan.FromDays(model.BOLT11Expiration) }); this.TempData.SetStatusMessageModel(new StatusMessageModel() { diff --git a/BTCPayServer/Controllers/UIStoresController.cs b/BTCPayServer/Controllers/UIStoresController.cs index dd08d999d..5926ca040 100644 --- a/BTCPayServer/Controllers/UIStoresController.cs +++ b/BTCPayServer/Controllers/UIStoresController.cs @@ -604,7 +604,8 @@ namespace BTCPayServer.Controllers AnyoneCanCreateInvoice = storeBlob.AnyoneCanInvoice, PaymentTolerance = storeBlob.PaymentTolerance, InvoiceExpiration = (int)storeBlob.InvoiceExpiration.TotalMinutes, - DefaultCurrency = storeBlob.DefaultCurrency + DefaultCurrency = storeBlob.DefaultCurrency, + BOLT11Expiration = (long)storeBlob.RefundBOLT11Expiration.TotalDays }; return View(vm); @@ -620,6 +621,7 @@ namespace BTCPayServer.Controllers blob.PaymentTolerance = model.PaymentTolerance; blob.DefaultCurrency = model.DefaultCurrency; blob.InvoiceExpiration = TimeSpan.FromMinutes(model.InvoiceExpiration); + blob.RefundBOLT11Expiration = TimeSpan.FromDays(model.BOLT11Expiration); if (CurrentStore.SetStoreBlob(blob)) { diff --git a/BTCPayServer/Data/Payouts/BitcoinLike/BitcoinLikePayoutHandler.cs b/BTCPayServer/Data/Payouts/BitcoinLike/BitcoinLikePayoutHandler.cs index 401e6c012..b11cb65c8 100644 --- a/BTCPayServer/Data/Payouts/BitcoinLike/BitcoinLikePayoutHandler.cs +++ b/BTCPayServer/Data/Payouts/BitcoinLike/BitcoinLikePayoutHandler.cs @@ -68,7 +68,7 @@ public class BitcoinLikePayoutHandler : IPayoutHandler await explorerClient.TrackAsync(TrackedSource.Create(bitcoinLikeClaimDestination.Address)); } - public Task<(IClaimDestination destination, string error)> ParseClaimDestination(PaymentMethodId paymentMethodId, string destination, bool validate) + public Task<(IClaimDestination destination, string error)> ParseClaimDestination(PaymentMethodId paymentMethodId, string destination) { var network = _btcPayNetworkProvider.GetNetwork(paymentMethodId.CryptoCode); destination = destination.Trim(); @@ -88,6 +88,11 @@ public class BitcoinLikePayoutHandler : IPayoutHandler } } + public (bool valid, string error) ValidateClaimDestination(IClaimDestination claimDestination, PullPaymentBlob pullPaymentBlob) + { + return (true, null); + } + public IPayoutProof ParseProof(PayoutData payout) { if (payout?.Proof is null) @@ -251,7 +256,7 @@ public class BitcoinLikePayoutHandler : IPayoutHandler var blob = payout.GetBlob(_jsonSerializerSettings); if (payout.GetPaymentMethodId() != paymentMethodId) continue; - var claim = await ParseClaimDestination(paymentMethodId, blob.Destination, false); + var claim = await ParseClaimDestination(paymentMethodId, blob.Destination); switch (claim.destination) { case UriClaimDestination uriClaimDestination: diff --git a/BTCPayServer/Data/Payouts/IPayoutHandler.cs b/BTCPayServer/Data/Payouts/IPayoutHandler.cs index ab74fa200..e87891076 100644 --- a/BTCPayServer/Data/Payouts/IPayoutHandler.cs +++ b/BTCPayServer/Data/Payouts/IPayoutHandler.cs @@ -14,7 +14,18 @@ public interface IPayoutHandler public bool CanHandle(PaymentMethodId paymentMethod); public Task TrackClaim(PaymentMethodId paymentMethodId, IClaimDestination claimDestination); //Allows payout handler to parse payout destinations on its own - public Task<(IClaimDestination destination, string error)> ParseClaimDestination(PaymentMethodId paymentMethodId, string destination, bool validate); + public Task<(IClaimDestination destination, string error)> ParseClaimDestination(PaymentMethodId paymentMethodId, string destination); + public (bool valid, string error) ValidateClaimDestination(IClaimDestination claimDestination, PullPaymentBlob pullPaymentBlob); + public async Task<(IClaimDestination destination, string error)> ParseAndValidateClaimDestination(PaymentMethodId paymentMethodId, string destination, PullPaymentBlob pullPaymentBlob) + { + var res = await ParseClaimDestination(paymentMethodId, destination); + if (res.destination is null) + return res; + var res2 = ValidateClaimDestination(res.destination, pullPaymentBlob); + if (!res2.valid) + return (null, res2.error); + return res; + } public IPayoutProof ParseProof(PayoutData payout); //Allows you to subscribe the main pull payment hosted service to events and prepare the handler void StartBackgroundCheck(Action subscribe); diff --git a/BTCPayServer/Data/Payouts/LightningLike/LightningLikePayoutHandler.cs b/BTCPayServer/Data/Payouts/LightningLike/LightningLikePayoutHandler.cs index baf4a0e84..e56765aaa 100644 --- a/BTCPayServer/Data/Payouts/LightningLike/LightningLikePayoutHandler.cs +++ b/BTCPayServer/Data/Payouts/LightningLike/LightningLikePayoutHandler.cs @@ -57,7 +57,7 @@ namespace BTCPayServer.Data.Payouts.LightningLike : LightningLikePayoutHandlerClearnetNamedClient); } - public async Task<(IClaimDestination destination, string error)> ParseClaimDestination(PaymentMethodId paymentMethodId, string destination, bool validate) + public async Task<(IClaimDestination destination, string error)> ParseClaimDestination(PaymentMethodId paymentMethodId, string destination) { destination = destination.Trim(); var network = _btcPayNetworkProvider.GetNetwork(paymentMethodId.CryptoCode); @@ -93,12 +93,7 @@ namespace BTCPayServer.Data.Payouts.LightningLike : null; if (result == null) - return (null, "A valid BOLT11 invoice (with 30+ day expiry) or LNURL Pay or Lightning address was not provided."); - if (validate && (invoice.ExpiryDate.UtcDateTime - DateTime.UtcNow).Days < 30) - { - return (null, - $"The BOLT11 invoice must have an expiry date of at least 30 days from submission (Provided was only {(invoice.ExpiryDate.UtcDateTime - DateTime.UtcNow).Days})."); - } + return (null, "A valid BOLT11 invoice or LNURL Pay or Lightning address was not provided."); if (invoice.ExpiryDate.UtcDateTime < DateTime.UtcNow) { return (null, @@ -108,6 +103,19 @@ namespace BTCPayServer.Data.Payouts.LightningLike return (result, null); } + public (bool valid, string error) ValidateClaimDestination(IClaimDestination claimDestination, PullPaymentBlob pullPaymentBlob) + { + if (claimDestination is not BoltInvoiceClaimDestination bolt) + return (true, null); + var invoice = bolt.PaymentRequest; + if ((invoice.ExpiryDate.UtcDateTime - DateTime.UtcNow) < pullPaymentBlob.BOLT11Expiration) + { + return (false, + $"The BOLT11 invoice must have an expiry date of at least {(long)pullPaymentBlob.BOLT11Expiration.TotalDays} days from submission (Provided was only {(invoice.ExpiryDate.UtcDateTime - DateTime.UtcNow).Days})."); + } + return (true, null); + } + public IPayoutProof ParseProof(PayoutData payout) { return null; diff --git a/BTCPayServer/Data/Payouts/LightningLike/UILightningLikePayoutController.cs b/BTCPayServer/Data/Payouts/LightningLike/UILightningLikePayoutController.cs index f376fc845..e8c345ec4 100644 --- a/BTCPayServer/Data/Payouts/LightningLike/UILightningLikePayoutController.cs +++ b/BTCPayServer/Data/Payouts/LightningLike/UILightningLikePayoutController.cs @@ -190,7 +190,7 @@ namespace BTCPayServer.Data.Payouts.LightningLike foreach (var payoutData in payoutDatas) { var blob = payoutData.GetBlob(_btcPayNetworkJsonSerializerSettings); - var claim = await payoutHandler.ParseClaimDestination(pmi, blob.Destination, false); + var claim = await payoutHandler.ParseClaimDestination(pmi, blob.Destination); try { switch (claim.destination) diff --git a/BTCPayServer/Data/PullPayments/PullPaymentBlob.cs b/BTCPayServer/Data/PullPayments/PullPaymentBlob.cs index c92768333..8e487cbd3 100644 --- a/BTCPayServer/Data/PullPayments/PullPaymentBlob.cs +++ b/BTCPayServer/Data/PullPayments/PullPaymentBlob.cs @@ -1,4 +1,5 @@ using System; +using System.ComponentModel; using BTCPayServer.Client.JsonConverters; using BTCPayServer.JsonConverters; using BTCPayServer.Payments; @@ -19,6 +20,12 @@ namespace BTCPayServer.Data [JsonConverter(typeof(TimeSpanJsonConverter.Seconds))] public TimeSpan? Period { get; set; } + [DefaultValue(typeof(TimeSpan), "30.00:00:00")] + [JsonProperty(DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)] + [JsonConverter(typeof(TimeSpanJsonConverter.Days))] + public TimeSpan BOLT11Expiration { get; set; } + + [JsonProperty(ItemConverterType = typeof(PaymentMethodIdJsonConverter))] public PaymentMethodId[] SupportedPaymentMethods { get; set; } diff --git a/BTCPayServer/Data/StoreBlob.cs b/BTCPayServer/Data/StoreBlob.cs index 635dcf0d0..3f96564b3 100644 --- a/BTCPayServer/Data/StoreBlob.cs +++ b/BTCPayServer/Data/StoreBlob.cs @@ -174,6 +174,11 @@ namespace BTCPayServer.Data [JsonExtensionData] public IDictionary AdditionalData { get; set; } = new Dictionary(); + [DefaultValue(typeof(TimeSpan), "30.00:00:00")] + [JsonProperty(DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)] + [JsonConverter(typeof(TimeSpanJsonConverter.Days))] + public TimeSpan RefundBOLT11Expiration { get; set; } + public class StoreHints { public bool Wallet { get; set; } diff --git a/BTCPayServer/HostedServices/PullPaymentHostedService.cs b/BTCPayServer/HostedServices/PullPaymentHostedService.cs index 7518eef41..b9c747f4b 100644 --- a/BTCPayServer/HostedServices/PullPaymentHostedService.cs +++ b/BTCPayServer/HostedServices/PullPaymentHostedService.cs @@ -34,6 +34,7 @@ namespace BTCPayServer.HostedServices public string EmbeddedCSS { get; set; } public PaymentMethodId[] PaymentMethodIds { get; set; } public TimeSpan? Period { get; set; } + public TimeSpan? BOLT11Expiration { get; set; } } public class PullPaymentHostedService : BaseAsyncService { @@ -113,7 +114,8 @@ namespace BTCPayServer.HostedServices CustomCSSLink = create.CustomCSSLink, Email = null, EmbeddedCSS = create.EmbeddedCSS, - } + }, + BOLT11Expiration = create.BOLT11Expiration ?? TimeSpan.FromDays(30.0) }); ctx.PullPayments.Add(o); await ctx.SaveChangesAsync(); @@ -296,7 +298,7 @@ namespace BTCPayServer.HostedServices var payoutHandler = _payoutHandlers.FindPayoutHandler(paymentMethod); if (payoutHandler is null) throw new InvalidOperationException($"No payout handler for {paymentMethod}"); - var dest = await payoutHandler.ParseClaimDestination(paymentMethod, payoutBlob.Destination, false); + var dest = await payoutHandler.ParseClaimDestination(paymentMethod, payoutBlob.Destination); decimal minimumCryptoAmount = await payoutHandler.GetMinimumPayoutAmount(paymentMethod, dest.destination); if (cryptoAmount < minimumCryptoAmount) { diff --git a/BTCPayServer/Hosting/MigrationStartupTask.cs b/BTCPayServer/Hosting/MigrationStartupTask.cs index c2f380052..979bad7c2 100644 --- a/BTCPayServer/Hosting/MigrationStartupTask.cs +++ b/BTCPayServer/Hosting/MigrationStartupTask.cs @@ -203,7 +203,7 @@ namespace BTCPayServer.Hosting { continue; } - var claim = await handler?.ParseClaimDestination(pmi, payoutData.GetBlob(_btcPayNetworkJsonSerializerSettings).Destination, false); + var claim = await handler?.ParseClaimDestination(pmi, payoutData.GetBlob(_btcPayNetworkJsonSerializerSettings).Destination); payoutData.Destination = claim.destination?.Id; } await ctx.SaveChangesAsync(); diff --git a/BTCPayServer/Models/StoreViewModels/PaymentViewModel.cs b/BTCPayServer/Models/StoreViewModels/PaymentViewModel.cs index 32d7fe47d..0ccd6f995 100644 --- a/BTCPayServer/Models/StoreViewModels/PaymentViewModel.cs +++ b/BTCPayServer/Models/StoreViewModels/PaymentViewModel.cs @@ -24,5 +24,9 @@ namespace BTCPayServer.Models.StoreViewModels [Display(Name = "Default currency")] [MaxLength(10)] public string DefaultCurrency { get; set; } + + [Display(Name = "Minimum acceptable expiration time for BOLT11 for refunds")] + [Range(1, 365 * 10)] + public long BOLT11Expiration { get; set; } } } diff --git a/BTCPayServer/Models/WalletViewModels/PullPaymentsModel.cs b/BTCPayServer/Models/WalletViewModels/PullPaymentsModel.cs index ab6a96fa5..c9b54f3b2 100644 --- a/BTCPayServer/Models/WalletViewModels/PullPaymentsModel.cs +++ b/BTCPayServer/Models/WalletViewModels/PullPaymentsModel.cs @@ -52,5 +52,8 @@ namespace BTCPayServer.Models.WalletViewModels [Display(Name = "Payment Methods")] public IEnumerable PaymentMethods { get; set; } public IEnumerable PaymentMethodItems { get; set; } + [Display(Name = "Minimum acceptable expiration time for BOLT11 for refunds")] + [Range(1, 365 * 10)] + public long BOLT11Expiration { get; set; } = 30; } } diff --git a/BTCPayServer/Views/UIStorePullPayments/NewPullPayment.cshtml b/BTCPayServer/Views/UIStorePullPayments/NewPullPayment.cshtml index 4a3b23846..9aa4bd2c6 100644 --- a/BTCPayServer/Views/UIStorePullPayments/NewPullPayment.cshtml +++ b/BTCPayServer/Views/UIStorePullPayments/NewPullPayment.cshtml @@ -73,6 +73,25 @@ +

+ +

+
+
+
+ +
+ + days +
+ +
+
+
+
diff --git a/BTCPayServer/Views/UIStores/Payment.cshtml b/BTCPayServer/Views/UIStores/Payment.cshtml index 8e827468a..c1e04cd6b 100644 --- a/BTCPayServer/Views/UIStores/Payment.cshtml +++ b/BTCPayServer/Views/UIStores/Payment.cshtml @@ -57,7 +57,14 @@ - +
+ +
+ + days +
+ +
diff --git a/BTCPayServer/wwwroot/swagger/v1/swagger.template.pull-payments.json b/BTCPayServer/wwwroot/swagger/v1/swagger.template.pull-payments.json index 83a0757b4..9008fa797 100644 --- a/BTCPayServer/wwwroot/swagger/v1/swagger.template.pull-payments.json +++ b/BTCPayServer/wwwroot/swagger/v1/swagger.template.pull-payments.json @@ -81,6 +81,13 @@ "nullable": true, "description": "The length of each period in seconds." }, + "BOLT11Expiration": { + "type": "string", + "example": 30, + "default": 30, + "nullable": true, + "description": "If lightning is activated, do not accept BOLT11 invoices with expiration less than … days" + }, "startsAt": { "type": "integer", "format": "unix timestamp in seconds", @@ -657,6 +664,11 @@ "nullable": true, "description": "The length of each period in seconds" }, + "BOLT11Expiration": { + "type": "string", + "example": 30, + "description": "If lightning is activated, do not accept BOLT11 invoices with expiration less than … days" + }, "archived": { "type": "boolean", "description": "Whether this pull payment is archived"