mirror of
https://github.com/btcpayserver/btcpayserver.git
synced 2024-11-19 01:43:50 +01:00
Payment Settled Webhook event (#2944)
* Payment Settled Webhook event resolves #2691 * Move payment methods to payment services
This commit is contained in:
parent
143d5f69c1
commit
6e3d6125c2
@ -11,6 +11,7 @@ namespace BTCPayServer.Client.Models
|
||||
InvoiceProcessing,
|
||||
InvoiceExpired,
|
||||
InvoiceSettled,
|
||||
InvoiceInvalid
|
||||
InvoiceInvalid,
|
||||
InvoicePaymentSettled,
|
||||
}
|
||||
}
|
||||
|
@ -10,72 +10,88 @@ namespace BTCPayServer.Client.Models
|
||||
{
|
||||
public WebhookInvoiceEvent()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public WebhookInvoiceEvent(WebhookEventType evtType)
|
||||
{
|
||||
this.Type = evtType;
|
||||
}
|
||||
[JsonProperty(Order = 1)]
|
||||
public string StoreId { get; set; }
|
||||
[JsonProperty(Order = 2)]
|
||||
public string InvoiceId { get; set; }
|
||||
|
||||
[JsonProperty(Order = 1)] public string StoreId { get; set; }
|
||||
[JsonProperty(Order = 2)] public string InvoiceId { get; set; }
|
||||
}
|
||||
|
||||
public class WebhookInvoiceSettledEvent : WebhookInvoiceEvent
|
||||
{
|
||||
public WebhookInvoiceSettledEvent()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public WebhookInvoiceSettledEvent(WebhookEventType evtType) : base(evtType)
|
||||
{
|
||||
}
|
||||
|
||||
public bool ManuallyMarked { get; set; }
|
||||
}
|
||||
|
||||
public class WebhookInvoiceInvalidEvent : WebhookInvoiceEvent
|
||||
{
|
||||
public WebhookInvoiceInvalidEvent()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public WebhookInvoiceInvalidEvent(WebhookEventType evtType) : base(evtType)
|
||||
{
|
||||
}
|
||||
|
||||
public bool ManuallyMarked { get; set; }
|
||||
}
|
||||
|
||||
public class WebhookInvoiceProcessingEvent : WebhookInvoiceEvent
|
||||
{
|
||||
public WebhookInvoiceProcessingEvent()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public WebhookInvoiceProcessingEvent(WebhookEventType evtType) : base(evtType)
|
||||
{
|
||||
}
|
||||
|
||||
public bool OverPaid { get; set; }
|
||||
}
|
||||
|
||||
public class WebhookInvoiceReceivedPaymentEvent : WebhookInvoiceEvent
|
||||
{
|
||||
public WebhookInvoiceReceivedPaymentEvent()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public WebhookInvoiceReceivedPaymentEvent(WebhookEventType evtType) : base(evtType)
|
||||
{
|
||||
}
|
||||
|
||||
public bool AfterExpiration { get; set; }
|
||||
public string PaymentMethod { get; set; }
|
||||
public InvoicePaymentMethodDataModel.Payment Payment { get; set; }
|
||||
}
|
||||
|
||||
public class WebhookInvoicePaymentSettledEvent : WebhookInvoiceReceivedPaymentEvent
|
||||
{
|
||||
public WebhookInvoicePaymentSettledEvent()
|
||||
{
|
||||
}
|
||||
|
||||
public WebhookInvoicePaymentSettledEvent(WebhookEventType evtType) : base(evtType)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public class WebhookInvoiceExpiredEvent : WebhookInvoiceEvent
|
||||
{
|
||||
public WebhookInvoiceExpiredEvent()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public WebhookInvoiceExpiredEvent(WebhookEventType evtType) : base(evtType)
|
||||
{
|
||||
}
|
||||
|
@ -3119,6 +3119,20 @@ namespace BTCPayServer.Tests
|
||||
c =>
|
||||
{
|
||||
Assert.False(c.AfterExpiration);
|
||||
Assert.Equal(new PaymentMethodId("BTC", PaymentTypes.BTCLike).ToStringNormalized(),c.PaymentMethod);
|
||||
Assert.NotNull(c.Payment);
|
||||
Assert.Equal(invoice.BitcoinAddress, c.Payment.Destination);
|
||||
Assert.StartsWith(txId.ToString(), c.Payment.Id);
|
||||
|
||||
});
|
||||
user.AssertHasWebhookEvent<WebhookInvoicePaymentSettledEvent>(WebhookEventType.InvoicePaymentSettled,
|
||||
c =>
|
||||
{
|
||||
Assert.False(c.AfterExpiration);
|
||||
Assert.Equal(new PaymentMethodId("BTC", PaymentTypes.BTCLike).ToStringNormalized(),c.PaymentMethod);
|
||||
Assert.NotNull(c.Payment);
|
||||
Assert.Equal(invoice.BitcoinAddress, c.Payment.Destination);
|
||||
Assert.StartsWith(txId.ToString(), c.Payment.Id);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -363,27 +363,28 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
PaymentLink =
|
||||
method.GetId().PaymentType.GetPaymentLink(method.Network, details, accounting.Due,
|
||||
Request.GetAbsoluteRoot()),
|
||||
Payments = payments.Select(paymentEntity =>
|
||||
{
|
||||
var data = paymentEntity.GetCryptoPaymentData();
|
||||
return new InvoicePaymentMethodDataModel.Payment()
|
||||
{
|
||||
Destination = data.GetDestination(),
|
||||
Id = data.GetPaymentId(),
|
||||
Status = !paymentEntity.Accounted
|
||||
? InvoicePaymentMethodDataModel.Payment.PaymentStatus.Invalid
|
||||
: data.PaymentConfirmed(paymentEntity, entity.SpeedPolicy) ||
|
||||
data.PaymentCompleted(paymentEntity)
|
||||
? InvoicePaymentMethodDataModel.Payment.PaymentStatus.Settled
|
||||
: InvoicePaymentMethodDataModel.Payment.PaymentStatus.Processing,
|
||||
Fee = paymentEntity.NetworkFee,
|
||||
Value = data.GetValue(),
|
||||
ReceivedDate = paymentEntity.ReceivedTime.DateTime
|
||||
};
|
||||
}).ToList()
|
||||
Payments = payments.Select(paymentEntity => ToPaymentModel(entity, paymentEntity)).ToList()
|
||||
};
|
||||
}).ToArray();
|
||||
}
|
||||
|
||||
public static InvoicePaymentMethodDataModel.Payment ToPaymentModel(InvoiceEntity entity, PaymentEntity paymentEntity)
|
||||
{
|
||||
var data = paymentEntity.GetCryptoPaymentData();
|
||||
return new InvoicePaymentMethodDataModel.Payment()
|
||||
{
|
||||
Destination = data.GetDestination(),
|
||||
Id = data.GetPaymentId(),
|
||||
Status = !paymentEntity.Accounted
|
||||
? InvoicePaymentMethodDataModel.Payment.PaymentStatus.Invalid
|
||||
: data.PaymentConfirmed(paymentEntity, entity.SpeedPolicy) || data.PaymentCompleted(paymentEntity)
|
||||
? InvoicePaymentMethodDataModel.Payment.PaymentStatus.Settled
|
||||
: InvoicePaymentMethodDataModel.Payment.PaymentStatus.Processing,
|
||||
Fee = paymentEntity.NetworkFee,
|
||||
Value = data.GetValue(),
|
||||
ReceivedDate = paymentEntity.ReceivedTime.DateTime
|
||||
};
|
||||
}
|
||||
private InvoiceData ToModel(InvoiceEntity entity)
|
||||
{
|
||||
return new InvoiceData()
|
||||
|
@ -1,5 +1,4 @@
|
||||
using BTCPayServer.Services.Invoices;
|
||||
using Microsoft.AspNetCore.Authorization.Infrastructure;
|
||||
|
||||
namespace BTCPayServer.Data
|
||||
{
|
||||
|
@ -7,6 +7,7 @@ namespace BTCPayServer.Events
|
||||
{
|
||||
Created = 1001,
|
||||
ReceivedPayment = 1002,
|
||||
PaymentSettled = 1014,
|
||||
PaidInFull = 1003,
|
||||
Expired = 1004,
|
||||
Confirmed = 1005,
|
||||
@ -21,6 +22,7 @@ namespace BTCPayServer.Events
|
||||
{
|
||||
public const string Created = "invoice_created";
|
||||
public const string ReceivedPayment = "invoice_receivedPayment";
|
||||
public const string PaymentSettled = "invoice_paymentSettled";
|
||||
public const string MarkedCompleted = "invoice_markedComplete";
|
||||
public const string MarkedInvalid = "invoice_markedInvalid";
|
||||
public const string Expired = "invoice_expired";
|
||||
@ -36,6 +38,7 @@ namespace BTCPayServer.Events
|
||||
{
|
||||
{Created, InvoiceEventCode.Created},
|
||||
{ReceivedPayment, InvoiceEventCode.ReceivedPayment},
|
||||
{PaymentSettled, InvoiceEventCode.PaymentSettled},
|
||||
{PaidInFull, InvoiceEventCode.PaidInFull},
|
||||
{Expired, InvoiceEventCode.Expired},
|
||||
{Confirmed, InvoiceEventCode.Confirmed},
|
||||
|
@ -311,6 +311,11 @@ namespace BTCPayServer.HostedServices
|
||||
{
|
||||
leases.Add(_EventAggregator.Subscribe<InvoiceEvent>(async e =>
|
||||
{
|
||||
if (e.EventCode == InvoiceEventCode.PaymentSettled)
|
||||
{
|
||||
//these are greenfield specific events
|
||||
return;
|
||||
}
|
||||
var invoice = await _InvoiceRepository.GetInvoice(e.Invoice.Id);
|
||||
if (invoice == null)
|
||||
return;
|
||||
|
@ -52,21 +52,24 @@ namespace BTCPayServer.HostedServices
|
||||
}
|
||||
}
|
||||
|
||||
readonly InvoiceRepository _InvoiceRepository;
|
||||
readonly EventAggregator _EventAggregator;
|
||||
readonly ExplorerClientProvider _ExplorerClientProvider;
|
||||
readonly InvoiceRepository _invoiceRepository;
|
||||
readonly EventAggregator _eventAggregator;
|
||||
readonly ExplorerClientProvider _explorerClientProvider;
|
||||
private readonly NotificationSender _notificationSender;
|
||||
private readonly PaymentService _paymentService;
|
||||
|
||||
public InvoiceWatcher(
|
||||
InvoiceRepository invoiceRepository,
|
||||
EventAggregator eventAggregator,
|
||||
ExplorerClientProvider explorerClientProvider,
|
||||
NotificationSender notificationSender)
|
||||
NotificationSender notificationSender,
|
||||
PaymentService paymentService)
|
||||
{
|
||||
_InvoiceRepository = invoiceRepository ?? throw new ArgumentNullException(nameof(invoiceRepository));
|
||||
_EventAggregator = eventAggregator ?? throw new ArgumentNullException(nameof(eventAggregator));
|
||||
_ExplorerClientProvider = explorerClientProvider;
|
||||
_invoiceRepository = invoiceRepository ?? throw new ArgumentNullException(nameof(invoiceRepository));
|
||||
_eventAggregator = eventAggregator ?? throw new ArgumentNullException(nameof(eventAggregator));
|
||||
_explorerClientProvider = explorerClientProvider;
|
||||
_notificationSender = notificationSender;
|
||||
_paymentService = paymentService;
|
||||
}
|
||||
|
||||
readonly CompositeDisposable leases = new CompositeDisposable();
|
||||
@ -239,7 +242,7 @@ namespace BTCPayServer.HostedServices
|
||||
|
||||
private async Task Wait(string invoiceId)
|
||||
{
|
||||
var invoice = await _InvoiceRepository.GetInvoice(invoiceId);
|
||||
var invoice = await _invoiceRepository.GetInvoice(invoiceId);
|
||||
try
|
||||
{
|
||||
// add 1 second to ensure watch won't trigger moments before invoice expires
|
||||
@ -274,11 +277,11 @@ namespace BTCPayServer.HostedServices
|
||||
_Loop = StartLoop(_Cts.Token);
|
||||
_ = WaitPendingInvoices();
|
||||
|
||||
leases.Add(_EventAggregator.Subscribe<Events.InvoiceNeedUpdateEvent>(b =>
|
||||
leases.Add(_eventAggregator.Subscribe<Events.InvoiceNeedUpdateEvent>(b =>
|
||||
{
|
||||
Watch(b.InvoiceId);
|
||||
}));
|
||||
leases.Add(_EventAggregator.Subscribe<Events.InvoiceEvent>(async b =>
|
||||
leases.Add(_eventAggregator.Subscribe<Events.InvoiceEvent>(async b =>
|
||||
{
|
||||
if (InvoiceEventNotification.HandlesEvent(b.Name))
|
||||
{
|
||||
@ -301,7 +304,7 @@ namespace BTCPayServer.HostedServices
|
||||
|
||||
private async Task WaitPendingInvoices()
|
||||
{
|
||||
await Task.WhenAll((await _InvoiceRepository.GetPendingInvoices())
|
||||
await Task.WhenAll((await _invoiceRepository.GetPendingInvoices())
|
||||
.Select(id => Wait(id)).ToArray());
|
||||
}
|
||||
|
||||
@ -318,28 +321,28 @@ namespace BTCPayServer.HostedServices
|
||||
try
|
||||
{
|
||||
cancellation.ThrowIfCancellationRequested();
|
||||
var invoice = await _InvoiceRepository.GetInvoice(invoiceId, true);
|
||||
var invoice = await _invoiceRepository.GetInvoice(invoiceId, true);
|
||||
if (invoice == null)
|
||||
break;
|
||||
var updateContext = new UpdateInvoiceContext(invoice);
|
||||
UpdateInvoice(updateContext);
|
||||
if (updateContext.Unaffect)
|
||||
{
|
||||
await _InvoiceRepository.UnaffectAddress(invoice.Id);
|
||||
await _invoiceRepository.UnaffectAddress(invoice.Id);
|
||||
}
|
||||
if (updateContext.Dirty)
|
||||
{
|
||||
await _InvoiceRepository.UpdateInvoiceStatus(invoice.Id, invoice.GetInvoiceState());
|
||||
await _invoiceRepository.UpdateInvoiceStatus(invoice.Id, invoice.GetInvoiceState());
|
||||
updateContext.Events.Insert(0, new InvoiceDataChangedEvent(invoice));
|
||||
}
|
||||
if (updateContext.IsBlobUpdated)
|
||||
{
|
||||
await _InvoiceRepository.UpdateInvoicePrice(invoice.Id, invoice);
|
||||
await _invoiceRepository.UpdateInvoicePrice(invoice.Id, invoice);
|
||||
}
|
||||
|
||||
foreach (var evt in updateContext.Events)
|
||||
{
|
||||
_EventAggregator.Publish(evt, evt.GetType());
|
||||
_eventAggregator.Publish(evt, evt.GetType());
|
||||
}
|
||||
|
||||
if (invoice.Status == InvoiceStatusLegacy.Complete ||
|
||||
@ -351,11 +354,11 @@ namespace BTCPayServer.HostedServices
|
||||
// say user used low fee and we only got 3 confirmations right before it's time to remove
|
||||
if (extendInvoiceMonitoring)
|
||||
{
|
||||
await _InvoiceRepository.ExtendInvoiceMonitor(invoice.Id);
|
||||
await _invoiceRepository.ExtendInvoiceMonitor(invoice.Id);
|
||||
}
|
||||
else if (await _InvoiceRepository.RemovePendingInvoice(invoice.Id))
|
||||
else if (await _invoiceRepository.RemovePendingInvoice(invoice.Id))
|
||||
{
|
||||
_EventAggregator.Publish(new InvoiceStopWatchedEvent(invoice.Id));
|
||||
_eventAggregator.Publish(new InvoiceStopWatchedEvent(invoice.Id));
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -389,7 +392,7 @@ namespace BTCPayServer.HostedServices
|
||||
if ((onChainPaymentData.ConfirmationCount < network.MaxTrackedConfirmation && payment.Accounted)
|
||||
&& (onChainPaymentData.Legacy || invoice.MonitoringExpiration < DateTimeOffset.UtcNow))
|
||||
{
|
||||
var transactionResult = await _ExplorerClientProvider.GetExplorerClient(payment.GetCryptoCode())?.GetTransactionAsync(onChainPaymentData.Outpoint.Hash);
|
||||
var transactionResult = await _explorerClientProvider.GetExplorerClient(payment.GetCryptoCode())?.GetTransactionAsync(onChainPaymentData.Outpoint.Hash);
|
||||
var confirmationCount = transactionResult?.Confirmations ?? 0;
|
||||
onChainPaymentData.ConfirmationCount = confirmationCount;
|
||||
payment.SetCryptoPaymentData(onChainPaymentData);
|
||||
@ -408,7 +411,7 @@ namespace BTCPayServer.HostedServices
|
||||
var updatedPaymentData = updateConfirmationCountIfNeeded.Where(a => a.Result != null).Select(a => a.Result).ToList();
|
||||
if (updatedPaymentData.Count > 0)
|
||||
{
|
||||
await _InvoiceRepository.UpdatePayments(updatedPaymentData);
|
||||
await _paymentService.UpdatePayments(updatedPaymentData);
|
||||
}
|
||||
|
||||
return extendInvoiceMonitoring;
|
||||
|
@ -9,6 +9,7 @@ using System.Threading;
|
||||
using System.Threading.Channels;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Client.Models;
|
||||
using BTCPayServer.Controllers.GreenField;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.Events;
|
||||
using BTCPayServer.Logging;
|
||||
@ -183,6 +184,8 @@ namespace BTCPayServer.HostedServices
|
||||
return new WebhookInvoiceEvent(WebhookEventType.InvoiceCreated);
|
||||
case WebhookEventType.InvoiceReceivedPayment:
|
||||
return new WebhookInvoiceReceivedPaymentEvent(WebhookEventType.InvoiceReceivedPayment);
|
||||
case WebhookEventType.InvoicePaymentSettled:
|
||||
return new WebhookInvoicePaymentSettledEvent(WebhookEventType.InvoicePaymentSettled);
|
||||
case WebhookEventType.InvoiceProcessing:
|
||||
return new WebhookInvoiceProcessingEvent(WebhookEventType.InvoiceProcessing);
|
||||
case WebhookEventType.InvoiceExpired:
|
||||
@ -232,7 +235,16 @@ namespace BTCPayServer.HostedServices
|
||||
case InvoiceEventCode.ReceivedPayment:
|
||||
return new WebhookInvoiceReceivedPaymentEvent(WebhookEventType.InvoiceReceivedPayment)
|
||||
{
|
||||
AfterExpiration = invoiceEvent.Invoice.Status.ToModernStatus() == InvoiceStatus.Expired || invoiceEvent.Invoice.Status.ToModernStatus() == InvoiceStatus.Invalid
|
||||
AfterExpiration = invoiceEvent.Invoice.Status.ToModernStatus() == InvoiceStatus.Expired || invoiceEvent.Invoice.Status.ToModernStatus() == InvoiceStatus.Invalid,
|
||||
PaymentMethod = invoiceEvent.Payment.GetPaymentMethodId().ToStringNormalized(),
|
||||
Payment = GreenFieldInvoiceController.ToPaymentModel(invoiceEvent.Invoice, invoiceEvent.Payment)
|
||||
};
|
||||
case InvoiceEventCode.PaymentSettled:
|
||||
return new WebhookInvoiceReceivedPaymentEvent(WebhookEventType.InvoicePaymentSettled)
|
||||
{
|
||||
AfterExpiration = invoiceEvent.Invoice.Status.ToModernStatus() == InvoiceStatus.Expired || invoiceEvent.Invoice.Status.ToModernStatus() == InvoiceStatus.Invalid,
|
||||
PaymentMethod = invoiceEvent.Payment.GetPaymentMethodId().ToStringNormalized(),
|
||||
Payment = GreenFieldInvoiceController.ToPaymentModel(invoiceEvent.Invoice, invoiceEvent.Payment)
|
||||
};
|
||||
default:
|
||||
return null;
|
||||
|
@ -102,11 +102,8 @@ namespace BTCPayServer.Hosting
|
||||
services.AddStartupTask<MigrationStartupTask>();
|
||||
//
|
||||
services.AddStartupTask<BlockExplorerLinkStartupTask>();
|
||||
services.TryAddSingleton<InvoiceRepository>(o =>
|
||||
{
|
||||
var dbContext = o.GetRequiredService<ApplicationDbContextFactory>();
|
||||
return new InvoiceRepository(dbContext, o.GetRequiredService<BTCPayNetworkProvider>(), o.GetService<EventAggregator>());
|
||||
});
|
||||
services.TryAddSingleton<InvoiceRepository>();
|
||||
services.AddSingleton<PaymentService>();
|
||||
services.AddSingleton<BTCPayServerEnvironment>();
|
||||
services.TryAddSingleton<TokenRepository>();
|
||||
services.TryAddSingleton<WalletRepository>();
|
||||
|
@ -29,7 +29,7 @@ namespace BTCPayServer.Payments.Bitcoin
|
||||
readonly EventAggregator _Aggregator;
|
||||
private readonly PayJoinRepository _payJoinRepository;
|
||||
readonly ExplorerClientProvider _ExplorerClients;
|
||||
readonly IHostApplicationLifetime _Lifetime;
|
||||
private readonly PaymentService _paymentService;
|
||||
readonly InvoiceRepository _InvoiceRepository;
|
||||
private TaskCompletionSource<bool> _RunningTask;
|
||||
private CancellationTokenSource _Cts;
|
||||
@ -39,7 +39,7 @@ namespace BTCPayServer.Payments.Bitcoin
|
||||
InvoiceRepository invoiceRepository,
|
||||
EventAggregator aggregator,
|
||||
PayJoinRepository payjoinRepository,
|
||||
IHostApplicationLifetime lifetime)
|
||||
PaymentService paymentService)
|
||||
{
|
||||
PollInterval = TimeSpan.FromMinutes(1.0);
|
||||
_Wallets = wallets;
|
||||
@ -47,7 +47,7 @@ namespace BTCPayServer.Payments.Bitcoin
|
||||
_ExplorerClients = explorerClients;
|
||||
_Aggregator = aggregator;
|
||||
_payJoinRepository = payjoinRepository;
|
||||
_Lifetime = lifetime;
|
||||
_paymentService = paymentService;
|
||||
}
|
||||
|
||||
readonly CompositeDisposable leases = new CompositeDisposable();
|
||||
@ -167,7 +167,7 @@ namespace BTCPayServer.Payments.Bitcoin
|
||||
.GetAllBitcoinPaymentData(false).Any(c => c.GetPaymentId() == paymentData.GetPaymentId());
|
||||
if (!alreadyExist)
|
||||
{
|
||||
var payment = await _InvoiceRepository.AddPayment(invoice.Id,
|
||||
var payment = await _paymentService.AddPayment(invoice.Id,
|
||||
DateTimeOffset.UtcNow, paymentData, network);
|
||||
if (payment != null)
|
||||
await ReceivedPayment(wallet, invoice, payment,
|
||||
@ -341,7 +341,7 @@ namespace BTCPayServer.Payments.Bitcoin
|
||||
await _payJoinRepository.TryUnlock(payjoinInformation.ContributedOutPoints);
|
||||
}
|
||||
|
||||
await _InvoiceRepository.UpdatePayments(updatedPaymentEntities);
|
||||
await _paymentService.UpdatePayments(updatedPaymentEntities);
|
||||
if (updatedPaymentEntities.Count != 0)
|
||||
_Aggregator.Publish(new Events.InvoiceNeedUpdateEvent(invoice.Id));
|
||||
return invoice;
|
||||
@ -383,7 +383,7 @@ namespace BTCPayServer.Payments.Bitcoin
|
||||
var paymentData = new BitcoinLikePaymentData(address, coin.Value, coin.OutPoint,
|
||||
transaction?.Transaction is null ? true : transaction.Transaction.RBF, coin.KeyPath);
|
||||
|
||||
var payment = await _InvoiceRepository.AddPayment(invoice.Id, coin.Timestamp, paymentData, network).ConfigureAwait(false);
|
||||
var payment = await _paymentService.AddPayment(invoice.Id, coin.Timestamp, paymentData, network).ConfigureAwait(false);
|
||||
alreadyAccounted.Add(coin.OutPoint);
|
||||
if (payment != null)
|
||||
{
|
||||
|
@ -31,6 +31,7 @@ namespace BTCPayServer.Payments.Lightning
|
||||
private readonly LightningClientFactoryService lightningClientFactory;
|
||||
private readonly LightningLikePaymentHandler _lightningLikePaymentHandler;
|
||||
private readonly StoreRepository _storeRepository;
|
||||
private readonly PaymentService _paymentService;
|
||||
readonly Channel<string> _CheckInvoices = Channel.CreateUnbounded<string>();
|
||||
Task _CheckingInvoice;
|
||||
readonly Dictionary<(string, string), LightningInstanceListener> _InstanceListeners = new Dictionary<(string, string), LightningInstanceListener>();
|
||||
@ -42,7 +43,8 @@ namespace BTCPayServer.Payments.Lightning
|
||||
LightningClientFactoryService lightningClientFactory,
|
||||
LightningLikePaymentHandler lightningLikePaymentHandler,
|
||||
StoreRepository storeRepository,
|
||||
IOptions<LightningNetworkOptions> options)
|
||||
IOptions<LightningNetworkOptions> options,
|
||||
PaymentService paymentService)
|
||||
{
|
||||
_Aggregator = aggregator;
|
||||
_InvoiceRepository = invoiceRepository;
|
||||
@ -51,6 +53,7 @@ namespace BTCPayServer.Payments.Lightning
|
||||
this.lightningClientFactory = lightningClientFactory;
|
||||
_lightningLikePaymentHandler = lightningLikePaymentHandler;
|
||||
_storeRepository = storeRepository;
|
||||
_paymentService = paymentService;
|
||||
Options = options;
|
||||
}
|
||||
|
||||
@ -67,7 +70,7 @@ namespace BTCPayServer.Payments.Lightning
|
||||
if (!_InstanceListeners.TryGetValue(instanceListenerKey, out var instanceListener) ||
|
||||
!instanceListener.IsListening)
|
||||
{
|
||||
instanceListener ??= new LightningInstanceListener(_InvoiceRepository, _Aggregator, lightningClientFactory, listenedInvoice.Network, GetLightningUrl(listenedInvoice.SupportedPaymentMethod));
|
||||
instanceListener ??= new LightningInstanceListener(_InvoiceRepository, _Aggregator, lightningClientFactory, listenedInvoice.Network, GetLightningUrl(listenedInvoice.SupportedPaymentMethod), _paymentService);
|
||||
var status = await instanceListener.PollPayment(listenedInvoice, cancellation);
|
||||
if (status is null ||
|
||||
status is LightningInvoiceStatus.Paid ||
|
||||
@ -309,9 +312,10 @@ namespace BTCPayServer.Payments.Lightning
|
||||
|
||||
public class LightningInstanceListener
|
||||
{
|
||||
private readonly InvoiceRepository invoiceRepository;
|
||||
private readonly InvoiceRepository _invoiceRepository;
|
||||
private readonly EventAggregator _eventAggregator;
|
||||
private readonly BTCPayNetwork network;
|
||||
private readonly BTCPayNetwork _network;
|
||||
private readonly PaymentService _paymentService;
|
||||
private readonly LightningClientFactoryService _lightningClientFactory;
|
||||
|
||||
public LightningConnectionString ConnectionString { get; }
|
||||
@ -320,13 +324,15 @@ namespace BTCPayServer.Payments.Lightning
|
||||
EventAggregator eventAggregator,
|
||||
LightningClientFactoryService lightningClientFactory,
|
||||
BTCPayNetwork network,
|
||||
LightningConnectionString connectionString)
|
||||
LightningConnectionString connectionString,
|
||||
PaymentService paymentService)
|
||||
{
|
||||
if (connectionString == null)
|
||||
throw new ArgumentNullException(nameof(connectionString));
|
||||
this.invoiceRepository = invoiceRepository;
|
||||
this._invoiceRepository = invoiceRepository;
|
||||
_eventAggregator = eventAggregator;
|
||||
this.network = network;
|
||||
this._network = network;
|
||||
_paymentService = paymentService;
|
||||
_lightningClientFactory = lightningClientFactory;
|
||||
ConnectionString = connectionString;
|
||||
}
|
||||
@ -337,12 +343,12 @@ namespace BTCPayServer.Payments.Lightning
|
||||
|
||||
internal async Task<LightningInvoiceStatus?> PollPayment(ListenedInvoice listenedInvoice, CancellationToken cancellation)
|
||||
{
|
||||
var client = _lightningClientFactory.Create(ConnectionString, network);
|
||||
var client = _lightningClientFactory.Create(ConnectionString, _network);
|
||||
LightningInvoice lightningInvoice = await client.GetInvoice(listenedInvoice.PaymentMethodDetails.InvoiceId);
|
||||
if (lightningInvoice?.Status is LightningInvoiceStatus.Paid &&
|
||||
await AddPayment(lightningInvoice, listenedInvoice.InvoiceId))
|
||||
{
|
||||
Logs.PayServer.LogInformation($"{network.CryptoCode} (Lightning): Payment detected via polling on {listenedInvoice.InvoiceId}");
|
||||
Logs.PayServer.LogInformation($"{_network.CryptoCode} (Lightning): Payment detected via polling on {listenedInvoice.InvoiceId}");
|
||||
}
|
||||
return lightningInvoice?.Status;
|
||||
}
|
||||
@ -360,17 +366,17 @@ namespace BTCPayServer.Payments.Lightning
|
||||
public CancellationTokenSource StopListeningCancellationTokenSource;
|
||||
async Task Listen(CancellationToken cancellation)
|
||||
{
|
||||
Logs.PayServer.LogInformation($"{network.CryptoCode} (Lightning): Start listening {ConnectionString.BaseUri}");
|
||||
Logs.PayServer.LogInformation($"{_network.CryptoCode} (Lightning): Start listening {ConnectionString.BaseUri}");
|
||||
try
|
||||
{
|
||||
var lightningClient = _lightningClientFactory.Create(ConnectionString, network);
|
||||
var lightningClient = _lightningClientFactory.Create(ConnectionString, _network);
|
||||
using (var session = await lightningClient.Listen(cancellation))
|
||||
{
|
||||
// Just in case the payment arrived after our last poll but before we listened.
|
||||
await PollAllListenedInvoices(cancellation);
|
||||
if (_ErrorAlreadyLogged)
|
||||
{
|
||||
Logs.PayServer.LogInformation($"{network.CryptoCode} (Lightning): Could reconnect successfully to {ConnectionString.BaseUri}");
|
||||
Logs.PayServer.LogInformation($"{_network.CryptoCode} (Lightning): Could reconnect successfully to {ConnectionString.BaseUri}");
|
||||
}
|
||||
_ErrorAlreadyLogged = false;
|
||||
while (!_ListenedInvoices.IsEmpty)
|
||||
@ -386,7 +392,7 @@ namespace BTCPayServer.Payments.Lightning
|
||||
{
|
||||
if (await AddPayment(notification, listenedInvoice.InvoiceId))
|
||||
{
|
||||
Logs.PayServer.LogInformation($"{network.CryptoCode} (Lightning): Payment detected via notification ({listenedInvoice.InvoiceId})");
|
||||
Logs.PayServer.LogInformation($"{_network.CryptoCode} (Lightning): Payment detected via notification ({listenedInvoice.InvoiceId})");
|
||||
}
|
||||
_ListenedInvoices.TryRemove(notification.Id, out var _);
|
||||
}
|
||||
@ -401,12 +407,12 @@ namespace BTCPayServer.Payments.Lightning
|
||||
catch (Exception ex) when (!cancellation.IsCancellationRequested && !_ErrorAlreadyLogged)
|
||||
{
|
||||
_ErrorAlreadyLogged = true;
|
||||
Logs.PayServer.LogError(ex, $"{network.CryptoCode} (Lightning): Error while contacting {ConnectionString.BaseUri}");
|
||||
Logs.PayServer.LogInformation($"{network.CryptoCode} (Lightning): Stop listening {ConnectionString.BaseUri}");
|
||||
Logs.PayServer.LogError(ex, $"{_network.CryptoCode} (Lightning): Error while contacting {ConnectionString.BaseUri}");
|
||||
Logs.PayServer.LogInformation($"{_network.CryptoCode} (Lightning): Stop listening {ConnectionString.BaseUri}");
|
||||
}
|
||||
catch (OperationCanceledException) when (cancellation.IsCancellationRequested) { }
|
||||
if (_ListenedInvoices.IsEmpty)
|
||||
Logs.PayServer.LogInformation($"{network.CryptoCode} (Lightning): No more invoice to listen on {ConnectionString.BaseUri}, releasing the connection.");
|
||||
Logs.PayServer.LogInformation($"{_network.CryptoCode} (Lightning): No more invoice to listen on {ConnectionString.BaseUri}, releasing the connection.");
|
||||
}
|
||||
|
||||
public DateTimeOffset? LastFullPoll { get; set; }
|
||||
@ -433,15 +439,15 @@ namespace BTCPayServer.Payments.Lightning
|
||||
|
||||
public async Task<bool> AddPayment(LightningInvoice notification, string invoiceId)
|
||||
{
|
||||
var payment = await invoiceRepository.AddPayment(invoiceId, notification.PaidAt.Value, new LightningLikePaymentData()
|
||||
var payment = await _paymentService.AddPayment(invoiceId, notification.PaidAt.Value, new LightningLikePaymentData()
|
||||
{
|
||||
BOLT11 = notification.BOLT11,
|
||||
PaymentHash = BOLT11PaymentRequest.Parse(notification.BOLT11, network.NBitcoinNetwork).PaymentHash,
|
||||
PaymentHash = BOLT11PaymentRequest.Parse(notification.BOLT11, _network.NBitcoinNetwork).PaymentHash,
|
||||
Amount = notification.AmountReceived ?? notification.Amount, // if running old version amount received might be unavailable
|
||||
}, network, accounted: true);
|
||||
}, _network, accounted: true);
|
||||
if (payment != null)
|
||||
{
|
||||
var invoice = await invoiceRepository.GetInvoice(invoiceId);
|
||||
var invoice = await _invoiceRepository.GetInvoice(invoiceId);
|
||||
if (invoice != null)
|
||||
_eventAggregator.Publish(new InvoiceEvent(invoice, InvoiceEvent.ReceivedPayment) { Payment = payment });
|
||||
}
|
||||
|
@ -91,6 +91,7 @@ namespace BTCPayServer.Payments.PayJoin
|
||||
private readonly BTCPayServerEnvironment _env;
|
||||
private readonly WalletReceiveService _walletReceiveService;
|
||||
private readonly StoreRepository _storeRepository;
|
||||
private readonly PaymentService _paymentService;
|
||||
|
||||
public PayJoinEndpointController(BTCPayNetworkProvider btcPayNetworkProvider,
|
||||
InvoiceRepository invoiceRepository, ExplorerClientProvider explorerClientProvider,
|
||||
@ -101,7 +102,8 @@ namespace BTCPayServer.Payments.PayJoin
|
||||
DelayedTransactionBroadcaster broadcaster,
|
||||
BTCPayServerEnvironment env,
|
||||
WalletReceiveService walletReceiveService,
|
||||
StoreRepository storeRepository)
|
||||
StoreRepository storeRepository,
|
||||
PaymentService paymentService)
|
||||
{
|
||||
_btcPayNetworkProvider = btcPayNetworkProvider;
|
||||
_invoiceRepository = invoiceRepository;
|
||||
@ -114,6 +116,7 @@ namespace BTCPayServer.Payments.PayJoin
|
||||
_env = env;
|
||||
_walletReceiveService = walletReceiveService;
|
||||
_storeRepository = storeRepository;
|
||||
_paymentService = paymentService;
|
||||
}
|
||||
|
||||
[HttpPost("")]
|
||||
@ -487,7 +490,7 @@ namespace BTCPayServer.Payments.PayJoin
|
||||
};
|
||||
if (invoice != null)
|
||||
{
|
||||
var payment = await _invoiceRepository.AddPayment(invoice.Id, DateTimeOffset.UtcNow, originalPaymentData, network, true);
|
||||
var payment = await _paymentService.AddPayment(invoice.Id, DateTimeOffset.UtcNow, originalPaymentData, network, true);
|
||||
if (payment is null)
|
||||
{
|
||||
return UnprocessableEntity(CreatePayjoinError("already-paid",
|
||||
|
@ -29,6 +29,7 @@ namespace BTCPayServer.Services.Altcoins.Ethereum.Services
|
||||
private readonly SettingsRepository _settingsRepository;
|
||||
private readonly InvoiceRepository _invoiceRepository;
|
||||
private readonly IConfiguration _configuration;
|
||||
private readonly PaymentService _paymentService;
|
||||
private readonly Dictionary<int, EthereumWatcher> _chainHostedServices = new Dictionary<int, EthereumWatcher>();
|
||||
|
||||
private readonly Dictionary<int, CancellationTokenSource> _chainHostedServiceCancellationTokenSources =
|
||||
@ -41,7 +42,8 @@ namespace BTCPayServer.Services.Altcoins.Ethereum.Services
|
||||
BTCPayNetworkProvider btcPayNetworkProvider,
|
||||
SettingsRepository settingsRepository,
|
||||
InvoiceRepository invoiceRepository,
|
||||
IConfiguration configuration) : base(
|
||||
IConfiguration configuration,
|
||||
PaymentService paymentService) : base(
|
||||
eventAggregator)
|
||||
{
|
||||
_httpClientFactory = httpClientFactory;
|
||||
@ -51,6 +53,7 @@ namespace BTCPayServer.Services.Altcoins.Ethereum.Services
|
||||
_settingsRepository = settingsRepository;
|
||||
_invoiceRepository = invoiceRepository;
|
||||
_configuration = configuration;
|
||||
_paymentService = paymentService;
|
||||
}
|
||||
|
||||
public override async Task StartAsync(CancellationToken cancellationToken)
|
||||
@ -186,7 +189,7 @@ namespace BTCPayServer.Services.Altcoins.Ethereum.Services
|
||||
_chainHostedServiceCancellationTokenSources.AddOrReplace(ethereumLikeConfiguration.ChainId, cts);
|
||||
_chainHostedServices.AddOrReplace(ethereumLikeConfiguration.ChainId,
|
||||
new EthereumWatcher(ethereumLikeConfiguration.ChainId, ethereumLikeConfiguration,
|
||||
_btcPayNetworkProvider, _eventAggregator, _invoiceRepository));
|
||||
_btcPayNetworkProvider, _eventAggregator, _invoiceRepository, _paymentService));
|
||||
await _chainHostedServices[ethereumLikeConfiguration.ChainId].StartAsync(CancellationTokenSource
|
||||
.CreateLinkedTokenSource(cancellationToken, cts.Token).Token);
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ namespace BTCPayServer.Services.Altcoins.Ethereum.Services
|
||||
{
|
||||
private readonly EventAggregator _eventAggregator;
|
||||
private readonly InvoiceRepository _invoiceRepository;
|
||||
private readonly PaymentService _paymentService;
|
||||
private int ChainId { get; }
|
||||
private readonly HashSet<PaymentMethodId> PaymentMethods;
|
||||
|
||||
@ -113,7 +114,7 @@ namespace BTCPayServer.Services.Altcoins.Ethereum.Services
|
||||
AccountIndex = response.PaymentMethodDetails.Index,
|
||||
XPub = response.PaymentMethodDetails.XPub
|
||||
};
|
||||
var payment = await _invoiceRepository.AddPayment(invoice.Id, DateTimeOffset.UtcNow,
|
||||
var payment = await _paymentService.AddPayment(invoice.Id, DateTimeOffset.UtcNow,
|
||||
paymentData, network, true);
|
||||
if (payment != null) ReceivedPayment(invoice, payment);
|
||||
}
|
||||
@ -125,7 +126,7 @@ namespace BTCPayServer.Services.Altcoins.Ethereum.Services
|
||||
{
|
||||
existingPayment.Accounted = false;
|
||||
|
||||
await _invoiceRepository.UpdatePayments(new List<PaymentEntity>() {existingPayment});
|
||||
await _paymentService.UpdatePayments(new List<PaymentEntity>() {existingPayment});
|
||||
if (response.Amount > 0)
|
||||
{
|
||||
var paymentData = new EthereumLikePaymentData()
|
||||
@ -148,7 +149,7 @@ namespace BTCPayServer.Services.Altcoins.Ethereum.Services
|
||||
AccountIndex = cd.AccountIndex,
|
||||
XPub = cd.XPub
|
||||
};
|
||||
var payment = await _invoiceRepository.AddPayment(invoice.Id, DateTimeOffset.UtcNow,
|
||||
var payment = await _paymentService.AddPayment(invoice.Id, DateTimeOffset.UtcNow,
|
||||
paymentData, network, true);
|
||||
if (payment != null) ReceivedPayment(invoice, payment);
|
||||
}
|
||||
@ -163,7 +164,7 @@ namespace BTCPayServer.Services.Altcoins.Ethereum.Services
|
||||
cd.BlockNumber = (long?)response.BlockParameter.BlockNumber.Value;
|
||||
|
||||
existingPayment.SetCryptoPaymentData(cd);
|
||||
await _invoiceRepository.UpdatePayments(new List<PaymentEntity>() {existingPayment});
|
||||
await _paymentService.UpdatePayments(new List<PaymentEntity>() {existingPayment});
|
||||
|
||||
_eventAggregator.Publish(new Events.InvoiceNeedUpdateEvent(invoice.Id));
|
||||
}
|
||||
@ -183,7 +184,7 @@ namespace BTCPayServer.Services.Altcoins.Ethereum.Services
|
||||
}
|
||||
|
||||
existingPayment.SetCryptoPaymentData(cd);
|
||||
await _invoiceRepository.UpdatePayments(new List<PaymentEntity>() {existingPayment});
|
||||
await _paymentService.UpdatePayments(new List<PaymentEntity>() {existingPayment});
|
||||
|
||||
_eventAggregator.Publish(new Events.InvoiceNeedUpdateEvent(invoice.Id));
|
||||
}
|
||||
@ -345,11 +346,12 @@ namespace BTCPayServer.Services.Altcoins.Ethereum.Services
|
||||
|
||||
public EthereumWatcher(int chainId, EthereumLikeConfiguration config,
|
||||
BTCPayNetworkProvider btcPayNetworkProvider,
|
||||
EventAggregator eventAggregator, InvoiceRepository invoiceRepository) :
|
||||
EventAggregator eventAggregator, InvoiceRepository invoiceRepository, PaymentService paymentService) :
|
||||
base(eventAggregator)
|
||||
{
|
||||
_eventAggregator = eventAggregator;
|
||||
_invoiceRepository = invoiceRepository;
|
||||
_paymentService = paymentService;
|
||||
ChainId = chainId;
|
||||
AuthenticationHeaderValue headerValue = null;
|
||||
if (!string.IsNullOrEmpty(config.Web3ProviderUsername))
|
||||
|
@ -27,6 +27,7 @@ namespace BTCPayServer.Services.Altcoins.Monero.Services
|
||||
private readonly MoneroLikeConfiguration _MoneroLikeConfiguration;
|
||||
private readonly BTCPayNetworkProvider _networkProvider;
|
||||
private readonly ILogger<MoneroListener> _logger;
|
||||
private readonly PaymentService _paymentService;
|
||||
private readonly CompositeDisposable leases = new CompositeDisposable();
|
||||
private readonly Queue<Func<CancellationToken, Task>> taskQueue = new Queue<Func<CancellationToken, Task>>();
|
||||
private CancellationTokenSource _Cts;
|
||||
@ -36,7 +37,8 @@ namespace BTCPayServer.Services.Altcoins.Monero.Services
|
||||
MoneroRPCProvider moneroRpcProvider,
|
||||
MoneroLikeConfiguration moneroLikeConfiguration,
|
||||
BTCPayNetworkProvider networkProvider,
|
||||
ILogger<MoneroListener> logger)
|
||||
ILogger<MoneroListener> logger,
|
||||
PaymentService paymentService)
|
||||
{
|
||||
_invoiceRepository = invoiceRepository;
|
||||
_eventAggregator = eventAggregator;
|
||||
@ -44,6 +46,7 @@ namespace BTCPayServer.Services.Altcoins.Monero.Services
|
||||
_MoneroLikeConfiguration = moneroLikeConfiguration;
|
||||
_networkProvider = networkProvider;
|
||||
_logger = logger;
|
||||
_paymentService = paymentService;
|
||||
}
|
||||
|
||||
public Task StartAsync(CancellationToken cancellationToken)
|
||||
@ -243,7 +246,7 @@ namespace BTCPayServer.Services.Altcoins.Monero.Services
|
||||
}
|
||||
|
||||
transferProcessingTasks.Add(
|
||||
_invoiceRepository.UpdatePayments(updatedPaymentEntities.Select(tuple => tuple.Item1).ToList()));
|
||||
_paymentService.UpdatePayments(updatedPaymentEntities.Select(tuple => tuple.Item1).ToList()));
|
||||
await Task.WhenAll(transferProcessingTasks);
|
||||
foreach (var valueTuples in updatedPaymentEntities.GroupBy(entity => entity.Item2))
|
||||
{
|
||||
@ -304,7 +307,7 @@ namespace BTCPayServer.Services.Altcoins.Monero.Services
|
||||
|
||||
if (paymentsToUpdate.Any())
|
||||
{
|
||||
await _invoiceRepository.UpdatePayments(paymentsToUpdate.Select(tuple => tuple.Payment).ToList());
|
||||
await _paymentService.UpdatePayments(paymentsToUpdate.Select(tuple => tuple.Payment).ToList());
|
||||
foreach (var valueTuples in paymentsToUpdate.GroupBy(entity => entity.invoice))
|
||||
{
|
||||
if (valueTuples.Any())
|
||||
@ -341,7 +344,7 @@ namespace BTCPayServer.Services.Altcoins.Monero.Services
|
||||
//if it doesnt, add it and assign a new monerolike address to the system if a balance is still due
|
||||
if (alreadyExistingPaymentThatMatches.Payment == null)
|
||||
{
|
||||
var payment = await _invoiceRepository.AddPayment(invoice.Id, DateTimeOffset.UtcNow,
|
||||
var payment = await _paymentService.AddPayment(invoice.Id, DateTimeOffset.UtcNow,
|
||||
paymentData, _networkProvider.GetNetwork<MoneroLikeSpecificBtcPayNetwork>(cryptoCode), true);
|
||||
if (payment != null)
|
||||
await ReceivedPayment(invoice, payment);
|
||||
|
@ -29,21 +29,21 @@ namespace BTCPayServer.Services.Invoices
|
||||
NBitcoin.JsonConverters.Serializer.RegisterFrontConverters(DefaultSerializerSettings);
|
||||
}
|
||||
|
||||
private readonly ApplicationDbContextFactory _ContextFactory;
|
||||
private readonly ApplicationDbContextFactory _applicationDbContextFactory;
|
||||
private readonly EventAggregator _eventAggregator;
|
||||
private readonly BTCPayNetworkProvider _Networks;
|
||||
private readonly BTCPayNetworkProvider _btcPayNetworkProvider;
|
||||
|
||||
public InvoiceRepository(ApplicationDbContextFactory contextFactory,
|
||||
BTCPayNetworkProvider networks, EventAggregator eventAggregator)
|
||||
{
|
||||
_ContextFactory = contextFactory;
|
||||
_Networks = networks;
|
||||
_applicationDbContextFactory = contextFactory;
|
||||
_btcPayNetworkProvider = networks;
|
||||
_eventAggregator = eventAggregator;
|
||||
}
|
||||
|
||||
public async Task<Data.WebhookDeliveryData> GetWebhookDelivery(string invoiceId, string deliveryId)
|
||||
{
|
||||
using var ctx = _ContextFactory.CreateContext();
|
||||
using var ctx = _applicationDbContextFactory.CreateContext();
|
||||
return await ctx.InvoiceWebhookDeliveries
|
||||
.Where(d => d.InvoiceId == invoiceId && d.DeliveryId == deliveryId)
|
||||
.Select(d => d.Delivery)
|
||||
@ -54,7 +54,7 @@ namespace BTCPayServer.Services.Invoices
|
||||
{
|
||||
return new InvoiceEntity()
|
||||
{
|
||||
Networks = _Networks,
|
||||
Networks = _btcPayNetworkProvider,
|
||||
Version = InvoiceEntity.Lastest_Version,
|
||||
InvoiceTime = DateTimeOffset.UtcNow,
|
||||
Metadata = new InvoiceMetadata()
|
||||
@ -64,7 +64,7 @@ namespace BTCPayServer.Services.Invoices
|
||||
public async Task<bool> RemovePendingInvoice(string invoiceId)
|
||||
{
|
||||
Logs.PayServer.LogInformation($"Remove pending invoice {invoiceId}");
|
||||
using (var ctx = _ContextFactory.CreateContext())
|
||||
using (var ctx = _applicationDbContextFactory.CreateContext())
|
||||
{
|
||||
ctx.PendingInvoices.Remove(new PendingInvoiceData() { Id = invoiceId });
|
||||
try
|
||||
@ -78,7 +78,7 @@ namespace BTCPayServer.Services.Invoices
|
||||
|
||||
public async Task<IEnumerable<InvoiceEntity>> GetInvoicesFromAddresses(string[] addresses)
|
||||
{
|
||||
using (var db = _ContextFactory.CreateContext())
|
||||
using (var db = _applicationDbContextFactory.CreateContext())
|
||||
{
|
||||
return (await db.AddressInvoices
|
||||
.Include(a => a.InvoiceData.Payments)
|
||||
@ -92,7 +92,7 @@ namespace BTCPayServer.Services.Invoices
|
||||
|
||||
public async Task<string[]> GetPendingInvoices()
|
||||
{
|
||||
using (var ctx = _ContextFactory.CreateContext())
|
||||
using (var ctx = _applicationDbContextFactory.CreateContext())
|
||||
{
|
||||
return await ctx.PendingInvoices.AsQueryable().Select(data => data.Id).ToArrayAsync();
|
||||
}
|
||||
@ -100,7 +100,7 @@ namespace BTCPayServer.Services.Invoices
|
||||
|
||||
public async Task<List<Data.WebhookDeliveryData>> GetWebhookDeliveries(string invoiceId)
|
||||
{
|
||||
using var ctx = _ContextFactory.CreateContext();
|
||||
using var ctx = _applicationDbContextFactory.CreateContext();
|
||||
return await ctx.InvoiceWebhookDeliveries
|
||||
.Where(s => s.InvoiceId == invoiceId)
|
||||
.Select(s => s.Delivery)
|
||||
@ -112,7 +112,7 @@ namespace BTCPayServer.Services.Invoices
|
||||
{
|
||||
if (storeId == null)
|
||||
throw new ArgumentNullException(nameof(storeId));
|
||||
using (var ctx = _ContextFactory.CreateContext())
|
||||
using (var ctx = _applicationDbContextFactory.CreateContext())
|
||||
{
|
||||
return await ctx.Apps.Where(a => a.StoreDataId == storeId && a.TagAllInvoices).ToArrayAsync();
|
||||
}
|
||||
@ -120,7 +120,7 @@ namespace BTCPayServer.Services.Invoices
|
||||
|
||||
public async Task UpdateInvoice(string invoiceId, UpdateCustomerModel data)
|
||||
{
|
||||
using (var ctx = _ContextFactory.CreateContext())
|
||||
using (var ctx = _applicationDbContextFactory.CreateContext())
|
||||
{
|
||||
var invoiceData = await ctx.Invoices.FindAsync(invoiceId).ConfigureAwait(false);
|
||||
if (invoiceData == null)
|
||||
@ -136,11 +136,11 @@ namespace BTCPayServer.Services.Invoices
|
||||
|
||||
public async Task ExtendInvoiceMonitor(string invoiceId)
|
||||
{
|
||||
using (var ctx = _ContextFactory.CreateContext())
|
||||
using (var ctx = _applicationDbContextFactory.CreateContext())
|
||||
{
|
||||
var invoiceData = await ctx.Invoices.FindAsync(invoiceId);
|
||||
|
||||
var invoice = invoiceData.GetBlob(_Networks);
|
||||
var invoice = invoiceData.GetBlob(_btcPayNetworkProvider);
|
||||
invoice.MonitoringExpiration = invoice.MonitoringExpiration.AddHours(1);
|
||||
invoiceData.Blob = ToBytes(invoice, null);
|
||||
|
||||
@ -152,13 +152,13 @@ namespace BTCPayServer.Services.Invoices
|
||||
{
|
||||
var textSearch = new HashSet<string>();
|
||||
invoice = Clone(invoice);
|
||||
invoice.Networks = _Networks;
|
||||
invoice.Networks = _btcPayNetworkProvider;
|
||||
invoice.Id = Encoders.Base58.EncodeData(RandomUtils.GetBytes(16));
|
||||
#pragma warning disable CS0618
|
||||
invoice.Payments = new List<PaymentEntity>();
|
||||
#pragma warning restore CS0618
|
||||
invoice.StoreId = storeId;
|
||||
using (var context = _ContextFactory.CreateContext())
|
||||
using (var context = _applicationDbContextFactory.CreateContext())
|
||||
{
|
||||
var invoiceData = new Data.InvoiceData()
|
||||
{
|
||||
@ -229,12 +229,12 @@ namespace BTCPayServer.Services.Invoices
|
||||
{
|
||||
var temp = new InvoiceData();
|
||||
temp.Blob = ToBytes(invoice);
|
||||
return temp.GetBlob(_Networks);
|
||||
return temp.GetBlob(_btcPayNetworkProvider);
|
||||
}
|
||||
|
||||
public async Task AddInvoiceLogs(string invoiceId, InvoiceLogs logs)
|
||||
{
|
||||
await using var context = _ContextFactory.CreateContext();
|
||||
await using var context = _applicationDbContextFactory.CreateContext();
|
||||
foreach (var log in logs.ToList())
|
||||
{
|
||||
await context.InvoiceEvents.AddAsync(new InvoiceEventData()
|
||||
@ -269,12 +269,12 @@ namespace BTCPayServer.Services.Invoices
|
||||
|
||||
public async Task<bool> NewPaymentDetails(string invoiceId, IPaymentMethodDetails paymentMethodDetails, BTCPayNetworkBase network)
|
||||
{
|
||||
await using var context = _ContextFactory.CreateContext();
|
||||
await using var context = _applicationDbContextFactory.CreateContext();
|
||||
var invoice = (await context.Invoices.Where(i => i.Id == invoiceId).ToListAsync()).FirstOrDefault();
|
||||
if (invoice == null)
|
||||
return false;
|
||||
|
||||
var invoiceEntity = invoice.GetBlob(_Networks);
|
||||
var invoiceEntity = invoice.GetBlob(_btcPayNetworkProvider);
|
||||
var paymentMethod = invoiceEntity.GetPaymentMethod(network, paymentMethodDetails.GetPaymentType());
|
||||
if (paymentMethod == null)
|
||||
return false;
|
||||
@ -313,13 +313,13 @@ namespace BTCPayServer.Services.Invoices
|
||||
|
||||
public async Task UpdateInvoicePaymentMethod(string invoiceId, PaymentMethod paymentMethod)
|
||||
{
|
||||
using (var context = _ContextFactory.CreateContext())
|
||||
using (var context = _applicationDbContextFactory.CreateContext())
|
||||
{
|
||||
var invoice = await context.Invoices.FindAsync(invoiceId);
|
||||
if (invoice == null)
|
||||
return;
|
||||
var network = paymentMethod.Network;
|
||||
var invoiceEntity = invoice.GetBlob(_Networks);
|
||||
var invoiceEntity = invoice.GetBlob(_btcPayNetworkProvider);
|
||||
var newDetails = paymentMethod.GetPaymentMethodDetails();
|
||||
var existing = invoiceEntity.GetPaymentMethod(paymentMethod.GetId());
|
||||
if (existing.GetPaymentMethodDetails().GetPaymentDestination() != newDetails.GetPaymentDestination() && newDetails.Activated)
|
||||
@ -346,7 +346,7 @@ namespace BTCPayServer.Services.Invoices
|
||||
|
||||
public async Task AddPendingInvoiceIfNotPresent(string invoiceId)
|
||||
{
|
||||
using (var context = _ContextFactory.CreateContext())
|
||||
using (var context = _applicationDbContextFactory.CreateContext())
|
||||
{
|
||||
if (!context.PendingInvoices.Any(a => a.Id == invoiceId))
|
||||
{
|
||||
@ -362,7 +362,7 @@ namespace BTCPayServer.Services.Invoices
|
||||
|
||||
public async Task AddInvoiceEvent(string invoiceId, object evt, InvoiceEventData.EventSeverity severity)
|
||||
{
|
||||
await using var context = _ContextFactory.CreateContext();
|
||||
await using var context = _applicationDbContextFactory.CreateContext();
|
||||
await context.InvoiceEvents.AddAsync(new InvoiceEventData()
|
||||
{
|
||||
Severity = severity,
|
||||
@ -396,7 +396,7 @@ namespace BTCPayServer.Services.Invoices
|
||||
|
||||
public async Task UnaffectAddress(string invoiceId)
|
||||
{
|
||||
await using var context = _ContextFactory.CreateContext();
|
||||
await using var context = _applicationDbContextFactory.CreateContext();
|
||||
MarkUnassigned(invoiceId, context, null);
|
||||
try
|
||||
{
|
||||
@ -416,7 +416,7 @@ namespace BTCPayServer.Services.Invoices
|
||||
|
||||
public async Task UpdateInvoiceStatus(string invoiceId, InvoiceState invoiceState)
|
||||
{
|
||||
using (var context = _ContextFactory.CreateContext())
|
||||
using (var context = _applicationDbContextFactory.CreateContext())
|
||||
{
|
||||
var invoiceData = await context.FindAsync<Data.InvoiceData>(invoiceId).ConfigureAwait(false);
|
||||
if (invoiceData == null)
|
||||
@ -430,12 +430,12 @@ namespace BTCPayServer.Services.Invoices
|
||||
{
|
||||
if (invoice.Type != InvoiceType.TopUp)
|
||||
throw new ArgumentException("The invoice type should be TopUp to be able to update invoice price", nameof(invoice));
|
||||
using (var context = _ContextFactory.CreateContext())
|
||||
using (var context = _applicationDbContextFactory.CreateContext())
|
||||
{
|
||||
var invoiceData = await context.FindAsync<Data.InvoiceData>(invoiceId).ConfigureAwait(false);
|
||||
if (invoiceData == null)
|
||||
return;
|
||||
var blob = invoiceData.GetBlob(_Networks);
|
||||
var blob = invoiceData.GetBlob(_btcPayNetworkProvider);
|
||||
blob.Price = invoice.Price;
|
||||
AddToTextSearch(context, invoiceData, new[] { invoice.Price.ToString(CultureInfo.InvariantCulture) });
|
||||
invoiceData.Blob = ToBytes(blob, null);
|
||||
@ -445,7 +445,7 @@ namespace BTCPayServer.Services.Invoices
|
||||
|
||||
public async Task MassArchive(string[] invoiceIds)
|
||||
{
|
||||
using (var context = _ContextFactory.CreateContext())
|
||||
using (var context = _applicationDbContextFactory.CreateContext())
|
||||
{
|
||||
var items = context.Invoices.Where(a => invoiceIds.Contains(a.Id));
|
||||
if (items == null)
|
||||
@ -464,7 +464,7 @@ namespace BTCPayServer.Services.Invoices
|
||||
|
||||
public async Task ToggleInvoiceArchival(string invoiceId, bool archived, string storeId = null)
|
||||
{
|
||||
using (var context = _ContextFactory.CreateContext())
|
||||
using (var context = _applicationDbContextFactory.CreateContext())
|
||||
{
|
||||
var invoiceData = await context.FindAsync<InvoiceData>(invoiceId).ConfigureAwait(false);
|
||||
if (invoiceData == null || invoiceData.Archived == archived ||
|
||||
@ -477,14 +477,14 @@ namespace BTCPayServer.Services.Invoices
|
||||
}
|
||||
public async Task<InvoiceEntity> UpdateInvoiceMetadata(string invoiceId, string storeId, JObject metadata)
|
||||
{
|
||||
using (var context = _ContextFactory.CreateContext())
|
||||
using (var context = _applicationDbContextFactory.CreateContext())
|
||||
{
|
||||
var invoiceData = await GetInvoiceRaw(invoiceId, context);
|
||||
if (invoiceData == null || (storeId != null &&
|
||||
!invoiceData.StoreDataId.Equals(storeId,
|
||||
StringComparison.InvariantCultureIgnoreCase)))
|
||||
return null;
|
||||
var blob = invoiceData.GetBlob(_Networks);
|
||||
var blob = invoiceData.GetBlob(_btcPayNetworkProvider);
|
||||
blob.Metadata = InvoiceMetadata.FromJObject(metadata);
|
||||
invoiceData.Blob = ToBytes(blob);
|
||||
await context.SaveChangesAsync().ConfigureAwait(false);
|
||||
@ -493,7 +493,7 @@ namespace BTCPayServer.Services.Invoices
|
||||
}
|
||||
public async Task<bool> MarkInvoiceStatus(string invoiceId, InvoiceStatus status)
|
||||
{
|
||||
using (var context = _ContextFactory.CreateContext())
|
||||
using (var context = _applicationDbContextFactory.CreateContext())
|
||||
{
|
||||
var invoiceData = await GetInvoiceRaw(invoiceId, context);
|
||||
if (invoiceData == null)
|
||||
@ -538,7 +538,7 @@ namespace BTCPayServer.Services.Invoices
|
||||
|
||||
public async Task<InvoiceEntity> GetInvoice(string id, bool inludeAddressData = false)
|
||||
{
|
||||
using (var context = _ContextFactory.CreateContext())
|
||||
using (var context = _applicationDbContextFactory.CreateContext())
|
||||
{
|
||||
var res = await GetInvoiceRaw(id, context, inludeAddressData);
|
||||
return res == null ? null : ToEntity(res);
|
||||
@ -547,7 +547,7 @@ namespace BTCPayServer.Services.Invoices
|
||||
public async Task<InvoiceEntity[]> GetInvoices(string[] invoiceIds)
|
||||
{
|
||||
var invoiceIdSet = invoiceIds.ToHashSet();
|
||||
using (var context = _ContextFactory.CreateContext())
|
||||
using (var context = _applicationDbContextFactory.CreateContext())
|
||||
{
|
||||
IQueryable<Data.InvoiceData> query =
|
||||
context
|
||||
@ -578,12 +578,12 @@ namespace BTCPayServer.Services.Invoices
|
||||
|
||||
private InvoiceEntity ToEntity(Data.InvoiceData invoice)
|
||||
{
|
||||
var entity = invoice.GetBlob(_Networks);
|
||||
var entity = invoice.GetBlob(_btcPayNetworkProvider);
|
||||
PaymentMethodDictionary paymentMethods = null;
|
||||
#pragma warning disable CS0618
|
||||
entity.Payments = invoice.Payments.Select(p =>
|
||||
{
|
||||
var paymentEntity = p.GetBlob(_Networks);
|
||||
var paymentEntity = p.GetBlob(_btcPayNetworkProvider);
|
||||
if (paymentEntity is null)
|
||||
return null;
|
||||
// PaymentEntity on version 0 does not have their own fee, because it was assumed that the payment method have fixed fee.
|
||||
@ -707,7 +707,7 @@ namespace BTCPayServer.Services.Invoices
|
||||
|
||||
public async Task<int> GetInvoicesTotal(InvoiceQuery queryObject)
|
||||
{
|
||||
using (var context = _ContextFactory.CreateContext())
|
||||
using (var context = _applicationDbContextFactory.CreateContext())
|
||||
{
|
||||
var query = GetInvoiceQuery(context, queryObject);
|
||||
return await query.CountAsync();
|
||||
@ -716,7 +716,7 @@ namespace BTCPayServer.Services.Invoices
|
||||
|
||||
public async Task<InvoiceEntity[]> GetInvoices(InvoiceQuery queryObject)
|
||||
{
|
||||
using (var context = _ContextFactory.CreateContext())
|
||||
using (var context = _applicationDbContextFactory.CreateContext())
|
||||
{
|
||||
var query = GetInvoiceQuery(context, queryObject);
|
||||
query = query.Include(o => o.Payments);
|
||||
@ -752,89 +752,7 @@ namespace BTCPayServer.Services.Invoices
|
||||
return status;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add a payment to an invoice
|
||||
/// </summary>
|
||||
/// <param name="invoiceId"></param>
|
||||
/// <param name="date"></param>
|
||||
/// <param name="paymentData"></param>
|
||||
/// <param name="cryptoCode"></param>
|
||||
/// <param name="accounted"></param>
|
||||
/// <returns>The PaymentEntity or null if already added</returns>
|
||||
public async Task<PaymentEntity> AddPayment(string invoiceId, DateTimeOffset date, CryptoPaymentData paymentData, BTCPayNetworkBase network, bool accounted = false)
|
||||
{
|
||||
using (var context = _ContextFactory.CreateContext())
|
||||
{
|
||||
var invoice = context.Invoices.Find(invoiceId);
|
||||
if (invoice == null)
|
||||
return null;
|
||||
InvoiceEntity invoiceEntity = invoice.GetBlob(_Networks);
|
||||
PaymentMethod paymentMethod = invoiceEntity.GetPaymentMethod(new PaymentMethodId(network.CryptoCode, paymentData.GetPaymentType()));
|
||||
IPaymentMethodDetails paymentMethodDetails = paymentMethod.GetPaymentMethodDetails();
|
||||
PaymentEntity entity = new PaymentEntity
|
||||
{
|
||||
Version = 1,
|
||||
#pragma warning disable CS0618
|
||||
CryptoCode = network.CryptoCode,
|
||||
#pragma warning restore CS0618
|
||||
ReceivedTime = date.UtcDateTime,
|
||||
Accounted = accounted,
|
||||
NetworkFee = paymentMethodDetails.GetNextNetworkFee(),
|
||||
Network = network
|
||||
};
|
||||
entity.SetCryptoPaymentData(paymentData);
|
||||
//TODO: abstract
|
||||
if (paymentMethodDetails is Payments.Bitcoin.BitcoinLikeOnChainPaymentMethod bitcoinPaymentMethod &&
|
||||
bitcoinPaymentMethod.NetworkFeeMode == NetworkFeeMode.MultiplePaymentsOnly &&
|
||||
bitcoinPaymentMethod.NextNetworkFee == Money.Zero)
|
||||
{
|
||||
bitcoinPaymentMethod.NextNetworkFee = bitcoinPaymentMethod.NetworkFeeRate.GetFee(100); // assume price for 100 bytes
|
||||
paymentMethod.SetPaymentMethodDetails(bitcoinPaymentMethod);
|
||||
invoiceEntity.SetPaymentMethod(paymentMethod);
|
||||
invoice.Blob = ToBytes(invoiceEntity, network);
|
||||
}
|
||||
PaymentData data = new PaymentData
|
||||
{
|
||||
Id = paymentData.GetPaymentId(),
|
||||
Blob = ToBytes(entity, entity.Network),
|
||||
InvoiceDataId = invoiceId,
|
||||
Accounted = accounted
|
||||
};
|
||||
|
||||
await context.Payments.AddAsync(data);
|
||||
|
||||
AddToTextSearch(context, invoice, paymentData.GetSearchTerms());
|
||||
try
|
||||
{
|
||||
await context.SaveChangesAsync().ConfigureAwait(false);
|
||||
}
|
||||
catch (DbUpdateException) { return null; } // Already exists
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task UpdatePayments(List<PaymentEntity> payments)
|
||||
{
|
||||
if (payments.Count == 0)
|
||||
return;
|
||||
using (var context = _ContextFactory.CreateContext())
|
||||
{
|
||||
foreach (var payment in payments)
|
||||
{
|
||||
var paymentData = payment.GetCryptoPaymentData();
|
||||
var data = new PaymentData();
|
||||
data.Id = paymentData.GetPaymentId();
|
||||
data.Accounted = payment.Accounted;
|
||||
data.Blob = ToBytes(payment, payment.Network);
|
||||
context.Attach(data);
|
||||
context.Entry(data).Property(o => o.Accounted).IsModified = true;
|
||||
context.Entry(data).Property(o => o.Blob).IsModified = true;
|
||||
}
|
||||
await context.SaveChangesAsync().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] ToBytes<T>(T obj, BTCPayNetworkBase network = null)
|
||||
internal static byte[] ToBytes<T>(T obj, BTCPayNetworkBase network = null)
|
||||
{
|
||||
return ZipUtils.Zip(ToJsonString(obj, network));
|
||||
}
|
||||
|
129
BTCPayServer/Services/Invoices/PaymentService.cs
Normal file
129
BTCPayServer/Services/Invoices/PaymentService.cs
Normal file
@ -0,0 +1,129 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Client.Models;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.Events;
|
||||
using BTCPayServer.Payments;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NBitcoin;
|
||||
|
||||
namespace BTCPayServer.Services.Invoices
|
||||
{
|
||||
public class PaymentService
|
||||
{
|
||||
private readonly ApplicationDbContextFactory _applicationDbContextFactory;
|
||||
private readonly BTCPayNetworkProvider _btcPayNetworkProvider;
|
||||
private readonly EventAggregator _eventAggregator;
|
||||
|
||||
public PaymentService(EventAggregator eventAggregator, ApplicationDbContextFactory applicationDbContextFactory, BTCPayNetworkProvider btcPayNetworkProvider)
|
||||
{
|
||||
_applicationDbContextFactory = applicationDbContextFactory;
|
||||
_btcPayNetworkProvider = btcPayNetworkProvider;
|
||||
_eventAggregator = eventAggregator;
|
||||
}
|
||||
/// <summary>
|
||||
/// Add a payment to an invoice
|
||||
/// </summary>
|
||||
/// <param name="invoiceId"></param>
|
||||
/// <param name="date"></param>
|
||||
/// <param name="paymentData"></param>
|
||||
/// <param name="cryptoCode"></param>
|
||||
/// <param name="accounted"></param>
|
||||
/// <returns>The PaymentEntity or null if already added</returns>
|
||||
public async Task<PaymentEntity> AddPayment(string invoiceId, DateTimeOffset date, CryptoPaymentData paymentData, BTCPayNetworkBase network, bool accounted = false)
|
||||
{
|
||||
await using var context = _applicationDbContextFactory.CreateContext();
|
||||
var invoice = await context.Invoices.FindAsync(invoiceId);
|
||||
if (invoice == null)
|
||||
return null;
|
||||
InvoiceEntity invoiceEntity = invoice.GetBlob(_btcPayNetworkProvider);
|
||||
PaymentMethod paymentMethod = invoiceEntity.GetPaymentMethod(new PaymentMethodId(network.CryptoCode, paymentData.GetPaymentType()));
|
||||
IPaymentMethodDetails paymentMethodDetails = paymentMethod.GetPaymentMethodDetails();
|
||||
PaymentEntity entity = new PaymentEntity
|
||||
{
|
||||
Version = 1,
|
||||
#pragma warning disable CS0618
|
||||
CryptoCode = network.CryptoCode,
|
||||
#pragma warning restore CS0618
|
||||
ReceivedTime = date.UtcDateTime,
|
||||
Accounted = accounted,
|
||||
NetworkFee = paymentMethodDetails.GetNextNetworkFee(),
|
||||
Network = network
|
||||
};
|
||||
entity.SetCryptoPaymentData(paymentData);
|
||||
//TODO: abstract
|
||||
if (paymentMethodDetails is Payments.Bitcoin.BitcoinLikeOnChainPaymentMethod bitcoinPaymentMethod &&
|
||||
bitcoinPaymentMethod.NetworkFeeMode == NetworkFeeMode.MultiplePaymentsOnly &&
|
||||
bitcoinPaymentMethod.NextNetworkFee == Money.Zero)
|
||||
{
|
||||
bitcoinPaymentMethod.NextNetworkFee = bitcoinPaymentMethod.NetworkFeeRate.GetFee(100); // assume price for 100 bytes
|
||||
paymentMethod.SetPaymentMethodDetails(bitcoinPaymentMethod);
|
||||
invoiceEntity.SetPaymentMethod(paymentMethod);
|
||||
invoice.Blob = InvoiceRepository.ToBytes(invoiceEntity, network);
|
||||
}
|
||||
PaymentData data = new PaymentData
|
||||
{
|
||||
Id = paymentData.GetPaymentId(),
|
||||
Blob = InvoiceRepository.ToBytes(entity, entity.Network),
|
||||
InvoiceDataId = invoiceId,
|
||||
Accounted = accounted
|
||||
};
|
||||
|
||||
await context.Payments.AddAsync(data);
|
||||
|
||||
InvoiceRepository.AddToTextSearch(context, invoice, paymentData.GetSearchTerms());
|
||||
var alreadyExists = false;
|
||||
try
|
||||
{
|
||||
await context.SaveChangesAsync().ConfigureAwait(false);
|
||||
}
|
||||
catch (DbUpdateException) { alreadyExists = true; }
|
||||
|
||||
if (alreadyExists)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (paymentData.PaymentConfirmed(entity, invoiceEntity.SpeedPolicy))
|
||||
{
|
||||
_eventAggregator.Publish(new InvoiceEvent(invoiceEntity, InvoiceEvent.PaymentSettled) { Payment = entity });
|
||||
}
|
||||
return entity;
|
||||
}
|
||||
|
||||
public async Task UpdatePayments(List<PaymentEntity> payments)
|
||||
{
|
||||
if (payments.Count == 0)
|
||||
return;
|
||||
await using var context = _applicationDbContextFactory.CreateContext();
|
||||
var paymentsDict = payments
|
||||
.Select(entity => (entity, entity.GetCryptoPaymentData()))
|
||||
.ToDictionary(tuple => tuple.Item2.GetPaymentId());
|
||||
var paymentIds = paymentsDict.Keys.ToArray();
|
||||
var dbPayments = await context.Payments
|
||||
.Include(data => data.InvoiceData)
|
||||
.Where(data => paymentIds.Contains(data.Id)).ToDictionaryAsync(data => data.Id);
|
||||
var eventsToSend = new List<InvoiceEvent>();
|
||||
foreach (KeyValuePair<string,(PaymentEntity entity, CryptoPaymentData)> payment in paymentsDict)
|
||||
{
|
||||
var dbPayment = dbPayments[payment.Key];
|
||||
var invBlob = dbPayment.InvoiceData.GetBlob(_btcPayNetworkProvider);
|
||||
var dbPaymentEntity = dbPayment.GetBlob(_btcPayNetworkProvider);
|
||||
var wasConfirmed = dbPayment.GetBlob(_btcPayNetworkProvider).GetCryptoPaymentData()
|
||||
.PaymentConfirmed(dbPaymentEntity, invBlob.SpeedPolicy);
|
||||
if (!wasConfirmed && payment.Value.Item2.PaymentConfirmed(payment.Value.entity, invBlob.SpeedPolicy))
|
||||
{
|
||||
eventsToSend.Add(new InvoiceEvent(invBlob, InvoiceEvent.PaymentSettled) { Payment = payment.Value.entity });
|
||||
}
|
||||
|
||||
dbPayment.Accounted = payment.Value.entity.Accounted;
|
||||
dbPayment.Blob = InvoiceRepository.ToBytes(payment.Value.entity, payment.Value.entity.Network);
|
||||
}
|
||||
await context.SaveChangesAsync().ConfigureAwait(false);
|
||||
eventsToSend.ForEach(_eventAggregator.Publish);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -53,6 +53,7 @@
|
||||
{
|
||||
("A new invoice has been created", WebhookEventType.InvoiceCreated),
|
||||
("A new payment has been received", WebhookEventType.InvoiceReceivedPayment),
|
||||
("A payment has been settled", WebhookEventType.InvoicePaymentSettled),
|
||||
("An invoice is processing", WebhookEventType.InvoiceProcessing),
|
||||
("An invoice has expired", WebhookEventType.InvoiceExpired),
|
||||
("An invoice has been settled", WebhookEventType.InvoiceSettled),
|
||||
|
@ -41,7 +41,9 @@
|
||||
]
|
||||
},
|
||||
"post": {
|
||||
"tags": [ "Webhooks" ],
|
||||
"tags": [
|
||||
"Webhooks"
|
||||
],
|
||||
"summary": "Create a new webhook",
|
||||
"description": "Create a new webhook",
|
||||
"requestBody": {
|
||||
@ -141,7 +143,9 @@
|
||||
]
|
||||
},
|
||||
"put": {
|
||||
"tags": [ "Webhooks" ],
|
||||
"tags": [
|
||||
"Webhooks"
|
||||
],
|
||||
"summary": "Update a webhook",
|
||||
"description": "Update a webhook",
|
||||
"requestBody": {
|
||||
@ -187,7 +191,9 @@
|
||||
]
|
||||
},
|
||||
"delete": {
|
||||
"tags": [ "Webhooks" ],
|
||||
"tags": [
|
||||
"Webhooks"
|
||||
],
|
||||
"summary": "Delete a webhook",
|
||||
"description": "Delete a webhook",
|
||||
"requestBody": {
|
||||
@ -483,7 +489,11 @@
|
||||
"timestamp": {
|
||||
"nullable": false,
|
||||
"description": "Timestamp of when the delivery got broadcasted",
|
||||
"allOf": [ {"$ref": "#/components/schemas/UnixTimestamp"}]
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/UnixTimestamp"
|
||||
}
|
||||
]
|
||||
},
|
||||
"httpCode": {
|
||||
"type": "number",
|
||||
@ -619,7 +629,11 @@
|
||||
},
|
||||
"timestamp": {
|
||||
"description": "The timestamp when this delivery has been created",
|
||||
"allOf": [ {"$ref": "#/components/schemas/UnixTimestamp"}]
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/UnixTimestamp"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -712,11 +726,28 @@
|
||||
"type": "boolean",
|
||||
"description": "Whether this payment has been sent after expiration of the invoice",
|
||||
"nullable": false
|
||||
},
|
||||
"paymentMethod": {
|
||||
"type": "string",
|
||||
"description": "What payment method was used for this payment",
|
||||
"nullable": false
|
||||
},
|
||||
"payment": {
|
||||
"description": "Details about the payment",
|
||||
"$ref": "#/components/schemas/InvoicePaymentMethodDataModel"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"WebhookInvoicePaymentSettledEvent": {
|
||||
"description": "Callback sent if the `type` is `InvoicePaymentSettled`",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/WebhookInvoiceReceivedPaymentEvent"
|
||||
}
|
||||
]
|
||||
},
|
||||
"WebhookInvoiceExpiredEvent": {
|
||||
"description": "Callback sent if the `type` is `InvoiceExpired`",
|
||||
"allOf": [
|
||||
@ -828,6 +859,36 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"InvoicePaymentSettled": {
|
||||
"post": {
|
||||
"summary": "InvoicePaymentSettled",
|
||||
"description": "An payment relating to an invoice has settled",
|
||||
"parameters": [
|
||||
{
|
||||
"in": "header",
|
||||
"name": "BTCPay-Sig",
|
||||
"required": true,
|
||||
"description": "The HMAC of the body's byte with the secret's of the webhook. `sha256=HMAC256(UTF8(webhook's secret), body)`",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"example": "sha256=b438519edde5c8144a4f9bcec51a9d346eca6506887c2ceeae1c0092884a97b9"
|
||||
}
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"Webhooks"
|
||||
],
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/WebhookInvoicePaymentSettledEvent"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"InvoicePaidInFull": {
|
||||
"post": {
|
||||
"summary": "InvoicePaidInFull",
|
||||
|
Loading…
Reference in New Issue
Block a user