From fbfab88257b53943d2c0ddeb697e0743b52b8d75 Mon Sep 17 00:00:00 2001 From: "nicolas.dorier" Date: Wed, 12 Jan 2022 00:10:55 +0900 Subject: [PATCH 1/2] Remove obsolete code in BitpayIPNSender --- .../HostedServices/BitpayIPNSender.cs | 70 ++----------------- 1 file changed, 5 insertions(+), 65 deletions(-) diff --git a/BTCPayServer/HostedServices/BitpayIPNSender.cs b/BTCPayServer/HostedServices/BitpayIPNSender.cs index 38e8f3c6b..5a980ca6d 100644 --- a/BTCPayServer/HostedServices/BitpayIPNSender.cs +++ b/BTCPayServer/HostedServices/BitpayIPNSender.cs @@ -151,7 +151,7 @@ namespace BTCPayServer.HostedServices var aggregatorEvent = new InvoiceIPNEvent(job.Notification.Data.Id, job.Notification.Event.Code, job.Notification.Event.Name); try { - HttpResponseMessage response = await SendNotification(job.Notification, cancellationToken); + using HttpResponseMessage response = await SendNotification(job.Notification, cancellationToken); reschedule = !response.IsSuccessStatusCode; aggregatorEvent.Error = reschedule ? $"Unexpected return code: {(int)response.StatusCode}" : null; _EventAggregator.Publish(aggregatorEvent); @@ -232,75 +232,15 @@ namespace BTCPayServer.HostedServices request.RequestUri = new Uri(notification.NotificationURL, UriKind.Absolute); request.Content = new StringContent(notificationString, UTF8, "application/json"); - var response = await Enqueue(notification.Data.Id, async () => - { - using (CancellationTokenSource cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken)) - { - cts.CancelAfter(TimeSpan.FromMinutes(1.0)); - return await _Client.SendAsync(request, cts.Token); - } - }); + + using CancellationTokenSource cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); + cts.CancelAfter(TimeSpan.FromMinutes(1.0)); + var response = await _Client.SendAsync(request, cts.Token); return response; } readonly Dictionary _SendingRequestsByInvoiceId = new Dictionary(); - - /// - /// Will make sure only one callback is called at once on the same invoiceId - /// - /// - /// - /// - private async Task Enqueue(string id, Func> sendRequest) - { - Task sending = null; - lock (_SendingRequestsByInvoiceId) - { - if (_SendingRequestsByInvoiceId.TryGetValue(id, out var executing)) - { - var completion = new TaskCompletionSource(); - sending = completion.Task; - _SendingRequestsByInvoiceId.Remove(id); - _SendingRequestsByInvoiceId.Add(id, sending); - executing.ContinueWith(_ => - { - sendRequest() - .ContinueWith(t => - { - if (t.Status == TaskStatus.RanToCompletion) - { - completion.TrySetResult(t.Result); - } - if (t.Status == TaskStatus.Faulted) - { - completion.TrySetException(t.Exception); - } - if (t.Status == TaskStatus.Canceled) - { - completion.TrySetCanceled(); - } - }, TaskScheduler.Default); - }, TaskScheduler.Default); - } - else - { - sending = sendRequest(); - _SendingRequestsByInvoiceId.Add(id, sending); - } - sending.ContinueWith(o => - { - lock (_SendingRequestsByInvoiceId) - { - _SendingRequestsByInvoiceId.TryGetValue(id, out var executing2); - if (executing2 == sending) - _SendingRequestsByInvoiceId.Remove(id); - } - }, TaskScheduler.Default); - } - return await sending; - } - readonly int MaxTry = 6; readonly CompositeDisposable leases = new CompositeDisposable(); From e4d9e3e22e2ce93c7a4f896693a5c0a6ec46103f Mon Sep 17 00:00:00 2001 From: "nicolas.dorier" Date: Wed, 12 Jan 2022 00:26:12 +0900 Subject: [PATCH 2/2] Do not send email twice for bitpay API, log whether IPN is an ExtendedNotification. (Fix #968) --- BTCPayServer/Events/InvoiceIPNEvent.cs | 17 ++++++++++++++--- BTCPayServer/HostedServices/BitpayIPNSender.cs | 18 ++++++++++++------ 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/BTCPayServer/Events/InvoiceIPNEvent.cs b/BTCPayServer/Events/InvoiceIPNEvent.cs index b4423586a..2f30bc7d8 100644 --- a/BTCPayServer/Events/InvoiceIPNEvent.cs +++ b/BTCPayServer/Events/InvoiceIPNEvent.cs @@ -2,16 +2,17 @@ namespace BTCPayServer.Events { public class InvoiceIPNEvent : IHasInvoiceId { - public InvoiceIPNEvent(string invoiceId, int? eventCode, string name) + public InvoiceIPNEvent(string invoiceId, int? eventCode, string name, bool extendedNotification) { InvoiceId = invoiceId; EventCode = eventCode; Name = name; + ExtendedNotification = extendedNotification; } public int? EventCode { get; set; } public string Name { get; set; } - + public bool ExtendedNotification { get; } public string InvoiceId { get; set; } public string Error { get; set; } @@ -20,7 +21,17 @@ namespace BTCPayServer.Events string ipnType = "IPN"; if (EventCode.HasValue) { - ipnType = $"IPN ({EventCode.Value} {Name})"; + string suffix = string.Empty; + if (ExtendedNotification) + suffix = " ExtendedNotification"; + ipnType = $"IPN ({EventCode.Value} {Name}{suffix})"; + } + else + { + string suffix = string.Empty; + if (ExtendedNotification) + suffix = " (ExtendedNotification)"; + ipnType += suffix; } if (Error == null) return $"{ipnType} sent for invoice {InvoiceId}"; diff --git a/BTCPayServer/HostedServices/BitpayIPNSender.cs b/BTCPayServer/HostedServices/BitpayIPNSender.cs index 5a980ca6d..0850b860c 100644 --- a/BTCPayServer/HostedServices/BitpayIPNSender.cs +++ b/BTCPayServer/HostedServices/BitpayIPNSender.cs @@ -62,7 +62,7 @@ namespace BTCPayServer.HostedServices _StoreRepository = storeRepository; } - async Task Notify(InvoiceEntity invoice, InvoiceEvent invoiceEvent, bool extendedNotification) + async Task Notify(InvoiceEntity invoice, InvoiceEvent invoiceEvent, bool extendedNotification, bool sendMail) { var dto = invoice.EntityToDTO(); var notification = new InvoicePaymentNotificationEventWrapper() @@ -122,7 +122,7 @@ namespace BTCPayServer.HostedServices #pragma warning restore CS0618 } - if ((invoice.ExtendedNotifications || invoiceEvent.Name != InvoiceEvent.Expired) && !String.IsNullOrEmpty(invoice.NotificationEmail)) + if (sendMail && !String.IsNullOrEmpty(invoice.NotificationEmail)) { var json = NBitcoin.JsonConverters.Serializer.ToString(notification); var store = await _StoreRepository.FindStore(invoice.StoreId); @@ -148,7 +148,7 @@ namespace BTCPayServer.HostedServices public async Task NotifyHttp(ScheduledJob job, CancellationToken cancellationToken) { bool reschedule = false; - var aggregatorEvent = new InvoiceIPNEvent(job.Notification.Data.Id, job.Notification.Event.Code, job.Notification.Event.Name); + var aggregatorEvent = new InvoiceIPNEvent(job.Notification.Data.Id, job.Notification.Event.Code, job.Notification.Event.Name, job.Notification.ExtendedNotification); try { using HttpResponseMessage response = await SendNotification(job.Notification, cancellationToken); @@ -256,6 +256,7 @@ namespace BTCPayServer.HostedServices var invoice = await _InvoiceRepository.GetInvoice(e.Invoice.Id); if (invoice == null) return; + bool sendMail = true; // we need to use the status in the event and not in the invoice. The invoice might now be in another status. if (invoice.FullNotifications) { @@ -268,17 +269,22 @@ namespace BTCPayServer.HostedServices e.Name == InvoiceEvent.Completed || e.Name == InvoiceEvent.ExpiredPaidPartial ) - _ = Notify(invoice, e, false); + { + _ = Notify(invoice, e, false, sendMail); + sendMail = false; + } } if (e.Name == InvoiceEvent.Confirmed) { - _ = Notify(invoice, e, false); + _ = Notify(invoice, e, false, sendMail); + sendMail = false; } if (invoice.ExtendedNotifications) { - _ = Notify(invoice, e, true); + _ = Notify(invoice, e, true, sendMail); + sendMail = false; } })); return Task.CompletedTask;