diff --git a/.env.example b/.env.example index ed6e513..99df7d4 100644 --- a/.env.example +++ b/.env.example @@ -1,3 +1,4 @@ EJABBERD_API_URL='https://xmpp.kosmos.org/api' +BTCPAY_API_URL='http://localhost:23001/api/v1' LNDHUB_API_URL='http://localhost:3023' LNDHUB_PUBLIC_URL='https://lndhub.kosmos.org' diff --git a/.env.production b/.env.production index e9bd695..228cf57 100644 --- a/.env.production +++ b/.env.production @@ -1,3 +1,4 @@ EJABBERD_API_URL='https://xmpp.kosmos.org:5443/api' +BTCPAY_API_URL='http://10.1.1.163:23001/api/v1' LNDHUB_API_URL='http://10.1.1.163:3023' LNDHUB_PUBLIC_URL='https://lndhub.kosmos.org' diff --git a/.env.test b/.env.test index 4d17742..103f43d 100644 --- a/.env.test +++ b/.env.test @@ -1,3 +1,4 @@ EJABBERD_API_URL='http://xmpp.example.com/api' +BTCPAY_API_URL='http://btcpay.example.com/api/v1' LNDHUB_API_URL='http://localhost:3023' LNDHUB_PUBLIC_URL='https://lndhub.kosmos.org' diff --git a/app/controllers/api/base_controller.rb b/app/controllers/api/base_controller.rb new file mode 100644 index 0000000..d788c58 --- /dev/null +++ b/app/controllers/api/base_controller.rb @@ -0,0 +1,5 @@ +class Api::BaseController < ApplicationController + + layout false + +end diff --git a/app/controllers/api/kredits_controller.rb b/app/controllers/api/kredits_controller.rb new file mode 100644 index 0000000..e54993d --- /dev/null +++ b/app/controllers/api/kredits_controller.rb @@ -0,0 +1,13 @@ +class Api::KreditsController < Api::BaseController + + def onchain_btc_balance + btcpay = BtcPay.new + balance = btcpay.onchain_wallet_balance + render json: balance + rescue => error + Rails.logger.warn "Failed to fetch kredits BTC wallet balance: #{error.message}" + render json: { error: 'Failed to fetch wallet balance' }, + status: 500 + end + +end diff --git a/app/services/btc_pay.rb b/app/services/btc_pay.rb new file mode 100644 index 0000000..becd506 --- /dev/null +++ b/app/services/btc_pay.rb @@ -0,0 +1,32 @@ +# +# API Docs: https://docs.btcpayserver.org/API/Greenfield/v1/ +# +class BtcPay + def initialize + @base_url = ENV["BTCPAY_API_URL"] + @store_id = Rails.application.credentials.btcpay[:store_id] + @auth_token = Rails.application.credentials.btcpay[:auth_token] + end + + def onchain_wallet_balance + res = get "stores/#{@store_id}/payment-methods/onchain/BTC/wallet" + + { + balance: res["balance"].to_f, + unconfirmed_balance: res["unconfirmedBalance"].to_f, + confirmed_balance: res["confirmedBalance"].to_f + } + end + + private + + def get(endpoint) + res = Faraday.get("#{@base_url}/#{endpoint}", {}, { + "Content-Type" => "application/json", + "Accept" => "application/json", + "Authorization" => "token #{@auth_token}" + }) + + JSON.parse(res.body) + end +end diff --git a/config/credentials.yml.enc b/config/credentials.yml.enc index a49b81a..c3dc2e3 100644 --- a/config/credentials.yml.enc +++ b/config/credentials.yml.enc @@ -1 +1 @@ -PRI1laGy6XiLpsII0ixFOTCpzzDbrRpIQ7GJ2flvA+UEEiPVQQ4e/pt6ZjumaZ/geTBHCRZklLtS1u6HjucVKHGSR4K/lE1q7u6nfi/wAvwGQdCTJeKf8PetdlsiVZg2OW4Wi2YF7qtb1ipGS/uRlOeLP8JOwp5BNVS1J6tvNX4T5gA7rrB4Cvtfwp1dwLWODHiXbeZK7SN2j7V3NPGZl0LbNg2jquC4DzQOBhMOlwu1IAgOAcqKbHh9tDabpVOcOD5pNSaVaoBO01P8ObbX8x0S0Ug7BPkM5nnyJTQdNH7LHKvtNmrUEk7+fd+VZR6WqEp6SFBoAA7XxvOLy9dIOqvXbAYnrNZX7iI4IpgZWRNIQ7G1qSBjpyPLKC7+nSPmvd4IcgCHVUBdYp0+yYDxLzwuuhPVtyAY4JAIdZY1mhYxx7BzGNiMc3p8AlMS6hPeO9lVQZykViBGnTjR+iah9OQYAihX9FYywtBkNvMXLiYqsVZmv6em9Uk/ivKSIsaFwNwrelCI5H4Q/+/hKFJ1JaMjbYheXCJgcGgfhiC+fIHig/8y8rXd9lWGa1T+PrpA8akcq/K8I6gbxYxpLWcy5Vnz/SyaMurLPVfCq4cD/JfpLSfy0HophaiycZ7D6dr43rnHsPfrt6kveDOgWUK9d7CqHxsWdXbCpJeBoh8/1usIsGsmyhfwHq0dEpRx78bR2EJeM4LOAuQgf69/5rFJydIbXq76l1BftTls1Pgh--OI8c/RQGQ30wT1Ff--M/ltnmdl+FRdlNliTb79lg== \ No newline at end of file +oDf4FcihlyfHQuf9SUwfr+UVA0kXoECtHV3vEwtBp2TktCWkWz7SPbSZ2cLT0+EknOKhuI9xutrs311YDU2EzqZba4fZ0+a6/ohVH4jUbk1XfiHZWBp4zh+9TZ5m5Tp2RxcXpdcWPY38mP4zHWFzobTOR/brLjuemozvh8MiBSPY4NN5NR6rbFo87auK6fvYO8ik/1Qwf6pQMoVDjcTh2983po1RU/gSevUmaYsmTTHcQ5T9O9wMIBc101iZyKhkAZG46a6eNYok8yqRm18AHWr+De2j7LlBaqYSz/BZA385RWhhqoeONArwyo3Az30Bv74VttUSJAPurkRg2wDF9t8A+cvf8CeYkZ6u398JLSJbZZ6YdQS5T4IcrnONRXtp3d/m+yw+XEzpluK3MvFbV02AhZk/xzkGK6xonhaTSh1ek9hXoYrUTBzu8HBzXwKjJMnvrAodldu++/rMwLsgVmFHqXC3dydVogatLev8z6ziuGkmeMAR1d9kzGBHM3FWgsWLD7j0Ug7MTMyNWioI3r6J2QTnxkyJGh3pKBlq8Fb/Q0ypERxOfSZVQQh2gB79RMEDIemdCmN5mCU3ojsxqAAip4v9C1BZMWPtom1sHLYQSd9Bh6i0nncrNEtZXcxe5Z8JCWQolHvvfoIF2rfJh2oXYLxNx5n/1fzaoSqBdLBgvsAMA0ZWfV1wa/5V/DCa9vaJjumzHYKcfCCYbVz9PjN9OUSfwrE3nWZu8Y0awsNgmeRQwI10j2+oYYSBp040I05Vj5FM7nCgHLpdupctwaH2/VlIq83OVI6VlbzxYEay7+R5ANmpiJ+vC466DzLv8LAeFOqh/XeNeOITUm9EzhGSgf98yuDc7vsi3gmc2g7atgQ3gpSje6vEfhigm3ukydaGfA==--IYQBMD+Tar1g+srm--01E5mujYFWvcpT8Qd9HxiA== \ No newline at end of file diff --git a/config/credentials/development.key b/config/credentials/development.key deleted file mode 100644 index ce648c5..0000000 --- a/config/credentials/development.key +++ /dev/null @@ -1 +0,0 @@ -2a8a17892dd9f41ea50c61310c83240b \ No newline at end of file diff --git a/config/credentials/development.yml.enc b/config/credentials/development.yml.enc deleted file mode 100644 index d2f41cb..0000000 --- a/config/credentials/development.yml.enc +++ /dev/null @@ -1 +0,0 @@ -zdpQvlbfqXzaExLzw2LGZkXj97HH42jWZFSTlINpf/HlFr6NubPxLkVxeGsittJj5rm8yL+V21zPxp30Z7Q7R45qyCKFtevtVeqb+u1nZ/FsVfkwb/7wDW9scurgXw==--SB6C0aaNf8qPdteG--9hP+6tpsMnyiAVVvAIq+7w== \ No newline at end of file diff --git a/config/credentials/test.yml.enc b/config/credentials/test.yml.enc index 416ee0e..5a1530a 100644 --- a/config/credentials/test.yml.enc +++ b/config/credentials/test.yml.enc @@ -1 +1 @@ -xgPOFd8315z7lFtTR5/nD6WDBM2M6Grt/pmkCPdaqlw0WAmFKzbiRGFsXoUQ02JNzvT1/FVtBSsAcyK1Pdr1QQztlWC+/ywaflloMBS4//D8IEXvEgCK6uff5gcf1A==--WbFrw9advCJ4mqsK--HTVHZqO0ddG1toFpY0KKgQ== \ No newline at end of file +IIjYiPSeZeMFhH8i8v8akXN4JrtGU+OsMQ8GAao/gVdesggriCBAQ8z+Vd0cmTf1SKYeT3OQDgygEekupr325P4eD9fZ+yi56EA/UMXQXMDVZAvZw7iwvKaOXpqisbWdJnomr1GXrHyR415Ce/Fxft3fgXDwMHJW2u+dDJgpE09uORnB9GXycFwHQmoIdXo=--iQ/Vcm0VcwHgUkwQ--tKHQW/45gM/s/NplqGPaxw== \ No newline at end of file diff --git a/config/routes.rb b/config/routes.rb index 8ecb38b..b0cf70e 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -25,6 +25,10 @@ Rails.application.routes.draw do get 'lnurlpay/:address', to: 'lnurlpay#index', constraints: { address: /[^\/]+/} get 'lnurlpay/:address/invoice', to: 'lnurlpay#invoice', constraints: { address: /[^\/]+/} + namespace :api do + get 'kredits/onchain_btc_balance', to: 'kredits#onchain_btc_balance' + end + namespace :admin do root to: 'dashboard#index' get 'invitations', to: 'invitations#index' diff --git a/spec/requests/api/kredits_spec.rb b/spec/requests/api/kredits_spec.rb new file mode 100644 index 0000000..0e1e464 --- /dev/null +++ b/spec/requests/api/kredits_spec.rb @@ -0,0 +1,43 @@ +require 'rails_helper' +require 'webmock/rspec' + +RSpec.describe "/api/kredits", type: :request do + + describe "GET /onchain_btc_balance" do + before do + stub_request(:get, "http://btcpay.example.com/api/v1/stores/123456/payment-methods/onchain/BTC/wallet") + .to_return(status: 200, headers: {}, body: { + balance: 0.91108606, + unconfirmedBalance: 0, + confirmedBalance: 0.91108606 + }.to_json) + end + + it "returns a formatted result for the onchain wallet balance" do + get api_kredits_onchain_btc_balance_path + + expect(response).to have_http_status(:ok) + + res = JSON.parse(response.body) + expect(res["balance"]).to eq(0.91108606) + expect(res["unconfirmed_balance"]).to eq(0) + expect(res["confirmed_balance"]).to eq(0.91108606) + end + + context "upstream request error" do + before do + stub_request(:get, "http://btcpay.example.com/api/v1/stores/123456/payment-methods/onchain/BTC/wallet") + .to_return(status: 500, headers: {}, body: "") + end + + it "returns a formatted error" do + get api_kredits_onchain_btc_balance_path + + expect(response).to have_http_status(:server_error) + + res = JSON.parse(response.body) + expect(res["error"]).not_to be_nil + end + end + end +end