Пример #1
0
        public async Task <ActionResult> ValidatePaymentRequest(string request)
        {
            var userAppId = User.Identity.GetUserId();

            if (userAppId == null)
            {
                Response.StatusCode = (int)HttpStatusCode.Unauthorized;
                return(Json(new { success = false, message = "User not authorized." }));
            }

            using (var db = new ZapContext())
            {
                var invoice = request.SanitizeXSS();

                LNTransaction t;
                try
                {
                    // Check if the request has previously been submitted
                    t = await db.LightningTransactions
                        .Where(tx => tx.PaymentRequest == invoice)
                        .SingleOrDefaultAsync().ConfigureAwait(true);
                }
                catch (InvalidOperationException)
                {
                    // source has more than one element.
                    return(Json(new { success = false, message = "Duplicate invoice - please use a new invoice." }));
                }

                if (t == null)
                {
                    // first time

                    // Get interface to LND
                    LndRpcClient lndClient = GetLndClient();

                    // Decode invoice
                    var decoded = lndClient.DecodePayment(invoice);

                    if (decoded != null)
                    {
                        double amount = Convert.ToDouble(decoded.num_satoshis, CultureInfo.InvariantCulture);

                        if (amount < 1)
                        {
                            return(Json(new { success = false, message = "Zero- or any-value invoices are not supported at this time" }));
                        }

                        if (amount > 50000)
                        {
                            return(Json(new { success = false, message = "Withdraws temporarily limited to 50000 Satoshi" }));
                        }

                        // Check user balance
                        var userFunds = await db.Users
                                        .Where(u => u.AppId == userAppId)
                                        .Select(u => new
                        {
                            u.Funds.Balance
                        })
                                        .FirstOrDefaultAsync().ConfigureAwait(true);

                        if (userFunds == null)
                        {
                            Response.StatusCode = (int)HttpStatusCode.InternalServerError;
                            return(Json(new { success = false, message = "User not found in database." }));
                        }

                        if (userFunds.Balance < amount)
                        {
                            return(Json(new { success = false, message = "Insufficient Funds. You have " + userFunds.Balance.ToString("0.", CultureInfo.InvariantCulture) + ", invoice is for " + decoded.num_satoshis + "." }));
                        }

                        // This is less than ideal for time checks...

                        // Check how much user withdrew previous 24 hours
                        var DayAgo = DateTime.UtcNow - TimeSpan.FromDays(1);

                        var txs = await db.LightningTransactions
                                  .Where(tx => tx.User.AppId == userAppId)
                                  .Where(tx => tx.TimestampCreated != null && tx.TimestampCreated > DayAgo)
                                  .Where(tx => !tx.IsDeposit)
                                  .Select(tx => tx.Amount).ToListAsync().ConfigureAwait(true);

                        var withdrawn24h = txs.Sum();

                        if (withdrawn24h > 100000)
                        {
                            return(Json(new { success = false, message = "Withdraws limited to 100,000 Satoshi within a 24 hour limit." }));
                        }

                        var HourAgo = DateTime.UtcNow - TimeSpan.FromHours(1);

                        var txs1h = await db.LightningTransactions
                                    .Where(tx => tx.User.AppId == userAppId)
                                    .Where(tx => tx.TimestampCreated != null && tx.TimestampCreated > HourAgo)
                                    .Where(tx => !tx.IsDeposit)
                                    .Select(tx => tx.Amount).ToListAsync().ConfigureAwait(true);

                        var withdrawn1h = txs1h.Sum();

                        if (withdrawn1h > 50000)
                        {
                            return(Json(new { success = false, message = "Withdraws limited to 50,000 Satoshi within a 1 hour limit." }));
                        }

                        // Save the invoice to database
                        var user = await db.Users
                                   .Where(u => u.AppId == userAppId)
                                   .FirstAsync().ConfigureAwait(true);

                        //create a new transaction record in database
                        t = new LNTransaction()
                        {
                            IsSettled        = false,
                            Memo             = (decoded.description ?? "Withdraw").SanitizeXSS(),
                            HashStr          = decoded.payment_hash,
                            PaymentHash      = decoded.payment_hash,
                            Amount           = Convert.ToInt64(decoded.num_satoshis, CultureInfo.InvariantCulture),
                            IsDeposit        = false,
                            TimestampSettled = null,
                            TimestampCreated = DateTime.UtcNow, //can't know
                            PaymentRequest   = invoice,
                            FeePaid_Satoshi  = 0,
                            NodePubKey       = decoded.destination,
                            User             = user,
                            WithdrawId       = Guid.NewGuid(),
                        };
                        db.LightningTransactions.Add(t);
                        await db.SaveChangesAsync().ConfigureAwait(true);

                        return(Json(new
                        {
                            success = true,
                            withdrawId = t.WithdrawId,
                            decoded.num_satoshis,
                            decoded.destination,
                        }));
                    }
                }
                else
                {
                    // re-submitted - don't create new DB entry

                    // Safety checks
                    if (t.IsSettled || t.IsIgnored || t.IsLimbo || t.IsDeposit || t.IsError)
                    {
                        Response.StatusCode = (int)HttpStatusCode.BadRequest;
                        return(Json(new { success = false, message = "Invalid withdraw request." }));
                    }

                    // Check balance now
                    var userFunds = await db.Users
                                    .Where(u => u.AppId == userAppId)
                                    .Select(u => new
                    {
                        u.Funds.Balance
                    })
                                    .FirstOrDefaultAsync().ConfigureAwait(true);

                    if (userFunds == null)
                    {
                        Response.StatusCode = (int)HttpStatusCode.InternalServerError;
                        return(Json(new { success = false, message = "User not found in database." }));
                    }

                    double amount = Convert.ToDouble(t.Amount, CultureInfo.InvariantCulture);

                    if (userFunds.Balance < amount)
                    {
                        return(Json(new
                        {
                            success = false,
                            message = "Insufficient Funds. You have "
                                      + userFunds.Balance.ToString("0.", CultureInfo.InvariantCulture)
                                      + ", invoice is for " + t.Amount + "."
                        }));
                    }

                    return(Json(new
                    {
                        success = true,
                        withdrawId = t.WithdrawId,//t.Id,
                        num_satoshis = t.Amount,
                        destination = t.NodePubKey,
                    }));
                }
            }
            return(Json(new
            {
                success = false,
                message = "Error decoding invoice."
            }));
        }
