mirror of
https://github.com/aljazceru/btcpayserver.git
synced 2025-12-17 22:14:26 +01:00
Adapt ln payouts to handle unknown status (#4382)
Co-authored-by: d11n <mail@dennisreimann.de>
This commit is contained in:
@@ -460,12 +460,12 @@ namespace BTCPayServer.Controllers.Greenfield
|
|||||||
ModelState.AddModelError(nameof(approvePayoutRequest.RateRule), "Invalid RateRule");
|
ModelState.AddModelError(nameof(approvePayoutRequest.RateRule), "Invalid RateRule");
|
||||||
return this.CreateValidationError(ModelState);
|
return this.CreateValidationError(ModelState);
|
||||||
}
|
}
|
||||||
var result = await _pullPaymentService.Approve(new PullPaymentHostedService.PayoutApproval()
|
var result = (await _pullPaymentService.Approve(new PullPaymentHostedService.PayoutApproval()
|
||||||
{
|
{
|
||||||
PayoutId = payoutId,
|
PayoutId = payoutId,
|
||||||
Revision = revision!.Value,
|
Revision = revision!.Value,
|
||||||
Rate = rateResult.BidAsk.Ask
|
Rate = rateResult.BidAsk.Ask
|
||||||
});
|
})).Result;
|
||||||
var errorMessage = PullPaymentHostedService.PayoutApproval.GetErrorMessage(result);
|
var errorMessage = PullPaymentHostedService.PayoutApproval.GetErrorMessage(result);
|
||||||
switch (result)
|
switch (result)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ using System.Collections.Generic;
|
|||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using BTCPayServer.Abstractions.Constants;
|
using BTCPayServer.Abstractions.Constants;
|
||||||
using BTCPayServer.Abstractions.Extensions;
|
using BTCPayServer.Abstractions.Extensions;
|
||||||
@@ -20,6 +21,7 @@ using BTCPayServer.Models.AppViewModels;
|
|||||||
using BTCPayServer.Payments;
|
using BTCPayServer.Payments;
|
||||||
using BTCPayServer.Payments.Lightning;
|
using BTCPayServer.Payments.Lightning;
|
||||||
using BTCPayServer.Plugins.PointOfSale.Models;
|
using BTCPayServer.Plugins.PointOfSale.Models;
|
||||||
|
using BTCPayServer.Services;
|
||||||
using BTCPayServer.Services.Apps;
|
using BTCPayServer.Services.Apps;
|
||||||
using BTCPayServer.Services.Invoices;
|
using BTCPayServer.Services.Invoices;
|
||||||
using BTCPayServer.Services.Rates;
|
using BTCPayServer.Services.Rates;
|
||||||
@@ -51,6 +53,7 @@ namespace BTCPayServer
|
|||||||
private readonly LightningAddressService _lightningAddressService;
|
private readonly LightningAddressService _lightningAddressService;
|
||||||
private readonly LightningLikePayoutHandler _lightningLikePayoutHandler;
|
private readonly LightningLikePayoutHandler _lightningLikePayoutHandler;
|
||||||
private readonly PullPaymentHostedService _pullPaymentHostedService;
|
private readonly PullPaymentHostedService _pullPaymentHostedService;
|
||||||
|
private readonly BTCPayNetworkJsonSerializerSettings _btcPayNetworkJsonSerializerSettings;
|
||||||
|
|
||||||
public UILNURLController(InvoiceRepository invoiceRepository,
|
public UILNURLController(InvoiceRepository invoiceRepository,
|
||||||
EventAggregator eventAggregator,
|
EventAggregator eventAggregator,
|
||||||
@@ -62,7 +65,8 @@ namespace BTCPayServer
|
|||||||
LinkGenerator linkGenerator,
|
LinkGenerator linkGenerator,
|
||||||
LightningAddressService lightningAddressService,
|
LightningAddressService lightningAddressService,
|
||||||
LightningLikePayoutHandler lightningLikePayoutHandler,
|
LightningLikePayoutHandler lightningLikePayoutHandler,
|
||||||
PullPaymentHostedService pullPaymentHostedService)
|
PullPaymentHostedService pullPaymentHostedService,
|
||||||
|
BTCPayNetworkJsonSerializerSettings btcPayNetworkJsonSerializerSettings)
|
||||||
{
|
{
|
||||||
_invoiceRepository = invoiceRepository;
|
_invoiceRepository = invoiceRepository;
|
||||||
_eventAggregator = eventAggregator;
|
_eventAggregator = eventAggregator;
|
||||||
@@ -75,11 +79,12 @@ namespace BTCPayServer
|
|||||||
_lightningAddressService = lightningAddressService;
|
_lightningAddressService = lightningAddressService;
|
||||||
_lightningLikePayoutHandler = lightningLikePayoutHandler;
|
_lightningLikePayoutHandler = lightningLikePayoutHandler;
|
||||||
_pullPaymentHostedService = pullPaymentHostedService;
|
_pullPaymentHostedService = pullPaymentHostedService;
|
||||||
|
_btcPayNetworkJsonSerializerSettings = btcPayNetworkJsonSerializerSettings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
[HttpGet("withdraw/pp/{pullPaymentId}")]
|
[HttpGet("withdraw/pp/{pullPaymentId}")]
|
||||||
public async Task<IActionResult> GetLNURLForPullPayment(string cryptoCode, string pullPaymentId, string pr)
|
public async Task<IActionResult> GetLNURLForPullPayment(string cryptoCode, string pullPaymentId, string pr, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
|
|
||||||
var network = _btcPayNetworkProvider.GetNetwork<BTCPayNetwork>(cryptoCode);
|
var network = _btcPayNetworkProvider.GetNetwork<BTCPayNetwork>(cryptoCode);
|
||||||
@@ -155,25 +160,28 @@ namespace BTCPayServer
|
|||||||
{
|
{
|
||||||
var client =
|
var client =
|
||||||
_lightningLikePaymentHandler.CreateLightningClient(pm, network);
|
_lightningLikePaymentHandler.CreateLightningClient(pm, network);
|
||||||
PayResponse payResult;
|
var payResult = await UILightningLikePayoutController.TrypayBolt(client,
|
||||||
try
|
claimResponse.PayoutData.GetBlob(_btcPayNetworkJsonSerializerSettings),
|
||||||
{
|
claimResponse.PayoutData, result, pmi, cancellationToken);
|
||||||
payResult = await client.Pay(pr);
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
payResult = new PayResponse(PayResult.Error, e.Message);
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (payResult.Result)
|
switch (payResult.Result)
|
||||||
{
|
{
|
||||||
case PayResult.Ok:
|
case PayResult.Ok:
|
||||||
|
case PayResult.Unknown:
|
||||||
await _pullPaymentHostedService.MarkPaid(new MarkPayoutRequest()
|
await _pullPaymentHostedService.MarkPaid(new MarkPayoutRequest()
|
||||||
{
|
{
|
||||||
PayoutId = claimResponse.PayoutData.Id, State = PayoutState.Completed
|
PayoutId = claimResponse.PayoutData.Id,
|
||||||
|
State = claimResponse.PayoutData.State,
|
||||||
|
Proof = claimResponse.PayoutData.GetProofBlobJson()
|
||||||
});
|
});
|
||||||
|
|
||||||
return Ok(new LNUrlStatusResponse {Status = "OK"});
|
return Ok(new LNUrlStatusResponse
|
||||||
|
{
|
||||||
|
Status = "OK",
|
||||||
|
Reason = payResult.Message
|
||||||
|
});
|
||||||
|
case PayResult.CouldNotFindRoute:
|
||||||
|
case PayResult.Error:
|
||||||
default:
|
default:
|
||||||
await _pullPaymentHostedService.Cancel(
|
await _pullPaymentHostedService.Cancel(
|
||||||
new PullPaymentHostedService.CancelRequest(new string[]
|
new PullPaymentHostedService.CancelRequest(new string[]
|
||||||
@@ -184,7 +192,7 @@ namespace BTCPayServer
|
|||||||
return Ok(new LNUrlStatusResponse
|
return Ok(new LNUrlStatusResponse
|
||||||
{
|
{
|
||||||
Status = "ERROR",
|
Status = "ERROR",
|
||||||
Reason = $"Pr could not be paid because {payResult.ErrorDetail}"
|
Reason = payResult.Message
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -338,11 +338,11 @@ namespace BTCPayServer.Controllers
|
|||||||
Revision = payout.GetBlob(_jsonSerializerSettings).Revision,
|
Revision = payout.GetBlob(_jsonSerializerSettings).Revision,
|
||||||
Rate = rateResult.BidAsk.Ask
|
Rate = rateResult.BidAsk.Ask
|
||||||
});
|
});
|
||||||
if (approveResult != PullPaymentHostedService.PayoutApproval.Result.Ok)
|
if (approveResult.Result != PullPaymentHostedService.PayoutApproval.Result.Ok)
|
||||||
{
|
{
|
||||||
TempData.SetStatusMessageModel(new StatusMessageModel()
|
TempData.SetStatusMessageModel(new StatusMessageModel()
|
||||||
{
|
{
|
||||||
Message = PullPaymentHostedService.PayoutApproval.GetErrorMessage(approveResult),
|
Message = PullPaymentHostedService.PayoutApproval.GetErrorMessage(approveResult.Result),
|
||||||
Severity = StatusMessageModel.StatusSeverity.Error
|
Severity = StatusMessageModel.StatusSeverity.Error
|
||||||
});
|
});
|
||||||
failed = true;
|
failed = true;
|
||||||
|
|||||||
@@ -257,9 +257,7 @@ namespace BTCPayServer.Data.Payouts.LightningLike
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static readonly TimeSpan SendTimeout = TimeSpan.FromSeconds(20);
|
|
||||||
public static async Task<ResultVM> TrypayBolt(
|
public static async Task<ResultVM> TrypayBolt(
|
||||||
ILightningClient lightningClient, PayoutBlob payoutBlob, PayoutData payoutData, BOLT11PaymentRequest bolt11PaymentRequest,
|
ILightningClient lightningClient, PayoutBlob payoutBlob, PayoutData payoutData, BOLT11PaymentRequest bolt11PaymentRequest,
|
||||||
PaymentMethodId pmi, CancellationToken cancellationToken)
|
PaymentMethodId pmi, CancellationToken cancellationToken)
|
||||||
@@ -281,17 +279,13 @@ namespace BTCPayServer.Data.Payouts.LightningLike
|
|||||||
var proofBlob = new PayoutLightningBlob() { PaymentHash = bolt11PaymentRequest.PaymentHash.ToString() };
|
var proofBlob = new PayoutLightningBlob() { PaymentHash = bolt11PaymentRequest.PaymentHash.ToString() };
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// TODO: Incorporate the changes from this PR here:
|
|
||||||
// https://github.com/btcpayserver/BTCPayServer.Lightning/pull/106
|
|
||||||
using var timeout = new CancellationTokenSource(SendTimeout);
|
|
||||||
using var c = CancellationTokenSource.CreateLinkedTokenSource(timeout.Token, cancellationToken);
|
|
||||||
var result = await lightningClient.Pay(bolt11PaymentRequest.ToString(),
|
var result = await lightningClient.Pay(bolt11PaymentRequest.ToString(),
|
||||||
new PayInvoiceParams()
|
new PayInvoiceParams()
|
||||||
{
|
{
|
||||||
Amount = bolt11PaymentRequest.MinimumAmount == LightMoney.Zero
|
Amount = bolt11PaymentRequest.MinimumAmount == LightMoney.Zero
|
||||||
? new LightMoney((decimal)payoutBlob.CryptoAmount, LightMoneyUnit.BTC)
|
? new LightMoney((decimal)payoutBlob.CryptoAmount, LightMoneyUnit.BTC)
|
||||||
: null
|
: null
|
||||||
}, c.Token);
|
}, cancellationToken);
|
||||||
string message = null;
|
string message = null;
|
||||||
if (result.Result == PayResult.Ok)
|
if (result.Result == PayResult.Ok)
|
||||||
{
|
{
|
||||||
@@ -309,6 +303,11 @@ namespace BTCPayServer.Data.Payouts.LightningLike
|
|||||||
// ignored
|
// ignored
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if(result.Result == PayResult.Unknown)
|
||||||
|
{
|
||||||
|
payoutData.State = PayoutState.InProgress;
|
||||||
|
message = "The payment has been initiated but is still in-flight.";
|
||||||
|
}
|
||||||
|
|
||||||
payoutData.SetProofBlob(proofBlob, null);
|
payoutData.SetProofBlob(proofBlob, null);
|
||||||
return new ResultVM
|
return new ResultVM
|
||||||
|
|||||||
@@ -79,10 +79,12 @@ namespace BTCPayServer.HostedServices
|
|||||||
OldRevision
|
OldRevision
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public record ApprovalResult(Result Result, decimal? CryptoAmount);
|
||||||
|
|
||||||
public string PayoutId { get; set; }
|
public string PayoutId { get; set; }
|
||||||
public int Revision { get; set; }
|
public int Revision { get; set; }
|
||||||
public decimal Rate { get; set; }
|
public decimal Rate { get; set; }
|
||||||
internal TaskCompletionSource<Result> Completion { get; set; }
|
internal TaskCompletionSource<ApprovalResult> Completion { get; set; }
|
||||||
|
|
||||||
public static string GetErrorMessage(Result result)
|
public static string GetErrorMessage(Result result)
|
||||||
{
|
{
|
||||||
@@ -333,10 +335,10 @@ namespace BTCPayServer.HostedServices
|
|||||||
return _rateFetcher.FetchRate(rule, cancellationToken);
|
return _rateFetcher.FetchRate(rule, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<PayoutApproval.Result> Approve(PayoutApproval approval)
|
public Task<PayoutApproval.ApprovalResult> Approve(PayoutApproval approval)
|
||||||
{
|
{
|
||||||
approval.Completion =
|
approval.Completion =
|
||||||
new TaskCompletionSource<PayoutApproval.Result>(TaskCreationOptions.RunContinuationsAsynchronously);
|
new TaskCompletionSource<PayoutApproval.ApprovalResult>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||||
if (!_Channel.Writer.TryWrite(approval))
|
if (!_Channel.Writer.TryWrite(approval))
|
||||||
throw new ObjectDisposedException(nameof(PullPaymentHostedService));
|
throw new ObjectDisposedException(nameof(PullPaymentHostedService));
|
||||||
return approval.Completion.Task;
|
return approval.Completion.Task;
|
||||||
@@ -351,26 +353,26 @@ namespace BTCPayServer.HostedServices
|
|||||||
.FirstOrDefaultAsync();
|
.FirstOrDefaultAsync();
|
||||||
if (payout is null)
|
if (payout is null)
|
||||||
{
|
{
|
||||||
req.Completion.SetResult(PayoutApproval.Result.NotFound);
|
req.Completion.SetResult(new PayoutApproval.ApprovalResult(PayoutApproval.Result.NotFound, null));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (payout.State != PayoutState.AwaitingApproval)
|
if (payout.State != PayoutState.AwaitingApproval)
|
||||||
{
|
{
|
||||||
req.Completion.SetResult(PayoutApproval.Result.InvalidState);
|
req.Completion.SetResult(new PayoutApproval.ApprovalResult(PayoutApproval.Result.InvalidState, null));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var payoutBlob = payout.GetBlob(this._jsonSerializerSettings);
|
var payoutBlob = payout.GetBlob(this._jsonSerializerSettings);
|
||||||
if (payoutBlob.Revision != req.Revision)
|
if (payoutBlob.Revision != req.Revision)
|
||||||
{
|
{
|
||||||
req.Completion.SetResult(PayoutApproval.Result.OldRevision);
|
req.Completion.SetResult(new PayoutApproval.ApprovalResult(PayoutApproval.Result.OldRevision, null));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!PaymentMethodId.TryParse(payout.PaymentMethodId, out var paymentMethod))
|
if (!PaymentMethodId.TryParse(payout.PaymentMethodId, out var paymentMethod))
|
||||||
{
|
{
|
||||||
req.Completion.SetResult(PayoutApproval.Result.NotFound);
|
req.Completion.SetResult(new PayoutApproval.ApprovalResult(PayoutApproval.Result.NotFound, null));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -388,7 +390,7 @@ namespace BTCPayServer.HostedServices
|
|||||||
await payoutHandler.GetMinimumPayoutAmount(paymentMethod, dest.destination);
|
await payoutHandler.GetMinimumPayoutAmount(paymentMethod, dest.destination);
|
||||||
if (cryptoAmount < minimumCryptoAmount)
|
if (cryptoAmount < minimumCryptoAmount)
|
||||||
{
|
{
|
||||||
req.Completion.TrySetResult(PayoutApproval.Result.TooLowAmount);
|
req.Completion.TrySetResult(new PayoutApproval.ApprovalResult(PayoutApproval.Result.TooLowAmount, null));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -397,7 +399,7 @@ namespace BTCPayServer.HostedServices
|
|||||||
payout.SetBlob(payoutBlob, _jsonSerializerSettings);
|
payout.SetBlob(payoutBlob, _jsonSerializerSettings);
|
||||||
await ctx.SaveChangesAsync();
|
await ctx.SaveChangesAsync();
|
||||||
|
|
||||||
req.Completion.SetResult(PayoutApproval.Result.Ok);
|
req.Completion.SetResult(new PayoutApproval.ApprovalResult(PayoutApproval.Result.Ok, payoutBlob.CryptoAmount));
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -566,18 +568,20 @@ namespace BTCPayServer.HostedServices
|
|||||||
var rateResult = await GetRate(payout, null, CancellationToken.None);
|
var rateResult = await GetRate(payout, null, CancellationToken.None);
|
||||||
if (rateResult.BidAsk != null)
|
if (rateResult.BidAsk != null)
|
||||||
{
|
{
|
||||||
var approveResult = new TaskCompletionSource<PayoutApproval.Result>();
|
var approveResultTask = new TaskCompletionSource<PayoutApproval.ApprovalResult>();
|
||||||
await HandleApproval(new PayoutApproval()
|
await HandleApproval(new PayoutApproval()
|
||||||
{
|
{
|
||||||
PayoutId = payout.Id,
|
PayoutId = payout.Id,
|
||||||
Revision = payoutBlob.Revision,
|
Revision = payoutBlob.Revision,
|
||||||
Rate = rateResult.BidAsk.Ask,
|
Rate = rateResult.BidAsk.Ask,
|
||||||
Completion = approveResult
|
Completion = approveResultTask
|
||||||
});
|
});
|
||||||
|
var approveResult = await approveResultTask.Task;
|
||||||
if ((await approveResult.Task) == PayoutApproval.Result.Ok)
|
if (approveResult.Result == PayoutApproval.Result.Ok)
|
||||||
{
|
{
|
||||||
payout.State = PayoutState.AwaitingPayment;
|
payout.State = PayoutState.AwaitingPayment;
|
||||||
|
payoutBlob.CryptoAmount = approveResult.CryptoAmount;
|
||||||
|
payout.SetBlob(payoutBlob, _jsonSerializerSettings);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ public static class PayoutProcessorsExtensions
|
|||||||
serviceCollection.AddSingleton<IPayoutProcessorFactory>(provider => provider.GetRequiredService<OnChainAutomatedPayoutSenderFactory>());
|
serviceCollection.AddSingleton<IPayoutProcessorFactory>(provider => provider.GetRequiredService<OnChainAutomatedPayoutSenderFactory>());
|
||||||
serviceCollection.AddSingleton<LightningAutomatedPayoutSenderFactory>();
|
serviceCollection.AddSingleton<LightningAutomatedPayoutSenderFactory>();
|
||||||
serviceCollection.AddSingleton<IPayoutProcessorFactory>(provider => provider.GetRequiredService<LightningAutomatedPayoutSenderFactory>());
|
serviceCollection.AddSingleton<IPayoutProcessorFactory>(provider => provider.GetRequiredService<LightningAutomatedPayoutSenderFactory>());
|
||||||
serviceCollection.AddHostedService<PayoutProcessorService>();
|
|
||||||
serviceCollection.AddSingleton<PayoutProcessorService>();
|
serviceCollection.AddSingleton<PayoutProcessorService>();
|
||||||
serviceCollection.AddHostedService(s=> s.GetRequiredService<PayoutProcessorService>());
|
serviceCollection.AddHostedService(s=> s.GetRequiredService<PayoutProcessorService>());
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user