예제 #1
0
        private async Task UpdateInvoice(UpdateInvoiceContext context)
        {
            var invoice = context.Invoice;

            if (invoice.Status == InvoiceStatus.New && invoice.ExpirationTime < DateTimeOffset.UtcNow)
            {
                context.MarkDirty();
                await _InvoiceRepository.UnaffectAddress(invoice.Id);

                invoice.Status = InvoiceStatus.Expired;
                context.Events.Add(new InvoiceEvent(invoice, 1004, InvoiceEvent.Expired));
                if (invoice.ExceptionStatus == InvoiceExceptionStatus.PaidPartial)
                {
                    context.Events.Add(new InvoiceEvent(invoice, 2000, InvoiceEvent.ExpiredPaidPartial));
                }
            }

            var payments          = invoice.GetPayments().Where(p => p.Accounted).ToArray();
            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, 1003, InvoiceEvent.PaidInFull));
                        invoice.Status          = InvoiceStatus.Paid;
                        invoice.ExceptionStatus = accounting.Paid > accounting.TotalDue ? InvoiceExceptionStatus.PaidOver : InvoiceExceptionStatus.None;
                        await _InvoiceRepository.UnaffectAddress(invoice.Id);

                        context.MarkDirty();
                    }
                    else if (invoice.Status == InvoiceStatus.Expired && invoice.ExceptionStatus != InvoiceExceptionStatus.PaidLate)
                    {
                        invoice.ExceptionStatus = InvoiceExceptionStatus.PaidLate;
                        context.Events.Add(new InvoiceEvent(invoice, 1009, 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))
                {
                    await _InvoiceRepository.UnaffectAddress(invoice.Id);

                    context.Events.Add(new InvoiceEvent(invoice, 1013, InvoiceEvent.FailedToConfirm));
                    invoice.Status = InvoiceStatus.Invalid;
                    context.MarkDirty();
                }
                else if (confirmedAccounting.Paid >= accounting.MinimumTotalDue)
                {
                    await _InvoiceRepository.UnaffectAddress(invoice.Id);

                    invoice.Status = InvoiceStatus.Confirmed;
                    context.Events.Add(new InvoiceEvent(invoice, 1005, 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, 1006, InvoiceEvent.Completed));
                    invoice.Status = InvoiceStatus.Complete;
                    context.MarkDirty();
                }
            }
        }
예제 #2
0
        private async Task UpdateInvoice(UpdateInvoiceContext context)
        {
            var invoice = context.Invoice;

            if (invoice.Status == "new" && invoice.ExpirationTime < DateTimeOffset.UtcNow)
            {
                context.MarkDirty();
                await _InvoiceRepository.UnaffectAddress(invoice.Id);

                context.Events.Add(new InvoiceEvent(invoice, 1004, "invoice_expired"));
                invoice.Status = "expired";
            }

            var payments          = invoice.GetPayments().Where(p => p.Accounted).ToArray();
            var allPaymentMethods = invoice.GetPaymentMethods(_NetworkProvider);
            var paymentMethod     = GetNearestClearedPayment(allPaymentMethods, out var accounting, _NetworkProvider);

            if (paymentMethod == null)
            {
                return;
            }
            var network = _NetworkProvider.GetNetwork(paymentMethod.GetId().CryptoCode);

            if (invoice.Status == "new" || invoice.Status == "expired")
            {
                if (accounting.Paid >= accounting.TotalDue)
                {
                    if (invoice.Status == "new")
                    {
                        context.Events.Add(new InvoiceEvent(invoice, 1003, "invoice_paidInFull"));
                        invoice.Status          = "paid";
                        invoice.ExceptionStatus = accounting.Paid > accounting.TotalDue ? "paidOver" : null;
                        await _InvoiceRepository.UnaffectAddress(invoice.Id);

                        context.MarkDirty();
                    }
                    else if (invoice.Status == "expired" && invoice.ExceptionStatus != "paidLate")
                    {
                        invoice.ExceptionStatus = "paidLate";
                        context.Events.Add(new InvoiceEvent(invoice, 1009, "invoice_paidAfterExpiration"));
                        context.MarkDirty();
                    }
                }

                if (accounting.Paid < accounting.TotalDue && invoice.GetPayments().Count != 0 && invoice.ExceptionStatus != "paidPartial")
                {
                    invoice.ExceptionStatus = "paidPartial";
                    context.MarkDirty();
                }
            }

            // Just make sure RBF did not cancelled a payment
            if (invoice.Status == "paid")
            {
                if (accounting.Paid == accounting.TotalDue && invoice.ExceptionStatus == "paidOver")
                {
                    invoice.ExceptionStatus = null;
                    context.MarkDirty();
                }

                if (accounting.Paid > accounting.TotalDue && invoice.ExceptionStatus != "paidOver")
                {
                    invoice.ExceptionStatus = "paidOver";
                    context.MarkDirty();
                }

                if (accounting.Paid < accounting.TotalDue)
                {
                    invoice.Status          = "new";
                    invoice.ExceptionStatus = accounting.Paid == Money.Zero ? null : "paidPartial";
                    context.MarkDirty();
                }
            }

            if (invoice.Status == "paid")
            {
                var confirmedAccounting = paymentMethod.Calculate(p => p.GetCryptoPaymentData().PaymentConfirmed(p, invoice.SpeedPolicy, network));

                if (// Is after the monitoring deadline
                    (invoice.MonitoringExpiration < DateTimeOffset.UtcNow)
                    &&
                    // And not enough amount confirmed
                    (confirmedAccounting.Paid < accounting.TotalDue))
                {
                    await _InvoiceRepository.UnaffectAddress(invoice.Id);

                    context.Events.Add(new InvoiceEvent(invoice, 1013, "invoice_failedToConfirm"));
                    invoice.Status = "invalid";
                    context.MarkDirty();
                }
                else if (confirmedAccounting.Paid >= accounting.TotalDue)
                {
                    await _InvoiceRepository.UnaffectAddress(invoice.Id);

                    context.Events.Add(new InvoiceEvent(invoice, 1005, "invoice_confirmed"));
                    invoice.Status = "confirmed";
                    context.MarkDirty();
                }
            }

            if (invoice.Status == "confirmed")
            {
                var completedAccounting = paymentMethod.Calculate(p => p.GetCryptoPaymentData().PaymentCompleted(p, network));
                if (completedAccounting.Paid >= accounting.TotalDue)
                {
                    context.Events.Add(new InvoiceEvent(invoice, 1006, "invoice_completed"));
                    invoice.Status = "complete";
                    context.MarkDirty();
                }
            }
        }
