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); }
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); }