btcpayserver/BTCPayServer/PaymentRequest/PaymentRequestHub.cs

208 lines
8.3 KiB
C#
Raw Normal View History

2019-01-14 22:43:29 +01:00
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using BTCPayServer.Controllers;
2020-06-28 10:55:27 +02:00
using BTCPayServer.Data;
2019-01-14 22:43:29 +01:00
using BTCPayServer.Events;
using BTCPayServer.HostedServices;
using BTCPayServer.Logging;
using BTCPayServer.Services.PaymentRequests;
2020-06-28 10:55:27 +02:00
using Microsoft.AspNetCore.Builder;
2019-03-09 08:08:31 +01:00
using Microsoft.AspNetCore.Http;
2020-06-28 10:55:27 +02:00
using Microsoft.AspNetCore.Mvc;
2019-10-03 11:46:09 +02:00
using Microsoft.AspNetCore.Routing;
2020-06-28 10:55:27 +02:00
using Microsoft.AspNetCore.SignalR;
using Microsoft.Extensions.Logging;
using PaymentRequestData = BTCPayServer.Client.Models.PaymentRequestData;
2019-01-14 22:43:29 +01:00
namespace BTCPayServer.PaymentRequest
{
public class PaymentRequestHub : Hub
{
private readonly PaymentRequestController _PaymentRequestController;
public const string InvoiceCreated = "InvoiceCreated";
public const string PaymentReceived = "PaymentReceived";
public const string InfoUpdated = "InfoUpdated";
public const string InvoiceError = "InvoiceError";
public const string CancelInvoiceError = "CancelInvoiceError";
public const string InvoiceCancelled = "InvoiceCancelled";
2019-01-14 22:43:29 +01:00
public PaymentRequestHub(PaymentRequestController paymentRequestController)
{
_PaymentRequestController = paymentRequestController;
}
public async Task ListenToPaymentRequest(string paymentRequestId)
{
if (Context.Items.ContainsKey("pr-id"))
{
await Groups.RemoveFromGroupAsync(Context.ConnectionId, Context.Items["pr-id"].ToString());
Context.Items.Remove("pr-id");
}
Context.Items.Add("pr-id", paymentRequestId);
await Groups.AddToGroupAsync(Context.ConnectionId, paymentRequestId);
}
public async Task Pay(decimal? amount = null)
{
_PaymentRequestController.ControllerContext.HttpContext = Context.GetHttpContext();
2019-02-22 11:37:45 +01:00
var result =
await _PaymentRequestController.PayPaymentRequest(Context.Items["pr-id"].ToString(), false, amount);
2019-01-14 22:43:29 +01:00
switch (result)
{
case OkObjectResult okObjectResult:
2020-06-28 10:55:27 +02:00
await Clients.Caller.SendCoreAsync(InvoiceCreated, new[] { okObjectResult.Value.ToString() });
2019-01-14 22:43:29 +01:00
break;
case ObjectResult objectResult:
2020-06-28 10:55:27 +02:00
await Clients.Caller.SendCoreAsync(InvoiceError, new[] { objectResult.Value });
2019-01-14 22:43:29 +01:00
break;
default:
await Clients.Caller.SendCoreAsync(InvoiceError, System.Array.Empty<object>());
break;
}
}
public async Task CancelUnpaidPendingInvoice()
{
_PaymentRequestController.ControllerContext.HttpContext = Context.GetHttpContext();
var result =
await _PaymentRequestController.CancelUnpaidPendingInvoice(Context.Items["pr-id"].ToString(), false);
switch (result)
{
case OkObjectResult okObjectResult:
await Clients.Group(Context.Items["pr-id"].ToString()).SendCoreAsync(InvoiceCancelled, System.Array.Empty<object>());
break;
2020-06-28 10:55:27 +02:00
default:
await Clients.Caller.SendCoreAsync(CancelInvoiceError, System.Array.Empty<object>());
break;
}
2019-01-14 22:43:29 +01:00
}
2019-03-09 08:08:31 +01:00
public static string GetHubPath(HttpRequest request)
{
return request.GetRelativePathOrAbsolute("/payment-requests/hub");
}
2019-10-03 11:46:09 +02:00
public static void Register(IEndpointRouteBuilder route)
2019-03-09 08:08:31 +01:00
{
route.MapHub<PaymentRequestHub>("/payment-requests/hub");
}
2019-01-14 22:43:29 +01:00
}
2019-02-22 11:37:45 +01:00
public class PaymentRequestStreamer : EventHostedServiceBase
2019-01-14 22:43:29 +01:00
{
private readonly IHubContext<PaymentRequestHub> _HubContext;
private readonly PaymentRequestRepository _PaymentRequestRepository;
private readonly PaymentRequestService _PaymentRequestService;
2019-02-22 11:37:45 +01:00
2019-01-14 22:43:29 +01:00
public PaymentRequestStreamer(EventAggregator eventAggregator,
IHubContext<PaymentRequestHub> hubContext,
PaymentRequestRepository paymentRequestRepository,
PaymentRequestService paymentRequestService) : base(eventAggregator)
{
_HubContext = hubContext;
_PaymentRequestRepository = paymentRequestRepository;
_PaymentRequestService = paymentRequestService;
}
public override async Task StartAsync(CancellationToken cancellationToken)
{
await base.StartAsync(cancellationToken);
2019-02-22 11:37:45 +01:00
_CheckingPendingPayments = CheckingPendingPayments(cancellationToken)
.ContinueWith(_ => _CheckingPendingPayments = null, TaskScheduler.Default);
2019-01-14 22:43:29 +01:00
}
private async Task CheckingPendingPayments(CancellationToken cancellationToken)
{
Logs.PayServer.LogInformation("Starting payment request expiration watcher");
var (total, items) = await _PaymentRequestRepository.FindPaymentRequests(new PaymentRequestQuery()
{
2020-06-28 10:55:27 +02:00
Status = new[] { Client.Models.PaymentRequestData.PaymentRequestStatus.Pending }
2019-01-14 22:43:29 +01:00
}, cancellationToken);
Logs.PayServer.LogInformation($"{total} pending payment requests being checked since last run");
2019-02-22 11:37:45 +01:00
await Task.WhenAll(items.Select(i => _PaymentRequestService.UpdatePaymentRequestStateIfNeeded(i))
.ToArray());
2019-01-14 22:43:29 +01:00
}
Task _CheckingPendingPayments;
2019-02-22 11:37:45 +01:00
2019-01-14 22:43:29 +01:00
public override async Task StopAsync(CancellationToken cancellationToken)
{
await base.StopAsync(cancellationToken);
await (_CheckingPendingPayments ?? Task.CompletedTask);
}
2019-02-22 11:37:45 +01:00
protected override void SubscribeToEvents()
2019-01-14 22:43:29 +01:00
{
Subscribe<InvoiceEvent>();
Subscribe<PaymentRequestUpdated>();
}
protected override async Task ProcessEvent(object evt, CancellationToken cancellationToken)
{
if (evt is InvoiceEvent invoiceEvent)
{
foreach (var paymentId in PaymentRequestRepository.GetPaymentIdsFromInternalTags(invoiceEvent.Invoice))
2019-01-14 22:43:29 +01:00
{
if (invoiceEvent.Name == InvoiceEvent.ReceivedPayment)
{
await _PaymentRequestService.UpdatePaymentRequestStateIfNeeded(paymentId);
var data = invoiceEvent.Payment.GetCryptoPaymentData();
await _HubContext.Clients.Group(paymentId).SendCoreAsync(PaymentRequestHub.PaymentReceived,
new object[]
{
2019-01-14 22:43:29 +01:00
data.GetValue(),
invoiceEvent.Payment.GetCryptoCode(),
invoiceEvent.Payment.GetPaymentMethodId()?.PaymentType?.ToString()
});
}
2019-02-22 11:37:45 +01:00
await InfoUpdated(paymentId);
}
2019-01-14 22:43:29 +01:00
}
else if (evt is PaymentRequestUpdated updated)
{
2019-02-22 11:37:45 +01:00
await InfoUpdated(updated.PaymentRequestId);
2019-01-14 22:43:29 +01:00
var expiry = updated.Data.GetBlob().ExpiryDate;
2020-06-28 10:55:27 +02:00
if (updated.Data.Status ==
PaymentRequestData.PaymentRequestStatus.Pending &&
2019-01-14 22:43:29 +01:00
expiry.HasValue)
{
QueueExpiryTask(
updated.PaymentRequestId,
expiry.Value,
cancellationToken);
}
}
}
private void QueueExpiryTask(string paymentRequestId, DateTime expiry, CancellationToken cancellationToken)
{
Task.Run(async () =>
{
var delay = expiry - DateTime.Now;
if (delay > TimeSpan.Zero)
await Task.Delay(delay, cancellationToken);
await _PaymentRequestService.UpdatePaymentRequestStateIfNeeded(paymentRequestId);
}, cancellationToken);
}
private async Task InfoUpdated(string paymentRequestId)
{
var req = await _PaymentRequestService.GetPaymentRequest(paymentRequestId);
2019-02-22 11:37:45 +01:00
if (req != null)
2019-01-14 22:43:29 +01:00
{
await _HubContext.Clients.Group(paymentRequestId)
2020-06-28 10:55:27 +02:00
.SendCoreAsync(PaymentRequestHub.InfoUpdated, new object[] { req });
2019-01-14 22:43:29 +01:00
}
}
}
}