예제 #3
0
        private async Task UpdateInvoice(UpdateInvoiceContext context)
        {
            var invoice = context.Invoice;
            //Fetch unknown payments
            var strategies             = invoice.GetDerivationStrategies(_NetworkProvider).ToArray();
            var getCoinsResponsesAsync = strategies
                                         .Select(d => _Wallet.GetCoins(d, context.KnownStates.TryGet(d.Network), _Cts.Token))
                                         .ToArray();
            await Task.WhenAll(getCoinsResponsesAsync);

            var getCoinsResponses = getCoinsResponsesAsync.Select(g => g.Result).ToArray();

            foreach (var response in getCoinsResponses)
            {
                response.Coins = response.Coins.Where(c => invoice.AvailableAddressHashes.Contains(c.ScriptPubKey.Hash.ToString() + response.Strategy.Network.CryptoCode)).ToArray();
            }
            var  coins        = getCoinsResponses.Where(s => s.Coins.Length != 0).FirstOrDefault();
            bool dirtyAddress = false;

            if (coins != null)
            {
                context.ModifiedKnownStates.Add(coins.Strategy.Network, coins.State);
                var alreadyAccounted = new HashSet <OutPoint>(invoice.Payments.Select(p => p.Outpoint));
                foreach (var coin in coins.Coins.Where(c => !alreadyAccounted.Contains(c.Outpoint)))
                {
                    var payment = await _InvoiceRepository.AddPayment(invoice.Id, coin, coins.Strategy.Network.CryptoCode).ConfigureAwait(false);

                    invoice.Payments.Add(payment);
                    context.Events.Add(new InvoicePaymentEvent(invoice.Id));
                    dirtyAddress = true;
                }
            }
            //////
            var network       = coins?.Strategy?.Network ?? _NetworkProvider.GetNetwork(invoice.GetCryptoData().First().Key);
            var cryptoData    = invoice.GetCryptoData(network);
            var cryptoDataAll = invoice.GetCryptoData();
            var accounting    = cryptoData.Calculate();

            if (invoice.Status == "new" && invoice.ExpirationTime < DateTimeOffset.UtcNow)
            {
                context.MarkDirty();
                await _InvoiceRepository.UnaffectAddress(invoice.Id);

                context.Events.Add(new InvoiceStatusChangedEvent(invoice, "expired"));
                invoice.Status = "expired";
            }

            if (invoice.Status == "new" || invoice.Status == "expired")
            {
                var totalPaid = (await GetPaymentsWithTransaction(network, invoice)).Select(p => p.Payment.GetValue(cryptoDataAll, cryptoData.CryptoCode)).Sum();
                if (totalPaid >= accounting.TotalDue)
                {
                    if (invoice.Status == "new")
                    {
                        context.Events.Add(new InvoiceStatusChangedEvent(invoice, "paid"));
                        invoice.Status          = "paid";
                        invoice.ExceptionStatus = null;
                        await _InvoiceRepository.UnaffectAddress(invoice.Id);

                        context.MarkDirty();
                    }
                    else if (invoice.Status == "expired")
                    {
                        invoice.ExceptionStatus = "paidLate";
                        context.MarkDirty();
                    }
                }

                if (totalPaid > accounting.TotalDue && invoice.ExceptionStatus != "paidOver")
                {
                    invoice.ExceptionStatus = "paidOver";
                    await _InvoiceRepository.UnaffectAddress(invoice.Id);

                    context.MarkDirty();
                }

                if (totalPaid < accounting.TotalDue && invoice.Payments.Count != 0 && invoice.ExceptionStatus != "paidPartial")
                {
                    invoice.ExceptionStatus = "paidPartial";
                    context.MarkDirty();
                    if (dirtyAddress)
                    {
                        var address = await _Wallet.ReserveAddressAsync(coins.Strategy);

                        Logs.PayServer.LogInformation("Generate new " + address);
                        await _InvoiceRepository.NewAddress(invoice.Id, address, network);
                    }
                }
            }

            if (invoice.Status == "paid")
            {
                var transactions = await GetPaymentsWithTransaction(network, invoice);

                if (invoice.SpeedPolicy == SpeedPolicy.HighSpeed)
                {
                    transactions = transactions.Where(t => t.Confirmations >= 1 || !t.Transaction.RBF);
                }
                else if (invoice.SpeedPolicy == SpeedPolicy.MediumSpeed)
                {
                    transactions = transactions.Where(t => t.Confirmations >= 1);
                }
                else if (invoice.SpeedPolicy == SpeedPolicy.LowSpeed)
                {
                    transactions = transactions.Where(t => t.Confirmations >= 6);
                }

                var totalConfirmed = transactions.Select(t => t.Payment.GetValue(cryptoDataAll, cryptoData.CryptoCode)).Sum();

                if (// Is after the monitoring deadline
                    (invoice.MonitoringExpiration < DateTimeOffset.UtcNow)
                    &&
                    // And not enough amount confirmed
                    (totalConfirmed < accounting.TotalDue))
                {
                    await _InvoiceRepository.UnaffectAddress(invoice.Id);

                    context.Events.Add(new InvoiceStatusChangedEvent(invoice, "invalid"));
                    invoice.Status = "invalid";
                    context.MarkDirty();
                }
                else if (totalConfirmed >= accounting.TotalDue)
                {
                    await _InvoiceRepository.UnaffectAddress(invoice.Id);

                    context.Events.Add(new InvoiceStatusChangedEvent(invoice, "confirmed"));
                    invoice.Status = "confirmed";
                    context.MarkDirty();
                }
            }

            if (invoice.Status == "confirmed")
            {
                var transactions = await GetPaymentsWithTransaction(network, invoice);

                transactions = transactions.Where(t => t.Confirmations >= 6);
                var totalConfirmed = transactions.Select(t => t.Payment.GetValue(cryptoDataAll, cryptoData.CryptoCode)).Sum();
                if (totalConfirmed >= accounting.TotalDue)
                {
                    context.Events.Add(new InvoiceStatusChangedEvent(invoice, "complete"));
                    invoice.Status = "complete";
                    context.MarkDirty();
                }
            }
        }
