mirror of
https://github.com/btcpayserver/btcpayserver.git
synced 2025-02-22 14:22:40 +01:00
Merge pull request #3289 from NicolasDorier/oiufew
Do not send email twice for bitpay API, log whether IPN is an ExtendedNotification. (Fix #968)
This commit is contained in:
commit
96259dfb49
2 changed files with 31 additions and 74 deletions
|
@ -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}";
|
||||
|
|
|
@ -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,10 +148,10 @@ 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
|
||||
{
|
||||
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<InvoiceIPNEvent>(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))
|
||||
{
|
||||
|
||||
using CancellationTokenSource cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
|
||||
cts.CancelAfter(TimeSpan.FromMinutes(1.0));
|
||||
return await _Client.SendAsync(request, cts.Token);
|
||||
}
|
||||
});
|
||||
var response = await _Client.SendAsync(request, cts.Token);
|
||||
return response;
|
||||
}
|
||||
|
||||
readonly Dictionary<string, Task> _SendingRequestsByInvoiceId = new Dictionary<string, Task>();
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Will make sure only one callback is called at once on the same invoiceId
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <param name="sendRequest"></param>
|
||||
/// <returns></returns>
|
||||
private async Task<T> Enqueue<T>(string id, Func<Task<T>> sendRequest)
|
||||
{
|
||||
Task<T> sending = null;
|
||||
lock (_SendingRequestsByInvoiceId)
|
||||
{
|
||||
if (_SendingRequestsByInvoiceId.TryGetValue(id, out var executing))
|
||||
{
|
||||
var completion = new TaskCompletionSource<T>();
|
||||
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();
|
||||
|
||||
|
@ -316,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)
|
||||
{
|
||||
|
@ -328,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;
|
||||
|
|
Loading…
Add table
Reference in a new issue