Пример #2
0
        // We have received asynchronous notification that a lightning invoice has been paid
        private async static Task NotifyClientsInvoicePaid(Invoice invoice)
        {
            // Check if the invoice received was paid.  LND also sends updates
            // for new invoices to the invoice stream.  We want to listen for settled invoices here.
            if (!invoice.settled.HasValue)
            {
                // Bad invoice
                // Todo - logging
                return;
            }
            if (!invoice.settled.Value)
            {
                // Optional - add some logic to check invoices on the stream.  These invoices
                // which are not settled are likely new deposit requests.  For the purposes of
                // this function, we only care about settled invoices.
                return;
            }

            // This is the amount which was paid - needed in case of 0 (any) value invoices
            var amount = Convert.ToInt64(invoice.amt_paid_sat, CultureInfo.InvariantCulture);

            // Update LN transaction status in db
            using (ZapContext db = new ZapContext())
            {
                // Check if unsettled transaction exists in db matching the invoice that was just settled.
                var tx = db.LightningTransactions
                         .Include(tr => tr.User)
                         .Where(tr => tr.PaymentRequest == invoice.payment_request)
                         .ToList();

                DateTime      settletime = DateTime.UtcNow;
                LNTransaction t;
                if (tx.Count > 0) // Shouldn't ever be more than one entry - could add a check for this.
                {
                    // We found it - mark it as settled.
                    t = tx.First();
                    t.TimestampSettled = DateTime.SpecifyKind(new DateTime(1970, 1, 1), DateTimeKind.Utc) + TimeSpan.FromSeconds(Convert.ToInt64(invoice.settle_date, CultureInfo.InvariantCulture));
                    t.IsSettled        = true;

                    if (t.Amount != amount)
                    {
                        if (t.Amount == 0)
                        {
                            // This was a zero-invoice
                            t.Amount = amount; // this will be saved to DB
                        }
                    }
                }
                else
                {
                    // This invoice is not in the db - it may not be related to this service.
                    // We still record it in our database for any possible user forensics/history later.
                    settletime = DateTime.SpecifyKind(new DateTime(1970, 1, 1), DateTimeKind.Utc)
                                 + TimeSpan.FromSeconds(Convert.ToInt64(invoice.settle_date, CultureInfo.InvariantCulture));

                    var HexEncoder  = new LightningLib.DataEncoders.HexEncoder(); // static method
                    var rhash_bytes = Convert.FromBase64String(invoice.r_hash);
                    var rhash_hex   = HexEncoder.EncodeData(rhash_bytes);

                    t = new LNTransaction()
                    {
                        IsSettled        = invoice.settled.Value,
                        Memo             = invoice.memo.SanitizeXSS(),
                        Amount           = amount,//Convert.ToInt64(invoice.value, CultureInfo.InvariantCulture),
                        HashStr          = invoice.r_hash,
                        PreimageHash     = rhash_hex,
                        IsDeposit        = true,
                        TimestampSettled = settletime,
                        TimestampCreated = DateTime.SpecifyKind(new DateTime(1970, 1, 1), DateTimeKind.Utc) + TimeSpan.FromSeconds(Convert.ToInt64(invoice.creation_date, CultureInfo.InvariantCulture)),
                        PaymentRequest   = invoice.payment_request,
                        User             = null,
                    };
                    db.LightningTransactions.Add(t);
                }
                await db.SaveChangesAsync().ConfigureAwait(true);

                // Financial transaction
                double userBalance = 0.0; // This value will be returned later

                if (t.User != null)       // the user could be null if it is an anonymous payment.
                {
                    var userFunds = await db.Users
                                    .Where(u => u.Id == t.User.Id)
                                    .Select(u => u.Funds)
                                    .FirstOrDefaultAsync().ConfigureAwait(true);

                    // Make every attempt to save user balance in DB
                    int  attempts = 0;
                    bool saveFailed;
                    bool saveAborted = false;
                    do
                    {
                        attempts++;
                        saveFailed = false;

                        if (attempts < 50)
                        {
                            // This really shouldn't happen!
                            if (userFunds == null)
                            {
                                // this should not happen? - verify.  Maybe this is the case for transactions related to votes?
                                // throw new Exception("Error accessing user information related to settled LN Transaction.");
                            }
                            else
                            {
                                // Update user balance - this is a deposit.
                                userFunds.Balance += amount;// Convert.ToInt64(invoice.value, CultureInfo.InvariantCulture);
                                userBalance        = Math.Floor(userFunds.Balance);
                            }
                            try
                            {
                                db.SaveChanges(); // synchronous
                            }
                            catch (System.Data.Entity.Infrastructure.DbUpdateConcurrencyException ex)
                            {
                                saveFailed = true;
                                var entry = ex.Entries.Single();
                                entry.Reload();
                            }
                        }
                        else
                        {
                            saveAborted = true;
                        }
                    }while (saveFailed);

                    // Don't record as settled if save was aborted due to DB concurrency failure.
                    // LND database will show it was settled, but DB not.
                    // Another process can check DB sync and correct later.
                    if (saveAborted == false)
                    {
                        t.IsSettled = invoice.settled.Value;
                        await db.SaveChangesAsync().ConfigureAwait(true);
                    }
                }
                else
                {
                    t.IsSettled = invoice.settled.Value;
                    await db.SaveChangesAsync().ConfigureAwait(true);
                }

                // Send live signal to listening clients on websockets/SignalR
                //var context = GlobalHost.ConnectionManager.GetHubContext<NotificationHub>();
                //context.Clients.All.NotifyInvoicePaid(new { invoice = invoice.payment_request, balance = userBalance, txid = t.Id });
                await NotificationService.SendPaymentNotification(
                    t.User == null? "" : t.User.AppId,
                    invoice : invoice.payment_request,
                    userBalance : userBalance,
                    txid : t.Id).ConfigureAwait(true);
            }
        }