예제 #4
0
        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();
                }
            }
        }
예제 #5
0
        private async Task UpdateInvoice(UpdateInvoiceContext context)
        {
            var invoice = context.Invoice;

            if (invoice.Status == "new" && invoice.ExpirationTime < DateTimeOffset.UtcNow)
            {
                context.MarkDirty();
                await _InvoiceRepository.UnaffectAddress(invoice.Id);

                context.Events.Add(new InvoiceEvent(invoice, 1004, "invoice_expired"));
                invoice.Status = "expired";
            }

            var derivationStrategies = invoice.GetDerivationStrategies(_NetworkProvider).ToArray();
            var payments             = await GetPaymentsWithTransaction(derivationStrategies, invoice);

            foreach (Task <NetworkCoins> coinsAsync in GetCoinsPerNetwork(context, invoice, derivationStrategies))
            {
                var coins = await coinsAsync;
                if (coins.TimestampedCoins.Length == 0)
                {
                    continue;
                }
                bool dirtyAddress = false;
                if (coins.State != null)
                {
                    context.ModifiedKnownStates.AddOrReplace(coins.Wallet.Network, coins.State);
                }
                var alreadyAccounted = new HashSet <OutPoint>(invoice.GetPayments(coins.Wallet.Network).Select(p => p.Outpoint));

                foreach (var coin in coins.TimestampedCoins.Where(c => !alreadyAccounted.Contains(c.Coin.Outpoint)))
                {
                    var payment = await _InvoiceRepository.AddPayment(invoice.Id, coin.DateTime, coin.Coin, coins.Wallet.Network.CryptoCode).ConfigureAwait(false);

#pragma warning disable CS0618
                    invoice.Payments.Add(payment);
#pragma warning restore CS0618
                    alreadyAccounted.Add(coin.Coin.Outpoint);
                    context.Events.Add(new InvoiceEvent(invoice, 1002, "invoice_receivedPayment"));
                    dirtyAddress = true;
                }
                if (dirtyAddress)
                {
                    payments = await GetPaymentsWithTransaction(derivationStrategies, invoice);
                }
                var network       = coins.Wallet.Network;
                var cryptoData    = invoice.GetCryptoData(network, _NetworkProvider);
                var cryptoDataAll = invoice.GetCryptoData(_NetworkProvider);
                var accounting    = cryptoData.Calculate();

                if (invoice.Status == "new" || invoice.Status == "expired")
                {
                    var totalPaid = payments.Select(p => p.Payment.GetValue(cryptoDataAll, cryptoData.CryptoCode)).Sum();
                    if (totalPaid >= accounting.TotalDue)
                    {
                        if (invoice.Status == "new")
                        {
                            context.Events.Add(new InvoiceEvent(invoice, 1003, "invoice_paidInFull"));
                            invoice.Status          = "paid";
                            invoice.ExceptionStatus = totalPaid > accounting.TotalDue ? "paidOver" : null;
                            await _InvoiceRepository.UnaffectAddress(invoice.Id);

                            context.MarkDirty();
                        }
                        else if (invoice.Status == "expired" && invoice.ExceptionStatus != "paidLate")
                        {
                            invoice.ExceptionStatus = "paidLate";
                            context.Events.Add(new InvoiceEvent(invoice, 1009, "invoice_paidAfterExpiration"));
                            context.MarkDirty();
                        }
                    }

                    if (totalPaid < accounting.TotalDue && invoice.GetPayments().Count != 0 && invoice.ExceptionStatus != "paidPartial")
                    {
                        invoice.ExceptionStatus = "paidPartial";
                        context.MarkDirty();
                        if (dirtyAddress)
                        {
                            var address = await coins.Wallet.ReserveAddressAsync(coins.Strategy);

                            Logs.PayServer.LogInformation("Generate new " + address);
                            await _InvoiceRepository.NewAddress(invoice.Id, address, network);
                        }
                    }
                }

                if (invoice.Status == "paid")
                {
                    IEnumerable <AccountedPaymentEntity> transactions = payments;
                    if (invoice.SpeedPolicy == SpeedPolicy.HighSpeed)
                    {
                        transactions = transactions.Where(t => t.Confirmations >= 1 || !t.Transaction.RBF);
                    }
                    else if (invoice.SpeedPolicy == SpeedPolicy.MediumSpeed)
                    {
                        transactions = transactions.Where(t => t.Confirmations >= 1);
                    }
                    else if (invoice.SpeedPolicy == SpeedPolicy.LowSpeed)
                    {
                        transactions = transactions.Where(t => t.Confirmations >= 6);
                    }

                    var totalConfirmed = transactions.Select(t => t.Payment.GetValue(cryptoDataAll, cryptoData.CryptoCode)).Sum();

                    if (// Is after the monitoring deadline
                        (invoice.MonitoringExpiration < DateTimeOffset.UtcNow)
                        &&
                        // And not enough amount confirmed
                        (totalConfirmed < accounting.TotalDue))
                    {
                        await _InvoiceRepository.UnaffectAddress(invoice.Id);

                        context.Events.Add(new InvoiceEvent(invoice, 1013, "invoice_failedToConfirm"));
                        invoice.Status = "invalid";
                        context.MarkDirty();
                    }
                    else if (totalConfirmed >= accounting.TotalDue)
                    {
                        await _InvoiceRepository.UnaffectAddress(invoice.Id);

                        context.Events.Add(new InvoiceEvent(invoice, 1005, "invoice_confirmed"));
                        invoice.Status = "confirmed";
                        context.MarkDirty();
                    }
                }

                if (invoice.Status == "confirmed")
                {
                    IEnumerable <AccountedPaymentEntity> transactions = payments;
                    transactions = transactions.Where(t => t.Confirmations >= 6);
                    var totalConfirmed = transactions.Select(t => t.Payment.GetValue(cryptoDataAll, cryptoData.CryptoCode)).Sum();
                    if (totalConfirmed >= accounting.TotalDue)
                    {
                        context.Events.Add(new InvoiceEvent(invoice, 1006, "invoice_completed"));
                        invoice.Status = "complete";
                        context.MarkDirty();
                    }
                }
            }
        }