Merge pull request #2079 from NicolasDorier/state-renaming

[Greenfield BREAKING CHANGE] Rename invoice states and payment states
This commit is contained in:
Nicolas Dorier 2020-11-23 19:27:41 +09:00 committed by GitHub
commit 8405ce6369
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 179 additions and 159 deletions

View File

@ -59,7 +59,7 @@ namespace BTCPayServer.Client
{
if (request == null)
throw new ArgumentNullException(nameof(request));
if (request.Status!= InvoiceStatus.Complete && request.Status!= InvoiceStatus.Invalid)
if (request.Status!= InvoiceStatus.Settled && request.Status!= InvoiceStatus.Invalid)
throw new ArgumentOutOfRangeException(nameof(request.Status), "Status can only be Invalid or Complete");
var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/invoices/{invoiceId}/status", bodyPayload: request,

View File

@ -18,4 +18,12 @@ namespace BTCPayServer.Client.Models
[JsonConverter(typeof(NBitcoin.JsonConverters.DateTimeToUnixTimeConverter))]
public DateTimeOffset CreatedTime { get; set; }
}
public enum InvoiceStatus
{
New,
Processing,
Expired,
Invalid,
Settled
}
}

View File

@ -53,8 +53,8 @@ namespace BTCPayServer.Client.Models
public enum PaymentStatus
{
Invalid,
AwaitingCompletion,
Complete
Processing,
Settled
}
}
}

View File

@ -1,12 +0,0 @@
namespace BTCPayServer.Client.Models
{
public enum InvoiceStatus
{
New,
Paid,
Expired,
Invalid,
Complete,
Confirmed
}
}

View File

@ -8,9 +8,9 @@ namespace BTCPayServer.Client.Models
{
InvoiceCreated,
InvoiceReceivedPayment,
InvoicePaidInFull,
InvoiceProcessing,
InvoiceExpired,
InvoiceConfirmed,
InvoiceSettled,
InvoiceInvalid
}
}

View File

