public static PaymentMethod GetNearestClearedPayment(PaymentMethodDictionary allPaymentMethods, out PaymentMethodAccounting accounting)
        {
            PaymentMethod result = null;

            accounting = null;
            decimal nearestToZero = 0.0m;

            foreach (var paymentMethod in allPaymentMethods)
            {
                var currentAccounting = paymentMethod.Calculate();
                var distanceFromZero  = Math.Abs(currentAccounting.DueUncapped.ToDecimal(MoneyUnit.BTC));
                if (result == null || distanceFromZero < nearestToZero)
                {
                    result        = paymentMethod;
                    nearestToZero = distanceFromZero;
                    accounting    = currentAccounting;
                }
            }
            return(result);
        }
Esempio n. 2
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();
                }
            }
        }
Esempio n. 3
0
        public static PaymentMethod GetNearestClearedPayment(PaymentMethodDictionary allPaymentMethods, out PaymentMethodAccounting accounting, BTCPayNetworkProvider networkProvider)
        {
            PaymentMethod result = null;

            accounting = null;
            decimal nearestToZero = 0.0m;

            foreach (var paymentMethod in allPaymentMethods)
            {
                if (networkProvider != null && networkProvider.GetNetwork(paymentMethod.GetId().CryptoCode) == null)
                {
                    continue;
                }
                var currentAccounting = paymentMethod.Calculate();
                var distanceFromZero  = Math.Abs(currentAccounting.DueUncapped.ToDecimal(MoneyUnit.BTC));
                if (result == null || distanceFromZero < nearestToZero)
                {
                    result        = paymentMethod;
                    nearestToZero = distanceFromZero;
                    accounting    = currentAccounting;
                }
            }
            return(result);
        }
 public abstract void PrepareInvoiceDto(InvoiceResponse invoiceResponse, InvoiceEntity invoiceEntity,
                                        InvoiceCryptoInfo invoiceCryptoInfo, PaymentMethodAccounting accounting, PaymentMethod info);