mirror of
https://github.com/btcpayserver/btcpayserver.git
synced 2025-02-20 13:34:37 +01:00
Make sure to only select accounted payments where we should (#2523)
This commit is contained in:
parent
776ded0b7e
commit
c551e5cd0a
16 changed files with 48 additions and 42 deletions
|
@ -320,7 +320,7 @@ namespace BTCPayServer.Tests
|
|||
await TestUtils.EventuallyAsync(async () =>
|
||||
{
|
||||
var invoice = await invoiceRepository.GetInvoice(invoiceId);
|
||||
var payments = invoice.GetPayments();
|
||||
var payments = invoice.GetPayments(false);
|
||||
Assert.Equal(2, payments.Count);
|
||||
var originalPayment = payments[0];
|
||||
var coinjoinPayment = payments[1];
|
||||
|
@ -1088,7 +1088,7 @@ retry:
|
|||
{
|
||||
var invoiceEntity = await tester.PayTester.GetService<InvoiceRepository>().GetInvoice(invoice7.Id);
|
||||
Assert.Equal(InvoiceStatusLegacy.Paid, invoiceEntity.Status);
|
||||
Assert.Contains(invoiceEntity.GetPayments(), p => p.Accounted &&
|
||||
Assert.Contains(invoiceEntity.GetPayments(false), p => p.Accounted &&
|
||||
((BitcoinLikePaymentData)p.GetCryptoPaymentData()).PayjoinInformation is null);
|
||||
});
|
||||
////Assert.Contains(receiverWalletPayJoinState.GetRecords(), item => item.InvoiceId == invoice7.Id && item.TxSeen);
|
||||
|
@ -1117,8 +1117,8 @@ retry:
|
|||
{
|
||||
var invoiceEntity = await tester.PayTester.GetService<InvoiceRepository>().GetInvoice(invoice7.Id);
|
||||
Assert.Equal(InvoiceStatusLegacy.New, invoiceEntity.Status);
|
||||
Assert.True(invoiceEntity.GetPayments().All(p => !p.Accounted));
|
||||
ourOutpoint = invoiceEntity.GetAllBitcoinPaymentData().First().PayjoinInformation.ContributedOutPoints[0];
|
||||
Assert.True(invoiceEntity.GetPayments(false).All(p => !p.Accounted));
|
||||
ourOutpoint = invoiceEntity.GetAllBitcoinPaymentData(false).First().PayjoinInformation.ContributedOutPoints[0];
|
||||
});
|
||||
var payjoinRepository = tester.PayTester.GetService<PayJoinRepository>();
|
||||
// The outpoint should now be available for next pj selection
|
||||
|
|
|
@ -1639,8 +1639,8 @@ namespace BTCPayServer.Tests
|
|||
{
|
||||
var i = await tester.PayTester.InvoiceRepository.GetInvoice(invoice2.Id);
|
||||
Assert.Equal(InvoiceStatusLegacy.New, i.Status);
|
||||
Assert.Single(i.GetPayments());
|
||||
Assert.False(i.GetPayments().First().Accounted);
|
||||
Assert.Single(i.GetPayments(false));
|
||||
Assert.False(i.GetPayments(false).First().Accounted);
|
||||
});
|
||||
|
||||
Logs.Tester.LogInformation(
|
||||
|
@ -1672,8 +1672,8 @@ namespace BTCPayServer.Tests
|
|||
await TestUtils.EventuallyAsync(async () =>
|
||||
{
|
||||
var invoiceEntity = await tester.PayTester.InvoiceRepository.GetInvoice(invoice.Id);
|
||||
var btcPayments = invoiceEntity.GetAllBitcoinPaymentData().ToArray();
|
||||
var payments = invoiceEntity.GetPayments().ToArray();
|
||||
var btcPayments = invoiceEntity.GetAllBitcoinPaymentData(false).ToArray();
|
||||
var payments = invoiceEntity.GetPayments(false).ToArray();
|
||||
Assert.Equal(tx1, btcPayments[0].Outpoint.Hash);
|
||||
Assert.False(payments[0].Accounted);
|
||||
Assert.Equal(tx1Bump, payments[1].Outpoint.Hash);
|
||||
|
|
|
@ -283,7 +283,7 @@ namespace BTCPayServer.Controllers.GreenField
|
|||
[Authorize(Policy = Policies.CanViewInvoices,
|
||||
AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
|
||||
[HttpGet("~/api/v1/stores/{storeId}/invoices/{invoiceId}/payment-methods")]
|
||||
public async Task<IActionResult> GetInvoicePaymentMethods(string storeId, string invoiceId)
|
||||
public async Task<IActionResult> GetInvoicePaymentMethods(string storeId, string invoiceId, bool onlyAccountedPayments = true)
|
||||
{
|
||||
var store = HttpContext.GetStoreData();
|
||||
if (store == null)
|
||||
|
@ -297,7 +297,7 @@ namespace BTCPayServer.Controllers.GreenField
|
|||
return InvoiceNotFound();
|
||||
}
|
||||
|
||||
return Ok(ToPaymentMethodModels(invoice));
|
||||
return Ok(ToPaymentMethodModels(invoice, onlyAccountedPayments));
|
||||
}
|
||||
|
||||
[Authorize(Policy = Policies.CanViewInvoices,
|
||||
|
@ -336,14 +336,14 @@ namespace BTCPayServer.Controllers.GreenField
|
|||
return this.CreateAPIError(404, "store-not-found", "The store was not found");
|
||||
}
|
||||
|
||||
private InvoicePaymentMethodDataModel[] ToPaymentMethodModels(InvoiceEntity entity)
|
||||
private InvoicePaymentMethodDataModel[] ToPaymentMethodModels(InvoiceEntity entity, bool includeAccountedPaymentOnly)
|
||||
{
|
||||
return entity.GetPaymentMethods().Select(
|
||||
method =>
|
||||
{
|
||||
var accounting = method.Calculate();
|
||||
var details = method.GetPaymentMethodDetails();
|
||||
var payments = method.ParentEntity.GetPayments().Where(paymentEntity =>
|
||||
var payments = method.ParentEntity.GetPayments(includeAccountedPaymentOnly).Where(paymentEntity =>
|
||||
paymentEntity.GetPaymentMethodId() == method.GetId());
|
||||
|
||||
return new InvoicePaymentMethodDataModel()
|
||||
|
|
|
@ -359,7 +359,7 @@ namespace BTCPayServer.Controllers
|
|||
return new InvoiceDetailsModel
|
||||
{
|
||||
Archived = invoice.Archived,
|
||||
Payments = invoice.GetPayments(),
|
||||
Payments = invoice.GetPayments(false),
|
||||
CryptoPayments = invoice.GetPaymentMethods().Select(
|
||||
data =>
|
||||
{
|
||||
|
@ -561,7 +561,7 @@ namespace BTCPayServer.Controllers
|
|||
Status = invoice.StatusString,
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
NetworkFee = paymentMethodDetails.GetNextNetworkFee(),
|
||||
IsMultiCurrency = invoice.GetPayments().Select(p => p.GetPaymentMethodId()).Concat(new[] { paymentMethod.GetId() }).Distinct().Count() > 1,
|
||||
IsMultiCurrency = invoice.GetPayments(false).Select(p => p.GetPaymentMethodId()).Concat(new[] { paymentMethod.GetId() }).Distinct().Count() > 1,
|
||||
StoreId = store.Id,
|
||||
AvailableCryptos = invoice.GetPaymentMethods()
|
||||
.Where(i => i.Network != null)
|
||||
|
|
|
@ -133,9 +133,9 @@ namespace BTCPayServer
|
|||
finally { try { webSocket.Dispose(); } catch { } }
|
||||
}
|
||||
|
||||
public static IEnumerable<BitcoinLikePaymentData> GetAllBitcoinPaymentData(this InvoiceEntity invoice)
|
||||
public static IEnumerable<BitcoinLikePaymentData> GetAllBitcoinPaymentData(this InvoiceEntity invoice, bool accountedOnly)
|
||||
{
|
||||
return invoice.GetPayments()
|
||||
return invoice.GetPayments(accountedOnly)
|
||||
.Where(p => p.GetPaymentMethodId()?.PaymentType == PaymentTypes.BTCLike)
|
||||
.Select(p => (BitcoinLikePaymentData)p.GetCryptoPaymentData())
|
||||
.Where(data => data != null);
|
||||
|
|
|
@ -101,7 +101,7 @@ namespace BTCPayServer.HostedServices
|
|||
}
|
||||
}
|
||||
|
||||
if (accounting.Paid < accounting.MinimumTotalDue && invoice.GetPayments().Count != 0 && invoice.ExceptionStatus != InvoiceExceptionStatus.PaidPartial)
|
||||
if (accounting.Paid < accounting.MinimumTotalDue && invoice.GetPayments(true).Count != 0 && invoice.ExceptionStatus != InvoiceExceptionStatus.PaidPartial)
|
||||
{
|
||||
invoice.ExceptionStatus = InvoiceExceptionStatus.PaidPartial;
|
||||
context.MarkDirty();
|
||||
|
@ -335,7 +335,7 @@ namespace BTCPayServer.HostedServices
|
|||
{
|
||||
bool extendInvoiceMonitoring = false;
|
||||
var updateConfirmationCountIfNeeded = invoice
|
||||
.GetPayments()
|
||||
.GetPayments(false)
|
||||
.Select<PaymentEntity, Task<PaymentEntity>>(async payment =>
|
||||
{
|
||||
var paymentData = payment.GetCryptoPaymentData();
|
||||
|
|
|
@ -46,7 +46,7 @@ namespace BTCPayServer.HostedServices
|
|||
UpdateTransactionLabel.InvoiceLabelTemplate(invoiceEvent.Invoice.Id)
|
||||
};
|
||||
|
||||
if (invoiceEvent.Invoice.GetPayments(invoiceEvent.Payment.GetCryptoCode()).Any(entity =>
|
||||
if (invoiceEvent.Invoice.GetPayments(invoiceEvent.Payment.GetCryptoCode(), false).Any(entity =>
|
||||
entity.GetCryptoPaymentData() is BitcoinLikePaymentData pData &&
|
||||
pData.PayjoinInformation?.CoinjoinTransactionHash == transactionId))
|
||||
{
|
||||
|
|
|
@ -111,8 +111,7 @@ namespace BTCPayServer.PaymentRequest
|
|||
State = state,
|
||||
StateFormatted = state.ToString(),
|
||||
Payments = entity
|
||||
.GetPayments()
|
||||
.Where(p => p.Accounted)
|
||||
.GetPayments(true)
|
||||
.Select(paymentEntity =>
|
||||
{
|
||||
var paymentData = paymentEntity.GetCryptoPaymentData();
|
||||
|
|
|
@ -157,7 +157,7 @@ namespace BTCPayServer.Payments.Bitcoin
|
|||
output.matchedOutput.Value, output.outPoint,
|
||||
evt.TransactionData.Transaction.RBF, output.Item1.KeyPath);
|
||||
|
||||
var alreadyExist = invoice.GetAllBitcoinPaymentData().Where(c => c.GetPaymentId() == paymentData.GetPaymentId()).Any();
|
||||
var alreadyExist = invoice.GetAllBitcoinPaymentData(false).Where(c => c.GetPaymentId() == paymentData.GetPaymentId()).Any();
|
||||
if (!alreadyExist)
|
||||
{
|
||||
var payment = await _InvoiceRepository.AddPayment(invoice.Id, DateTimeOffset.UtcNow, paymentData, network);
|
||||
|
@ -220,7 +220,7 @@ namespace BTCPayServer.Payments.Bitcoin
|
|||
{
|
||||
|
||||
List<PaymentEntity> updatedPaymentEntities = new List<PaymentEntity>();
|
||||
var transactions = await wallet.GetTransactions(invoice.GetAllBitcoinPaymentData()
|
||||
var transactions = await wallet.GetTransactions(invoice.GetAllBitcoinPaymentData(false)
|
||||
.Select(p => p.Outpoint.Hash)
|
||||
.ToArray(), true);
|
||||
bool? originalPJBroadcasted = null;
|
||||
|
@ -228,7 +228,7 @@ namespace BTCPayServer.Payments.Bitcoin
|
|||
bool cjPJBroadcasted = false;
|
||||
PayjoinInformation payjoinInformation = null;
|
||||
var paymentEntitiesByPrevOut = new Dictionary<OutPoint, PaymentEntity>();
|
||||
foreach (var payment in invoice.GetPayments(wallet.Network))
|
||||
foreach (var payment in invoice.GetPayments(wallet.Network, false))
|
||||
{
|
||||
if (payment.GetPaymentMethodId()?.PaymentType != PaymentTypes.BTCLike)
|
||||
continue;
|
||||
|
@ -347,7 +347,7 @@ namespace BTCPayServer.Payments.Bitcoin
|
|||
var invoice = await _InvoiceRepository.GetInvoice(invoiceId, true);
|
||||
if (invoice == null)
|
||||
continue;
|
||||
var alreadyAccounted = invoice.GetAllBitcoinPaymentData().Select(p => p.Outpoint).ToHashSet();
|
||||
var alreadyAccounted = invoice.GetAllBitcoinPaymentData(false).Select(p => p.Outpoint).ToHashSet();
|
||||
var strategy = GetDerivationStrategy(invoice, network);
|
||||
if (strategy == null)
|
||||
continue;
|
||||
|
|
|
@ -300,7 +300,7 @@ namespace BTCPayServer.Payments.PayJoin
|
|||
paymentAddress = paymentDetails.GetDepositAddress(network.NBitcoinNetwork);
|
||||
paymentAddressIndex = paymentDetails.KeyPath;
|
||||
|
||||
if (invoice.GetAllBitcoinPaymentData().Any())
|
||||
if (invoice.GetAllBitcoinPaymentData(false).Any())
|
||||
{
|
||||
ctx.DoNotBroadcast();
|
||||
return UnprocessableEntity(CreatePayjoinError("already-paid",
|
||||
|
|
|
@ -242,7 +242,7 @@ namespace BTCPayServer.Services.Altcoins.Ethereum.Services
|
|||
.Select(entity => (
|
||||
Invoice: entity,
|
||||
PaymentMethodDetails: entity.GetPaymentMethods().TryGet(paymentMethodId),
|
||||
ExistingPayments: entity.GetPayments(network).Select(paymentEntity => (Payment: paymentEntity,
|
||||
ExistingPayments: entity.GetPayments(network, true).Select(paymentEntity => (Payment: paymentEntity,
|
||||
PaymentData: (EthereumLikePaymentData)paymentEntity.GetCryptoPaymentData(),
|
||||
Invoice: entity))
|
||||
)).Where(tuple => tuple.PaymentMethodDetails?.GetPaymentMethodDetails()?.Activated is true).ToList();
|
||||
|
|
|
@ -372,7 +372,7 @@ namespace BTCPayServer.Services.Altcoins.Monero.Services
|
|||
|
||||
private IEnumerable<PaymentEntity> GetAllMoneroLikePayments(InvoiceEntity invoice, string cryptoCode)
|
||||
{
|
||||
return invoice.GetPayments()
|
||||
return invoice.GetPayments(false)
|
||||
.Where(p => p.GetPaymentMethodId() == new PaymentMethodId(cryptoCode, MoneroPaymentType.Instance));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -359,7 +359,7 @@ namespace BTCPayServer.Services.Apps
|
|||
|
||||
// If the user get a donation via other mean, he can register an invoice manually for such amount
|
||||
// then mark the invoice as complete
|
||||
var payments = p.GetPayments();
|
||||
var payments = p.GetPayments(true);
|
||||
if (payments.Count == 0 &&
|
||||
p.ExceptionStatus == InvoiceExceptionStatus.Marked &&
|
||||
p.Status == InvoiceStatusLegacy.Complete)
|
||||
|
|
|
@ -59,11 +59,8 @@ namespace BTCPayServer.Services.Invoices.Export
|
|||
var currency = Currencies.GetNumberFormatInfo(invoice.Currency, true);
|
||||
var invoiceDue = invoice.Price;
|
||||
// in this first version we are only exporting invoices that were paid
|
||||
foreach (var payment in invoice.GetPayments())
|
||||
foreach (var payment in invoice.GetPayments(true))
|
||||
{
|
||||
// not accounted payments are payments which got double spent like RBfed
|
||||
if (!payment.Accounted)
|
||||
continue;
|
||||
var cryptoCode = payment.GetPaymentMethodId().CryptoCode;
|
||||
var pdata = payment.GetCryptoPaymentData();
|
||||
|
||||
|
|
|
@ -326,17 +326,17 @@ namespace BTCPayServer.Services.Invoices
|
|||
public List<PaymentEntity> Payments { get; set; }
|
||||
|
||||
#pragma warning disable CS0618
|
||||
public List<PaymentEntity> GetPayments()
|
||||
public List<PaymentEntity> GetPayments(bool accountedOnly)
|
||||
{
|
||||
return Payments?.Where(entity => entity.GetPaymentMethodId() != null).ToList() ?? new List<PaymentEntity>();
|
||||
return Payments?.Where(entity => entity.GetPaymentMethodId() != null && (!accountedOnly || entity.Accounted)).ToList() ?? new List<PaymentEntity>();
|
||||
}
|
||||
public List<PaymentEntity> GetPayments(string cryptoCode)
|
||||
public List<PaymentEntity> GetPayments(string cryptoCode, bool accountedOnly)
|
||||
{
|
||||
return GetPayments().Where(p => p.CryptoCode == cryptoCode).ToList();
|
||||
return GetPayments(accountedOnly).Where(p => p.CryptoCode == cryptoCode).ToList();
|
||||
}
|
||||
public List<PaymentEntity> GetPayments(BTCPayNetworkBase network)
|
||||
public List<PaymentEntity> GetPayments(BTCPayNetworkBase network, bool accountedOnly)
|
||||
{
|
||||
return GetPayments(network.CryptoCode);
|
||||
return GetPayments(network.CryptoCode, accountedOnly);
|
||||
}
|
||||
#pragma warning restore CS0618
|
||||
public bool Refundable { get; set; }
|
||||
|
@ -449,7 +449,7 @@ namespace BTCPayServer.Services.Invoices
|
|||
var paymentId = info.GetId();
|
||||
cryptoInfo.Url = ServerUrl.WithTrailingSlash() + $"i/{paymentId}/{Id}";
|
||||
|
||||
cryptoInfo.Payments = GetPayments(info.Network).Select(entity =>
|
||||
cryptoInfo.Payments = GetPayments(info.Network, true).Select(entity =>
|
||||
{
|
||||
var data = entity.GetCryptoPaymentData();
|
||||
return new InvoicePaymentInfo()
|
||||
|
@ -980,8 +980,8 @@ namespace BTCPayServer.Services.Invoices
|
|||
bool paidEnough = paid >= Extensions.RoundUp(totalDue, precision);
|
||||
int txRequired = 0;
|
||||
|
||||
_ = ParentEntity.GetPayments()
|
||||
.Where(p => p.Accounted && paymentPredicate(p))
|
||||
_ = ParentEntity.GetPayments(true)
|
||||
.Where(p => paymentPredicate(p))
|
||||
.OrderBy(p => p.ReceivedTime)
|
||||
.Select(_ =>
|
||||
{
|
||||
|
|
|
@ -347,6 +347,16 @@
|
|||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "onlyAccountedPayments",
|
||||
"in": "query",
|
||||
"required": false,
|
||||
"description": "If default or true, only returns payments which are accounted (in Bitcoin, this mean not returning RBF'd or double spent payments)",
|
||||
"schema": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"description": "View information about the specified invoice's payment methods",
|
||||
|
|
Loading…
Add table
Reference in a new issue