require "rqrcode" require "lnurl" class Services::LightningController < ApplicationController before_action :authenticate_user! before_action :authenticate_with_lndhub before_action :set_current_section before_action :fetch_balance def index @wallet_url = "lndhub://#{current_user.ln_account}:#{current_user.ln_password}@#{ENV['LNDHUB_PUBLIC_URL']}" initialize_lndhub_qr_code end def transactions @transactions = fetch_transactions end def qr_lnurlp lnurlp_url = "https://kosmos.org/.well-known/lnurlp/#{current_user.cn}" lnurlp_bech32 = Lnurl.new(lnurlp_url).to_bech32 qr_code = RQRCode::QRCode.new("lightning:" + lnurlp_bech32) respond_to do |format| format.svg do qr_svg = qr_code.as_svg( color: "000", shape_rendering: "crispEdges", module_size: 6, standalone: true, use_path: true, svg_attributes: { class: 'inline-block' } ) send_data( qr_svg, filename: "bitcoin-lightning-#{current_user.address}.svg", type: "image/svg+xml" ) end format.png do qr_png = qr_code.as_png( fill: "white", color: "black", size: 1024, ) send_data( qr_png, filename: "bitcoin-lightning-#{current_user.address}.png", type: "image/png" ) end end end private def initialize_lndhub_qr_code qr_code = RQRCode::QRCode.new(@wallet_url) @lndhub_qr_svg = qr_code.as_svg( color: "000", shape_rendering: "crispEdges", module_size: 6, standalone: true, use_path: true, svg_attributes: { class: 'inline-block' } ) end def authenticate_with_lndhub(options={}) if session[:ln_auth_token].present? && !options[:force_reauth] @ln_auth_token = session[:ln_auth_token] else lndhub = Lndhub.new auth_token = lndhub.authenticate(current_user) session[:ln_auth_token] = auth_token @ln_auth_token = auth_token end rescue => e Sentry.capture_exception(e) if Setting.sentry_enabled? end def set_current_section @current_section = :services end def fetch_balance lndhub = Lndhub.new data = lndhub.balance @ln_auth_token @balance = data["BTC"]["AvailableBalance"] rescue nil rescue AuthError authenticate_with_lndhub(force_reauth: true) raise if @fetch_balance_retried @fetch_balance_retried = true fetch_balance end def fetch_transactions lndhub = Lndhub.new txs = lndhub.gettxs @ln_auth_token invoices = lndhub.getuserinvoices(@ln_auth_token).select{|i| i["ispaid"]} process_transactions(txs + invoices) rescue AuthError authenticate_with_lndhub(force_reauth: true) raise if @fetch_transactions_retried @fetch_transactions_retried = true fetch_transactions end def process_transactions(txs) txs.collect do |tx| if tx["type"] == "bitcoind_tx" tx["amount_sats"] = (tx["amount"] * 100000000).to_i tx["datetime"] = Time.at(tx["time"].to_i) tx["title"] = "Received" tx["description"] = "On-chain topup" tx["received"] = true else tx["amount_sats"] = tx["value"] || tx["amt"] tx["fee"] = tx["type"] == "paid_invoice" ? tx["fee"] : nil tx["datetime"] = Time.at(tx["timestamp"].to_i) tx["title"] = tx["type"] == "paid_invoice" ? "Sent" : "Received" tx["description"] = tx["memo"] || tx["description"] tx["received"] = tx["type"] == "user_invoice" end end # Handle an edge case where lndhub.go includes a failed payment in the # list, which wasn't actually booked txs.reject!{ |tx| tx["type"] == "paid_invoice" && tx["payment_preimage"].blank? } txs.sort{ |a,b| b["datetime"] <=> a["datetime"] } end end