private void UpdateInvoice(UpdateInvoiceContext context) { var invoice = context.Invoice; if (invoice.Status == InvoiceStatus.New && invoice.ExpirationTime <= DateTimeOffset.UtcNow) { context.MarkDirty(); context.UnaffectAddresses(); invoice.Status = InvoiceStatus.Expired; context.Events.Add(new InvoiceEvent(invoice, InvoiceEvent.Expired)); if (invoice.ExceptionStatus == InvoiceExceptionStatus.PaidPartial) { context.Events.Add(new InvoiceEvent(invoice, InvoiceEvent.ExpiredPaidPartial)); } } var allPaymentMethods = invoice.GetPaymentMethods(); var paymentMethod = GetNearestClearedPayment(allPaymentMethods, out var accounting); if (paymentMethod == null) { return; } if (invoice.Status == InvoiceStatus.New || invoice.Status == InvoiceStatus.Expired) { if (accounting.Paid >= accounting.MinimumTotalDue) { if (invoice.Status == InvoiceStatus.New) { context.Events.Add(new InvoiceEvent(invoice, InvoiceEvent.PaidInFull)); invoice.Status = InvoiceStatus.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) { invoice.ExceptionStatus = InvoiceExceptionStatus.PaidLate; context.Events.Add(new InvoiceEvent(invoice, InvoiceEvent.PaidAfterExpiration)); context.MarkDirty(); } } if (accounting.Paid < accounting.MinimumTotalDue && invoice.GetPayments().Count != 0 && invoice.ExceptionStatus != InvoiceExceptionStatus.PaidPartial) { invoice.ExceptionStatus = InvoiceExceptionStatus.PaidPartial; context.MarkDirty(); } } // Just make sure RBF did not cancelled a payment if (invoice.Status == InvoiceStatus.Paid) { if (accounting.MinimumTotalDue <= accounting.Paid && accounting.Paid <= accounting.TotalDue && invoice.ExceptionStatus == InvoiceExceptionStatus.PaidOver) { invoice.ExceptionStatus = InvoiceExceptionStatus.None; context.MarkDirty(); } if (accounting.Paid > accounting.TotalDue && invoice.ExceptionStatus != InvoiceExceptionStatus.PaidOver) { invoice.ExceptionStatus = InvoiceExceptionStatus.PaidOver; context.MarkDirty(); } if (accounting.Paid < accounting.MinimumTotalDue) { invoice.Status = InvoiceStatus.New; invoice.ExceptionStatus = accounting.Paid == Money.Zero ? InvoiceExceptionStatus.None : InvoiceExceptionStatus.PaidPartial; context.MarkDirty(); } } if (invoice.Status == InvoiceStatus.Paid) { var confirmedAccounting = paymentMethod.Calculate(p => p.GetCryptoPaymentData().PaymentConfirmed(p, invoice.SpeedPolicy)); if (// Is after the monitoring deadline (invoice.MonitoringExpiration < DateTimeOffset.UtcNow) && // And not enough amount confirmed (confirmedAccounting.Paid < accounting.MinimumTotalDue)) { context.UnaffectAddresses(); context.Events.Add(new InvoiceEvent(invoice, InvoiceEvent.FailedToConfirm)); invoice.Status = InvoiceStatus.Invalid; context.MarkDirty(); } else if (confirmedAccounting.Paid >= accounting.MinimumTotalDue) { context.UnaffectAddresses(); invoice.Status = InvoiceStatus.Confirmed; context.Events.Add(new InvoiceEvent(invoice, InvoiceEvent.Confirmed)); context.MarkDirty(); } } if (invoice.Status == InvoiceStatus.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; context.MarkDirty(); } } }
private void UpdateInvoice(UpdateInvoiceContext context) { var invoice = context.Invoice; if (invoice.Status == InvoiceStatusLegacy.New && invoice.ExpirationTime <= DateTimeOffset.UtcNow) { context.MarkDirty(); context.UnaffectAddresses(); invoice.Status = InvoiceStatusLegacy.Expired; var paidPartial = invoice.ExceptionStatus == InvoiceExceptionStatus.PaidPartial; context.Events.Add(new InvoiceEvent(invoice, InvoiceEvent.Expired) { PaidPartial = paidPartial }); if (invoice.ExceptionStatus == InvoiceExceptionStatus.PaidPartial) { context.Events.Add(new InvoiceEvent(invoice, InvoiceEvent.ExpiredPaidPartial) { PaidPartial = paidPartial }); } } var allPaymentMethods = invoice.GetPaymentMethods(); var paymentMethod = GetNearestClearedPayment(allPaymentMethods, out var accounting); if (allPaymentMethods.Any() && paymentMethod == null) { return; } if (accounting is null && invoice.Price is 0m) { accounting = new PaymentMethodAccounting() { Due = Money.Zero, Paid = Money.Zero, CryptoPaid = Money.Zero, DueUncapped = Money.Zero, NetworkFee = Money.Zero, TotalDue = Money.Zero, TxCount = 0, TxRequired = 0, MinimumTotalDue = Money.Zero, NetworkFeeAlreadyPaid = Money.Zero }; } if (invoice.Status == InvoiceStatusLegacy.New || invoice.Status == InvoiceStatusLegacy.Expired) { var isPaid = invoice.IsUnsetTopUp() ? accounting.Paid > Money.Zero : accounting.Paid >= accounting.MinimumTotalDue; if (isPaid) { if (invoice.Status == InvoiceStatusLegacy.New) { context.Events.Add(new InvoiceEvent(invoice, InvoiceEvent.PaidInFull)); invoice.Status = InvoiceStatusLegacy.Paid; if (invoice.IsUnsetTopUp()) { invoice.ExceptionStatus = InvoiceExceptionStatus.None; invoice.Price = (accounting.Paid - accounting.NetworkFeeAlreadyPaid).ToDecimal(MoneyUnit.BTC) * paymentMethod.Rate; accounting = paymentMethod.Calculate(); context.BlobUpdated(); } else { invoice.ExceptionStatus = accounting.Paid > accounting.TotalDue ? InvoiceExceptionStatus.PaidOver : InvoiceExceptionStatus.None; } context.UnaffectAddresses(); context.MarkDirty(); } else if (invoice.Status == InvoiceStatusLegacy.Expired && invoice.ExceptionStatus != InvoiceExceptionStatus.PaidLate) { invoice.ExceptionStatus = InvoiceExceptionStatus.PaidLate; context.Events.Add(new InvoiceEvent(invoice, InvoiceEvent.PaidAfterExpiration)); context.MarkDirty(); } } if (accounting.Paid < accounting.MinimumTotalDue && invoice.GetPayments(true).Count != 0 && invoice.ExceptionStatus != InvoiceExceptionStatus.PaidPartial) { invoice.ExceptionStatus = InvoiceExceptionStatus.PaidPartial; context.MarkDirty(); } } // Just make sure RBF did not cancelled a payment if (invoice.Status == InvoiceStatusLegacy.Paid) { if (accounting.MinimumTotalDue <= accounting.Paid && accounting.Paid <= accounting.TotalDue && invoice.ExceptionStatus == InvoiceExceptionStatus.PaidOver) { invoice.ExceptionStatus = InvoiceExceptionStatus.None; context.MarkDirty(); } if (accounting.Paid > accounting.TotalDue && invoice.ExceptionStatus != InvoiceExceptionStatus.PaidOver) { invoice.ExceptionStatus = InvoiceExceptionStatus.PaidOver; context.MarkDirty(); } if (accounting.Paid < accounting.MinimumTotalDue) { invoice.Status = InvoiceStatusLegacy.New; invoice.ExceptionStatus = accounting.Paid == Money.Zero ? InvoiceExceptionStatus.None : InvoiceExceptionStatus.PaidPartial; context.MarkDirty(); } } if (invoice.Status == InvoiceStatusLegacy.Paid) { var confirmedAccounting = paymentMethod?.Calculate(p => p.GetCryptoPaymentData().PaymentConfirmed(p, invoice.SpeedPolicy)) ?? accounting; if (// Is after the monitoring deadline (invoice.MonitoringExpiration < DateTimeOffset.UtcNow) && // And not enough amount confirmed (confirmedAccounting.Paid < accounting.MinimumTotalDue)) { context.UnaffectAddresses(); context.Events.Add(new InvoiceEvent(invoice, InvoiceEvent.FailedToConfirm)); invoice.Status = InvoiceStatusLegacy.Invalid; context.MarkDirty(); } else if (confirmedAccounting.Paid >= accounting.MinimumTotalDue) { context.UnaffectAddresses(); invoice.Status = InvoiceStatusLegacy.Confirmed; context.Events.Add(new InvoiceEvent(invoice, InvoiceEvent.Confirmed)); context.MarkDirty(); } } if (invoice.Status == InvoiceStatusLegacy.Confirmed) { var completedAccounting = paymentMethod?.Calculate(p => p.GetCryptoPaymentData().PaymentCompleted(p)) ?? accounting; if (completedAccounting.Paid >= accounting.MinimumTotalDue) { context.Events.Add(new InvoiceEvent(invoice, InvoiceEvent.Completed)); invoice.Status = InvoiceStatusLegacy.Complete; context.MarkDirty(); } } }