2021-10-18 05:37:59 +02:00
using System ;
using System.Collections.Generic ;
using System.Linq ;
using System.Net.Http ;
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 ;
2021-10-18 05:37:59 +02:00
using BTCPayServer.Validation ;
using LNURL ;
2021-11-04 08:21:01 +01:00
using Microsoft.AspNetCore.Authorization ;
2021-10-18 05:37:59 +02:00
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 ;
2021-10-18 05:37:59 +02:00
public LightningLikePayoutHandler ( BTCPayNetworkProvider btcPayNetworkProvider ,
2021-11-04 08:21:01 +01:00
IHttpClientFactory httpClientFactory , UserService userService , IAuthorizationService authorizationService )
2021-10-18 05:37:59 +02:00
{
_btcPayNetworkProvider = btcPayNetworkProvider ;
_httpClientFactory = httpClientFactory ;
2021-11-04 08:21:01 +01:00
_userService = userService ;
_authorizationService = authorizationService ;
2021-10-18 05:37:59 +02:00
}
public bool CanHandle ( PaymentMethodId paymentMethod )
{
return paymentMethod . PaymentType = = LightningPaymentType . 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 , bool validate )
{
destination = destination . Trim ( ) ;
var network = _btcPayNetworkProvider . GetNetwork < BTCPayNetwork > ( paymentMethodId . CryptoCode ) ;
try
{
string lnurlTag = null ;
var lnurl = EmailValidator . IsEmail ( destination )
? LNURL . LNURL . ExtractUriFromInternetIdentifier ( destination )
: LNURL . LNURL . Parse ( destination , out lnurlTag ) ;
if ( lnurlTag is null )
{
var info = ( LNURLPayRequest ) ( await LNURL . LNURL . FetchInformation ( lnurl , CreateClient ( lnurl ) ) ) ;
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
2021-10-18 05:37:59 +02:00
{
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 ;
if ( result = = null ) return ( null , "A valid BOLT11 invoice (with 30+ day expiry) or LNURL Pay or Lightning address was not provided." ) ;
if ( validate & & ( invoice . ExpiryDate . UtcDateTime - DateTime . UtcNow ) . Days < 30 )
{
return ( null ,
$"The BOLT11 invoice must have an expiry date of at least 30 days from submission (Provided was only {(invoice.ExpiryDate.UtcDateTime - DateTime.UtcNow).Days})." ) ;
}
if ( invoice . ExpiryDate . UtcDateTime < DateTime . UtcNow )
{
return ( null ,
"The BOLT11 invoice submitted has expired." ) ;
}
return ( result , null ) ;
}
public IPayoutProof ParseProof ( PayoutData payout )
{
return null ;
}
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-10-18 05:37:59 +02:00
{
2021-11-04 08:21:01 +01:00
var result = new List < PaymentMethodId > ( ) ;
var methods = storeData . GetEnabledPaymentMethods ( _btcPayNetworkProvider ) . Where ( id = > id . PaymentId . PaymentType = = LightningPaymentType . Instance ) . OfType < LightningSupportedPaymentMethod > ( ) ;
foreach ( LightningSupportedPaymentMethod supportedPaymentMethod in methods )
{
if ( ! supportedPaymentMethod . IsInternalNode )
{
result . Add ( supportedPaymentMethod . PaymentId ) ;
continue ;
}
foreach ( UserStore storeDataUserStore in storeData . UserStores )
{
if ( ! await _userService . IsAdminUser ( storeDataUserStore . ApplicationUserId ) ) continue ;
result . Add ( supportedPaymentMethod . PaymentId ) ;
break ;
}
}
return result ;
2021-10-18 05:37:59 +02:00
}
public Task < IActionResult > InitiatePayment ( PaymentMethodId paymentMethodId , string [ ] payoutIds )
{
return Task . FromResult < IActionResult > ( new RedirectToActionResult ( "ConfirmLightningPayout" ,
"LightningLikePayout" , new { cryptoCode = paymentMethodId . CryptoCode , payoutIds } ) ) ;
}
}
}