Пример #3
0
        public ActionResult GetDepositInvoice(string amount, string memo, string anon, string use, int?useId, int?useAction)
        {
            Response.AddHeader("X-Frame-Options", "DENY");
            bool isAnon = !(anon == null || anon != "1");

            if (!isAnon && !User.Identity.IsAuthenticated)
            {
                // This is a user-related invoice, and no user is logged in.
                return(RedirectToAction("Login", "Account", new { returnUrl = Request.Url.ToString() }));
            }

            string userId;

            if (isAnon)
            {
                userId = null;
            }
            else
            {
                userId = User.Identity.GetUserId();
            }

            if (string.IsNullOrEmpty(memo))
            {
                memo = "Zapread.com";
            }

            if (Convert.ToInt64(amount, CultureInfo.InvariantCulture) > 50000)
            {
                return(Json(new { success = false, message = "Deposits temporarily limited to 50000 satoshi" }));
            }

            LndRpcClient lndClient = GetLndClient();

            var inv = lndClient.AddInvoice(Convert.ToInt64(amount, CultureInfo.InvariantCulture), memo: memo.SanitizeXSS(), expiry: "3600");

            LnRequestInvoiceResponse resp = new LnRequestInvoiceResponse()
            {
                Invoice = inv.payment_request,
                Result  = "success",
                success = true,
            };

            //Create transaction record (not settled)
            using (ZapContext db = new ZapContext())
            {
                // TODO: ensure user exists?
                User user = null;
                if (userId != null)
                {
                    user = db.Users.Where(u => u.AppId == userId).First();
                }
                TransactionUse       usedFor       = TransactionUse.Undefined;
                TransactionUseAction usedForAction = TransactionUseAction.Undefined;
                int usedForId = useId != null ? useId.Value : -1;
                if (use == "tip")
                {
                    usedFor = TransactionUse.Tip;
                }
                else if (use == "votePost")
                {
                    usedFor = TransactionUse.VotePost;
                }
                else if (use == "voteComment")
                {
                    usedFor = TransactionUse.VoteComment;
                }
                else if (use == "userDeposit")
                {
                    usedFor   = TransactionUse.UserDeposit;
                    usedForId = userId != null ? user.Id : -1;
                }

                if (useAction != null)
                {
                    if (useAction.Value == 0)
                    {
                        usedForAction = TransactionUseAction.VoteDown;
                    }
                    else if (useAction.Value == 1)
                    {
                        usedForAction = TransactionUseAction.VoteUp;
                    }
                }

                var rhash_bytes = Convert.FromBase64String(inv.r_hash);
                var rhash_hex   = HexEncoder.EncodeData(rhash_bytes);

                //create a new transaction record in database
                LNTransaction t = new LNTransaction()
                {
                    User             = user,
                    IsSettled        = false,
                    IsSpent          = false,
                    Memo             = memo.SanitizeXSS(),
                    Amount           = Convert.ToInt64(amount, CultureInfo.InvariantCulture),
                    HashStr          = inv.r_hash, // B64 encoded
                    PreimageHash     = rhash_hex,
                    IsDeposit        = true,
                    TimestampCreated = DateTime.Now,
                    PaymentRequest   = inv.payment_request,
                    UsedFor          = usedFor,
                    UsedForId        = usedForId,
                    UsedForAction    = usedForAction,
                };
                db.LightningTransactions.Add(t);
                db.SaveChanges();
                resp.Id = t.Id;
            }

            if (true) // debugging
            {
                // If a listener is not already running, this should start
                // Check if there is one already online.
                var numListeners = lndTransactionListeners.Count(kvp => kvp.Value.IsLive);

                // If we don't have one running - start it and subscribe
                if (numListeners < 1)
                {
                    var listener = lndClient.GetListener();
                    lndTransactionListeners.TryAdd(listener.ListenerId, listener);           // keep alive while we wait for payment
                    listener.InvoicePaid +=
                        async(invoice) => await NotifyClientsInvoicePaid(invoice)
                        .ConfigureAwait(true);                                               // handle payment message

                    listener.StreamLost += OnListenerLost;                                   // stream lost
                    var a = new Task(() => listener.Start());                                // listen for payment
                    a.Start();
                }
            }
            return(Json(resp));
        }