@ -22,13 +22,13 @@ namespace BTCPayServer.Client.Models
public string InvoiceId { get; set; }
}
public class WebhookInvoiceConfirmedEvent : WebhookInvoiceEvent
public class WebhookInvoiceSettledEvent : WebhookInvoiceEvent
{
public WebhookInvoiceConfirmedEvent()
public WebhookInvoiceSettledEvent()
{
}
public WebhookInvoiceConfirmedEvent(WebhookEventType evtType) : base(evtType)
public WebhookInvoiceSettledEvent(WebhookEventType evtType) : base(evtType)
{
}
@ -46,18 +46,17 @@ namespace BTCPayServer.Client.Models
public bool ManuallyMarked { get; set; }
}
public class WebhookInvoicePaidEvent : WebhookInvoiceEvent
public class WebhookInvoiceProcessingEvent : WebhookInvoiceEvent
{
public WebhookInvoicePaidEvent()
public WebhookInvoiceProcessingEvent()
{
}
public WebhookInvoicePaidEvent(WebhookEventType evtType) : base(evtType)
public WebhookInvoiceProcessingEvent(WebhookEventType evtType) : base(evtType)
{
}
public bool OverPaid { get; set; }
public bool PaidAfterExpiration { get; set; }
}
public class WebhookInvoiceReceivedPaymentEvent : WebhookInvoiceEvent
{

View File

@ -949,10 +949,10 @@ namespace BTCPayServer.Tests
var invoice = await viewOnly.GetInvoice(user.StoreId, newInvoice.Id);
Assert.Equal(newInvoice.Metadata, invoice.Metadata);
var paymentMethods = await viewOnly.GetInvoicePaymentMethods(user.StoreId, newInvoice.Id);
Assert.Equal(1, paymentMethods.Length);
Assert.Single(paymentMethods);
var paymentMethod = paymentMethods.First();
Assert.Equal("BTC", paymentMethod.PaymentMethod);
Assert.Equal(0, paymentMethod.Payments.Count);
Assert.Empty(paymentMethod.Payments);
//update
@ -962,7 +962,7 @@ namespace BTCPayServer.Tests
{
await client.MarkInvoiceStatus(user.StoreId, invoice.Id, new MarkInvoiceStatusRequest()
{
Status = InvoiceStatus.Complete
Status = InvoiceStatus.Settled
});
});
@ -982,7 +982,7 @@ namespace BTCPayServer.Tests
Assert.NotNull(await client.GetInvoice(user.StoreId, invoice.Id));
foreach (var marked in new[] { InvoiceStatus.Complete, InvoiceStatus.Invalid })
foreach (var marked in new[] { InvoiceStatus.Settled, InvoiceStatus.Invalid })
{
var inv = await client.CreateInvoice(user.StoreId,
new CreateInvoiceRequest() { Currency = "USD", Amount = 100 });
@ -992,10 +992,10 @@ namespace BTCPayServer.Tests
Status = marked
});
var result = await client.GetInvoice(user.StoreId, inv.Id);
if (marked == InvoiceStatus.Complete)
if (marked == InvoiceStatus.Settled)
{
Assert.Equal(InvoiceStatus.Complete, result.Status);
user.AssertHasWebhookEvent<WebhookInvoiceConfirmedEvent>(WebhookEventType.InvoiceConfirmed,
Assert.Equal(InvoiceStatus.Settled, result.Status);
user.AssertHasWebhookEvent<WebhookInvoiceSettledEvent>(WebhookEventType.InvoiceSettled,
o =>
{
Assert.Equal(inv.Id, o.InvoiceId);

View File

@ -270,7 +270,7 @@ namespace BTCPayServer.Tests
await TestUtils.EventuallyAsync(async () =>
{
var invoice = await s.Server.PayTester.GetService<InvoiceRepository>().GetInvoice(invoiceId);
Assert.Equal(InvoiceStatus.Paid, invoice.Status);
Assert.Equal(InvoiceStatusLegacy.Paid, invoice.Status);
});
s.GoToInvoices();
@ -330,7 +330,7 @@ namespace BTCPayServer.Tests
{
var invoice = await s.Server.PayTester.GetService<InvoiceRepository>().GetInvoice(invoiceId);
var dto = invoice.EntityToDTO();
Assert.Equal(InvoiceStatus.Paid, invoice.Status);
Assert.Equal(InvoiceStatusLegacy.Paid, invoice.Status);
});
s.GoToInvoices();
paymentValueRowColumn = s.Driver.FindElement(By.Id($"invoice_{invoiceId}"))
@ -720,7 +720,7 @@ retry:
await TestUtils.EventuallyAsync(async () =>
{
var invoice = await tester.PayTester.GetService<InvoiceRepository>().GetInvoice(lastInvoiceId);
Assert.Equal(InvoiceStatus.Paid, invoice.Status);
Assert.Equal(InvoiceStatusLegacy.Paid, invoice.Status);
Assert.Equal(InvoiceExceptionStatus.None, invoice.ExceptionStatus);
var coins = await btcPayWallet.GetUnspentCoins(receiverUser.DerivationScheme);
foreach (var coin in coins)
@ -1047,7 +1047,7 @@ retry:
await TestUtils.EventuallyAsync(async () =>
{
var invoiceEntity = await tester.PayTester.GetService<InvoiceRepository>().GetInvoice(invoice7.Id);
Assert.Equal(InvoiceStatus.Paid, invoiceEntity.Status);
Assert.Equal(InvoiceStatusLegacy.Paid, invoiceEntity.Status);
Assert.Contains(invoiceEntity.GetPayments(), p => p.Accounted &&
((BitcoinLikePaymentData)p.GetCryptoPaymentData()).PayjoinInformation is null);
});
@ -1076,7 +1076,7 @@ retry:
await TestUtils.EventuallyAsync(async () =>
{
var invoiceEntity = await tester.PayTester.GetService<InvoiceRepository>().GetInvoice(invoice7.Id);
Assert.Equal(InvoiceStatus.New, invoiceEntity.Status);
Assert.Equal(InvoiceStatusLegacy.New, invoiceEntity.Status);
Assert.True(invoiceEntity.GetPayments().All(p => !p.Accounted));
ourOutpoint = invoiceEntity.GetAllBitcoinPaymentData().First().PayjoinInformation.ContributedOutPoints[0];
});

View File

@ -207,12 +207,12 @@ namespace BTCPayServer.Tests
pair => pair.Key == "Id" && pair.Value.ToString() == invoiceId);
var invoice = user.BitPay.GetInvoice(invoiceId, Facade.Merchant);
Assert.Equal(InvoiceState.ToString(InvoiceStatus.New), invoice.Status);
Assert.Equal(InvoiceState.ToString(InvoiceStatusLegacy.New), invoice.Status);
Assert.IsType<OkObjectResult>(await
paymentRequestController.CancelUnpaidPendingInvoice(paymentRequestId, false));
invoice = user.BitPay.GetInvoice(invoiceId, Facade.Merchant);
Assert.Equal(InvoiceState.ToString(InvoiceStatus.Invalid), invoice.Status);
Assert.Equal(InvoiceState.ToString(InvoiceStatusLegacy.Invalid), invoice.Status);
Assert.IsType<BadRequestObjectResult>(await
paymentRequestController.CancelUnpaidPendingInvoice(paymentRequestId, false));

View File

@ -624,7 +624,7 @@ namespace BTCPayServer.Tests
new SelectElement(s.Driver.FindElement(By.Name("Everything")))
.SelectByValue("false");
s.Driver.FindElement(By.Id("InvoiceCreated")).Click();
s.Driver.FindElement(By.Id("InvoicePaidInFull")).Click();
s.Driver.FindElement(By.Id("InvoiceProcessing")).Click();
s.Driver.FindElement(By.Name("add")).Click();
}
@ -657,7 +657,7 @@ namespace BTCPayServer.Tests
Assert.Contains($"value=\"{value}\"", s.Driver.PageSource);
}
// This one should be checked
Assert.Contains($"value=\"InvoicePaidInFull\" checked", s.Driver.PageSource);
Assert.Contains($"value=\"InvoiceProcessing\" checked", s.Driver.PageSource);
Assert.Contains($"value=\"InvoiceCreated\" checked", s.Driver.PageSource);
// This one never been checked
Assert.DoesNotContain($"value=\"InvoiceReceivedPayment\" checked", s.Driver.PageSource);

View File

@ -785,7 +785,7 @@ namespace BTCPayServer.Tests
}, evt => evt.InvoiceId == invoice.Id);
var fetchedInvoice = await tester.PayTester.InvoiceRepository.GetInvoice(evt.InvoiceId);
Assert.Contains(fetchedInvoice.Status, new[] { InvoiceStatus.Complete, InvoiceStatus.Confirmed });
Assert.Contains(fetchedInvoice.Status, new[] { InvoiceStatusLegacy.Complete, InvoiceStatusLegacy.Confirmed });
Assert.Equal(InvoiceExceptionStatus.None, fetchedInvoice.ExceptionStatus);
Logs.Tester.LogInformation($"Paying invoice {invoice.Id} original full amount bolt11 invoice ");
@ -1090,7 +1090,7 @@ namespace BTCPayServer.Tests
response.EnsureSuccessStatusCode();
AssertConnectionDropped();
Logs.Tester.LogInformation("Querying an onion address which can't be found should send http 500");
Logs.Tester.LogInformation("Querying an onin address which can't be found should send http 500");
response = await client.GetAsync("http://dwoduwoi.onion/");
Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode);
AssertConnectionDropped();
@ -1460,7 +1460,7 @@ namespace BTCPayServer.Tests
await TestUtils.EventuallyAsync(async () =>
{
var i = await tester.PayTester.InvoiceRepository.GetInvoice(invoice2.Id);
Assert.Equal(InvoiceStatus.New, i.Status);
Assert.Equal(InvoiceStatusLegacy.New, i.Status);
Assert.Single(i.GetPayments());
Assert.False(i.GetPayments().First().Accounted);
});
@ -2743,12 +2743,12 @@ namespace BTCPayServer.Tests
});
// Test on the webhooks
user.AssertHasWebhookEvent<WebhookInvoiceConfirmedEvent>(WebhookEventType.InvoiceConfirmed,
user.AssertHasWebhookEvent<WebhookInvoiceSettledEvent>(WebhookEventType.InvoiceSettled,
c =>
{
Assert.False(c.ManuallyMarked);
});
user.AssertHasWebhookEvent<WebhookInvoicePaidEvent>(WebhookEventType.InvoicePaidInFull,
user.AssertHasWebhookEvent<WebhookInvoiceProcessingEvent>(WebhookEventType.InvoiceProcessing,
c =>
{
Assert.True(c.OverPaid);

View File

@ -268,8 +268,8 @@ namespace BTCPayServer.Controllers.GreenField
? InvoicePaymentMethodDataModel.Payment.PaymentStatus.Invalid
: data.PaymentConfirmed(paymentEntity, entity.SpeedPolicy) ||
data.PaymentCompleted(paymentEntity)
? InvoicePaymentMethodDataModel.Payment.PaymentStatus.Complete
: InvoicePaymentMethodDataModel.Payment.PaymentStatus.AwaitingCompletion,
? InvoicePaymentMethodDataModel.Payment.PaymentStatus.Settled
: InvoicePaymentMethodDataModel.Payment.PaymentStatus.Processing,
Fee = paymentEntity.NetworkFee,
Value = data.GetValue(),
ReceivedDate = paymentEntity.ReceivedTime.DateTime
@ -287,7 +287,7 @@ namespace BTCPayServer.Controllers.GreenField
CreatedTime = entity.InvoiceTime,
Amount = entity.Price,
Id = entity.Id,
Status = entity.Status,
Status = entity.Status.ToModernStatus(),
AdditionalStatus = entity.ExceptionStatus,
Currency = entity.Currency,
Metadata = entity.Metadata.ToJObject(),

View File

@ -150,13 +150,13 @@ namespace BTCPayServer.Controllers
bool CanRefund(InvoiceState invoiceState)
{
return invoiceState.Status == InvoiceStatus.Confirmed ||
invoiceState.Status == InvoiceStatus.Complete ||
(invoiceState.Status == InvoiceStatus.Expired &&
return invoiceState.Status == InvoiceStatusLegacy.Confirmed ||
invoiceState.Status == InvoiceStatusLegacy.Complete ||
(invoiceState.Status == InvoiceStatusLegacy.Expired &&
(invoiceState.ExceptionStatus == InvoiceExceptionStatus.PaidLate ||
invoiceState.ExceptionStatus == InvoiceExceptionStatus.PaidOver ||
invoiceState.ExceptionStatus == InvoiceExceptionStatus.PaidPartial)) ||
invoiceState.Status == InvoiceStatus.Invalid;
invoiceState.Status == InvoiceStatusLegacy.Invalid;
}
[HttpGet]
@ -638,7 +638,7 @@ namespace BTCPayServer.Controllers
if (!HttpContext.WebSockets.IsWebSocketRequest)
return NotFound();
var invoice = await _InvoiceRepository.GetInvoice(invoiceId);
if (invoice == null || invoice.Status == InvoiceStatus.Complete || invoice.Status == InvoiceStatus.Invalid || invoice.Status == InvoiceStatus.Expired)
if (invoice == null || invoice.Status == InvoiceStatusLegacy.Complete || invoice.Status == InvoiceStatusLegacy.Invalid || invoice.Status == InvoiceStatusLegacy.Expired)
return NotFound();
var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync();
CompositeDisposable leases = new CompositeDisposable();
@ -715,7 +715,7 @@ namespace BTCPayServer.Controllers
model.Invoices.Add(new InvoiceModel()
{
Status = state,
ShowCheckout = invoice.Status == InvoiceStatus.New,
ShowCheckout = invoice.Status == InvoiceStatusLegacy.New,
Date = invoice.InvoiceTime,
InvoiceId = invoice.Id,
OrderId = invoice.Metadata.OrderId ?? string.Empty,
@ -873,7 +873,7 @@ namespace BTCPayServer.Controllers
}
else if (newState == "complete")
{
await _InvoiceRepository.MarkInvoiceStatus(invoiceId, InvoiceStatus.Complete);
await _InvoiceRepository.MarkInvoiceStatus(invoiceId, InvoiceStatus.Settled);
model.StatusString = new InvoiceState("complete", "marked").ToString();
}

View File

@ -200,7 +200,7 @@ namespace BTCPayServer.Controllers
throw new BitpayHttpException(400, "Invalid email");
entity.RefundMail = entity.Metadata.BuyerEmail;
}
entity.Status = InvoiceStatus.New;
entity.Status = InvoiceStatusLegacy.New;
HashSet<CurrencyPair> currencyPairsToFetch = new HashSet<CurrencyPair>();
var rules = storeBlob.GetRateRules(_NetworkProvider);
var excludeFilter = storeBlob.GetExcludedPaymentMethods(); // Here we can compose filters from other origin with PaymentFilter.Any()

View File

@ -238,8 +238,8 @@ namespace BTCPayServer.Controllers
var stateAllowedToDisplay = new HashSet<InvoiceState>()
{
new InvoiceState(InvoiceStatus.New, InvoiceExceptionStatus.None),
new InvoiceState(InvoiceStatus.New, InvoiceExceptionStatus.PaidPartial),
new InvoiceState(InvoiceStatusLegacy.New, InvoiceExceptionStatus.None),
new InvoiceState(InvoiceStatusLegacy.New, InvoiceExceptionStatus.PaidPartial),
};
var currentInvoice = result
.Invoices
@ -303,7 +303,7 @@ namespace BTCPayServer.Controllers
}
var invoices = result.Invoices.Where(requestInvoice =>
requestInvoice.State.Status == InvoiceStatus.New && !requestInvoice.Payments.Any());
requestInvoice.State.Status == InvoiceStatusLegacy.New && !requestInvoice.Payments.Any());
if (!invoices.Any())
{

View File

@ -86,7 +86,7 @@ namespace BTCPayServer.Controllers
.ToArray();
var firstInvoiceStillPending =
matchedExistingInvoices.FirstOrDefault(entity => entity.GetInvoiceState().Status == InvoiceStatus.New);
matchedExistingInvoices.FirstOrDefault(entity => entity.GetInvoiceState().Status == InvoiceStatusLegacy.New);
if (firstInvoiceStillPending != null)
{
return Ok(new
@ -98,7 +98,7 @@ namespace BTCPayServer.Controllers
var firstInvoiceSettled =
matchedExistingInvoices.LastOrDefault(entity =>
new[] {InvoiceStatus.Paid, InvoiceStatus.Complete, InvoiceStatus.Confirmed}.Contains(
new[] {InvoiceStatusLegacy.Paid, InvoiceStatusLegacy.Complete, InvoiceStatusLegacy.Confirmed}.Contains(
entity.GetInvoiceState().Status));
var store = await _Repo.FindStore(storeId);
@ -119,7 +119,7 @@ namespace BTCPayServer.Controllers
{
//if BTCPay was shut down before the tx managed to get registered on shopify, this will fix it on the next UI load in shopify
if (client != null && order?.FinancialStatus == "pending" &&
firstInvoiceSettled.Status != InvoiceStatus.Paid)
firstInvoiceSettled.Status != InvoiceStatusLegacy.Paid)
{
await new OrderTransactionRegisterLogic(client).Process(orderId, firstInvoiceSettled.Id,
firstInvoiceSettled.Currency,

View File

@ -99,11 +99,11 @@ namespace BTCPayServer.HostedServices
// So here, we just override the status expressed by the notification
if (invoiceEvent.Name == InvoiceEvent.Confirmed)
{
notification.Data.Status = InvoiceState.ToString(InvoiceStatus.Confirmed);
notification.Data.Status = InvoiceState.ToString(InvoiceStatusLegacy.Confirmed);
}
if (invoiceEvent.Name == InvoiceEvent.PaidInFull)
{
notification.Data.Status = InvoiceState.ToString(InvoiceStatus.Paid);
notification.Data.Status = InvoiceState.ToString(InvoiceStatusLegacy.Paid);
}
//////////////////

View File

@ -68,11 +68,11 @@ namespace BTCPayServer.HostedServices
private void UpdateInvoice(UpdateInvoiceContext context)
{
var invoice = context.Invoice;
if (invoice.Status == InvoiceStatus.New && invoice.ExpirationTime <= DateTimeOffset.UtcNow)
if (invoice.Status == InvoiceStatusLegacy.New && invoice.ExpirationTime <= DateTimeOffset.UtcNow)
{
context.MarkDirty();
context.UnaffectAddresses();
invoice.Status = InvoiceStatus.Expired;
invoice.Status = InvoiceStatusLegacy.Expired;
context.Events.Add(new InvoiceEvent(invoice, InvoiceEvent.Expired));
if (invoice.ExceptionStatus == InvoiceExceptionStatus.PaidPartial)
context.Events.Add(new InvoiceEvent(invoice, InvoiceEvent.ExpiredPaidPartial));
@ -81,19 +81,19 @@ namespace BTCPayServer.HostedServices
var paymentMethod = GetNearestClearedPayment(allPaymentMethods, out var accounting);
if (paymentMethod == null)
return;
if (invoice.Status == InvoiceStatus.New || invoice.Status == InvoiceStatus.Expired)
if (invoice.Status == InvoiceStatusLegacy.New || invoice.Status == InvoiceStatusLegacy.Expired)
{
if (accounting.Paid >= accounting.MinimumTotalDue)
{
if (invoice.Status == InvoiceStatus.New)
if (invoice.Status == InvoiceStatusLegacy.New)
{
context.Events.Add(new InvoiceEvent(invoice, InvoiceEvent.PaidInFull));
invoice.Status = InvoiceStatus.Paid;
invoice.Status = InvoiceStatusLegacy.Paid;
invoice.ExceptionStatus = accounting.Paid > accounting.TotalDue ? InvoiceExceptionStatus.PaidOver : InvoiceExceptionStatus.None;
context.UnaffectAddresses();
context.MarkDirty();
}
else if (invoice.Status == InvoiceStatus.Expired && invoice.ExceptionStatus != InvoiceExceptionStatus.PaidLate)
else if (invoice.Status == InvoiceStatusLegacy.Expired && invoice.ExceptionStatus != InvoiceExceptionStatus.PaidLate)
{
invoice.ExceptionStatus = InvoiceExceptionStatus.PaidLate;
context.Events.Add(new InvoiceEvent(invoice, InvoiceEvent.PaidAfterExpiration));
@ -109,7 +109,7 @@ namespace BTCPayServer.HostedServices
}
// Just make sure RBF did not cancelled a payment
if (invoice.Status == InvoiceStatus.Paid)
if (invoice.Status == InvoiceStatusLegacy.Paid)
{
if (accounting.MinimumTotalDue <= accounting.Paid && accounting.Paid <= accounting.TotalDue && invoice.ExceptionStatus == InvoiceExceptionStatus.PaidOver)
{
@ -125,13 +125,13 @@ namespace BTCPayServer.HostedServices
if (accounting.Paid < accounting.MinimumTotalDue)
{
invoice.Status = InvoiceStatus.New;
invoice.Status = InvoiceStatusLegacy.New;
invoice.ExceptionStatus = accounting.Paid == Money.Zero ? InvoiceExceptionStatus.None : InvoiceExceptionStatus.PaidPartial;
context.MarkDirty();
}
}
if (invoice.Status == InvoiceStatus.Paid)
if (invoice.Status == InvoiceStatusLegacy.Paid)
{
var confirmedAccounting = paymentMethod.Calculate(p => p.GetCryptoPaymentData().PaymentConfirmed(p, invoice.SpeedPolicy));
@ -143,25 +143,25 @@ namespace BTCPayServer.HostedServices
{
context.UnaffectAddresses();
context.Events.Add(new InvoiceEvent(invoice, InvoiceEvent.FailedToConfirm));
invoice.Status = InvoiceStatus.Invalid;
invoice.Status = InvoiceStatusLegacy.Invalid;
context.MarkDirty();
}
else if (confirmedAccounting.Paid >= accounting.MinimumTotalDue)
{
context.UnaffectAddresses();
invoice.Status = InvoiceStatus.Confirmed;
invoice.Status = InvoiceStatusLegacy.Confirmed;
context.Events.Add(new InvoiceEvent(invoice, InvoiceEvent.Confirmed));
context.MarkDirty();
}
}
if (invoice.Status == InvoiceStatus.Confirmed)
if (invoice.Status == InvoiceStatusLegacy.Confirmed)
{
var completedAccounting = paymentMethod.Calculate(p => p.GetCryptoPaymentData().PaymentCompleted(p));
if (completedAccounting.Paid >= accounting.MinimumTotalDue)
{
context.Events.Add(new InvoiceEvent(invoice, InvoiceEvent.Completed));
invoice.Status = InvoiceStatus.Complete;
invoice.Status = InvoiceStatusLegacy.Complete;
context.MarkDirty();
}
}
@ -299,8 +299,8 @@ namespace BTCPayServer.HostedServices
_EventAggregator.Publish(evt, evt.GetType());
}
if (invoice.Status == InvoiceStatus.Complete ||
((invoice.Status == InvoiceStatus.Invalid || invoice.Status == InvoiceStatus.Expired) && invoice.MonitoringExpiration < DateTimeOffset.UtcNow))
if (invoice.Status == InvoiceStatusLegacy.Complete ||
((invoice.Status == InvoiceStatusLegacy.Invalid || invoice.Status == InvoiceStatusLegacy.Expired) && invoice.MonitoringExpiration < DateTimeOffset.UtcNow))
{
var extendInvoiceMonitoring = await UpdateConfirmationCount(invoice);

View File

@ -17,6 +17,7 @@ using BTCPayServer.Client.Models;
using BTCPayServer.Data;
using BTCPayServer.Events;
using BTCPayServer.Logging;
using BTCPayServer.Services.Invoices;
using BTCPayServer.Services.Stores;
using Microsoft.CodeAnalysis.Operations;
using Microsoft.EntityFrameworkCore;
@ -163,10 +164,11 @@ namespace BTCPayServer.HostedServices
switch (eventCode)
{
case InvoiceEventCode.Completed:
case InvoiceEventCode.PaidAfterExpiration:
return null;
case InvoiceEventCode.Confirmed:
case InvoiceEventCode.MarkedCompleted:
return new WebhookInvoiceConfirmedEvent(WebhookEventType.InvoiceConfirmed)
return new WebhookInvoiceSettledEvent(WebhookEventType.InvoiceSettled)
{
ManuallyMarked = eventCode == InvoiceEventCode.MarkedCompleted
};
@ -185,16 +187,14 @@ namespace BTCPayServer.HostedServices
ManuallyMarked = eventCode == InvoiceEventCode.MarkedInvalid
};
case InvoiceEventCode.PaidInFull:
case InvoiceEventCode.PaidAfterExpiration:
return new WebhookInvoicePaidEvent(WebhookEventType.InvoicePaidInFull)
return new WebhookInvoiceProcessingEvent(WebhookEventType.InvoiceProcessing)
{
OverPaid = invoiceEvent.Invoice.ExceptionStatus == InvoiceExceptionStatus.PaidOver,
PaidAfterExpiration = eventCode == InvoiceEventCode.PaidAfterExpiration
};
case InvoiceEventCode.ReceivedPayment:
return new WebhookInvoiceReceivedPaymentEvent(WebhookEventType.InvoiceReceivedPayment)
{
AfterExpiration = invoiceEvent.Invoice.Status == InvoiceStatus.Expired || invoiceEvent.Invoice.Status == InvoiceStatus.Invalid
AfterExpiration = invoiceEvent.Invoice.Status.ToModernStatus() == InvoiceStatus.Expired || invoiceEvent.Invoice.Status.ToModernStatus() == InvoiceStatus.Invalid
};
default:
return null;

View File

@ -142,7 +142,7 @@ namespace BTCPayServer.Models.PaymentRequestViewModels
public decimal Amount { get; set; }
public string AmountFormatted { get; set; }
public InvoiceState State { get; set; }
public InvoiceStatus Status { get; set; }
public InvoiceStatusLegacy Status { get; set; }
public string StateFormatted { get; set; }
public List<PaymentRequestInvoicePayment> Payments { get; set; }

View File

@ -83,7 +83,7 @@ namespace BTCPayServer.PaymentRequest
var paymentStats = _AppService.GetContributionsByPaymentMethodId(blob.Currency, invoices, true);
var amountDue = blob.Amount - paymentStats.TotalCurrency;
var pendingInvoice = invoices.OrderByDescending(entity => entity.InvoiceTime)
.FirstOrDefault(entity => entity.Status == InvoiceStatus.New);
.FirstOrDefault(entity => entity.Status == InvoiceStatusLegacy.New);
return new ViewPaymentRequestViewModel(pr)
{

View File

@ -144,7 +144,7 @@ namespace BTCPayServer.Payments.Lightning
_CheckInvoices.Writer.TryWrite(inv.Invoice.Id);
}
if (inv.Name == InvoiceEvent.ReceivedPayment && inv.Invoice.Status == InvoiceStatus.New && inv.Invoice.ExceptionStatus == InvoiceExceptionStatus.PaidPartial)
if (inv.Name == InvoiceEvent.ReceivedPayment && inv.Invoice.Status == InvoiceStatusLegacy.New && inv.Invoice.ExceptionStatus == InvoiceExceptionStatus.PaidPartial)
{
var pm = inv.Invoice.GetPaymentMethods().First();
if (pm.Calculate().Due.GetValue(pm.Network as BTCPayNetwork) > 0m)
@ -155,7 +155,7 @@ namespace BTCPayServer.Payments.Lightning
}));
leases.Add(_Aggregator.Subscribe<Events.InvoiceDataChangedEvent>(async inv =>
{
if (inv.State.Status == InvoiceStatus.New &&
if (inv.State.Status == InvoiceStatusLegacy.New &&
inv.State.ExceptionStatus == InvoiceExceptionStatus.PaidPartial)
{

View File

@ -89,9 +89,9 @@ namespace BTCPayServer.Services.Apps
}
var invoices = await GetInvoicesForApp(appData, lastResetDate);
var completeInvoices = invoices.Where(entity => entity.Status == InvoiceStatus.Complete || entity.Status == InvoiceStatus.Confirmed).ToArray();
var pendingInvoices = invoices.Where(entity => !(entity.Status == InvoiceStatus.Complete || entity.Status == InvoiceStatus.Confirmed)).ToArray();
var paidInvoices = invoices.Where(entity => entity.Status == InvoiceStatus.Complete || entity.Status == InvoiceStatus.Confirmed || entity.Status == InvoiceStatus.Paid).ToArray();
var completeInvoices = invoices.Where(entity => entity.Status == InvoiceStatusLegacy.Complete || entity.Status == InvoiceStatusLegacy.Confirmed).ToArray();
var pendingInvoices = invoices.Where(entity => !(entity.Status == InvoiceStatusLegacy.Complete || entity.Status == InvoiceStatusLegacy.Confirmed)).ToArray();
var paidInvoices = invoices.Where(entity => entity.Status == InvoiceStatusLegacy.Complete || entity.Status == InvoiceStatusLegacy.Confirmed || entity.Status == InvoiceStatusLegacy.Paid).ToArray();
var pendingPayments = GetContributionsByPaymentMethodId(settings.TargetCurrency, pendingInvoices, !settings.EnforceTargetAmount);
var currentPayments = GetContributionsByPaymentMethodId(settings.TargetCurrency, completeInvoices, !settings.EnforceTargetAmount);
@ -176,10 +176,10 @@ namespace BTCPayServer.Services.Apps
StoreId = new[] { appData.StoreData.Id },
OrderId = appData.TagAllInvoices ? null : new[] { GetCrowdfundOrderId(appData.Id) },
Status = new string[]{
InvoiceState.ToString(InvoiceStatus.New),
InvoiceState.ToString(InvoiceStatus.Paid),
InvoiceState.ToString(InvoiceStatus.Confirmed),
InvoiceState.ToString(InvoiceStatus.Complete)},
InvoiceState.ToString(InvoiceStatusLegacy.New),
InvoiceState.ToString(InvoiceStatusLegacy.Paid),
InvoiceState.ToString(InvoiceStatusLegacy.Confirmed),
InvoiceState.ToString(InvoiceStatusLegacy.Complete)},
StartDate = startDate
});
@ -340,7 +340,7 @@ namespace BTCPayServer.Services.Apps
contribution.Value = contribution.CurrencyValue;
// For hardcap, we count newly created invoices as part of the contributions
if (!softcap && p.Status == InvoiceStatus.New)
if (!softcap && p.Status == InvoiceStatusLegacy.New)
return new[] { contribution };
// If the user get a donation via other mean, he can register an invoice manually for such amount
@ -348,7 +348,7 @@ namespace BTCPayServer.Services.Apps
var payments = p.GetPayments();
if (payments.Count == 0 &&
p.ExceptionStatus == InvoiceExceptionStatus.Marked &&
p.Status == InvoiceStatus.Complete)
p.Status == InvoiceStatusLegacy.Complete)
return new[] { contribution };
contribution.CurrencyValue = 0m;
@ -356,7 +356,7 @@ namespace BTCPayServer.Services.Apps
// If an invoice has been marked invalid, remove the contribution
if (p.ExceptionStatus == InvoiceExceptionStatus.Marked &&
p.Status == InvoiceStatus.Invalid)
p.Status == InvoiceStatusLegacy.Invalid)
return new[] { contribution };

View File

@ -219,7 +219,7 @@ namespace BTCPayServer.Services.Invoices
}
[JsonIgnore]
public InvoiceStatus Status { get; set; }
public InvoiceStatusLegacy Status { get; set; }
[JsonProperty(PropertyName = "status")]
[Obsolete("Use Status instead")]
public string StatusString => InvoiceState.ToString(Status);
@ -578,24 +578,54 @@ namespace BTCPayServer.Services.Invoices
}
}
public enum InvoiceStatusLegacy
{
New,
Paid,
Expired,
Invalid,
Complete,
Confirmed
}
public static class InvoiceStatusLegacyExtensions
{
public static InvoiceStatus ToModernStatus(this InvoiceStatusLegacy legacy)
{
switch (legacy)
{
case InvoiceStatusLegacy.Complete:
case InvoiceStatusLegacy.Confirmed:
return InvoiceStatus.Settled;
case InvoiceStatusLegacy.Expired:
return InvoiceStatus.Expired;
case InvoiceStatusLegacy.Invalid:
return InvoiceStatus.Invalid;
case InvoiceStatusLegacy.Paid:
return InvoiceStatus.Processing;
case InvoiceStatusLegacy.New:
return InvoiceStatus.New;
default:
throw new NotSupportedException();
}
}
}
public class InvoiceState
{
static readonly Dictionary<string, InvoiceStatus> _StringToInvoiceStatus;
static readonly Dictionary<InvoiceStatus, string> _InvoiceStatusToString;
static readonly Dictionary<string, InvoiceStatusLegacy> _StringToInvoiceStatus;
static readonly Dictionary<InvoiceStatusLegacy, string> _InvoiceStatusToString;
static readonly Dictionary<string, InvoiceExceptionStatus> _StringToExceptionStatus;
static readonly Dictionary<InvoiceExceptionStatus, string> _ExceptionStatusToString;
static InvoiceState()
{
_StringToInvoiceStatus = new Dictionary<string, InvoiceStatus>();
_StringToInvoiceStatus.Add("paid", InvoiceStatus.Paid);
_StringToInvoiceStatus.Add("expired", InvoiceStatus.Expired);
_StringToInvoiceStatus.Add("invalid", InvoiceStatus.Invalid);
_StringToInvoiceStatus.Add("complete", InvoiceStatus.Complete);
_StringToInvoiceStatus.Add("new", InvoiceStatus.New);
_StringToInvoiceStatus.Add("confirmed", InvoiceStatus.Confirmed);
_StringToInvoiceStatus = new Dictionary<string, InvoiceStatusLegacy>();
_StringToInvoiceStatus.Add("paid", InvoiceStatusLegacy.Paid);
_StringToInvoiceStatus.Add("expired", InvoiceStatusLegacy.Expired);
_StringToInvoiceStatus.Add("invalid", InvoiceStatusLegacy.Invalid);
_StringToInvoiceStatus.Add("complete", InvoiceStatusLegacy.Complete);
_StringToInvoiceStatus.Add("new", InvoiceStatusLegacy.New);
_StringToInvoiceStatus.Add("confirmed", InvoiceStatusLegacy.Confirmed);
_InvoiceStatusToString = _StringToInvoiceStatus.ToDictionary(kv => kv.Value, kv => kv.Key);
_StringToExceptionStatus = new Dictionary<string, InvoiceExceptionStatus>();
@ -612,16 +642,16 @@ namespace BTCPayServer.Services.Invoices
Status = _StringToInvoiceStatus[status];
ExceptionStatus = _StringToExceptionStatus[exceptionStatus ?? string.Empty];
}
public InvoiceState(InvoiceStatus status, InvoiceExceptionStatus exceptionStatus)
public InvoiceState(InvoiceStatusLegacy status, InvoiceExceptionStatus exceptionStatus)
{
Status = status;
ExceptionStatus = exceptionStatus;
}
public InvoiceStatus Status { get; }
public InvoiceStatusLegacy Status { get; }
public InvoiceExceptionStatus ExceptionStatus { get; }
public static string ToString(InvoiceStatus status)
public static string ToString(InvoiceStatusLegacy status)
{
return _InvoiceStatusToString[status];
}
@ -633,20 +663,20 @@ namespace BTCPayServer.Services.Invoices
public bool CanMarkComplete()
{
return (Status == InvoiceStatus.Paid) ||
((Status == InvoiceStatus.New || Status == InvoiceStatus.Expired) && ExceptionStatus == InvoiceExceptionStatus.PaidPartial) ||
((Status == InvoiceStatus.New || Status == InvoiceStatus.Expired) && ExceptionStatus == InvoiceExceptionStatus.PaidLate) ||
(Status != InvoiceStatus.Complete && ExceptionStatus == InvoiceExceptionStatus.Marked) ||
(Status == InvoiceStatus.Invalid);
return (Status == InvoiceStatusLegacy.Paid) ||
((Status == InvoiceStatusLegacy.New || Status == InvoiceStatusLegacy.Expired) && ExceptionStatus == InvoiceExceptionStatus.PaidPartial) ||
((Status == InvoiceStatusLegacy.New || Status == InvoiceStatusLegacy.Expired) && ExceptionStatus == InvoiceExceptionStatus.PaidLate) ||
(Status != InvoiceStatusLegacy.Complete && ExceptionStatus == InvoiceExceptionStatus.Marked) ||
(Status == InvoiceStatusLegacy.Invalid);
}
public bool CanMarkInvalid()
{
return (Status == InvoiceStatus.Paid) ||
(Status == InvoiceStatus.New) ||
((Status == InvoiceStatus.New || Status == InvoiceStatus.Expired) && ExceptionStatus == InvoiceExceptionStatus.PaidPartial) ||
((Status == InvoiceStatus.New || Status == InvoiceStatus.Expired) && ExceptionStatus == InvoiceExceptionStatus.PaidLate) ||
(Status != InvoiceStatus.Invalid && ExceptionStatus == InvoiceExceptionStatus.Marked);
return (Status == InvoiceStatusLegacy.Paid) ||
(Status == InvoiceStatusLegacy.New) ||
((Status == InvoiceStatusLegacy.New || Status == InvoiceStatusLegacy.Expired) && ExceptionStatus == InvoiceExceptionStatus.PaidPartial) ||
((Status == InvoiceStatusLegacy.New || Status == InvoiceStatusLegacy.Expired) && ExceptionStatus == InvoiceExceptionStatus.PaidLate) ||
(Status != InvoiceStatusLegacy.Invalid && ExceptionStatus == InvoiceExceptionStatus.Marked);
}
public override int GetHashCode()

View File

@ -453,15 +453,17 @@ retry:
context.Attach(invoiceData);
string eventName;
string legacyStatus;
switch (status)
{
case InvoiceStatus.Complete:
case InvoiceStatus.Settled:
if (!invoiceData.GetInvoiceState().CanMarkComplete())
{
return false;
}
eventName = InvoiceEvent.MarkedCompleted;
legacyStatus = InvoiceStatusLegacy.Complete.ToString();
break;
case InvoiceStatus.Invalid:
if (!invoiceData.GetInvoiceState().CanMarkInvalid())
@ -469,12 +471,13 @@ retry:
return false;
}
eventName = InvoiceEvent.MarkedInvalid;
legacyStatus = InvoiceStatusLegacy.Invalid.ToString();
break;
default:
return false;
}
invoiceData.Status = status.ToString().ToLowerInvariant();
invoiceData.Status = legacyStatus.ToLowerInvariant();
invoiceData.ExceptionStatus = InvoiceExceptionStatus.Marked.ToString().ToLowerInvariant();
_eventAggregator.Publish(new InvoiceEvent(ToEntity(invoiceData), eventName));
await context.SaveChangesAsync();

View File

@ -49,14 +49,14 @@ namespace BTCPayServer.Services.Shopify
var shopifyOrderId = invoice.GetInternalTags(SHOPIFY_ORDER_ID_PREFIX).FirstOrDefault();
if (shopifyOrderId != null)
{
if (new[] {InvoiceStatus.Invalid, InvoiceStatus.Expired}.Contains(invoice.GetInvoiceState()
if (new[] {InvoiceStatusLegacy.Invalid, InvoiceStatusLegacy.Expired}.Contains(invoice.GetInvoiceState()
.Status) && invoice.ExceptionStatus != InvoiceExceptionStatus.None)
{
//you have failed us, customer
await RegisterTransaction(invoice, shopifyOrderId, false);
}
else if (new[] {InvoiceStatus.Complete, InvoiceStatus.Confirmed}.Contains(
else if (new[] {InvoiceStatusLegacy.Complete, InvoiceStatusLegacy.Confirmed}.Contains(
invoice.Status))
{
await RegisterTransaction(invoice, shopifyOrderId, true);

View File

@ -11,11 +11,10 @@
string StatusTextClass(InvoiceState state)
{
switch (state.Status)
switch (state.Status.ToModernStatus())
{
case InvoiceStatus.Confirmed:
case InvoiceStatus.Complete:
case InvoiceStatus.Paid:
case InvoiceStatus.Settled:
case InvoiceStatus.Processing:
return "text-success";
case InvoiceStatus.Expired:
switch (state.ExceptionStatus)

View File

@ -55,9 +55,9 @@
{
("A new invoice has been created", WebhookEventType.InvoiceCreated),
("A new payment has been received", WebhookEventType.InvoiceReceivedPayment),
("An invoice is fully paid", WebhookEventType.InvoicePaidInFull),
("An invoice is processing", WebhookEventType.InvoiceProcessing),
("An invoice has expired", WebhookEventType.InvoiceExpired),
("An invoice has been confirmed", WebhookEventType.InvoiceConfirmed),
("An invoice has been settled", WebhookEventType.InvoiceSettled),
("An invoice became invalid", WebhookEventType.InvoiceInvalid)
})
{

View File

@ -521,19 +521,17 @@
"description": "",
"x-enumNames": [
"New",
"Paid",
"Processing",
"Expired",
"Invalid",
"Complete",
"Confirmed"
"Settled"
],
"enum": [
"New",
"Paid",
"Processing",
"Expired",
"Invalid",
"Complete",
"Confirmed"
"Settled"
]
},
"InvoiceAdditionalStatus": {
@ -778,13 +776,13 @@
"description": "",
"x-enumNames": [
"Invalid",
"AwaitingCompletion",
"Complete"
"Processing",
"Settled"
],
"enum": [
"Invalid",
"AwaitingCompletion",
"Complete"
"Processing",
"Settled"
]
}
}

View File

@ -579,7 +579,7 @@
},
"specificEvents": {
"type": "string",
"description": "If `everything` is false, the specific events that the endpoint is interested in. Current events are: `InvoiceCreated`, `InvoiceReceivedPayment`, `InvoicePaidInFull`, `InvoiceExpired`, `InvoiceConfirmed`, `InvoiceInvalid`.",
"description": "If `everything` is false, the specific events that the endpoint is interested in. Current events are: `InvoiceCreated`, `InvoiceReceivedPayment`, `InvoiceProcessing`, `InvoiceExpired`, `InvoiceSettled`, `InvoiceInvalid`.",
"nullable": false
}
}
@ -644,8 +644,8 @@
}
]
},
"WebhookInvoiceConfirmedEvent": {
"description": "Callback sent if the `type` is `InvoiceConfirmed`",
"WebhookInvoiceSettledEvent": {
"description": "Callback sent if the `type` is `InvoiceSettled`",
"allOf": [
{
"$ref": "#/components/schemas/WebhookInvoiceEvent"
@ -680,8 +680,8 @@
}
]
},
"WebhookInvoicePaidEvent": {
"description": "Callback sent if the `type` is `InvoicePaidInFull`",
"WebhookInvoiceProcessingEvent": {
"description": "Callback sent if the `type` is `InvoiceProcessing`",
"allOf": [
{
"$ref": "#/components/schemas/WebhookInvoiceEvent"
@ -693,11 +693,6 @@
"type": "boolean",
"description": "Whether this invoice has received more money than expected",
"nullable": false
},
"paidAfterExpiration": {
"type": "boolean",
"description": "Whether this invoice has been paid too late",
"nullable": false
}
}
}
@ -832,9 +827,9 @@
}
}
},
"Invoice paid": {
"Invoice processing": {
"post": {
"summary": "Invoice paid",
"summary": "Invoice processing",
"description": "An invoice has been fully paid",
"parameters": [
{
@ -855,7 +850,7 @@
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/WebhookInvoicePaidEvent"
"$ref": "#/components/schemas/WebhookInvoiceProcessingEvent"
}
}
}
@ -892,10 +887,10 @@
}
}
},
"Invoice confirmed": {
"Invoice settled": {
"post": {
"summary": "Invoice confirmed",
"description": "An invoice has been confirmed, order considered settled",
"summary": "Invoice settled",
"description": "An invoice has been settled",
"parameters": [
{
"in": "header",
@ -915,7 +910,7 @@
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/WebhookInvoiceConfirmedEvent"
"$ref": "#/components/schemas/WebhookInvoiceSettledEvent"
}
}
}