diff --git a/.env.example b/.env.example index aa77727..b7244d8 100644 --- a/.env.example +++ b/.env.example @@ -34,6 +34,8 @@ EJABBERD_ADMIN_URL='https://xmpp.kosmos.org/admin' EJABBERD_API_URL='https://xmpp.kosmos.org/api' BTCPAY_API_URL='http://localhost:23001/api/v1' +BTCPAY_STORE_ID='' +BTCPAY_AUTH_TOKEN='' LNDHUB_API_URL='http://localhost:3023' LNDHUB_PUBLIC_URL='https://lndhub.kosmos.org' diff --git a/.env.test b/.env.test index 33761c3..c032b0e 100644 --- a/.env.test +++ b/.env.test @@ -2,13 +2,14 @@ PRIMARY_DOMAIN=kosmos.org REDIS_URL='redis://localhost:6379/0' +BTCPAY_API_URL='http://btcpay.example.com/api/v1' +BTCPAY_STORE_ID='123456' + DISCOURSE_PUBLIC_URL='http://discourse.example.com' DISCOURSE_CONNECT_SECRET='discourse_connect_ftw' EJABBERD_API_URL='http://xmpp.example.com/api' -BTCPAY_API_URL='http://btcpay.example.com/api/v1' - LNDHUB_API_URL='http://localhost:3026' LNDHUB_PUBLIC_URL='https://lndhub.kosmos.org' LNDHUB_PUBLIC_KEY='024cd3be18617f39cf645851e3ba63f51fc13f0bb09e3bb25e6fd4de556486d946' diff --git a/app/components/form_elements/fieldset_resettable_setting_component.rb b/app/components/form_elements/fieldset_resettable_setting_component.rb index 18a4609..bfaa1a0 100644 --- a/app/components/form_elements/fieldset_resettable_setting_component.rb +++ b/app/components/form_elements/fieldset_resettable_setting_component.rb @@ -6,7 +6,7 @@ module FormElements @tag = tag @positioning = :vertical @title = title - @descripton = description + @description = description @key = key.to_sym @type = type @resettable = is_resettable?(@key) diff --git a/app/components/sidenav_link_component.html.erb b/app/components/sidenav_link_component.html.erb index a49bbab..a229606 100644 --- a/app/components/sidenav_link_component.html.erb +++ b/app/components/sidenav_link_component.html.erb @@ -1,4 +1,8 @@ <%= link_to @path, class: @link_class, title: (@disabled ? "Coming soon" : nil) do %> + <% if @icon.present? %> <%= render partial: "icons/#{@icon}", locals: { custom_class: @icon_class } %> + <% elsif @text_icon.present? %> + <%= @text_icon %> + <% end %> <%= @name %> <% end %> diff --git a/app/components/sidenav_link_component.rb b/app/components/sidenav_link_component.rb index 60e58ed..9436a67 100644 --- a/app/components/sidenav_link_component.rb +++ b/app/components/sidenav_link_component.rb @@ -1,11 +1,13 @@ # frozen_string_literal: true class SidenavLinkComponent < ViewComponent::Base - def initialize(name:, level: 1, path:, icon:, active: false, disabled: false) + def initialize(name:, level: 1, path:, icon: nil, text_icon: nil, + active: false, disabled: false) @name = name @level = level @path = path @icon = icon + @text_icon = text_icon @active = active @disabled = disabled @link_class = class_names_link(path) diff --git a/app/controllers/admin/settings/services_controller.rb b/app/controllers/admin/settings/services_controller.rb index 54384c6..a228f5e 100644 --- a/app/controllers/admin/settings/services_controller.rb +++ b/app/controllers/admin/settings/services_controller.rb @@ -3,7 +3,7 @@ class Admin::Settings::ServicesController < Admin::SettingsController @service = params[:s] if @service.blank? - redirect_to admin_settings_services_path(params: { s: "discourse" }) + redirect_to admin_settings_services_path(params: { s: "btcpay" }) end end diff --git a/app/controllers/api/btcpay_controller.rb b/app/controllers/api/btcpay_controller.rb new file mode 100644 index 0000000..d973af2 --- /dev/null +++ b/app/controllers/api/btcpay_controller.rb @@ -0,0 +1,29 @@ +class Api::BtcpayController < Api::BaseController + before_action :require_feature_enabled + + def onchain_btc_balance + balance = BtcpayManager::FetchOnchainWalletBalance.call + render json: balance + rescue => error + Rails.logger.warn "Failed to fetch BTC wallet balance: #{error.message}" + render json: { error: 'Failed to fetch wallet balance' }, + status: 500 + end + + def lightning_btc_balance + balance = BtcpayManager::FetchLightningWalletBalance.call + render json: balance + rescue => error + Rails.logger.warn "Failed to fetch BTC lightning balance: #{error.message}" + render json: { error: 'Failed to fetch wallet balance' }, + status: 500 + end + + private + + def require_feature_enabled + unless Setting.btcpay_publish_wallet_balances + http_status :not_found and return + end + end +end diff --git a/app/controllers/api/kredits_controller.rb b/app/controllers/api/kredits_controller.rb deleted file mode 100644 index e54993d..0000000 --- a/app/controllers/api/kredits_controller.rb +++ /dev/null @@ -1,13 +0,0 @@ -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/models/setting.rb b/app/models/setting.rb index 0a13905..9cafa2e 100644 --- a/app/models/setting.rb +++ b/app/models/setting.rb @@ -36,7 +36,25 @@ class Setting < RailsSettings::Base # field :sentry_enabled, type: :boolean, readonly: true, - default: (ENV["SENTRY_DSN"].present?.to_s || false) + default: ENV["SENTRY_DSN"].present? + + # + # BTCPay Server + # + + field :btcpay_api_url, type: :string, + default: ENV["BTCPAY_API_URL"].presence + + field :btcpay_enabled, type: :boolean, + default: ENV["BTCPAY_API_URL"].present? + + field :btcpay_store_id, type: :string, + default: ENV["BTCPAY_STORE_ID"].presence + + field :btcpay_auth_token, type: :string, + default: ENV["BTCPAY_AUTH_TOKEN"].presence + + field :btcpay_publish_wallet_balances, type: :boolean, default: true # # Discourse @@ -46,7 +64,7 @@ class Setting < RailsSettings::Base default: ENV["DISCOURSE_PUBLIC_URL"].presence field :discourse_enabled, type: :boolean, - default: (ENV["DISCOURSE_PUBLIC_URL"].present?.to_s || false) + default: ENV["DISCOURSE_PUBLIC_URL"].present? field :discourse_connect_secret, type: :string, default: ENV["DISCOURSE_CONNECT_SECRET"].presence @@ -59,14 +77,14 @@ class Setting < RailsSettings::Base default: ENV["DRONECI_PUBLIC_URL"].presence field :droneci_enabled, type: :boolean, - default: (ENV["DRONECI_PUBLIC_URL"].present?.to_s || false) + default: ENV["DRONECI_PUBLIC_URL"].present? # # ejabberd # field :ejabberd_enabled, type: :boolean, - default: (ENV["EJABBERD_API_URL"].present?.to_s || false) + default: ENV["EJABBERD_API_URL"].present? field :ejabberd_api_url, type: :string, default: ENV["EJABBERD_API_URL"].presence @@ -85,7 +103,7 @@ class Setting < RailsSettings::Base default: ENV["GITEA_PUBLIC_URL"].presence field :gitea_enabled, type: :boolean, - default: (ENV["GITEA_PUBLIC_URL"].present?.to_s || false) + default: ENV["GITEA_PUBLIC_URL"].present? # # Lightning Network @@ -95,16 +113,19 @@ class Setting < RailsSettings::Base default: ENV["LNDHUB_API_URL"].presence field :lndhub_enabled, type: :boolean, - default: (ENV["LNDHUB_API_URL"].present?.to_s || false) + default: ENV["LNDHUB_API_URL"].present? + + field :lndhub_admin_token, type: :string, + default: ENV["LNDHUB_ADMIN_TOKEN"].presence field :lndhub_admin_enabled, type: :boolean, - default: (ENV["LNDHUB_ADMIN_UI"] || false) + default: ENV["LNDHUB_ADMIN_UI"] || false field :lndhub_public_key, type: :string, default: (ENV["LNDHUB_PUBLIC_KEY"] || "") field :lndhub_keysend_enabled, type: :boolean, - default: -> { self.lndhub_public_key.present?.to_s || false } + default: -> { self.lndhub_public_key.present? } # # Mastodon @@ -114,7 +135,7 @@ class Setting < RailsSettings::Base default: ENV["MASTODON_PUBLIC_URL"].presence field :mastodon_enabled, type: :boolean, - default: (ENV["MASTODON_PUBLIC_URL"].present?.to_s || false) + default: ENV["MASTODON_PUBLIC_URL"].present? field :mastodon_address_domain, type: :string, default: ENV["MASTODON_ADDRESS_DOMAIN"].presence || self.primary_domain @@ -127,7 +148,7 @@ class Setting < RailsSettings::Base default: ENV["MEDIAWIKI_PUBLIC_URL"].presence field :mediawiki_enabled, type: :boolean, - default: (ENV["MEDIAWIKI_PUBLIC_URL"].present?.to_s || false) + default: ENV["MEDIAWIKI_PUBLIC_URL"].present? # # Nostr @@ -140,7 +161,7 @@ class Setting < RailsSettings::Base # field :remotestorage_enabled, type: :boolean, - default: (ENV["RS_STORAGE_URL"].present?.to_s || false) + default: ENV["RS_STORAGE_URL"].present? field :rs_storage_url, type: :string, default: ENV["RS_STORAGE_URL"].presence diff --git a/app/services/btc_pay.rb b/app/services/btc_pay.rb deleted file mode 100644 index becd506..0000000 --- a/app/services/btc_pay.rb +++ /dev/null @@ -1,32 +0,0 @@ -# -# 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/app/services/btcpay_manager/fetch_lightning_wallet_balance.rb b/app/services/btcpay_manager/fetch_lightning_wallet_balance.rb new file mode 100644 index 0000000..533d2f1 --- /dev/null +++ b/app/services/btcpay_manager/fetch_lightning_wallet_balance.rb @@ -0,0 +1,11 @@ +module BtcpayManager + class FetchLightningWalletBalance < BtcpayManagerService + def call + res = get "stores/#{store_id}/lightning/BTC/balance" + + { + balance: res["offchain"]["local"].to_i / 1000 # msats to sats + } + end + end +end diff --git a/app/services/btcpay_manager/fetch_onchain_wallet_balance.rb b/app/services/btcpay_manager/fetch_onchain_wallet_balance.rb new file mode 100644 index 0000000..e28f197 --- /dev/null +++ b/app/services/btcpay_manager/fetch_onchain_wallet_balance.rb @@ -0,0 +1,13 @@ +module BtcpayManager + class FetchOnchainWalletBalance < BtcpayManagerService + def call + res = get "stores/#{store_id}/payment-methods/onchain/BTC/wallet" + + { + balance: (res["balance"].to_f * 100000000).to_i, # BTC to sats + unconfirmed_balance: (res["unconfirmedBalance"].to_f * 100000000).to_i, + confirmed_balance: (res["confirmedBalance"].to_f * 100000000).to_i + } + end + end +end diff --git a/app/services/btcpay_manager_service.rb b/app/services/btcpay_manager_service.rb new file mode 100644 index 0000000..d8fbf09 --- /dev/null +++ b/app/services/btcpay_manager_service.rb @@ -0,0 +1,24 @@ +# +# API Docs: https://docs.btcpayserver.org/API/Greenfield/v1/ +# +class BtcpayManagerService < ApplicationService + attr_reader :base_url, :store_id, :auth_token + + def initialize + @base_url = Setting.btcpay_api_url + @store_id = Setting.btcpay_store_id + @auth_token = Setting.btcpay_auth_token + 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/app/services/lndhub_v2.rb b/app/services/lndhub_v2.rb index e0a84dd..b816fcb 100644 --- a/app/services/lndhub_v2.rb +++ b/app/services/lndhub_v2.rb @@ -14,7 +14,7 @@ class LndhubV2 < Lndhub end def create_account(payload={}) - post "v2/users", payload, admin_token: Rails.application.credentials.lndhub[:admin_token] + post "v2/users", payload, admin_token: Setting.lndhub_admin_token end def create_invoice(payload) diff --git a/app/views/admin/settings/services/_btcpay.html.erb b/app/views/admin/settings/services/_btcpay.html.erb new file mode 100644 index 0000000..9565826 --- /dev/null +++ b/app/views/admin/settings/services/_btcpay.html.erb @@ -0,0 +1,37 @@ +