Пример #4
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="request"></param>
        /// <param name="userId"></param>
        /// <param name="lndClient"></param>
        /// <returns></returns>
        public object TryWithdrawal(string request, string userId, string ip, LndRpcClient lndClient)
        {
            // Check if payment request is ok
            // Check if already paid
            var decoded = lndClient.DecodePayment(request);

            if (decoded == null)
            {
                return(new { Result = "Error decoding invoice." });
            }

            if (decoded.destination == null)
            {
                return(new { Result = "Error decoding invoice." });
            }

            using (var db = new ZapContext())
            {
                var user = db.Users
                           .Include(usr => usr.Funds)
                           .FirstOrDefault(u => u.AppId == userId);

                if (user == null)
                {
                    MailingService.Send(new UserEmailModel()
                    {
                        Destination = "*****@*****.**",
                        Body        = " Withdraw from user which doesn't exist.",
                        Email       = "",
                        Name        = "zapread.com Monitoring",
                        Subject     = "User withdraw error",
                    });

                    // Don't reveal information that user doesn't exist
                    return(new { Result = "Error processing request." });
                }

                if (user.Funds.Balance < Convert.ToDouble(decoded.num_satoshis))
                {
                    return(new { Result = "Insufficient Funds. You have " + user.Funds.Balance.ToString("0.") + ", invoice is for " + decoded.num_satoshis + "." });
                }

                //insert transaction as pending
                LNTransaction t = new LNTransaction()
                {
                    IsSettled        = false,
                    Memo             = decoded.description ?? "Withdraw",
                    HashStr          = decoded.payment_hash,
                    Amount           = Convert.ToInt64(decoded.num_satoshis),
                    IsDeposit        = false,
                    TimestampSettled = DateTime.UtcNow,
                    TimestampCreated = DateTime.UtcNow, //can't know
                    PaymentRequest   = request,
                    FeePaid_Satoshi  = 0,
                    NodePubKey       = decoded.destination,
                    User             = user,
                };
                db.LightningTransactions.Add(t);
                db.SaveChanges();

                SendPaymentResponse paymentresult;

                //all (should be) ok - make the payment
                if (WithdrawRequests.TryAdd(request, DateTime.UtcNow))
                {
                    paymentresult = lndClient.PayInvoice(request);
                }
                else
                {
                    //double request!
                    return(new { Result = "Please click only once.  Payment already in processing." });
                }

                WithdrawRequests.TryRemove(request, out DateTime reqInitTime);

                if (paymentresult == null)
                {
                    t.ErrorMessage = "Error executing payment.";
                    db.SaveChanges();
                    return(new { Result = "Error executing payment." });
                }

                if (paymentresult.error != null && paymentresult.error != "")
                {
                    t.ErrorMessage = "Error: " + paymentresult.error;
                    db.SaveChanges();
                    return(new { Result = "Error: " + paymentresult.error });
                }

                if (paymentresult.payment_error != null)
                {
                    t.ErrorMessage = "Error: " + paymentresult.payment_error;
                    db.SaveChanges();
                    return(new { Result = "Error: " + paymentresult.payment_error });
                }

                // should this be done here? Is there an async/sync check that payment was sent successfully?
                user.Funds.Balance -= Convert.ToDouble(decoded.num_satoshis);
                db.SaveChanges();

                //update transaction status
                t.IsSettled       = true;
                t.FeePaid_Satoshi = (paymentresult.payment_route.total_fees == null ? 0 : Convert.ToInt64(paymentresult.payment_route.total_fees));

                //db.LightningTransactions.Add(t);
                db.SaveChanges();
                return(new { Result = "success", Fees = 0 });
            }
        }
