public async Task <InvoiceEntity> CreateInvoiceAsync(string storeId, InvoiceEntity invoice)
        {
            invoice          = Clone(invoice);
            invoice.Id       = Encoders.Base58.EncodeData(RandomUtils.GetBytes(16));
            invoice.Payments = new List <PaymentEntity>();
            invoice.StoreId  = storeId;
            using (var context = _ContextFactory.CreateContext())
            {
                context.Invoices.Add(new InvoiceData()
                {
                    StoreDataId   = storeId,
                    Id            = invoice.Id,
                    Created       = invoice.InvoiceTime,
                    Blob          = ToBytes(invoice),
                    OrderId       = invoice.OrderId,
                    Status        = invoice.Status,
                    ItemCode      = invoice.ProductInformation.ItemCode,
                    CustomerEmail = invoice.RefundMail
                });
                context.AddressInvoices.Add(new AddressInvoiceData()
                {
                    Address       = invoice.DepositAddress.ScriptPubKey.Hash.ToString(),
                    InvoiceDataId = invoice.Id,
                    CreatedTime   = DateTimeOffset.UtcNow,
                });
                context.HistoricalAddressInvoices.Add(new HistoricalAddressInvoiceData()
                {
                    InvoiceDataId = invoice.Id,
                    Address       = invoice.DepositAddress.ToString(),
                    Assigned      = DateTimeOffset.UtcNow
                });
                context.PendingInvoices.Add(new PendingInvoiceData()
                {
                    Id = invoice.Id
                });
                await context.SaveChangesAsync().ConfigureAwait(false);
            }

            AddToTextSearch(invoice.Id,
                            invoice.Id,
                            invoice.DepositAddress.ToString(),
                            invoice.InvoiceTime.ToString(CultureInfo.InvariantCulture),
                            invoice.ProductInformation.Price.ToString(CultureInfo.InvariantCulture),
                            invoice.GetTotalCryptoDue().ToString(),
                            invoice.OrderId,
                            ToString(invoice.BuyerInformation),
                            ToString(invoice.ProductInformation),
                            invoice.StoreId
                            );

            return(invoice);
        }
Esempio n. 2
0
        private async Task <(bool NeedSave, UTXOChanges Changes)> UpdateInvoice(UTXOChanges changes, InvoiceEntity invoice)
        {
            bool needSave = false;
            //Fetch unknown payments
            var strategy = _DerivationFactory.Parse(invoice.DerivationStrategy);

            changes = await _ExplorerClient.SyncAsync(strategy, changes, !LongPollingMode, _Cts.Token).ConfigureAwait(false);

            var         utxos         = changes.Confirmed.UTXOs.Concat(changes.Unconfirmed.UTXOs).ToArray();
            List <Coin> receivedCoins = new List <Coin>();

            foreach (var received in utxos)
            {
                if (invoice.AvailableAddressHashes.Contains(received.Output.ScriptPubKey.Hash.ToString()))
                {
                    receivedCoins.Add(new Coin(received.Outpoint, received.Output));
                }
            }

            var  alreadyAccounted = new HashSet <OutPoint>(invoice.Payments.Select(p => p.Outpoint));
            bool dirtyAddress     = false;

            foreach (var coin in receivedCoins.Where(c => !alreadyAccounted.Contains(c.Outpoint)))
            {
                var payment = await _InvoiceRepository.AddPayment(invoice.Id, coin).ConfigureAwait(false);

                invoice.Payments.Add(payment);
                dirtyAddress = true;
            }
            //////

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

                invoice.Status = "expired";
                if (invoice.FullNotifications)
                {
                    _NotificationManager.Notify(invoice);
                }
            }

            if (invoice.Status == "new" || invoice.Status == "expired")
            {
                var totalPaid = (await GetPaymentsWithTransaction(invoice)).Select(p => p.Payment.Output.Value).Sum();
                if (totalPaid >= invoice.GetTotalCryptoDue())
                {
                    if (invoice.Status == "new")
                    {
                        invoice.Status = "paid";
                        if (invoice.FullNotifications)
                        {
                            _NotificationManager.Notify(invoice);
                        }
                        invoice.ExceptionStatus = null;
                        await _InvoiceRepository.UnaffectAddress(invoice.Id);

                        needSave = true;
                    }
                    else if (invoice.Status == "expired")
                    {
                        invoice.ExceptionStatus = "paidLate";
                        needSave = true;
                    }
                }

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

                    needSave = true;
                }

                if (totalPaid < invoice.GetTotalCryptoDue() && invoice.Payments.Count != 0 && invoice.ExceptionStatus != "paidPartial")
                {
                    Logs.PayServer.LogInformation("Paid to " + invoice.DepositAddress);
                    invoice.ExceptionStatus = "paidPartial";
                    needSave = true;
                    if (dirtyAddress)
                    {
                        var address = await _Wallet.ReserveAddressAsync(_DerivationFactory.Parse(invoice.DerivationStrategy));

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

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

                var chainConfirmedTransactions = transactions.Where(t => t.Confirmations >= 1);
                if (invoice.SpeedPolicy == SpeedPolicy.HighSpeed)
                {
                    transactions = transactions.Where(t => !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 chainTotalConfirmed = chainConfirmedTransactions.Select(t => t.Payment.Output.Value).Sum();

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

                    invoice.Status = "invalid";
                    needSave       = true;
                    if (invoice.FullNotifications)
                    {
                        _NotificationManager.Notify(invoice);
                    }
                }
                else
                {
                    var totalConfirmed = transactions.Select(t => t.Payment.Output.Value).Sum();
                    if (totalConfirmed >= invoice.GetTotalCryptoDue())
                    {
                        await _InvoiceRepository.UnaffectAddress(invoice.Id);

                        invoice.Status = "confirmed";
                        _NotificationManager.Notify(invoice);
                        needSave = true;
                    }
                }
            }

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

                transactions = transactions.Where(t => t.Confirmations >= 6);
                var totalConfirmed = transactions.Select(t => t.Payment.Output.Value).Sum();
                if (totalConfirmed >= invoice.GetTotalCryptoDue())
                {
                    invoice.Status = "complete";
                    if (invoice.FullNotifications)
                    {
                        _NotificationManager.Notify(invoice);
                    }
                    needSave = true;
                }
            }
            return(needSave, changes);
        }