Fix the states of invoice to match bitpay

This commit is contained in:
nicolas.dorier 2017-10-23 17:44:04 +09:00
parent 6d14fe9c30
commit 6ba6a34df2
4 changed files with 35 additions and 35 deletions

View file

@ -255,7 +255,7 @@ namespace BTCPayServer.Tests
{ {
tester.SimulateCallback(invoiceAddress); tester.SimulateCallback(invoiceAddress);
var localInvoice = user.BitPay.GetInvoice(invoice.Id, Facade.Merchant); var localInvoice = user.BitPay.GetInvoice(invoice.Id, Facade.Merchant);
Assert.Equal("paidPartial", localInvoice.Status); Assert.Equal("new", localInvoice.Status);
Assert.Equal(firstPayment, localInvoice.BtcPaid); Assert.Equal(firstPayment, localInvoice.BtcPaid);
txFee = localInvoice.BtcDue - invoice.BtcDue; txFee = localInvoice.BtcDue - invoice.BtcDue;
Assert.Equal("paidPartial", localInvoice.ExceptionStatus); Assert.Equal("paidPartial", localInvoice.ExceptionStatus);
@ -311,7 +311,7 @@ namespace BTCPayServer.Tests
{ {
tester.SimulateCallback(invoiceAddress); tester.SimulateCallback(invoiceAddress);
var localInvoice = user.BitPay.GetInvoice(invoice.Id, Facade.Merchant); var localInvoice = user.BitPay.GetInvoice(invoice.Id, Facade.Merchant);
Assert.Equal("paidOver", localInvoice.Status); Assert.Equal("paid", localInvoice.Status);
Assert.Equal(Money.Zero, localInvoice.BtcDue); Assert.Equal(Money.Zero, localInvoice.BtcDue);
Assert.Equal("paidOver", (string)((JValue)localInvoice.ExceptionStatus).Value); Assert.Equal("paidOver", (string)((JValue)localInvoice.ExceptionStatus).Value);
}); });

View file

@ -2,7 +2,7 @@
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.0</TargetFramework> <TargetFramework>netcoreapp2.0</TargetFramework>
<Version>1.0.0.20</Version> <Version>1.0.0.21</Version>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Compile Remove="Build\dockerfiles\**" /> <Compile Remove="Build\dockerfiles\**" />

View file

