From a0988c250da9511cf63ecdd6eb20d342b1168c67 Mon Sep 17 00:00:00 2001 From: Andrew Camilleri Date: Wed, 10 Jul 2024 04:05:25 +0200 Subject: [PATCH] Handle LNURL Payouts failing due to amount restriction (#6061) * Handle LNURL payouts better when amount is not allowee * docker-compose: Add missing restart for merchant CLN container * show sats not msats --------- Co-authored-by: Dennis Reimann --- .../LightningLikePayoutHandler.cs | 28 +++++++++++++++++-- .../UILightningLikePayoutController.cs | 4 ++- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/BTCPayServer/Data/Payouts/LightningLike/LightningLikePayoutHandler.cs b/BTCPayServer/Data/Payouts/LightningLike/LightningLikePayoutHandler.cs index f0ee56af9..b20514609 100644 --- a/BTCPayServer/Data/Payouts/LightningLike/LightningLikePayoutHandler.cs +++ b/BTCPayServer/Data/Payouts/LightningLike/LightningLikePayoutHandler.cs @@ -89,7 +89,11 @@ namespace BTCPayServer.Data.Payouts.LightningLike { using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(30)); using var t = CancellationTokenSource.CreateLinkedTokenSource(timeout.Token, cancellationToken); - var info = (LNURLPayRequest)(await LNURL.LNURL.FetchInformation(lnurl, CreateClient(lnurl), t.Token)); + var rawInfo = await LNURL.LNURL.FetchInformation(lnurl, CreateClient(lnurl), t.Token); + if(rawInfo is null) + return (null, "The LNURL / Lightning Address provided was not online."); + if(rawInfo is not LNURLPayRequest info) + return (null, "The LNURL was not a valid LNURL Pay request."); lnurlTag = info.Tag; } @@ -159,9 +163,27 @@ namespace BTCPayServer.Data.Payouts.LightningLike return Task.CompletedTask; } - public Task GetMinimumPayoutAmount(IClaimDestination claimDestination) + public async Task GetMinimumPayoutAmount(IClaimDestination claimDestination) { - return Task.FromResult(Money.Satoshis(1).ToDecimal(MoneyUnit.BTC)); + if(claimDestination is LNURLPayClaimDestinaton lnurlPayClaimDestinaton) + { + try + { + var lnurl = lnurlPayClaimDestinaton.LNURL.IsValidEmail() + ? LNURL.LNURL.ExtractUriFromInternetIdentifier(lnurlPayClaimDestinaton.LNURL) + : LNURL.LNURL.Parse(lnurlPayClaimDestinaton.LNURL, out var lnurlTag); + + using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(30)); + var rawInfo = await LNURL.LNURL.FetchInformation(lnurl, CreateClient(lnurl), timeout.Token); + if (rawInfo is LNURLPayRequest info) + return info.MinSendable.ToDecimal(LightMoneyUnit.BTC); + } + catch + { + // ignored + } + } + return Money.Satoshis(1).ToDecimal(MoneyUnit.BTC); } public Dictionary> GetPayoutSpecificActions() diff --git a/BTCPayServer/Data/Payouts/LightningLike/UILightningLikePayoutController.cs b/BTCPayServer/Data/Payouts/LightningLike/UILightningLikePayoutController.cs index a5b471253..bf0f1580a 100644 --- a/BTCPayServer/Data/Payouts/LightningLike/UILightningLikePayoutController.cs +++ b/BTCPayServer/Data/Payouts/LightningLike/UILightningLikePayoutController.cs @@ -245,13 +245,15 @@ namespace BTCPayServer.Data.Payouts.LightningLike var lm = new LightMoney(blob.CryptoAmount.Value, LightMoneyUnit.BTC); if (lm > lnurlInfo.MaxSendable || lm < lnurlInfo.MinSendable) { + + payoutData.State = PayoutState.Cancelled; return (null, new ResultVM { PayoutId = payoutData.Id, Result = PayResult.Error, Destination = blob.Destination, Message = - $"The LNURL provided would not generate an invoice of {lm.MilliSatoshi}msats" + $"The LNURL provided would not generate an invoice of {lm.ToDecimal(LightMoneyUnit.Satoshi)} sats" }); }