Пример #5
0
        public ActionResult GetDepositInvoice(string amount, string memo, string anon)
        {
            bool isAnon = !(anon == null || anon != "1");

            if (!isAnon && !User.Identity.IsAuthenticated)
            {
                // This is a user-related invoice, and no user is logged in.
                return(RedirectToAction("Login", "Account", new { returnUrl = Request.Url.ToString() }));
            }

            string userId;

            if (isAnon)
            {
                userId = null;
            }
            else
            {
                userId = User.Identity.GetUserId();
            }

            if (memo == null || memo == "")
            {
                memo = "Zapread.com";
            }

            var lndClient = new LndRpcClient(
                host: System.Configuration.ConfigurationManager.AppSettings["LnMainnetHost"],
                macaroonAdmin: System.Configuration.ConfigurationManager.AppSettings["LnMainnetMacaroonAdmin"],
                macaroonRead: System.Configuration.ConfigurationManager.AppSettings["LnMainnetMacaroonRead"],
                macaroonInvoice: System.Configuration.ConfigurationManager.AppSettings["LnMainnetMacaroonInvoice"]);

            var inv = lndClient.AddInvoice(Convert.ToInt64(amount), memo: memo, expiry: "432000");

            LnRequestInvoiceResponse resp = new LnRequestInvoiceResponse()
            {
                Invoice = inv.payment_request,
                Result  = "success",
            };

            //Create transaction record (not settled)
            using (ZapContext db = new ZapContext())
            {
                // TODO: ensure user exists?
                zapread.com.Models.User user = null;
                if (userId != null)
                {
                    user = db.Users.Where(u => u.AppId == userId).First();
                }

                //create a new transaction
                LNTransaction t = new LNTransaction()
                {
                    User             = user,
                    IsSettled        = false,
                    IsSpent          = false,
                    Memo             = memo,
                    Amount           = Convert.ToInt64(amount),
                    HashStr          = inv.r_hash,
                    IsDeposit        = true,
                    TimestampCreated = DateTime.Now,
                    PaymentRequest   = inv.payment_request,
                    UsedFor          = TransactionUse.UserDeposit,
                    UsedForId        = userId != null ? user.Id : -1,
                };
                db.LightningTransactions.Add(t);
                db.SaveChanges();
            }

            // If a listener is not already running, this should start
            // Check if there is one already online.
            var numListeners = lndTransactionListeners.Count(kvp => kvp.Value.IsLive);

            // If we don't have one running - start it and subscribe
            if (numListeners < 1)
            {
                var listener = lndClient.GetListener();
                lndTransactionListeners.TryAdd(listener.ListenerId, listener);           // keep alive while we wait for payment
                listener.InvoicePaid += NotifyClientsInvoicePaid;                        // handle payment message
                listener.StreamLost  += OnListenerLost;                                  // stream lost
                var a = new Task(() => listener.Start());                                // listen for payment
                a.Start();
            }

            return(Json(resp));
        }
