2021-10-18 05:37:59 +02:00
using System ;
using System.Collections.Generic ;
using System.Linq ;
using System.Net.Http ;
2022-11-22 20:17:29 +09:00
using System.Threading ;
2021-10-18 05:37:59 +02:00
using System.Threading.Tasks ;
using BTCPayServer.Abstractions.Models ;
using BTCPayServer.Client.Models ;
2024-05-01 10:22:07 +09:00
using BTCPayServer.Configuration ;
2023-04-07 08:58:41 +02: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-11-04 08:21:01 +01:00
using BTCPayServer.Payments.Lightning ;
2024-05-01 10:22:07 +09:00
using BTCPayServer.Payouts ;
2021-11-04 08:21:01 +01:00
using BTCPayServer.Services ;
2024-04-04 16:31:04 +09:00
using BTCPayServer.Services.Invoices ;
2021-10-18 05:37:59 +02:00
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 ;
2024-05-01 10:22:07 +09:00
using Microsoft.Extensions.Options ;
using MimeKit ;
2021-10-18 05:37:59 +02:00
using NBitcoin ;
namespace BTCPayServer.Data.Payouts.LightningLike
{
2024-05-01 10:22:07 +09:00
public class LightningLikePayoutHandler : IPayoutHandler , IHasNetwork
2021-10-18 05:37:59 +02:00
{
2024-05-01 10:22:07 +09:00
public string Currency { get ; }
public PayoutMethodId PayoutMethodId { get ; }
public PaymentMethodId PaymentMethodId { get ; }
private readonly IOptions < LightningNetworkOptions > _options ;
private PaymentMethodHandlerDictionary _paymentHandlers ;
public BTCPayNetwork Network { get ; }
2024-05-09 17:20:24 +09:00
public string [ ] DefaultRateRules = > Network . DefaultRateRules ;
2024-05-01 10:22:07 +09:00
2021-10-18 05:37:59 +02:00
public const string LightningLikePayoutHandlerOnionNamedClient =
nameof ( LightningLikePayoutHandlerOnionNamedClient ) ;
public const string LightningLikePayoutHandlerClearnetNamedClient =
nameof ( LightningLikePayoutHandlerClearnetNamedClient ) ;
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
2024-05-01 10:22:07 +09:00
public LightningLikePayoutHandler (
PayoutMethodId payoutMethodId ,
IOptions < LightningNetworkOptions > options ,
BTCPayNetwork network ,
PaymentMethodHandlerDictionary paymentHandlers ,
2021-11-04 08:21:01 +01:00
IHttpClientFactory httpClientFactory , UserService userService , IAuthorizationService authorizationService )
2021-10-18 05:37:59 +02:00
{
2024-05-01 10:22:07 +09:00
_paymentHandlers = paymentHandlers ;
Network = network ;
PayoutMethodId = payoutMethodId ;
_options = options ;
PaymentMethodId = PaymentTypes . LN . GetPaymentMethodId ( network . CryptoCode ) ;
2021-10-18 05:37:59 +02:00
_httpClientFactory = httpClientFactory ;
2021-11-04 08:21:01 +01:00
_userService = userService ;
_authorizationService = authorizationService ;
2024-05-01 10:22:07 +09:00
Currency = network . CryptoCode ;
2021-10-18 05:37:59 +02:00
}
2023-04-07 08:58:41 +02:00
public Task TrackClaim ( ClaimRequest claimRequest , PayoutData payoutData )
2021-10-18 05:37:59 +02:00
{
return Task . CompletedTask ;
}
public HttpClient CreateClient ( Uri uri )
{
return _httpClientFactory . CreateClient ( uri . IsOnion ( )
? LightningLikePayoutHandlerOnionNamedClient
: LightningLikePayoutHandlerClearnetNamedClient ) ;
}
2024-05-01 10:22:07 +09:00
public async Task < ( IClaimDestination destination , string error ) > ParseClaimDestination ( string destination , CancellationToken cancellationToken )
2021-10-18 05:37:59 +02:00
{
destination = destination . Trim ( ) ;
try
{
string lnurlTag = null ;
2022-07-15 05:37:47 +02:00
var lnurl = destination . IsValidEmail ( )
2021-10-18 05:37:59 +02:00
? LNURL . LNURL . ExtractUriFromInternetIdentifier ( destination )
: LNURL . LNURL . Parse ( destination , out lnurlTag ) ;
if ( lnurlTag is null )
{
2022-11-22 20:17:29 +09:00
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 ) ) ;
2021-10-18 05:37:59 +02:00
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 =
2024-05-01 10:22:07 +09:00
BOLT11PaymentRequest . TryParse ( destination , out var invoice , Network . NBitcoinNetwork )
2021-10-18 05:37:59 +02:00
? new BoltInvoiceClaimDestination ( destination , invoice )
: null ;
2021-12-31 16:59:02 +09:00
if ( result = = null )
2022-01-24 20:17:09 +09:00
return ( null , "A valid BOLT11 invoice or LNURL Pay or Lightning address was not provided." ) ;
2021-10-18 05:37:59 +02:00
if ( invoice . ExpiryDate . UtcDateTime < DateTime . UtcNow )
{
return ( null ,
"The BOLT11 invoice submitted has expired." ) ;
}
return ( result , null ) ;
}
2022-01-24 20:17:09 +09:00
public ( bool valid , string error ) ValidateClaimDestination ( IClaimDestination claimDestination , PullPaymentBlob pullPaymentBlob )
{
if ( claimDestination is not BoltInvoiceClaimDestination bolt )
return ( true , null ) ;
var invoice = bolt . PaymentRequest ;
2022-04-24 05:19:34 +02:00
if ( pullPaymentBlob is not null & & ( invoice . ExpiryDate . UtcDateTime - DateTime . UtcNow ) < pullPaymentBlob . BOLT11Expiration )
2022-01-24 20:17:09 +09:00
{
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 ) ;
}
2021-10-18 05:37:59 +02:00
public IPayoutProof ParseProof ( PayoutData payout )
{
2022-08-17 09:45:51 +02:00
BitcoinLikePayoutHandler . ParseProofType ( payout . Proof , out var raw , out var proofType ) ;
if ( proofType is null )
{
return null ;
}
2022-11-15 10:40:57 +01:00
if ( proofType = = PayoutLightningBlob . PayoutLightningBlobProofType )
2022-08-17 09:45:51 +02:00
{
2022-11-15 10:40:57 +01:00
return raw . ToObject < PayoutLightningBlob > ( ) ;
2022-08-17 09:45:51 +02:00
}
2022-11-15 10:40:57 +01:00
return raw . ToObject < ManualPayoutProof > ( ) ;
2021-10-18 05:37:59 +02:00
}
public void StartBackgroundCheck ( Action < Type [ ] > subscribe )
{
}
public Task BackgroundCheck ( object o )
{
return Task . CompletedTask ;
}
2024-05-01 10:22:07 +09:00
public Task < decimal > GetMinimumPayoutAmount ( IClaimDestination claimDestination )
2021-10-18 05:37:59 +02:00
{
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 ) ;
}
2024-05-01 10:22:07 +09:00
public bool IsSupported ( StoreData storeData )
2021-10-18 05:37:59 +02:00
{
2024-05-01 10:22:07 +09:00
return storeData . GetPaymentMethodConfig < LightningPaymentMethodConfig > ( PaymentMethodId , _paymentHandlers , true ) ? . IsConfigured ( Network , _options . Value ) is true ;
2021-10-18 05:37:59 +02:00
}
2024-05-01 10:22:07 +09:00
public Task < IActionResult > InitiatePayment ( string [ ] payoutIds )
2021-10-18 05:37:59 +02:00
{
2024-05-01 10:22:07 +09:00
var cryptoCode = Network . CryptoCode ;
2021-10-18 05:37:59 +02:00
return Task . FromResult < IActionResult > ( new RedirectToActionResult ( "ConfirmLightningPayout" ,
2024-04-04 16:31:04 +09:00
"UILightningLikePayout" , new { cryptoCode , payoutIds } ) ) ;
2021-10-18 05:37:59 +02:00
}
2023-01-06 14:18:07 +01:00
2021-10-18 05:37:59 +02:00
}
}