private async Task <HttpResponseMessage> SendNotification(InvoicePaymentNotificationEventWrapper notification, CancellationToken cancellationToken) { var request = new HttpRequestMessage(); request.Method = HttpMethod.Post; var notificationString = NBitcoin.JsonConverters.Serializer.ToString(notification); var jobj = JObject.Parse(notificationString); if (notification.ExtendedNotification) { jobj.Remove("extendedNotification"); jobj.Remove("notificationURL"); notificationString = jobj.ToString(); } else { notificationString = jobj["data"].ToString(); } request.RequestUri = new Uri(notification.NotificationURL, UriKind.Absolute); request.Content = new StringContent(notificationString, UTF8, "application/json"); using CancellationTokenSource cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); cts.CancelAfter(TimeSpan.FromMinutes(1.0)); var response = await _Client.SendAsync(request, cts.Token); return(response); }
private async Task <HttpResponseMessage> SendNotification(InvoicePaymentNotificationEventWrapper notification, CancellationToken cancellation) { var request = new HttpRequestMessage(); request.Method = HttpMethod.Post; var notificationString = NBitcoin.JsonConverters.Serializer.ToString(notification); var jobj = JObject.Parse(notificationString); if (notification.ExtendedNotification) { jobj.Remove("extendedNotification"); jobj.Remove("notificationURL"); notificationString = jobj.ToString(); } else { notificationString = jobj["data"].ToString(); } request.RequestUri = new Uri(notification.NotificationURL, UriKind.Absolute); request.Content = new StringContent(notificationString, UTF8, "application/json"); var response = await Enqueue(notification.Data.Id, async() => await _Client.SendAsync(request, cancellation)); return(response); }
private async Task <HttpResponseMessage> SendNotification(InvoiceEntity invoice, int?eventCode, string name, CancellationToken cancellation) { var request = new HttpRequestMessage(); request.Method = HttpMethod.Post; var dto = invoice.EntityToDTO(_NetworkProvider); InvoicePaymentNotification notification = new InvoicePaymentNotification() { Id = dto.Id, Currency = dto.Currency, CurrentTime = dto.CurrentTime, ExceptionStatus = dto.ExceptionStatus, ExpirationTime = dto.ExpirationTime, InvoiceTime = dto.InvoiceTime, PosData = dto.PosData, Price = dto.Price, Status = dto.Status, BuyerFields = invoice.RefundMail == null ? null : new Newtonsoft.Json.Linq.JObject() { new JProperty("buyerEmail", invoice.RefundMail) }, PaymentSubtotals = dto.PaymentSubtotals, PaymentTotals = dto.PaymentTotals, AmountPaid = dto.AmountPaid, ExchangeRates = dto.ExchangeRates }; // We keep backward compatibility with bitpay by passing BTC info to the notification // we don't pass other info, as it is a bad idea to use IPN data for logic processing (can be faked) var btcCryptoInfo = dto.CryptoInfo.FirstOrDefault(c => c.GetpaymentMethodId() == new PaymentMethodId("BTC", Payments.PaymentTypes.BTCLike)); if (btcCryptoInfo != null) { #pragma warning disable CS0618 notification.Rate = dto.Rate; notification.Url = dto.Url; notification.BTCDue = dto.BTCDue; notification.BTCPaid = dto.BTCPaid; notification.BTCPrice = dto.BTCPrice; #pragma warning restore CS0618 } string notificationString = null; if (eventCode.HasValue) { var wrapper = new InvoicePaymentNotificationEventWrapper(); wrapper.Data = notification; wrapper.Event = new InvoicePaymentNotificationEvent() { Code = eventCode.Value, Name = name }; notificationString = JsonConvert.SerializeObject(wrapper); } else { notificationString = JsonConvert.SerializeObject(notification); } request.RequestUri = new Uri(invoice.NotificationURL, UriKind.Absolute); request.Content = new StringContent(notificationString, UTF8, "application/json"); var response = await Enqueue(invoice.Id, async() => await _Client.SendAsync(request, cancellation)); return(response); }
async Task Notify(InvoiceEntity invoice, InvoiceEvent invoiceEvent, bool extendedNotification, bool sendMail) { var dto = invoice.EntityToDTO(); var notification = new InvoicePaymentNotificationEventWrapper() { Data = new InvoicePaymentNotification() { Id = dto.Id, Currency = dto.Currency, CurrentTime = dto.CurrentTime, ExceptionStatus = dto.ExceptionStatus, ExpirationTime = dto.ExpirationTime, InvoiceTime = dto.InvoiceTime, PosData = dto.PosData, Price = dto.Price, Status = dto.Status, BuyerFields = invoice.RefundMail == null ? null : new Newtonsoft.Json.Linq.JObject() { new JProperty("buyerEmail", invoice.RefundMail) }, PaymentSubtotals = dto.PaymentSubtotals, PaymentTotals = dto.PaymentTotals, AmountPaid = dto.AmountPaid, ExchangeRates = dto.ExchangeRates, OrderId = dto.OrderId }, Event = new InvoicePaymentNotificationEvent() { Code = (int)invoiceEvent.EventCode, Name = invoiceEvent.Name }, ExtendedNotification = extendedNotification, NotificationURL = invoice.NotificationURL?.AbsoluteUri }; // For lightning network payments, paid, confirmed and completed come all at once. // So despite the event is "paid" or "confirmed" the Status of the invoice is technically complete // This confuse loggers who think their endpoint get duplicated events // So here, we just override the status expressed by the notification if (invoiceEvent.Name == InvoiceEvent.Confirmed) { notification.Data.Status = InvoiceState.ToString(InvoiceStatusLegacy.Confirmed); } if (invoiceEvent.Name == InvoiceEvent.PaidInFull) { notification.Data.Status = InvoiceState.ToString(InvoiceStatusLegacy.Paid); } ////////////////// // We keep backward compatibility with bitpay by passing BTC info to the notification // we don't pass other info, as it is a bad idea to use IPN data for logic processing (can be faked) var btcCryptoInfo = dto.CryptoInfo.FirstOrDefault(c => c.GetpaymentMethodId() == new PaymentMethodId("BTC", Payments.PaymentTypes.BTCLike) && !string.IsNullOrEmpty(c.Address)); if (btcCryptoInfo != null) { #pragma warning disable CS0618 notification.Data.Rate = dto.Rate; notification.Data.Url = dto.Url; notification.Data.BTCDue = dto.BTCDue; notification.Data.BTCPaid = dto.BTCPaid; notification.Data.BTCPrice = dto.BTCPrice; #pragma warning restore CS0618 } if (sendMail && !String.IsNullOrEmpty(invoice.NotificationEmail)) { var json = NBitcoin.JsonConverters.Serializer.ToString(notification); var store = await _StoreRepository.FindStore(invoice.StoreId); var storeName = store.StoreName ?? "BTCPay Server"; var emailBody = $"Store: {storeName}<br>" + $"Invoice ID: {notification.Data.Id}<br>" + $"Status: {notification.Data.Status}<br>" + $"Amount: {notification.Data.Price} {notification.Data.Currency}<br>" + $"<br><details><summary>Details</summary><pre>{json}</pre></details>"; (await _EmailSenderFactory.GetEmailSender(invoice.StoreId)).SendEmail( invoice.NotificationEmail, $"{storeName} Invoice Notification - ${invoice.StoreId}", emailBody); } if (invoice.NotificationURL != null) { _Queue.Enqueue(invoice.Id, (cancellationToken) => NotifyHttp(new ScheduledJob() { TryCount = 0, Notification = notification }, cancellationToken)); } }
void Notify(InvoiceEntity invoice, InvoiceEvent invoiceEvent, bool extendedNotification) { var dto = invoice.EntityToDTO(_NetworkProvider); var notification = new InvoicePaymentNotificationEventWrapper() { Data = new InvoicePaymentNotification() { Id = dto.Id, Currency = dto.Currency, CurrentTime = dto.CurrentTime, ExceptionStatus = dto.ExceptionStatus, ExpirationTime = dto.ExpirationTime, InvoiceTime = dto.InvoiceTime, PosData = dto.PosData, Price = dto.Price, Status = dto.Status, BuyerFields = invoice.RefundMail == null ? null : new Newtonsoft.Json.Linq.JObject() { new JProperty("buyerEmail", invoice.RefundMail) }, PaymentSubtotals = dto.PaymentSubtotals, PaymentTotals = dto.PaymentTotals, AmountPaid = dto.AmountPaid, ExchangeRates = dto.ExchangeRates, }, Event = new InvoicePaymentNotificationEvent() { Code = invoiceEvent.EventCode, Name = invoiceEvent.Name }, ExtendedNotification = extendedNotification, NotificationURL = invoice.NotificationURL }; // For lightning network payments, paid, confirmed and completed come all at once. // So despite the event is "paid" or "confirmed" the Status of the invoice is technically complete // This confuse loggers who think their endpoint get duplicated events // So here, we just override the status expressed by the notification if (invoiceEvent.Name == InvoiceEvent.Confirmed) { notification.Data.Status = InvoiceState.ToString(InvoiceStatus.Confirmed); } if (invoiceEvent.Name == InvoiceEvent.PaidInFull) { notification.Data.Status = InvoiceState.ToString(InvoiceStatus.Paid); } ////////////////// // We keep backward compatibility with bitpay by passing BTC info to the notification // we don't pass other info, as it is a bad idea to use IPN data for logic processing (can be faked) var btcCryptoInfo = dto.CryptoInfo.FirstOrDefault(c => c.GetpaymentMethodId() == new PaymentMethodId("BTC", Payments.PaymentTypes.BTCLike)); if (btcCryptoInfo != null) { #pragma warning disable CS0618 notification.Data.Rate = dto.Rate; notification.Data.Url = dto.Url; notification.Data.BTCDue = dto.BTCDue; notification.Data.BTCPaid = dto.BTCPaid; notification.Data.BTCPrice = dto.BTCPrice; #pragma warning restore CS0618 } CancellationTokenSource cts = new CancellationTokenSource(10000); if (!String.IsNullOrEmpty(invoice.NotificationEmail)) { var emailBody = NBitcoin.JsonConverters.Serializer.ToString(notification); _EmailSenderFactory.GetEmailSender(invoice.StoreId).SendEmail( invoice.NotificationEmail, $"BtcPayServer Invoice Notification - ${invoice.StoreId}", emailBody); } if (string.IsNullOrEmpty(invoice.NotificationURL) || !Uri.IsWellFormedUriString(invoice.NotificationURL, UriKind.Absolute)) { return; } var invoiceStr = NBitcoin.JsonConverters.Serializer.ToString(new ScheduledJob() { TryCount = 0, Notification = notification }); if (!string.IsNullOrEmpty(invoice.NotificationURL)) { _JobClient.Schedule(() => NotifyHttp(invoiceStr), TimeSpan.Zero); } }