From 163f805f3b922b4030e0a6cbc720f9f14c6cf5d4 Mon Sep 17 00:00:00 2001 From: d11n Date: Thu, 14 Sep 2023 12:53:48 +0200 Subject: [PATCH] Plugins: Add hook for resolving Lightning Address (#5322) --- .../Controllers/LightningAddressService.cs | 8 +- BTCPayServer/Controllers/UILNURLController.cs | 91 +++++++++++-------- .../Plugins/LightningAddresssResolver.cs | 14 +++ 3 files changed, 71 insertions(+), 42 deletions(-) create mode 100644 BTCPayServer/Plugins/LightningAddresssResolver.cs diff --git a/BTCPayServer/Controllers/LightningAddressService.cs b/BTCPayServer/Controllers/LightningAddressService.cs index 18d29ff07..aabfcad0a 100644 --- a/BTCPayServer/Controllers/LightningAddressService.cs +++ b/BTCPayServer/Controllers/LightningAddressService.cs @@ -48,7 +48,7 @@ public class LightningAddressService { return await _memoryCache.GetOrCreateAsync(GetKey(username), async entry => { - var result = await Get(new LightningAddressQuery() { Usernames = new[] { username } }); + var result = await Get(new LightningAddressQuery { Usernames = new[] { username } }); return result.FirstOrDefault(); }); } @@ -62,7 +62,7 @@ public class LightningAddressService { data.Username = NormalizeUsername(data.Username); await using var context = _applicationDbContextFactory.CreateContext(); - var result = (await GetCore(context, new LightningAddressQuery() { Usernames = new[] { data.Username } })) + var result = (await GetCore(context, new LightningAddressQuery { Usernames = new[] { data.Username } })) .FirstOrDefault(); if (result is not null) { @@ -84,7 +84,7 @@ public class LightningAddressService { username = NormalizeUsername(username); await using var context = _applicationDbContextFactory.CreateContext(); - var x = (await GetCore(context, new LightningAddressQuery() { Usernames = new[] { username } })).FirstOrDefault(); + var x = (await GetCore(context, new LightningAddressQuery { Usernames = new[] { username } })).FirstOrDefault(); if (x is null) return true; if (storeId is not null && x.StoreDataId != storeId) @@ -100,7 +100,7 @@ public class LightningAddressService public async Task Set(LightningAddressData data, ApplicationDbContext context) { - var result = (await GetCore(context, new LightningAddressQuery() { Usernames = new[] { data.Username } })) + var result = (await GetCore(context, new LightningAddressQuery { Usernames = new[] { data.Username } })) .FirstOrDefault(); if (result is not null) { diff --git a/BTCPayServer/Controllers/UILNURLController.cs b/BTCPayServer/Controllers/UILNURLController.cs index 27a660607..d071d7a08 100644 --- a/BTCPayServer/Controllers/UILNURLController.cs +++ b/BTCPayServer/Controllers/UILNURLController.cs @@ -3,7 +3,6 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; -using System.Net.Http; using System.Threading; using System.Threading.Tasks; using BTCPayServer.Abstractions.Constants; @@ -18,9 +17,9 @@ using BTCPayServer.Data.Payouts.LightningLike; using BTCPayServer.Events; using BTCPayServer.HostedServices; using BTCPayServer.Lightning; -using BTCPayServer.Logging; using BTCPayServer.Payments; using BTCPayServer.Payments.Lightning; +using BTCPayServer.Plugins; using BTCPayServer.Plugins.Crowdfund; using BTCPayServer.Plugins.PointOfSale; using BTCPayServer.Plugins.PointOfSale.Models; @@ -32,7 +31,6 @@ using BTCPayServer.Services.Stores; using LNURL; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Cors; -using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Routing; using NBitcoin; @@ -368,41 +366,56 @@ namespace BTCPayServer [IgnoreAntiforgeryToken] public async Task ResolveLightningAddress(string username) { + if (string.IsNullOrEmpty(username)) + return NotFound("Unknown username"); + + LNURLPayRequest lnurlRequest = null; + + // Check core and fall back to lookup Lightning Address via plugins var lightningAddressSettings = await _lightningAddressService.ResolveByAddress(username); - if (lightningAddressSettings is null || username is null) - return NotFound("Unknown username"); - - var store = await _storeRepository.FindStore(lightningAddressSettings.StoreDataId); - var cryptoCode = "BTC"; - if (store is null) - return NotFound("Unknown username"); - if (GetLNUrlPaymentMethodId(cryptoCode, store, out var lnUrlMethod) is null) - return NotFound("LNUrl not available for store"); - - var blob = lightningAddressSettings.GetBlob(); - - var lnurlRequest = new LNURLPayRequest() + if (lightningAddressSettings is null) { - Tag = "payRequest", - MinSendable = blob?.Min is decimal min ? new LightMoney(min, LightMoneyUnit.Satoshi) : null, - MaxSendable = blob?.Max is decimal max ? new LightMoney(max, LightMoneyUnit.Satoshi) : null, - CommentAllowed = lnUrlMethod.LUD12Enabled ? 2000 : 0 - }; + var resolver = (LightningAddressResolver)await _pluginHookService.ApplyFilter("resolve-lnurlp-request-for-lightning-address", + new LightningAddressResolver(username)); + + lnurlRequest = resolver.LNURLPayRequest; + if (lnurlRequest is null) + return NotFound("Unknown username"); + } + else + { + var store = await _storeRepository.FindStore(lightningAddressSettings.StoreDataId); + if (store is null) + return NotFound("Unknown username"); + + var cryptoCode = "BTC"; + if (GetLNUrlPaymentMethodId(cryptoCode, store, out var lnUrlMethod) is null) + return NotFound("LNURL not available for store"); + + var blob = lightningAddressSettings.GetBlob(); + lnurlRequest = new LNURLPayRequest + { + Tag = "payRequest", + MinSendable = blob?.Min is decimal min ? new LightMoney(min, LightMoneyUnit.Satoshi) : null, + MaxSendable = blob?.Max is decimal max ? new LightMoney(max, LightMoneyUnit.Satoshi) : null, + CommentAllowed = lnUrlMethod.LUD12Enabled ? 2000 : 0 + }; + + var lnUrlMetadata = new Dictionary + { + ["text/identifier"] = $"{username}@{Request.Host}" + }; + SetLNUrlDescriptionMetadata(lnUrlMetadata, store, store.GetStoreBlob(), null); + lnurlRequest.Metadata = + JsonConvert.SerializeObject(lnUrlMetadata.Select(kv => new[] { kv.Key, kv.Value })); + + lnurlRequest.Callback = new Uri(_linkGenerator.GetUriByAction( + action: nameof(GetLNURLForLightningAddress), + controller: "UILNURL", + values: new { cryptoCode, username }, Request.Scheme, Request.Host, Request.PathBase)); + } + NormalizeSendable(lnurlRequest); - - var lnUrlMetadata = new Dictionary() - { - ["text/identifier"] = $"{username}@{Request.Host}" - }; - SetLNUrlDescriptionMetadata(lnUrlMetadata, store, store.GetStoreBlob(), null); - lnurlRequest.Metadata = - JsonConvert.SerializeObject(lnUrlMetadata.Select(kv => new[] { kv.Key, kv.Value })); - - lnurlRequest.Callback = new Uri(_linkGenerator.GetUriByAction( - action: nameof(GetLNURLForLightningAddress), - controller: "UILNURL", - values: new { cryptoCode, username }, Request.Scheme, Request.Host, Request.PathBase)); - lnurlRequest = await _pluginHookService.ApplyFilter("modify-lnurlp-request", lnurlRequest) as LNURLPayRequest; return Ok(lnurlRequest); } @@ -417,21 +430,23 @@ namespace BTCPayServer return NotFound("Unknown username"); var blob = lightningAddressSettings.GetBlob(); var store = await _storeRepository.FindStore(lightningAddressSettings.StoreDataId); + if (store is null) + return NotFound("Unknown username"); var result = await GetLNURLRequest( cryptoCode, store, store.GetStoreBlob(), - new CreateInvoiceRequest() + new CreateInvoiceRequest { Currency = blob?.CurrencyCode, Metadata = blob?.InvoiceMetadata }, - new LNURLPayRequest() + new LNURLPayRequest { MinSendable = blob?.Min is decimal min ? new LightMoney(min, LightMoneyUnit.Satoshi) : null, MaxSendable = blob?.Max is decimal max ? new LightMoney(max, LightMoneyUnit.Satoshi) : null, }, - new Dictionary() + new Dictionary { { "text/identifier", $"{username}@{Request.Host}" } }); diff --git a/BTCPayServer/Plugins/LightningAddresssResolver.cs b/BTCPayServer/Plugins/LightningAddresssResolver.cs new file mode 100644 index 000000000..ab60e2a0e --- /dev/null +++ b/BTCPayServer/Plugins/LightningAddresssResolver.cs @@ -0,0 +1,14 @@ +using LNURL; + +namespace BTCPayServer.Plugins; + +public class LightningAddressResolver +{ + public string Username { get; set; } + public LNURLPayRequest LNURLPayRequest { get; set; } + + public LightningAddressResolver(string username) + { + Username = username; + } +}