Пример #6
0
        // We have received asynchronous notification that a lightning invoice has been paid
        private static void NotifyClientsInvoicePaid(Invoice invoice)
        {
            // Check if the invoice received was paid.  LND also sends updates
            // for new invoices to the invoice stream.  We want to listen for settled invoices here.
            if (!invoice.settled.Value)
            {
                // Optional - add some logic to check invoices on the stream.  These invoices
                // which are not settled are likely new deposit requests.  For the purposes of
                // this function, we only care about settled invoices.
                return;
            }
            var context = GlobalHost.ConnectionManager.GetHubContext <NotificationHub>();

            // Update LN transaction status in db
            using (ZapContext db = new ZapContext())
            {
                // Check if unsettled transaction exists in db matching the invoice that was just settled.
                var tx = db.LightningTransactions
                         .Include(tr => tr.User)
                         .Where(tr => tr.PaymentRequest == invoice.payment_request)
                         .ToList();

                DateTime      settletime = DateTime.UtcNow;
                LNTransaction t;
                if (tx.Count > 0)
                {
                    // We found it - mark it as settled.
                    t = tx.First();
                    t.TimestampSettled = DateTime.SpecifyKind(new DateTime(1970, 1, 1), DateTimeKind.Utc) + TimeSpan.FromSeconds(Convert.ToInt64(invoice.settle_date));
                    t.IsSettled        = true;
                }
                else
                {
                    // This invoice is not in the db - it may not be related to this service.
                    // We still record it in our database for any possible user forensics/history later.
                    settletime = DateTime.SpecifyKind(new DateTime(1970, 1, 1), DateTimeKind.Utc) + TimeSpan.FromSeconds(Convert.ToInt64(invoice.settle_date));
                    t          = new LNTransaction()
                    {
                        IsSettled        = invoice.settled.Value,
                        Memo             = invoice.memo,
                        Amount           = Convert.ToInt64(invoice.value),
                        HashStr          = invoice.r_hash,
                        IsDeposit        = true,
                        TimestampSettled = settletime,
                        TimestampCreated = DateTime.SpecifyKind(new DateTime(1970, 1, 1), DateTimeKind.Utc) + TimeSpan.FromSeconds(Convert.ToInt64(invoice.creation_date)),
                        PaymentRequest   = invoice.payment_request,
                        User             = null,
                    };
                    db.LightningTransactions.Add(t);
                }

                var    user        = t.User;
                double userBalance = 0.0;

                if (user == null)
                {
                    // this should not happen? - verify.  Maybe this is the case for transactions related to votes?
                    // throw new Exception("Error accessing user information related to settled LN Transaction.");
                }
                else
                {
                    // Update user balance - this is a deposit.
                    user.Funds.Balance += Convert.ToInt64(invoice.value);
                    userBalance         = Math.Floor(user.Funds.Balance);
                }

                t.IsSettled = invoice.settled.Value;
                db.SaveChanges();

                // Send live signal to listening clients on websockets/SignalR
                context.Clients.All.NotifyInvoicePaid(new { invoice = invoice.payment_request, balance = userBalance, txid = t.Id });
            }
        }