@ -107,13 +107,13 @@ namespace BTCPayServer.Services.Invoices
{ {
bool needSave = false; bool needSave = false;
if(invoice.Status != "invalid" && invoice.ExpirationTime < DateTimeOffset.UtcNow && (invoice.Status == "new" || invoice.Status == "paidPartial")) if(invoice.Status == "new" && invoice.ExpirationTime < DateTimeOffset.UtcNow)
{ {
needSave = true; needSave = true;
invoice.Status = "invalid"; invoice.Status = "expired";
} }
if(invoice.Status == "invalid" || invoice.Status == "new" || invoice.Status == "paidPartial") if(invoice.Status == "expired" || invoice.Status == "new" || invoice.Status == "invalid")
{ {
var strategy = _DerivationFactory.Parse(invoice.DerivationStrategy); var strategy = _DerivationFactory.Parse(invoice.DerivationStrategy);
changes = await _ExplorerClient.SyncAsync(strategy, changes, !LongPollingMode, _Cts.Token).ConfigureAwait(false); changes = await _ExplorerClient.SyncAsync(strategy, changes, !LongPollingMode, _Cts.Token).ConfigureAwait(false);
@ -135,18 +135,19 @@ namespace BTCPayServer.Services.Invoices
{ {
var payment = await _InvoiceRepository.AddPayment(invoice.Id, coin).ConfigureAwait(false); var payment = await _InvoiceRepository.AddPayment(invoice.Id, coin).ConfigureAwait(false);
invoice.Payments.Add(payment); invoice.Payments.Add(payment);
if(invoice.Status == "new") if(invoice.Status == "expired")
{ {
invoice.Status = "paidPartial"; if(invoice.ExceptionStatus == null)
invoice.ExceptionStatus = "paidLate";
needSave = true; needSave = true;
} }
} }
} }
if(invoice.Status == "paidPartial") if(invoice.Status == "new")
{ {
var totalPaid = invoice.Payments.Select(p => p.Output.Value).Sum(); var totalPaid = invoice.Payments.Select(p => p.Output.Value).Sum();
if(totalPaid == invoice.GetTotalCryptoDue()) if(totalPaid >= invoice.GetTotalCryptoDue())
{ {
invoice.Status = "paid"; invoice.Status = "paid";
if(invoice.FullNotifications) if(invoice.FullNotifications)
@ -157,45 +158,37 @@ namespace BTCPayServer.Services.Invoices
needSave = true; needSave = true;
} }
if(totalPaid > invoice.GetTotalCryptoDue()) if(totalPaid > invoice.GetTotalCryptoDue() && invoice.ExceptionStatus != "paidOver")
{ {
invoice.Status = "paidOver";
invoice.ExceptionStatus = "paidOver"; invoice.ExceptionStatus = "paidOver";
needSave = true; needSave = true;
} }
if(totalPaid < invoice.GetTotalCryptoDue() && invoice.ExceptionStatus == null) if(totalPaid < invoice.GetTotalCryptoDue() && invoice.Payments.Count != 0 && invoice.ExceptionStatus != "paidPartial")
{ {
invoice.ExceptionStatus = "paidPartial"; invoice.ExceptionStatus = "paidPartial";
needSave = true; needSave = true;
} }
} }
if(invoice.Status == "paid" || invoice.Status == "paidOver") if(invoice.Status == "paid")
{ {
var getTransactions = invoice.Payments.Select(o => o.Outpoint.Hash).Select(o => _ExplorerClient.GetTransactionAsync(o, _Cts.Token)).ToArray(); var transactions = await GetPaymentsWithTransaction(invoice);
await Task.WhenAll(getTransactions).ConfigureAwait(false);
var transactions = getTransactions.Select(c => c.GetAwaiter().GetResult()).ToArray();
bool confirmed = false;
var minConf = transactions.Select(t => t.Confirmations).Min();
if(invoice.SpeedPolicy == SpeedPolicy.HighSpeed) if(invoice.SpeedPolicy == SpeedPolicy.HighSpeed)
{ {
if(minConf > 0) transactions = transactions.Where(t => !t.Transaction.Transaction.RBF);
confirmed = true;
else
confirmed = !transactions.Any(t => t.Transaction.RBF);
} }
else if(invoice.SpeedPolicy == SpeedPolicy.MediumSpeed) else if(invoice.SpeedPolicy == SpeedPolicy.MediumSpeed)
{ {
confirmed = minConf >= 1; transactions = transactions.Where(t => t.Transaction.Confirmations >= 1);
} }
else if(invoice.SpeedPolicy == SpeedPolicy.LowSpeed) else if(invoice.SpeedPolicy == SpeedPolicy.LowSpeed)
{ {
confirmed = minConf >= 6; transactions = transactions.Where(t => t.Transaction.Confirmations >= 6);
} }
if(confirmed) var totalConfirmed = transactions.Select(t => t.Payment.Output.Value).Sum();
if(totalConfirmed >= invoice.GetTotalCryptoDue())
{ {
invoice.Status = "confirmed"; invoice.Status = "confirmed";
_NotificationManager.Notify(invoice); _NotificationManager.Notify(invoice);
@ -205,11 +198,10 @@ namespace BTCPayServer.Services.Invoices
if(invoice.Status == "confirmed") if(invoice.Status == "confirmed")
{ {
var getTransactions = invoice.Payments.Select(o => o.Outpoint.Hash).Select(o => _ExplorerClient.GetTransactionAsync(o, _Cts.Token)).ToArray(); var transactions = await GetPaymentsWithTransaction(invoice);
await Task.WhenAll(getTransactions).ConfigureAwait(false); transactions = transactions.Where(t => t.Transaction.Confirmations >= 6);
var transactions = getTransactions.Select(c => c.GetAwaiter().GetResult()).ToArray(); var totalConfirmed = transactions.Select(t => t.Payment.Output.Value).Sum();
var minConf = transactions.Select(t => t.Confirmations).Min(); if(totalConfirmed >= invoice.GetTotalCryptoDue())
if(minConf >= 6)
{ {
invoice.Status = "complete"; invoice.Status = "complete";
if(invoice.FullNotifications) if(invoice.FullNotifications)
@ -221,6 +213,15 @@ namespace BTCPayServer.Services.Invoices
return (needSave, changes); return (needSave, changes);
} }
private async Task<IEnumerable<(PaymentEntity Payment, TransactionResult Transaction)>> GetPaymentsWithTransaction(InvoiceEntity invoice)
{
var getPayments = invoice.Payments
.Select(async o => (Payment: o, Transaction: await _ExplorerClient.GetTransactionAsync(o.Outpoint.Hash, _Cts.Token)))
.ToArray();
await Task.WhenAll(getPayments).ConfigureAwait(false);
var transactions = getPayments.Select(c => (Payment: c.Result.Payment, Transaction: c.Result.Transaction));
return transactions;
}
TimeSpan _PollInterval; TimeSpan _PollInterval;
public TimeSpan PollInterval public TimeSpan PollInterval

View file

@ -166,7 +166,6 @@ function onDataCallback(jsonData) {
window.parent.postMessage({ "invoiceId": srvModel.invoiceId, "status": newStatus }, "*"); window.parent.postMessage({ "invoiceId": srvModel.invoiceId, "status": newStatus }, "*");
} }
if (newStatus == "complete" || if (newStatus == "complete" ||
newStatus == "paidOver" ||
newStatus == "confirmed" || newStatus == "confirmed" ||
newStatus == "paid") { newStatus == "paid") {
if ($(".modal-dialog").hasClass("expired")) { if ($(".modal-dialog").hasClass("expired")) {
@ -192,7 +191,7 @@ function onDataCallback(jsonData) {
$("#paid").addClass("active"); $("#paid").addClass("active");
} }
if (newStatus == "invalid") { if (newStatus == "expired" || newStatus == "invalid") { //TODO: different state if the invoice is invalid (failed to confirm after timeout)
$(".timer-row").removeClass("expiring-soon"); $(".timer-row").removeClass("expiring-soon");
$(".timer-row__message span").html("Invoice expired."); $(".timer-row__message span").html("Invoice expired.");
$(".timer-row__spinner").html(""); $(".timer-row__spinner").html("");