2021-10-18 05:37:59 +02:00
|
|
|
using System;
|
|
|
|
using System.Collections.Generic;
|
|
|
|
using System.Linq;
|
2022-08-17 09:45:51 +02:00
|
|
|
using System.Threading;
|
2021-10-18 05:37:59 +02:00
|
|
|
using System.Threading.Tasks;
|
|
|
|
using BTCPayServer.Abstractions.Constants;
|
2021-11-04 08:21:01 +01:00
|
|
|
using BTCPayServer.Client;
|
2021-10-18 05:37:59 +02:00
|
|
|
using BTCPayServer.Client.Models;
|
|
|
|
using BTCPayServer.Configuration;
|
2023-12-01 10:50:05 +01:00
|
|
|
using BTCPayServer.HostedServices;
|
2021-10-18 05:37:59 +02:00
|
|
|
using BTCPayServer.Lightning;
|
|
|
|
using BTCPayServer.Payments;
|
2024-04-04 16:31:04 +09:00
|
|
|
using BTCPayServer.Payments.Bitcoin;
|
2021-10-18 05:37:59 +02:00
|
|
|
using BTCPayServer.Payments.Lightning;
|
2024-10-19 21:33:34 +09:00
|
|
|
using BTCPayServer.PayoutProcessors.Lightning;
|
2024-05-01 10:22:07 +09:00
|
|
|
using BTCPayServer.Payouts;
|
2021-11-04 08:21:01 +01:00
|
|
|
using BTCPayServer.Security;
|
2021-10-18 05:37:59 +02:00
|
|
|
using BTCPayServer.Services;
|
2024-04-04 16:31:04 +09:00
|
|
|
using BTCPayServer.Services.Invoices;
|
2022-02-24 13:57:02 +01:00
|
|
|
using BTCPayServer.Services.Stores;
|
2021-10-18 05:37:59 +02:00
|
|
|
using LNURL;
|
|
|
|
using Microsoft.AspNetCore.Authorization;
|
|
|
|
using Microsoft.AspNetCore.Identity;
|
|
|
|
using Microsoft.AspNetCore.Mvc;
|
|
|
|
using Microsoft.EntityFrameworkCore;
|
|
|
|
using Microsoft.Extensions.Options;
|
2022-07-15 05:37:47 +02:00
|
|
|
using NBitcoin;
|
2021-10-18 05:37:59 +02:00
|
|
|
|
|
|
|
namespace BTCPayServer.Data.Payouts.LightningLike
|
|
|
|
{
|
2022-02-24 13:57:02 +01:00
|
|
|
[Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie)]
|
2021-10-18 05:37:59 +02:00
|
|
|
[AutoValidateAntiforgeryToken]
|
2022-01-07 12:32:00 +09:00
|
|
|
public class UILightningLikePayoutController : Controller
|
2021-10-18 05:37:59 +02:00
|
|
|
{
|
|
|
|
private readonly ApplicationDbContextFactory _applicationDbContextFactory;
|
2024-10-19 21:33:34 +09:00
|
|
|
private readonly LightningAutomatedPayoutSenderFactory _lightningAutomatedPayoutSenderFactory;
|
2021-10-18 05:37:59 +02:00
|
|
|
private readonly UserManager<ApplicationUser> _userManager;
|
|
|
|
private readonly BTCPayNetworkJsonSerializerSettings _btcPayNetworkJsonSerializerSettings;
|
2024-05-01 10:22:07 +09:00
|
|
|
private readonly PayoutMethodHandlerDictionary _payoutHandlers;
|
2024-04-04 16:31:04 +09:00
|
|
|
private readonly PaymentMethodHandlerDictionary _handlers;
|
2021-10-18 05:37:59 +02:00
|
|
|
private readonly LightningClientFactoryService _lightningClientFactoryService;
|
|
|
|
private readonly IOptions<LightningNetworkOptions> _options;
|
2021-11-04 08:21:01 +01:00
|
|
|
private readonly IAuthorizationService _authorizationService;
|
2023-12-01 10:50:05 +01:00
|
|
|
private readonly EventAggregator _eventAggregator;
|
2022-02-24 13:57:02 +01:00
|
|
|
private readonly StoreRepository _storeRepository;
|
2021-10-18 05:37:59 +02:00
|
|
|
|
2022-01-07 12:32:00 +09:00
|
|
|
public UILightningLikePayoutController(ApplicationDbContextFactory applicationDbContextFactory,
|
2024-10-19 21:33:34 +09:00
|
|
|
LightningAutomatedPayoutSenderFactory lightningAutomatedPayoutSenderFactory,
|
2021-10-18 05:37:59 +02:00
|
|
|
UserManager<ApplicationUser> userManager,
|
|
|
|
BTCPayNetworkJsonSerializerSettings btcPayNetworkJsonSerializerSettings,
|
2024-05-01 10:22:07 +09:00
|
|
|
PayoutMethodHandlerDictionary payoutHandlers,
|
2024-04-04 16:31:04 +09:00
|
|
|
PaymentMethodHandlerDictionary handlers,
|
2022-02-24 13:57:02 +01:00
|
|
|
StoreRepository storeRepository,
|
2021-10-18 05:37:59 +02:00
|
|
|
LightningClientFactoryService lightningClientFactoryService,
|
2024-10-19 21:33:34 +09:00
|
|
|
IOptions<LightningNetworkOptions> options,
|
2023-12-01 10:50:05 +01:00
|
|
|
IAuthorizationService authorizationService,
|
|
|
|
EventAggregator eventAggregator)
|
2021-10-18 05:37:59 +02:00
|
|
|
{
|
|
|
|
_applicationDbContextFactory = applicationDbContextFactory;
|
2024-10-19 21:33:34 +09:00
|
|
|
_lightningAutomatedPayoutSenderFactory = lightningAutomatedPayoutSenderFactory;
|
2021-10-18 05:37:59 +02:00
|
|
|
_userManager = userManager;
|
|
|
|
_btcPayNetworkJsonSerializerSettings = btcPayNetworkJsonSerializerSettings;
|
|
|
|
_payoutHandlers = payoutHandlers;
|
2024-04-04 16:31:04 +09:00
|
|
|
_handlers = handlers;
|
2021-10-18 05:37:59 +02:00
|
|
|
_lightningClientFactoryService = lightningClientFactoryService;
|
|
|
|
_options = options;
|
2022-02-24 13:57:02 +01:00
|
|
|
_storeRepository = storeRepository;
|
2021-11-04 08:21:01 +01:00
|
|
|
_authorizationService = authorizationService;
|
2023-12-01 10:50:05 +01:00
|
|
|
_eventAggregator = eventAggregator;
|
2021-10-18 05:37:59 +02:00
|
|
|
}
|
|
|
|
|
2024-05-01 10:22:07 +09:00
|
|
|
private async Task<List<PayoutData>> GetPayouts(ApplicationDbContext dbContext, PayoutMethodId pmi,
|
2021-10-18 05:37:59 +02:00
|
|
|
string[] payoutIds)
|
|
|
|
{
|
|
|
|
var userId = _userManager.GetUserId(User);
|
|
|
|
if (string.IsNullOrEmpty(userId))
|
|
|
|
{
|
|
|
|
return new List<PayoutData>();
|
|
|
|
}
|
|
|
|
|
|
|
|
var pmiStr = pmi.ToString();
|
|
|
|
|
|
|
|
var approvedStores = new Dictionary<string, bool>();
|
|
|
|
|
|
|
|
return (await dbContext.Payouts
|
|
|
|
.Include(data => data.PullPaymentData)
|
2023-08-15 15:11:00 +02:00
|
|
|
.Include(data => data.StoreData)
|
2021-10-18 05:37:59 +02:00
|
|
|
.ThenInclude(data => data.UserStores)
|
2023-05-26 16:49:32 +02:00
|
|
|
.ThenInclude(data => data.StoreRole)
|
2021-10-18 05:37:59 +02:00
|
|
|
.Where(data =>
|
|
|
|
payoutIds.Contains(data.Id) &&
|
|
|
|
data.State == PayoutState.AwaitingPayment &&
|
2024-06-28 20:07:53 +09:00
|
|
|
data.PayoutMethodId == pmiStr)
|
2021-10-18 05:37:59 +02:00
|
|
|
.ToListAsync())
|
|
|
|
.Where(payout =>
|
|
|
|
{
|
2023-08-15 15:11:00 +02:00
|
|
|
if (approvedStores.TryGetValue(payout.StoreDataId, out var value))
|
2021-12-31 16:59:02 +09:00
|
|
|
return value;
|
2023-08-15 15:11:00 +02:00
|
|
|
value = payout.StoreData.UserStores
|
2023-05-26 16:49:32 +02:00
|
|
|
.Any(store => store.ApplicationUserId == userId && store.StoreRole.Permissions.Contains(Policies.CanModifyStoreSettings));
|
2023-08-15 15:11:00 +02:00
|
|
|
approvedStores.Add(payout.StoreDataId, value);
|
2021-10-18 05:37:59 +02:00
|
|
|
return value;
|
|
|
|
}).ToList();
|
|
|
|
}
|
|
|
|
|
|
|
|
[HttpGet("pull-payments/payouts/lightning/{cryptoCode}")]
|
|
|
|
public async Task<IActionResult> ConfirmLightningPayout(string cryptoCode, string[] payoutIds)
|
|
|
|
{
|
2022-02-24 13:57:02 +01:00
|
|
|
await SetStoreContext();
|
2022-11-23 21:02:47 +09:00
|
|
|
|
2024-05-01 10:22:07 +09:00
|
|
|
var pmi = PayoutTypes.LN.GetPayoutMethodId(cryptoCode);
|
2021-10-18 05:37:59 +02:00
|
|
|
|
|
|
|
await using var ctx = _applicationDbContextFactory.CreateContext();
|
|
|
|
var payouts = await GetPayouts(ctx, pmi, payoutIds);
|
|
|
|
|
|
|
|
var vm = payouts.Select(payoutData =>
|
|
|
|
{
|
|
|
|
var blob = payoutData.GetBlob(_btcPayNetworkJsonSerializerSettings);
|
|
|
|
|
2022-06-02 11:03:06 +02:00
|
|
|
return new ConfirmVM
|
2021-10-18 05:37:59 +02:00
|
|
|
{
|
2024-08-28 18:52:08 +09:00
|
|
|
Amount = payoutData.Amount.Value,
|
2021-12-31 16:59:02 +09:00
|
|
|
Destination = blob.Destination,
|
|
|
|
PayoutId = payoutData.Id
|
2021-10-18 05:37:59 +02:00
|
|
|
};
|
|
|
|
}).ToList();
|
|
|
|
return View(vm);
|
|
|
|
}
|
|
|
|
|
|
|
|
[HttpPost("pull-payments/payouts/lightning/{cryptoCode}")]
|
2022-11-22 20:17:29 +09:00
|
|
|
public async Task<IActionResult> ProcessLightningPayout(string cryptoCode, string[] payoutIds, CancellationToken cancellationToken)
|
2021-10-18 05:37:59 +02:00
|
|
|
{
|
2022-02-24 13:57:02 +01:00
|
|
|
await SetStoreContext();
|
2022-11-23 21:02:47 +09:00
|
|
|
|
2024-05-01 10:22:07 +09:00
|
|
|
var pmi = PayoutTypes.LN.GetPayoutMethodId(cryptoCode);
|
|
|
|
var paymentMethodId = PaymentTypes.LN.GetPaymentMethodId(cryptoCode);
|
|
|
|
var payoutHandler = (LightningLikePayoutHandler)_payoutHandlers.TryGet(pmi);
|
2021-10-18 05:37:59 +02:00
|
|
|
|
2024-10-19 21:33:34 +09:00
|
|
|
IEnumerable<IGrouping<string, PayoutData>> payouts;
|
|
|
|
using (var ctx = _applicationDbContextFactory.CreateContext())
|
|
|
|
{
|
|
|
|
payouts = (await GetPayouts(ctx, pmi, payoutIds)).GroupBy(data => data.StoreDataId);
|
|
|
|
}
|
2021-10-18 05:37:59 +02:00
|
|
|
var results = new List<ResultVM>();
|
|
|
|
|
|
|
|
//we group per store and init the transfers by each
|
2021-12-31 16:59:02 +09:00
|
|
|
var authorizedForInternalNode = (await _authorizationService.AuthorizeAsync(User, null, new PolicyRequirement(Policies.CanModifyServerSettings))).Succeeded;
|
2021-10-18 05:37:59 +02:00
|
|
|
foreach (var payoutDatas in payouts)
|
|
|
|
{
|
2023-08-15 15:11:00 +02:00
|
|
|
var store = payoutDatas.First().StoreData;
|
2024-10-19 21:33:34 +09:00
|
|
|
var authorized = await _authorizationService.AuthorizeAsync(User, store, new PolicyRequirement(Policies.CanUseLightningNodeInStore));
|
|
|
|
if (!authorized.Succeeded)
|
|
|
|
{
|
|
|
|
results.AddRange(FailAll(payoutDatas, "You need the 'btcpay.store.canuselightningnode' permission for this action"));
|
|
|
|
continue;
|
|
|
|
}
|
2024-05-01 10:22:07 +09:00
|
|
|
var lightningSupportedPaymentMethod = store.GetPaymentMethodConfig<LightningPaymentMethodConfig>(paymentMethodId, _handlers);
|
2021-11-04 08:21:01 +01:00
|
|
|
|
|
|
|
if (lightningSupportedPaymentMethod.IsInternalNode && !authorizedForInternalNode)
|
|
|
|
{
|
2024-10-19 21:33:34 +09:00
|
|
|
results.AddRange(FailAll(payoutDatas, "You are currently using the internal Lightning node for this payout's store but you are not a server admin."));
|
2021-11-04 08:21:01 +01:00
|
|
|
continue;
|
|
|
|
}
|
2024-10-19 21:33:34 +09:00
|
|
|
var processor = _lightningAutomatedPayoutSenderFactory.ConstructProcessor(new PayoutProcessorData()
|
|
|
|
{
|
|
|
|
Store = store,
|
|
|
|
StoreId = store.Id,
|
|
|
|
PayoutMethodId = pmi.ToString(),
|
|
|
|
Processor = LightningAutomatedPayoutSenderFactory.ProcessorName,
|
|
|
|
Id = Guid.NewGuid().ToString()
|
|
|
|
});
|
2021-12-31 16:59:02 +09:00
|
|
|
|
2021-10-18 05:37:59 +02:00
|
|
|
var client =
|
2024-05-01 10:22:07 +09:00
|
|
|
lightningSupportedPaymentMethod.CreateLightningClient(payoutHandler.Network, _options.Value,
|
2021-10-18 05:37:59 +02:00
|
|
|
_lightningClientFactoryService);
|
|
|
|
|
2024-10-19 21:33:34 +09:00
|
|
|
foreach (var payout in payoutDatas)
|
2023-12-01 10:50:05 +01:00
|
|
|
{
|
2024-10-19 21:33:34 +09:00
|
|
|
results.Add(await processor.HandlePayout(payout, client, cancellationToken));
|
2023-12-01 10:50:05 +01:00
|
|
|
}
|
|
|
|
}
|
2021-10-18 05:37:59 +02:00
|
|
|
return View("LightningPayoutResult", results);
|
|
|
|
}
|
2022-07-15 05:37:47 +02:00
|
|
|
|
2024-10-19 21:33:34 +09:00
|
|
|
private ResultVM[] FailAll(IEnumerable<PayoutData> payouts, string message)
|
|
|
|
{
|
|
|
|
return payouts.Select(p => Fail(p, message)).ToArray();
|
2022-07-15 05:37:47 +02:00
|
|
|
}
|
2024-10-19 21:33:34 +09:00
|
|
|
private ResultVM Fail(PayoutData payoutData, string message)
|
2022-07-15 05:37:47 +02:00
|
|
|
{
|
2024-10-19 21:33:34 +09:00
|
|
|
var blob = payoutData.GetBlob(_btcPayNetworkJsonSerializerSettings);
|
|
|
|
return new ResultVM
|
2022-07-15 05:37:47 +02:00
|
|
|
{
|
2024-10-19 21:33:34 +09:00
|
|
|
PayoutId = payoutData.Id,
|
|
|
|
Result = PayResult.Error,
|
|
|
|
Destination = blob.Destination,
|
|
|
|
Message = message
|
|
|
|
};
|
2022-07-15 05:37:47 +02:00
|
|
|
}
|
|
|
|
|
2022-02-24 13:57:02 +01:00
|
|
|
private async Task SetStoreContext()
|
|
|
|
{
|
|
|
|
var storeId = HttpContext.GetUserPrefsCookie()?.CurrentStoreId;
|
2022-11-23 21:02:47 +09:00
|
|
|
if (string.IsNullOrEmpty(storeId))
|
|
|
|
return;
|
|
|
|
|
2022-02-24 13:57:02 +01:00
|
|
|
var userId = _userManager.GetUserId(User);
|
|
|
|
var store = await _storeRepository.FindStore(storeId, userId);
|
|
|
|
if (store != null)
|
|
|
|
{
|
|
|
|
HttpContext.SetStoreData(store);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-18 05:37:59 +02:00
|
|
|
public class ResultVM
|
|
|
|
{
|
|
|
|
public string PayoutId { get; set; }
|
|
|
|
public string Destination { get; set; }
|
|
|
|
public PayResult Result { get; set; }
|
|
|
|
public string Message { get; set; }
|
|
|
|
}
|
|
|
|
|
|
|
|
public class ConfirmVM
|
|
|
|
{
|
|
|
|
public string PayoutId { get; set; }
|
|
|
|
public string Destination { get; set; }
|
|
|
|
public decimal Amount { get; set; }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|