btcpayserver/BTCPayServer/Data/Payouts/LightningLike/LightningLikePayoutHandler.cs

193 lines
7.7 KiB
C#
Raw Normal View History

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using BTCPayServer.Abstractions.Models;
using BTCPayServer.Client.Models;
using BTCPayServer.Lightning;
using BTCPayServer.Payments;
2021-11-04 08:21:01 +01:00
using BTCPayServer.Payments.Lightning;
using BTCPayServer.Services;
using LNURL;
2021-11-04 08:21:01 +01:00
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using NBitcoin;
namespace BTCPayServer.Data.Payouts.LightningLike
{
public class LightningLikePayoutHandler : IPayoutHandler
{
public const string LightningLikePayoutHandlerOnionNamedClient =
nameof(LightningLikePayoutHandlerOnionNamedClient);
public const string LightningLikePayoutHandlerClearnetNamedClient =
nameof(LightningLikePayoutHandlerClearnetNamedClient);
private readonly BTCPayNetworkProvider _btcPayNetworkProvider;
private readonly IHttpClientFactory _httpClientFactory;
2021-11-04 08:21:01 +01:00
private readonly UserService _userService;
private readonly IAuthorizationService _authorizationService;
public LightningLikePayoutHandler(BTCPayNetworkProvider btcPayNetworkProvider,
2021-11-04 08:21:01 +01:00
IHttpClientFactory httpClientFactory, UserService userService, IAuthorizationService authorizationService)
{
_btcPayNetworkProvider = btcPayNetworkProvider;
_httpClientFactory = httpClientFactory;
2021-11-04 08:21:01 +01:00
_userService = userService;
_authorizationService = authorizationService;
}
public bool CanHandle(PaymentMethodId paymentMethod)
{
return (paymentMethod.PaymentType == LightningPaymentType.Instance || paymentMethod.PaymentType == LNURLPayPaymentType.Instance) &&
_btcPayNetworkProvider.GetNetwork<BTCPayNetwork>(paymentMethod.CryptoCode)?.SupportLightning is true;
}
public Task TrackClaim(PaymentMethodId paymentMethodId, IClaimDestination claimDestination)
{
return Task.CompletedTask;
}
public HttpClient CreateClient(Uri uri)
{
return _httpClientFactory.CreateClient(uri.IsOnion()
? LightningLikePayoutHandlerOnionNamedClient
: LightningLikePayoutHandlerClearnetNamedClient);
}
public async Task<(IClaimDestination destination, string error)> ParseClaimDestination(PaymentMethodId paymentMethodId, string destination, CancellationToken cancellationToken)
{
destination = destination.Trim();
var network = _btcPayNetworkProvider.GetNetwork<BTCPayNetwork>(paymentMethodId.CryptoCode);
try
{
string lnurlTag = null;
var lnurl = destination.IsValidEmail()
? LNURL.LNURL.ExtractUriFromInternetIdentifier(destination)
: LNURL.LNURL.Parse(destination, out lnurlTag);
if (lnurlTag is null)
{
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));
lnurlTag = info.Tag;
}
if (lnurlTag.Equals("payRequest", StringComparison.InvariantCultureIgnoreCase))
{
return (new LNURLPayClaimDestinaton(destination), null);
}
}
catch (FormatException)
{
}
2021-10-29 14:50:18 +02:00
catch
{
return (null, "The LNURL / Lightning Address provided was not online.");
}
var result =
BOLT11PaymentRequest.TryParse(destination, out var invoice, network.NBitcoinNetwork)
? new BoltInvoiceClaimDestination(destination, invoice)
: null;
2021-12-31 16:59:02 +09:00
if (result == null)
return (null, "A valid BOLT11 invoice or LNURL Pay or Lightning address was not provided.");
if (invoice.ExpiryDate.UtcDateTime < DateTime.UtcNow)
{
return (null,
"The BOLT11 invoice submitted has expired.");
}
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;
Transfer Processors (#3476) * Automated Transfer processors This PR introduces a few things: * Payouts can now be directly nested under a store instead of through a pull payment. * The Wallet Send screen now has an option to "schedule" instead of simply creating a transaction. When you click on schedule, all transaction destinations are converted into approved payouts. Any options relating to fees or coin selection are discarded. * There is a new concept introduced, called "Transfer Processors". Transfer Processors are services for stores that process payouts that are awaiting payment. Each processor specifies which payment methods it can handle. BTCPay Server will have some forms of transfer processors baked in but it has been designed to allow the Plugin System to provide additional processors. * The initial transfer processors provided are "automated processors", for on chain and lightning payment methods. They can be configured to process payouts every X amount of minutes. For on-chain, this means payments are batched into one transaction, resulting in more efficient and cheaper fees for processing. * * fix build * extract * remove magic string stuff * fix error message when scheduling * Paginate migration * add payout count to payment method tab * remove unused var * add protip * optimzie payout migration dramatically * Remove useless double condition * Fix bunch of warnings * Remove warning * Remove warnigns * Rename to Payout processors * fix typo Co-authored-by: Nicolas Dorier <nicolas.dorier@gmail.com>
2022-04-24 05:19:34 +02:00
if (pullPaymentBlob is not null && (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)
{
BitcoinLikePayoutHandler.ParseProofType(payout.Proof, out var raw, out var proofType);
if (proofType is null)
{
return null;
}
if (proofType == PayoutLightningBlob.PayoutLightningBlobProofType)
{
return raw.ToObject<PayoutLightningBlob>();
}
return raw.ToObject<ManualPayoutProof>();
}
public void StartBackgroundCheck(Action<Type[]> subscribe)
{
}
public Task BackgroundCheck(object o)
{
return Task.CompletedTask;
}
public Task<decimal> GetMinimumPayoutAmount(PaymentMethodId paymentMethodId, IClaimDestination claimDestination)
{
return Task.FromResult(Money.Satoshis(1).ToDecimal(MoneyUnit.BTC));
}
public Dictionary<PayoutState, List<(string Action, string Text)>> GetPayoutSpecificActions()
{
return new Dictionary<PayoutState, List<(string Action, string Text)>>();
}
public Task<StatusMessageModel> DoSpecificAction(string action, string[] payoutIds, string storeId)
{
return Task.FromResult<StatusMessageModel>(null);
}
2021-11-04 08:21:01 +01:00
public async Task<IEnumerable<PaymentMethodId>> GetSupportedPaymentMethods(StoreData storeData)
{
2021-11-04 08:21:01 +01:00
var result = new List<PaymentMethodId>();
2021-12-31 16:59:02 +09:00
var methods = storeData.GetEnabledPaymentMethods(_btcPayNetworkProvider).Where(id => id.PaymentId.PaymentType == LightningPaymentType.Instance).OfType<LightningSupportedPaymentMethod>();
2021-11-04 08:21:01 +01:00
foreach (LightningSupportedPaymentMethod supportedPaymentMethod in methods)
{
if (!supportedPaymentMethod.IsInternalNode)
{
result.Add(supportedPaymentMethod.PaymentId);
continue;
}
foreach (UserStore storeDataUserStore in storeData.UserStores)
{
2021-12-31 16:59:02 +09:00
if (!await _userService.IsAdminUser(storeDataUserStore.ApplicationUserId))
continue;
2021-11-04 08:21:01 +01:00
result.Add(supportedPaymentMethod.PaymentId);
break;
}
2021-12-31 16:59:02 +09:00
2021-11-04 08:21:01 +01:00
}
return result;
}
public Task<IActionResult> InitiatePayment(PaymentMethodId paymentMethodId, string[] payoutIds)
{
return Task.FromResult<IActionResult>(new RedirectToActionResult("ConfirmLightningPayout",
2022-01-07 12:32:00 +09:00
"UILightningLikePayout", new { cryptoCode = paymentMethodId.CryptoCode, payoutIds }));
}
}
}