Poll for charge invoice

This commit is contained in:
nicolas.dorier 2018-02-26 14:52:08 +09:00
parent adc6bea4dc
commit 5924f1730c
3 changed files with 53 additions and 16 deletions

View file

@ -37,9 +37,9 @@ namespace BTCPayServer.Payments.Lightning.CLightning
this._Uri = uri;
this._Network = network;
if (uri.UserInfo == null)
throw new ArgumentException(paramName:nameof(uri), message:"User information not present in uri");
throw new ArgumentException(paramName: nameof(uri), message: "User information not present in uri");
var userInfo = uri.UserInfo.Split(':');
if(userInfo.Length != 2)
if (userInfo.Length != 2)
throw new ArgumentException(paramName: nameof(uri), message: "User information not present in uri");
Credentials = new NetworkCredential(userInfo[0], userInfo[1]);
}
@ -85,6 +85,18 @@ namespace BTCPayServer.Payments.Lightning.CLightning
{
return GetInfoAsync().GetAwaiter().GetResult();
}
public async Task<ChargeInvoice> GetInvoice(string invoiceId, CancellationToken cancellation = default(CancellationToken))
{
var request = CreateMessage(HttpMethod.Get, $"invoice/{invoiceId}");
var message = await _Client.SendAsync(request, cancellation);
if (message.StatusCode == HttpStatusCode.NotFound)
return null;
message.EnsureSuccessStatusCode();
var content = await message.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<ChargeInvoice>(content);
}
public async Task<GetInfoResponse> GetInfoAsync(CancellationToken cancellation = default(CancellationToken))
{
var request = CreateMessage(HttpMethod.Get, "info");

View file

@ -10,7 +10,7 @@ using Newtonsoft.Json;
namespace BTCPayServer.Payments.Lightning.CLightning
{
public class ChargeInvoiceNotification
public class ChargeInvoice
{
public string Id { get; set; }
@ -42,7 +42,7 @@ namespace BTCPayServer.Payments.Lightning.CLightning
}
ArraySegment<byte> _Buffer;
public async Task<ChargeInvoiceNotification> NextEvent(CancellationToken cancellation = default(CancellationToken))
public async Task<ChargeInvoice> NextEvent(CancellationToken cancellation = default(CancellationToken))
{
var buffer = _Buffer;
var array = _Buffer.Array;
@ -96,10 +96,10 @@ namespace BTCPayServer.Payments.Lightning.CLightning
}
UTF8Encoding UTF8 = new UTF8Encoding(false, true);
private ChargeInvoiceNotification ParseMessage(ArraySegment<byte> buffer)
private ChargeInvoice ParseMessage(ArraySegment<byte> buffer)
{
var str = UTF8.GetString(buffer.Array, 0, buffer.Count);
return JsonConvert.DeserializeObject<ChargeInvoiceNotification>(str, new JsonSerializerSettings());
return JsonConvert.DeserializeObject<ChargeInvoice>(str, new JsonSerializerSettings());
}
private async Task CloseSocketAndThrow(WebSocketCloseStatus status, string description, CancellationToken cancellation)

View file

@ -44,24 +44,25 @@ namespace BTCPayServer.Payments.Lightning
{
if (inv.Name == "invoice_created")
{
await EnsureListening(inv.InvoiceId);
await EnsureListening(inv.InvoiceId, false);
}
}));
_ListenPoller = new Timer(async s =>
{
await Task.WhenAll((await _InvoiceRepository.GetPendingInvoices())
.Select(async invoiceId => await EnsureListening(invoiceId))
.Select(async invoiceId => await EnsureListening(invoiceId, true))
.ToArray());
}, null, 0, (int)PollInterval.TotalMilliseconds);
leases.Add(_ListenPoller);
return Task.CompletedTask;
}
private async Task EnsureListening(string invoiceId)
private async Task EnsureListening(string invoiceId, bool poll)
{
if (Listening(invoiceId))
return;
var invoice = await _InvoiceRepository.GetInvoice(null, invoiceId);
foreach (var paymentMethod in invoice.GetPaymentMethods(_NetworkProvider)
.Where(c => c.GetId().PaymentType == PaymentTypes.LightningLike))
@ -74,6 +75,7 @@ namespace BTCPayServer.Payments.Lightning
if (lightningSupportedMethod == null)
continue;
var network = _NetworkProvider.GetNetwork(paymentMethod.GetId().CryptoCode);
var listenedInvoice = new ListenedInvoice()
{
Uri = lightningSupportedMethod.GetLightningChargeUrl(false).AbsoluteUri,
@ -83,6 +85,19 @@ namespace BTCPayServer.Payments.Lightning
Network = network,
InvoiceId = invoice.Id
};
if (poll)
{
var charge = GetChargeClient(lightningSupportedMethod, network);
var chargeInvoice = await charge.GetInvoice(lightningMethod.InvoiceId);
if (chargeInvoice == null)
continue;
if(chargeInvoice.Status == "paid")
await AddPayment(network, chargeInvoice, listenedInvoice);
if (chargeInvoice.Status == "paid" || chargeInvoice.Status == "expired")
continue;
}
StartListening(listenedInvoice);
}
}
@ -110,7 +125,7 @@ namespace BTCPayServer.Payments.Lightning
try
{
Logs.PayServer.LogInformation($"{supportedPaymentMethod.CryptoCode} (Lightning): Start listening {supportedPaymentMethod.GetLightningChargeUrl(false)}");
var charge = new ChargeClient(supportedPaymentMethod.GetLightningChargeUrl(true), network.NBitcoinNetwork);
var charge = GetChargeClient(supportedPaymentMethod, network);
var session = await charge.Listen(_Cts.Token);
while (true)
{
@ -124,12 +139,7 @@ namespace BTCPayServer.Payments.Lightning
{
if (notification.Status == "paid" && notification.PaidAt.HasValue)
{
await _InvoiceRepository.AddPayment(listenedInvoice.InvoiceId, notification.PaidAt.Value, new LightningLikePaymentData()
{
BOLT11 = notification.PaymentRequest,
Amount = notification.MilliSatoshi
}, network.CryptoCode, accounted: true);
_Aggregator.Publish(new InvoiceEvent(listenedInvoice.InvoiceId, 1002, "invoice_receivedPayment"));
await AddPayment(network, notification, listenedInvoice);
if (DoneListening(listenedInvoice))
break;
}
@ -151,6 +161,21 @@ namespace BTCPayServer.Payments.Lightning
Logs.PayServer.LogInformation($"{supportedPaymentMethod.CryptoCode} (Lightning): Stop listening {supportedPaymentMethod.GetLightningChargeUrl(false)}");
}
private async Task AddPayment(BTCPayNetwork network, ChargeInvoice notification, ListenedInvoice listenedInvoice)
{
await _InvoiceRepository.AddPayment(listenedInvoice.InvoiceId, notification.PaidAt.Value, new LightningLikePaymentData()
{
BOLT11 = notification.PaymentRequest,
Amount = notification.MilliSatoshi
}, network.CryptoCode, accounted: true);
_Aggregator.Publish(new InvoiceEvent(listenedInvoice.InvoiceId, 1002, "invoice_receivedPayment"));
}
private static ChargeClient GetChargeClient(LightningSupportedPaymentMethod supportedPaymentMethod, BTCPayNetwork network)
{
return new ChargeClient(supportedPaymentMethod.GetLightningChargeUrl(true), network.NBitcoinNetwork);
}
List<Task> _ListeningLightning = new List<Task>();
MultiValueDictionary<string, ListenedInvoice> _ListenedInvoiceByLightningUrl = new MultiValueDictionary<string, ListenedInvoice>();
Dictionary<string, ListenedInvoice> _ListenedInvoiceByChargeInvoiceId = new Dictionary<string, ListenedInvoice>();