The Big Cleanup: Refactor BTCPay internals (#5809)

This commit is contained in:
Nicolas Dorier
2024-04-04 16:31:04 +09:00
committed by GitHub
parent 69b589a401
commit 6cc1751924
265 changed files with 8289 additions and 7673 deletions

View File

@@ -34,6 +34,7 @@ using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Routing;
using NBitcoin;
using NBitpayClient;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using LightningAddressData = BTCPayServer.Data.LightningAddressData;
@@ -47,8 +48,6 @@ namespace BTCPayServer
{
private readonly InvoiceRepository _invoiceRepository;
private readonly EventAggregator _eventAggregator;
private readonly BTCPayNetworkProvider _btcPayNetworkProvider;
private readonly LightningLikePaymentHandler _lightningLikePaymentHandler;
private readonly StoreRepository _storeRepository;
private readonly AppService _appService;
private readonly UIInvoiceController _invoiceController;
@@ -59,11 +58,11 @@ namespace BTCPayServer
private readonly BTCPayNetworkJsonSerializerSettings _btcPayNetworkJsonSerializerSettings;
private readonly IPluginHookService _pluginHookService;
private readonly InvoiceActivator _invoiceActivator;
private readonly PaymentMethodHandlerDictionary _handlers;
public UILNURLController(InvoiceRepository invoiceRepository,
EventAggregator eventAggregator,
BTCPayNetworkProvider btcPayNetworkProvider,
LightningLikePaymentHandler lightningLikePaymentHandler,
PaymentMethodHandlerDictionary handlers,
StoreRepository storeRepository,
AppService appService,
UIInvoiceController invoiceController,
@@ -77,8 +76,7 @@ namespace BTCPayServer
{
_invoiceRepository = invoiceRepository;
_eventAggregator = eventAggregator;
_btcPayNetworkProvider = btcPayNetworkProvider;
_lightningLikePaymentHandler = lightningLikePaymentHandler;
_handlers = handlers;
_storeRepository = storeRepository;
_appService = appService;
_invoiceController = invoiceController;
@@ -98,13 +96,13 @@ namespace BTCPayServer
[NonAction]
internal async Task<IActionResult> GetLNURLForPullPayment(string cryptoCode, string pullPaymentId, string pr, string k1, CancellationToken cancellationToken)
{
var network = _btcPayNetworkProvider.GetNetwork<BTCPayNetwork>(cryptoCode);
var network = GetNetwork(cryptoCode);
if (network is null || !network.SupportLightning)
{
return NotFound();
}
var pmi = new PaymentMethodId(cryptoCode, PaymentTypes.LightningLike);
var pmi = PaymentTypes.LN.GetPaymentMethodId(cryptoCode);
var pp = await _pullPaymentHostedService.GetPullPayment(pullPaymentId, true);
if (!pp.IsRunning() || !pp.IsSupported(pmi))
{
@@ -149,9 +147,7 @@ namespace BTCPayServer
if (result.MinimumAmount < request.MinWithdrawable || result.MinimumAmount > request.MaxWithdrawable)
return BadRequest(new LNUrlStatusResponse { Status = "ERROR", Reason = $"Payment request was not within bounds ({request.MinWithdrawable.ToUnit(LightMoneyUnit.Satoshi)} - {request.MaxWithdrawable.ToUnit(LightMoneyUnit.Satoshi)} sats)" });
var store = await _storeRepository.FindStore(pp.StoreId);
var pm = store!.GetSupportedPaymentMethods(_btcPayNetworkProvider)
.OfType<LightningSupportedPaymentMethod>()
.FirstOrDefault(method => method.PaymentId == pmi);
var pm = store!.GetPaymentMethodConfig<LightningPaymentMethodConfig>(pmi, _handlers);
if (pm is null)
{
return NotFound();
@@ -169,12 +165,13 @@ namespace BTCPayServer
if (claimResponse.Result != ClaimRequest.ClaimResult.Ok)
return BadRequest(new LNUrlStatusResponse { Status = "ERROR", Reason = "Payment request could not be paid" });
var lightningHandler = _handlers.GetLightningHandler(network);
switch (claimResponse.PayoutData.State)
{
case PayoutState.AwaitingPayment:
{
var client =
_lightningLikePaymentHandler.CreateLightningClient(pm, network);
lightningHandler.CreateLightningClient(pm);
var payResult = await UILightningLikePayoutController.TrypayBolt(client,
claimResponse.PayoutData.GetBlob(_btcPayNetworkJsonSerializerSettings),
claimResponse.PayoutData, result, pmi, cancellationToken);
@@ -226,10 +223,18 @@ namespace BTCPayServer
return Ok(request);
}
private BTCPayNetwork GetNetwork(string cryptoCode)
{
if (!_handlers.TryGetValue(PaymentTypes.LNURL.GetPaymentMethodId(cryptoCode), out var o) ||
o is not LNURLPayPaymentHandler { Network: var network })
return null;
return network;
}
[HttpGet("pay/app/{appId}/{itemCode}")]
public async Task<IActionResult> GetLNURLForApp(string cryptoCode, string appId, string itemCode = null)
{
var network = _btcPayNetworkProvider.GetNetwork<BTCPayNetwork>(cryptoCode);
var network = GetNetwork(cryptoCode);
if (network is null || !network.SupportLightning)
{
return NotFound();
@@ -514,7 +519,7 @@ namespace BTCPayServer
{
createInvoice.Checkout ??= new InvoiceDataBase.CheckoutOptions();
createInvoice.Checkout.LazyPaymentMethods = false;
createInvoice.Checkout.PaymentMethods = new[] { pmi.ToStringNormalized() };
createInvoice.Checkout.PaymentMethods = new[] { pmi.ToString() };
i = await _invoiceController.CreateInvoiceCoreRaw(createInvoice, store, Request.GetAbsoluteRoot(), additionalTags);
}
catch (Exception e)
@@ -540,14 +545,17 @@ namespace BTCPayServer
lnurlRequest ??= new LNURLPayRequest();
lnUrlMetadata ??= new Dictionary<string, string>();
var pm = i.GetPaymentMethod(pmi);
var pm = i.GetPaymentPrompt(pmi);
if (pm is null)
return null;
var paymentMethodDetails = (LNURLPayPaymentMethodDetails)pm.GetPaymentMethodDetails();
var handler = ((LNURLPayPaymentHandler)_handlers[pmi]);
var paymentMethodDetails = handler.ParsePaymentPromptDetails(pm.Details);
bool updatePaymentMethodDetails = false;
List<string> searchTerms = new List<string>();
if (lnUrlMetadata?.TryGetValue("text/identifier", out var lnAddress) is true && lnAddress is not null)
{
paymentMethodDetails.ConsumedLightningAddress = lnAddress;
searchTerms.Add(lnAddress);
updatePaymentMethodDetails = true;
}
@@ -561,7 +569,7 @@ namespace BTCPayServer
lnurlRequest.Callback = new Uri(_linkGenerator.GetUriByAction(
action: nameof(GetLNURLForInvoice),
controller: "UILNURL",
values: new { pmi.CryptoCode, invoiceId = i.Id }, Request.Scheme, Request.Host, Request.PathBase));
values: new { cryptoCode, invoiceId = i.Id }, Request.Scheme, Request.Host, Request.PathBase));
lnurlRequest.Metadata = JsonConvert.SerializeObject(lnUrlMetadata.Select(kv => new[] { kv.Key, kv.Value }));
if (i.Type != InvoiceType.TopUp)
{
@@ -580,8 +588,10 @@ namespace BTCPayServer
}
if (updatePaymentMethodDetails)
{
pm.SetPaymentMethodDetails(paymentMethodDetails);
await _invoiceRepository.UpdateInvoicePaymentMethod(i.Id, pm);
pm.Details = JToken.FromObject(paymentMethodDetails, handler.Serializer);
await _invoiceRepository.UpdatePaymentDetails(i.Id, handler, paymentMethodDetails);
await _invoiceRepository.AddSearchTerms(i.Id, searchTerms);
_eventAggregator.Publish(new InvoiceNewPaymentDetailsEvent(i.Id, paymentMethodDetails, pmi));
}
return lnurlRequest;
}
@@ -605,18 +615,16 @@ namespace BTCPayServer
lnurlRequest.MaxSendable = LightMoney.FromUnit(6.12m, LightMoneyUnit.BTC);
}
PaymentMethodId GetLNUrlPaymentMethodId(string cryptoCode, Data.StoreData store, out LNURLPaySupportedPaymentMethod lnUrlSettings)
PaymentMethodId GetLNUrlPaymentMethodId(string cryptoCode, Data.StoreData store, out LNURLPaymentMethodConfig lnUrlSettings)
{
lnUrlSettings = null;
var network = _btcPayNetworkProvider.GetNetwork<BTCPayNetwork>(cryptoCode);
var network = GetNetwork(cryptoCode);
if (network is null || !network.SupportLightning)
return null;
var pmi = new PaymentMethodId(cryptoCode, PaymentTypes.LNURLPay);
var lnpmi = new PaymentMethodId(cryptoCode, PaymentTypes.LightningLike);
var methods = store.GetSupportedPaymentMethods(_btcPayNetworkProvider);
var lnUrlMethod =
methods.FirstOrDefault(method => method.PaymentId == pmi) as LNURLPaySupportedPaymentMethod;
var lnMethod = methods.FirstOrDefault(method => method.PaymentId == lnpmi);
var pmi = PaymentTypes.LNURL.GetPaymentMethodId(cryptoCode);
var lnpmi = PaymentTypes.LN.GetPaymentMethodId(cryptoCode);
var lnUrlMethod = store.GetPaymentMethodConfig<LNURLPaymentMethodConfig>(pmi, _handlers);
var lnMethod = store.GetPaymentMethodConfig<LightningPaymentMethodConfig>(lnpmi, _handlers);
if (lnUrlMethod is null || lnMethod is null)
return null;
var blob = store.GetStoreBlob();
@@ -632,7 +640,7 @@ namespace BTCPayServer
public async Task<IActionResult> GetLNURLForInvoice(string invoiceId, string cryptoCode,
[FromQuery] long? amount = null, string comment = null)
{
var network = _btcPayNetworkProvider.GetNetwork<BTCPayNetwork>(cryptoCode);
var network = GetNetwork(cryptoCode);
if (network is null || !network.SupportLightning)
{
return NotFound();
@@ -651,25 +659,25 @@ namespace BTCPayServer
var pmi = GetLNUrlPaymentMethodId(cryptoCode, store, out var lnurlSupportedPaymentMethod);
if (pmi is null)
return NotFound();
var lightningPaymentMethod = i.GetPaymentMethod(pmi);
var paymentMethodDetails =
lightningPaymentMethod?.GetPaymentMethodDetails() as LNURLPayPaymentMethodDetails;
if (paymentMethodDetails is not null && !paymentMethodDetails.Activated)
var handler = ((LNURLPayPaymentHandler)_handlers[pmi]);
var lightningPaymentMethod = i.GetPaymentPrompt(pmi);
var promptDetails = handler.ParsePaymentPromptDetails(lightningPaymentMethod.Details);
if (promptDetails is null)
{
if (!await _invoiceActivator.ActivateInvoicePaymentMethod(pmi, i, store))
if (!await _invoiceActivator.ActivateInvoicePaymentMethod(i.Id, pmi))
return NotFound();
i = await _invoiceRepository.GetInvoice(invoiceId, true);
lightningPaymentMethod = i.GetPaymentMethod(pmi);
paymentMethodDetails = lightningPaymentMethod.GetPaymentMethodDetails() as LNURLPayPaymentMethodDetails;
lightningPaymentMethod = i.GetPaymentPrompt(pmi);
promptDetails = handler.ParsePaymentPromptDetails(lightningPaymentMethod.Details);
}
if (paymentMethodDetails?.LightningSupportedPaymentMethod is null)
var lnConfig = _handlers.GetLightningConfig(store, network);
if (lnConfig is null)
return NotFound();
LNURLPayRequest lnurlPayRequest = paymentMethodDetails.PayRequest;
LNURLPayRequest lnurlPayRequest = promptDetails.PayRequest;
var blob = store.GetStoreBlob();
if (paymentMethodDetails.PayRequest is null)
if (promptDetails.PayRequest is null)
{
lnurlPayRequest = await CreateLNUrlRequestFromInvoice(cryptoCode, i, store, blob, allowOverpay: false);
if (lnurlPayRequest is null)
@@ -705,23 +713,20 @@ namespace BTCPayServer
if (lnurlSupportedPaymentMethod.LUD12Enabled)
{
comment = comment?.Truncate(2000);
if (paymentMethodDetails.ProvidedComment != comment)
if (promptDetails.ProvidedComment != comment)
{
paymentMethodDetails.ProvidedComment = comment;
promptDetails.ProvidedComment = comment;
updatePaymentMethod = true;
}
}
if (string.IsNullOrEmpty(paymentMethodDetails.BOLT11) || paymentMethodDetails.GeneratedBoltAmount != amt)
if (string.IsNullOrEmpty(lightningPaymentMethod.Destination) || promptDetails.GeneratedBoltAmount != amt)
{
var client =
_lightningLikePaymentHandler.CreateLightningClient(
paymentMethodDetails.LightningSupportedPaymentMethod, network);
if (!string.IsNullOrEmpty(paymentMethodDetails.BOLT11))
var client = _handlers.GetLightningHandler(network).CreateLightningClient(lnConfig);
if (!string.IsNullOrEmpty(lightningPaymentMethod.Destination))
{
try
{
await client.CancelInvoice(paymentMethodDetails.InvoiceId);
await client.CancelInvoice(promptDetails.InvoiceId);
}
catch (Exception)
{
@@ -764,26 +769,26 @@ namespace BTCPayServer
});
}
paymentMethodDetails.BOLT11 = invoice.BOLT11;
paymentMethodDetails.PaymentHash = string.IsNullOrEmpty(invoice.PaymentHash) ? null : uint256.Parse(invoice.PaymentHash);
paymentMethodDetails.Preimage = string.IsNullOrEmpty(invoice.Preimage) ? null : uint256.Parse(invoice.Preimage);
paymentMethodDetails.InvoiceId = invoice.Id;
paymentMethodDetails.GeneratedBoltAmount = amt;
lightningPaymentMethod.Destination = invoice.BOLT11;
promptDetails.PaymentHash = string.IsNullOrEmpty(invoice.PaymentHash) ? null : uint256.Parse(invoice.PaymentHash);
promptDetails.Preimage = string.IsNullOrEmpty(invoice.Preimage) ? null : uint256.Parse(invoice.Preimage);
promptDetails.InvoiceId = invoice.Id;
promptDetails.GeneratedBoltAmount = amt;
lightningPaymentMethod.Details = JToken.FromObject(promptDetails, handler.Serializer);
updatePaymentMethod = true;
}
if (updatePaymentMethod)
{
lightningPaymentMethod.SetPaymentMethodDetails(paymentMethodDetails);
await _invoiceRepository.UpdateInvoicePaymentMethod(invoiceId, lightningPaymentMethod);
_eventAggregator.Publish(new InvoiceNewPaymentDetailsEvent(invoiceId, paymentMethodDetails, pmi));
await _invoiceRepository.UpdatePrompt(invoiceId, lightningPaymentMethod);
_eventAggregator.Publish(new InvoiceNewPaymentDetailsEvent(invoiceId, promptDetails, pmi));
}
return Ok(new LNURLPayRequest.LNURLPayRequestCallbackResponse
{
Disposable = true,
Routes = Array.Empty<string>(),
Pr = paymentMethodDetails.BOLT11,
Pr = lightningPaymentMethod.Destination,
SuccessAction = successAction
});
}
@@ -800,8 +805,8 @@ namespace BTCPayServer
[HttpGet("~/stores/{storeId}/plugins/lightning-address")]
public async Task<IActionResult> EditLightningAddress(string storeId)
{
if (ControllerContext.HttpContext.GetStoreData().GetEnabledPaymentIds(_btcPayNetworkProvider)
.All(id => id.PaymentType != LNURLPayPaymentType.Instance))
if (ControllerContext.HttpContext.GetStoreData().GetEnabledPaymentIds()
.All(id => _handlers.TryGet(id) is not LNURLPayPaymentHandler))
{
TempData.SetStatusMessageModel(new StatusMessageModel
{