public async Task CheckPendingTransaction(Transaction transaction, CancellationToken cancellationToken = default(CancellationToken))
        {
            var invoice = await _btcpayService.GetLightningInvoice(transaction.InvoiceId, cancellationToken);

            if (invoice.Status == LightningInvoiceStatus.Paid)
            {
                await MarkTransactionPaid(transaction, invoice.AmountReceived, invoice.PaidAt);
            }
        }
        public async Task <Transaction> UpdateTransaction(Transaction transaction)
        {
            await using var dbContext = _dbContextFactory.CreateDbContext();
            var entry = dbContext.Entry(transaction);

            entry.State = EntityState.Modified;

            await dbContext.SaveChangesAsync();

            return(entry.Entity);
        }
        private async Task MarkTransactionPaid(Transaction transaction, LightMoney amountSettled, DateTimeOffset?date)
        {
            _logger.LogInformation($"Marking transaction {transaction.TransactionId} as paid.");

            transaction.AmountSettled = amountSettled;
            transaction.PaidAt        = date;

            await UpdateTransaction(transaction);

            await _transactionHub.Clients.All.SendAsync("transaction-update", new
            {
                transaction.TransactionId,
                transaction.InvoiceId,
                transaction.WalletId,
                transaction.Status,
                transaction.IsPaid,
                transaction.IsExpired,
                Event = "paid"
            });
        }
 public async Task RemoveTransaction(Transaction transaction)
 {
     await using var dbContext = _dbContextFactory.CreateDbContext();
     dbContext.Transactions.Remove(transaction);
     await dbContext.SaveChangesAsync();
 }
        public async Task <Transaction> Send(Wallet wallet, BOLT11PaymentRequest bolt11, string paymentRequest)
        {
            await using var dbContext = _dbContextFactory.CreateDbContext();
            var amount = bolt11.MinimumAmount;

            if (bolt11.ExpiryDate <= DateTimeOffset.UtcNow)
            {
                throw new Exception($"Payment request already expired at {bolt11.ExpiryDate}.");
            }

            if (wallet.Balance < amount)
            {
                var balanceSats = wallet.Balance.ToUnit(LightMoneyUnit.Satoshi);
                var amountSats  = amount.ToUnit(LightMoneyUnit.Satoshi);
                throw new Exception($"Insufficient balance: {balanceSats} sats, tried to send {amountSats} sats.");
            }

            // pay via the node and fall back to internal payment
            Transaction internalReceivingTransaction = null;

            try
            {
                await _btcpayService.PayLightningInvoice(new LightningInvoicePayRequest
                {
                    PaymentRequest = paymentRequest
                });
            }
            catch (GreenFieldAPIException ex) when(ex.APIError.Code == "could-not-find-route")
            {
                internalReceivingTransaction = await GetTransaction(new TransactionQuery
                {
                    PaymentRequest = paymentRequest,
                    HasInvoiceId   = true
                });

                if (internalReceivingTransaction == null)
                {
                    throw;
                }
            }

            if (internalReceivingTransaction != null)
            {
                if (internalReceivingTransaction.IsExpired)
                {
                    throw new Exception($"Payment request already expired at {internalReceivingTransaction.ExpiresAt}.");
                }
                if (internalReceivingTransaction.IsPaid)
                {
                    throw new Exception($"Payment request has already been paid.");
                }
            }

            // https://docs.microsoft.com/en-us/ef/core/saving/transactions#controlling-transactions
            await using var dbTransaction = await dbContext.Database.BeginTransactionAsync();

            var now   = DateTimeOffset.UtcNow;
            var entry = await dbContext.Transactions.AddAsync(new Transaction
            {
                WalletId       = wallet.WalletId,
                PaymentRequest = paymentRequest,
                Amount         = amount,
                AmountSettled  = new LightMoney(amount.MilliSatoshi * -1),
                ExpiresAt      = bolt11.ExpiryDate,
                Description    = bolt11.ShortDescription,
                PaidAt         = now
            });

            await dbContext.SaveChangesAsync();

            if (internalReceivingTransaction != null)
            {
                await MarkTransactionPaid(internalReceivingTransaction, amount, now);
            }
            await dbTransaction.CommitAsync();

            return(entry.Entity);
        }