using System.Linq; using System.Threading; using System.Threading.Tasks; using BTCPayServer.Abstractions.Constants; using BTCPayServer.Abstractions.Contracts; using BTCPayServer.Abstractions.Extensions; using BTCPayServer.Client; using BTCPayServer.Client.Models; using BTCPayServer.Configuration; using BTCPayServer.Data; using BTCPayServer.Lightning; using BTCPayServer.Payments; using BTCPayServer.Payments.Lightning; using BTCPayServer.Services; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Cors; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Options; namespace BTCPayServer.Controllers.Greenfield { [ApiController] [Authorize(AuthenticationSchemes = AuthenticationSchemes.Greenfield)] [LightningUnavailableExceptionFilter] [EnableCors(CorsPolicies.All)] public class GreenfieldStoreLightningNodeApiController : GreenfieldLightningNodeApiController { private readonly IOptions _lightningNetworkOptions; private readonly LightningClientFactoryService _lightningClientFactory; private readonly BTCPayNetworkProvider _btcPayNetworkProvider; public GreenfieldStoreLightningNodeApiController( IOptions lightningNetworkOptions, LightningClientFactoryService lightningClientFactory, BTCPayNetworkProvider btcPayNetworkProvider, PoliciesSettings policiesSettings, IAuthorizationService authorizationService) : base( btcPayNetworkProvider, policiesSettings, authorizationService) { _lightningNetworkOptions = lightningNetworkOptions; _lightningClientFactory = lightningClientFactory; _btcPayNetworkProvider = btcPayNetworkProvider; } [Authorize(Policy = Policies.CanUseLightningNodeInStore, AuthenticationSchemes = AuthenticationSchemes.Greenfield)] [HttpGet("~/api/v1/stores/{storeId}/lightning/{cryptoCode}/info")] public override Task GetInfo(string cryptoCode, CancellationToken cancellationToken = default) { return base.GetInfo(cryptoCode, cancellationToken); } [Authorize(Policy = Policies.CanUseLightningNodeInStore, AuthenticationSchemes = AuthenticationSchemes.Greenfield)] [HttpGet("~/api/v1/stores/{storeId}/lightning/{cryptoCode}/balance")] public override Task GetBalance(string cryptoCode, CancellationToken cancellationToken = default) { return base.GetBalance(cryptoCode, cancellationToken); } [Authorize(Policy = Policies.CanUseLightningNodeInStore, AuthenticationSchemes = AuthenticationSchemes.Greenfield)] [HttpPost("~/api/v1/stores/{storeId}/lightning/{cryptoCode}/connect")] public override Task ConnectToNode(string cryptoCode, ConnectToNodeRequest request, CancellationToken cancellationToken = default) { return base.ConnectToNode(cryptoCode, request, cancellationToken); } [Authorize(Policy = Policies.CanUseLightningNodeInStore, AuthenticationSchemes = AuthenticationSchemes.Greenfield)] [HttpGet("~/api/v1/stores/{storeId}/lightning/{cryptoCode}/channels")] public override Task GetChannels(string cryptoCode, CancellationToken cancellationToken = default) { return base.GetChannels(cryptoCode, cancellationToken); } [Authorize(Policy = Policies.CanUseLightningNodeInStore, AuthenticationSchemes = AuthenticationSchemes.Greenfield)] [HttpPost("~/api/v1/stores/{storeId}/lightning/{cryptoCode}/channels")] public override Task OpenChannel(string cryptoCode, OpenLightningChannelRequest request, CancellationToken cancellationToken = default) { return base.OpenChannel(cryptoCode, request, cancellationToken); } [Authorize(Policy = Policies.CanUseLightningNodeInStore, AuthenticationSchemes = AuthenticationSchemes.Greenfield)] [HttpPost("~/api/v1/stores/{storeId}/lightning/{cryptoCode}/address")] public override Task GetDepositAddress(string cryptoCode, CancellationToken cancellationToken = default) { return base.GetDepositAddress(cryptoCode, cancellationToken); } [Authorize(Policy = Policies.CanUseLightningNodeInStore, AuthenticationSchemes = AuthenticationSchemes.Greenfield)] [HttpGet("~/api/v1/stores/{storeId}/lightning/{cryptoCode}/payments/{paymentHash}")] public override Task GetPayment(string cryptoCode, string paymentHash, CancellationToken cancellationToken = default) { return base.GetPayment(cryptoCode, paymentHash, cancellationToken); } [Authorize(Policy = Policies.CanUseLightningNodeInStore, AuthenticationSchemes = AuthenticationSchemes.Greenfield)] [HttpPost("~/api/v1/stores/{storeId}/lightning/{cryptoCode}/invoices/pay")] public override Task PayInvoice(string cryptoCode, PayLightningInvoiceRequest lightningInvoice, CancellationToken cancellationToken = default) { return base.PayInvoice(cryptoCode, lightningInvoice, cancellationToken); } [Authorize(Policy = Policies.CanUseLightningNodeInStore, AuthenticationSchemes = AuthenticationSchemes.Greenfield)] [HttpGet("~/api/v1/stores/{storeId}/lightning/{cryptoCode}/invoices/{id}")] public override Task GetInvoice(string cryptoCode, string id, CancellationToken cancellationToken = default) { return base.GetInvoice(cryptoCode, id, cancellationToken); } [Authorize(Policy = Policies.CanCreateLightningInvoiceInStore, AuthenticationSchemes = AuthenticationSchemes.Greenfield)] [HttpPost("~/api/v1/stores/{storeId}/lightning/{cryptoCode}/invoices")] public override Task CreateInvoice(string cryptoCode, CreateLightningInvoiceRequest request, CancellationToken cancellationToken = default) { return base.CreateInvoice(cryptoCode, request, cancellationToken); } protected override Task GetLightningClient(string cryptoCode, bool doingAdminThings) { var network = _btcPayNetworkProvider.GetNetwork(cryptoCode); if (network == null) { throw ErrorCryptoCodeNotFound(); } var store = HttpContext.GetStoreData(); if (store == null) { throw new JsonHttpException(StoreNotFound()); } var id = new PaymentMethodId(cryptoCode, PaymentTypes.LightningLike); var existing = store.GetSupportedPaymentMethods(_btcPayNetworkProvider) .OfType() .FirstOrDefault(d => d.PaymentId == id); if (existing == null) throw ErrorLightningNodeNotConfiguredForStore(); if (existing.GetExternalLightningUrl() is LightningConnectionString connectionString) { return Task.FromResult(_lightningClientFactory.Create(connectionString, network)); } else if (existing.IsInternalNode && _lightningNetworkOptions.Value.InternalLightningByCryptoCode.TryGetValue(network.CryptoCode, out var internalLightningNode)) { if (!User.IsInRole(Roles.ServerAdmin) && doingAdminThings) { throw ErrorShouldBeAdminForInternalNode(); } return Task.FromResult(_lightningClientFactory.Create(internalLightningNode, network)); } throw ErrorLightningNodeNotConfiguredForStore(); } private IActionResult StoreNotFound() { return this.CreateAPIError(404, "store-not-found", "The store was not found"); } } }