BTCPay Server

+ + +
+

REST API

+ diff --git a/app/views/admin/settings/services/_lndhub.html.erb b/app/views/admin/settings/services/_lndhub.html.erb index a68ad37..ac679c4 100644 --- a/app/views/admin/settings/services/_lndhub.html.erb +++ b/app/views/admin/settings/services/_lndhub.html.erb @@ -9,29 +9,35 @@ ) %> <% if Setting.lndhub_enabled? %> <%= render FormElements::FieldsetResettableSettingComponent.new( - key: :lndhub_api_url, - title: "API URL" - ) %> - <% end %> - <%= render FormElements::FieldsetToggleComponent.new( - form: f, - attribute: :lndhub_admin_enabled, - enabled: Setting.lndhub_admin_enabled?, - title: "Enable LNDHub admin panel", - description: "LNDHub database configuration present and admin panel enabled" - ) %> - <%= render FormElements::FieldsetToggleComponent.new( - form: f, - attribute: :lndhub_keysend_enabled, - enabled: Setting.lndhub_keysend_enabled?, - title: "Enable keysend payments", - description: "Allow users to receive invoice-less payments to their Lightning Address" - ) %> - <% if Setting.lndhub_keysend_enabled? %> + key: :lndhub_api_url, + title: "API URL" + ) %> <%= render FormElements::FieldsetResettableSettingComponent.new( - key: :lndhub_public_key, - title: "Public key", - description: "The public key of the Lightning node used by LNDHub" - ) %> + key: :lndhub_admin_token, + type: :password, + title: "Admin token", + description: "Auth token for creating new lndhub accounts" + ) %> + <%= render FormElements::FieldsetToggleComponent.new( + form: f, + attribute: :lndhub_admin_enabled, + enabled: Setting.lndhub_admin_enabled?, + title: "Enable LNDHub admin panel", + description: "LNDHub database configuration present and admin panel enabled" + ) %> + <%= render FormElements::FieldsetToggleComponent.new( + form: f, + attribute: :lndhub_keysend_enabled, + enabled: Setting.lndhub_keysend_enabled?, + title: "Enable keysend payments", + description: "Allow users to receive invoice-less payments to their Lightning Address" + ) %> + <% if Setting.lndhub_keysend_enabled? %> + <%= render FormElements::FieldsetResettableSettingComponent.new( + key: :lndhub_public_key, + title: "Public key", + description: "The public key of the Lightning node used by LNDHub" + ) %> + <% end %> <% end %> diff --git a/app/views/shared/_admin_sidenav_settings_services.html.erb b/app/views/shared/_admin_sidenav_settings_services.html.erb index 973777f..8650277 100644 --- a/app/views/shared/_admin_sidenav_settings_services.html.erb +++ b/app/views/shared/_admin_sidenav_settings_services.html.erb @@ -1,63 +1,70 @@ +<%= render SidenavLinkComponent.new( + level: 2, + name: "BTCPay", + path: admin_settings_services_path(params: { s: "btcpay" }), + text_icon: Setting.btcpay_enabled? ? "◉" : "○", + active: current_page?(admin_settings_services_path(params: { s: "btcpay" })), +) %> <%= render SidenavLinkComponent.new( level: 2, name: "Discourse", path: admin_settings_services_path(params: { s: "discourse" }), - icon: Setting.discourse_enabled? ? "check" : "x", + text_icon: Setting.discourse_enabled? ? "◉" : "○", active: current_page?(admin_settings_services_path(params: { s: "discourse" })), ) %> <%= render SidenavLinkComponent.new( level: 2, name: "Drone CI", path: admin_settings_services_path(params: { s: "droneci" }), - icon: Setting.droneci_enabled? ? "check" : "x", + text_icon: Setting.droneci_enabled? ? "◉" : "○", active: current_page?(admin_settings_services_path(params: { s: "droneci" })), ) %> <%= render SidenavLinkComponent.new( level: 2, name: "ejabberd", path: admin_settings_services_path(params: { s: "ejabberd" }), - icon: Setting.ejabberd_enabled? ? "check" : "x", + text_icon: Setting.ejabberd_enabled? ? "◉" : "○", active: current_page?(admin_settings_services_path(params: { s: "ejabberd" })), ) %> <%= render SidenavLinkComponent.new( level: 2, name: "Gitea", path: admin_settings_services_path(params: { s: "gitea" }), - icon: Setting.gitea_enabled? ? "check" : "x", + text_icon: Setting.gitea_enabled? ? "◉" : "○", active: current_page?(admin_settings_services_path(params: { s: "gitea" })), ) %> <%= render SidenavLinkComponent.new( level: 2, name: "LNDHub", path: admin_settings_services_path(params: { s: "lndhub" }), - icon: Setting.lndhub_enabled? ? "check" : "x", + text_icon: Setting.lndhub_enabled? ? "◉" : "○", active: current_page?(admin_settings_services_path(params: { s: "lndhub" })), ) %> <%= render SidenavLinkComponent.new( level: 2, name: "Mastodon", path: admin_settings_services_path(params: { s: "mastodon" }), - icon: Setting.mastodon_enabled? ? "check" : "x", + text_icon: Setting.mastodon_enabled? ? "◉" : "○", active: current_page?(admin_settings_services_path(params: { s: "mastodon" })), ) %> <%= render SidenavLinkComponent.new( level: 2, name: "MediaWiki", path: admin_settings_services_path(params: { s: "mediawiki" }), - icon: Setting.mediawiki_enabled? ? "check" : "x", + text_icon: Setting.mediawiki_enabled? ? "◉" : "○", active: current_page?(admin_settings_services_path(params: { s: "mediawiki" })), ) %> <%= render SidenavLinkComponent.new( level: 2, name: "Nostr", path: admin_settings_services_path(params: { s: "nostr" }), - icon: Setting.nostr_enabled? ? "check" : "x", + text_icon: Setting.nostr_enabled? ? "◉" : "○", active: current_page?(admin_settings_services_path(params: { s: "nostr" })), ) %> <%= render SidenavLinkComponent.new( level: 2, name: "RemoteStorage", path: admin_settings_services_path(params: { s: "remotestorage" }), - icon: Setting.remotestorage_enabled? ? "check" : "x", + text_icon: Setting.remotestorage_enabled? ? "◉" : "○", active: current_page?(admin_settings_services_path(params: { s: "remotestorage" })), ) %> diff --git a/config/credentials.yml.enc b/config/credentials.yml.enc index 978af61..de77871 100644 --- a/config/credentials.yml.enc +++ b/config/credentials.yml.enc @@ -1 +1 @@ -yEs5CyuAbqphlDWgtw/YQvkPn+EN4ecen2dAjs7zvYErkRRWp99FinGlQIMe6NRkMLLLSIj2BwR/wlscn1kLpIfwGpxfSZ89srK3do6Mb5QogpxdUsnQB8qv5PTGRQFBcjM47s1Q5m0t+OKxGvOnLyKnQp+cVS2KFJMbSzQarW8wIZSz2gKArn9Ttk0kqUHMlJWNY7Yh6xIrrxlEalaTOVzPdtnF7u8Tobminu15eeWHMormMRz4dYSaDc6hUtfpdy1NzOHaeXIU9A9RY/iytxuIQNgcMAlcWbPe//rVk/unH2F8xqSOfed4h/nC08F/qq4z8va3kEXBSdW/G91aIDMu1mo0kX3YNibq8s25C/CfGpzw39ozJ9erTBH7hy6nfmxU6qZuWcTGDj3NOfKe/XIfDcpOjsqkT2IOFARrYodb67q23IuOufraK1/FD4LXu8l0S8/Oi0cqMjtPPs7tS0M1C3DrbmlEzGKETrHpmoKHqjA0rgOmK4ZZM9LeI+l8Z+fDpYcCak9fLGGxnjf+nKiYMSUtm9+1dwycG2lpBV6fbmIKHJWngO2jVGcycODkc525oUaAO4hdPMqrz1AdU3AzYmLJTxW3aZ4uL5NyEJ7TbUBC0HT7h2gEi/tUry4cfD2EsM9bCrCUNuMBrnPqd4r8AvORoqqYIw1IEsP0RgWa2+hfeG1QCjBRPFHQOcqo+W25CelivMe79qI08w0iC8S4hfOQO4QrmMgtd1BhcR+wVpVE3X9EJZi3Hl7z14hXcSic+gkswJMtVZcnJL4rmZ0iEW1mpqUuegsX5vB/4qPxiQyeB80pg8Q33shvUbixzSBkl6znmLSiIffsiDsGOsnuzfl/MUT+JBs3UswNt4tSp7nEwhUjKFHrZHrAJiGCdtIS6yDPGe3HfQv1JkQ+9A8zv88hRmzeIx2JyT/shtIqGo+4ZTJd5cma--Lij/n0+cpstyZD28--FOUhwW3y+0jdaYkKvG2xrg== \ No newline at end of file +tmI5vm7qZhaigr52jEBVWkRdj+EE+9OmPh3vWXC7kA/OHuuucpr7SodychuMkQDPLM0BLk88LFsqvRIR+mqnLWpRC+P9aeUFE6ohxSWzcAd7Y4sgxUD8zpCRPndrwTw0hxXXj1WZSYeWn4BoAB34aV+gYen2MajZF3a95hJGtS5yjgWxvLVkQQKqRDfykkfX6fCS0BPo5X7sT7m4xwCATD/D4219wajm5W3TIdkriHtwt28ZLspaRWA5e0UkzKf8+/Gaj2CrW7UWcvew8R93zQ5RA2/Sp3sDTVN+kLz9I9Q095lQC0ywCAEFYHeKmc2tjrzqRaAAWu06xmWLqGIg21G+A/UU9lUJOkIpxQACWoOfS2IoXR1nXhgXMopkz3aCBXDxKw554v4H2QyOceOsuRf2C685ibMqzQkKMmJ4tcbiOJL77DUc08JTjB8Dq4Ohr8sMzXbV/hATevjYoRP0XarLekqhLv90ZLuIVY16DwB0CzACeNBKeKbeLqJF51upRRWgi+gTbYpV04yUwnXdyssF8mydWocgihrTryBi8F6PsuhBGcaYdP+0yibnGxDCC4x2rupbBfMj2OIX7pYzgtIHB3Eo954Y+bCoggqbE/Qrb9VVXNMgtKgLt8EGWU2tg6wl9QicitIq87uLDAade93zTn6rmcKPywjMDo6jbVIs653ZdUhiKdHGdpnJccbgQ/iLSPB1umNnCeaEX5jM+K9zBvl7ZMCdSk1YIQ==--ekKumqLiSlVJNwMe--K/ecXmmMT1x+WnIXMbHBDw== \ No newline at end of file diff --git a/config/routes.rb b/config/routes.rb index 3dc922a..e7fef10 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -54,7 +54,8 @@ Rails.application.routes.draw do post 'webhooks/lndhub', to: 'webhooks#lndhub' namespace :api do - get 'kredits/onchain_btc_balance', to: 'kredits#onchain_btc_balance' + get 'btcpay/onchain_btc_balance', to: 'btcpay#onchain_btc_balance' + get 'btcpay/lightning_btc_balance', to: 'btcpay#lightning_btc_balance' end namespace :admin do diff --git a/spec/features/admin/settings_spec.rb b/spec/features/admin/settings_spec.rb index 757f991..01a43f4 100644 --- a/spec/features/admin/settings_spec.rb +++ b/spec/features/admin/settings_spec.rb @@ -23,7 +23,7 @@ RSpec.describe 'Admin/global settings', type: :feature do scenario "Opening service settings shows page for first service" do visit admin_settings_services_path - expect(current_url).to eq(admin_settings_services_url(params: { s: "discourse" })) + expect(current_url).to eq(admin_settings_services_url(params: { s: "btcpay" })) end scenario "View service settings" do diff --git a/spec/requests/api/btcpay_spec.rb b/spec/requests/api/btcpay_spec.rb new file mode 100644 index 0000000..9dc5728 --- /dev/null +++ b/spec/requests/api/btcpay_spec.rb @@ -0,0 +1,103 @@ +require 'rails_helper' +require 'webmock/rspec' + +RSpec.describe "/api/btcpay", 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_btcpay_onchain_btc_balance_path + + expect(response).to have_http_status(:ok) + + res = JSON.parse(response.body) + expect(res["balance"]).to eq(91108606) + expect(res["unconfirmed_balance"]).to eq(0) + expect(res["confirmed_balance"]).to eq(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_btcpay_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 + + context "feature disabled" do + before do + Setting.btcpay_publish_wallet_balances = false + end + + it "returns a 404 status" do + get api_btcpay_onchain_btc_balance_path + + expect(response).to have_http_status(:not_found) + end + end + end + + describe "GET /lightning_btc_balance" do + before do + stub_request(:get, "http://btcpay.example.com/api/v1/stores/123456/lightning/BTC/balance") + .to_return(status: 200, headers: {}, body: { + offchain: { + local: 4200000000 + }, + }.to_json) + end + + it "returns a formatted result for the onchain wallet balance" do + get api_btcpay_lightning_btc_balance_path + + expect(response).to have_http_status(:ok) + + res = JSON.parse(response.body) + expect(res["balance"]).to eq(4200000) + end + + context "upstream request error" do + before do + stub_request(:get, "http://btcpay.example.com/api/v1/stores/123456/lightning/BTC/balance") + .to_return(status: 500, headers: {}, body: "") + end + + it "returns a formatted error" do + get api_btcpay_lightning_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 + + context "feature disabled" do + before do + Setting.btcpay_publish_wallet_balances = false + end + + it "returns a 404 status" do + get api_btcpay_lightning_btc_balance_path + + expect(response).to have_http_status(:not_found) + end + end + end +end diff --git a/spec/requests/api/kredits_spec.rb b/spec/requests/api/kredits_spec.rb deleted file mode 100644 index 0e1e464..0000000 --- a/spec/requests/api/kredits_spec.rb +++ /dev/null @@ -1,43 +0,0 @@ -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