Add LndHub wallets #33
58
app/controllers/lnurlpay_controller.rb
Normal file
58
app/controllers/lnurlpay_controller.rb
Normal file
@@ -0,0 +1,58 @@
|
||||
class LnurlpayController < ApplicationController
|
||||
before_action :find_user_by_address
|
||||
|
||||
def index
|
||||
render json: {
|
||||
status: "OK",
|
||||
callback: "https://accounts.kosmos.org/lnurlpay/#{@user.address}/invoice",
|
||||
tag: "payRequest",
|
||||
maxSendable: 1000000,
|
||||
minSendable: 1000,
|
||||
metadata: metadata(@user.address),
|
||||
commentAllowed: 255
|
||||
|
|
||||
}
|
||||
end
|
||||
|
||||
def invoice
|
||||
amount = params[:amount].to_i # msats
|
||||
address = params[:address]
|
||||
|
||||
validate_amount(amount)
|
||||
|
||||
# amount = amount / 1000 # we need sats
|
||||
payment_request = @user.ln_create_invoice({
|
||||
|
bumi
commented
this must be this must be `amount/1000` - amount is in msats but we create invoices for sats.
|
||||
amount: amount,
|
||||
description_hash: Digest::SHA2.hexdigest(metadata(address))
|
||||
})
|
||||
|
||||
render json: {
|
||||
status: "OK",
|
||||
successAction: {
|
||||
tag: "message",
|
||||
message: "Payment received. Thanks!"
|
||||
},
|
||||
routes: [],
|
||||
pr: payment_request
|
||||
}
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def find_user_by_address
|
||||
address = params[:address].split("@")
|
||||
@user = User.where(cn: address.first, ou: address.last).first
|
||||
http_status :not_found if @user.nil?
|
||||
end
|
||||
|
||||
def metadata(address)
|
||||
"[[\"text/identifier\", \"#{address}\"], [\"text/plain\", \"Sats for #{address}\"]]"
|
||||
end
|
||||
|
||||
def validate_amount(amount)
|
||||
if amount > 1000000 || amount < 1000
|
||||
|
galfert
commented
Shouldn't this also give an error response code? Maybe 422? Shouldn't this also give an error response code? Maybe 422?
raucao
commented
You would think so, but no. That's how ldnhub clients are expected to implement errors. ;) You would think so, but no. That's how ldnhub clients are expected to implement errors. ;)
galfert
commented
bumi
commented
the lnurl spec specifically says to ignore HTTP status codes :( - no idea why... but because of that it's better to return a 200 the lnurl spec specifically says to ignore HTTP status codes :( - no idea why... but because of that it's better to return a 200
|
||||
render json: { status: "ERROR", reason: "Invalid amount" }
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
@@ -27,27 +27,18 @@ class WalletController < ApplicationController
|
||||
if session["ln_auth_token"].present?
|
||||
@ln_auth_token = session["ln_auth_token"]
|
||||
else
|
||||
res = Faraday.post("#{ENV["LNDHUB_API_URL"]}/auth?type=auth",
|
||||
{ login: current_user.ln_login, password: current_user.ln_password }.to_json,
|
||||
"Content-Type" => "application/json",
|
||||
"Accept" => "application/json")
|
||||
|
||||
credentials = JSON.parse(res.body)
|
||||
session["ln_auth_token"] = credentials["access_token"]
|
||||
@ln_auth_token = credentials["access_token"]
|
||||
lndhub = Lndhub.new
|
||||
auth_token = lndhub.authenticate(current_user)
|
||||
session["ln_auth_token"] = auth_token
|
||||
@ln_auth_token = auth_token
|
||||
end
|
||||
rescue
|
||||
# TODO add exception tracking
|
||||
end
|
||||
|
||||
def fetch_balance
|
||||
res = Faraday.get("#{ENV["LNDHUB_API_URL"]}/balance", {}, {
|
||||
"Content-Type" => "application/json",
|
||||
"Accept" => "application/json",
|
||||
"Authorization" => "Bearer #{@ln_auth_token}"
|
||||
})
|
||||
|
||||
data = JSON.parse(res.body)
|
||||
data["BTC"]["AvailableBalance"]
|
||||
lndhub = Lndhub.new
|
||||
data = lndhub.balance @ln_auth_token
|
||||
data["BTC"]["AvailableBalance"]
|
||||
end
|
||||
end
|
||||
|
||||
2
app/helpers/lnurlpay_helper.rb
Normal file
2
app/helpers/lnurlpay_helper.rb
Normal file
@@ -0,0 +1,2 @@
|
||||
module LnurlpayHelper
|
||||
end
|
||||
@@ -2,14 +2,10 @@ class CreateLndhubWalletJob < ApplicationJob
|
||||
queue_as :default
|
||||
|
||||
def perform(user)
|
||||
res = Faraday.post("#{ENV["LNDHUB_API_URL"]}/create",
|
||||
{ partnerid: "bluewallet", accounttype: "common" }.to_json,
|
||||
"Content-Type" => "application/json")
|
||||
|
||||
credentials = JSON.parse(res.body)
|
||||
lndhub = Lndhub.new
|
||||
credentials = lndhub.create({ partnerid: user.ou, accounttype: "user" })
|
||||
|
||||
user.update! ln_login: credentials["login"],
|
||||
ln_password: credentials["password"]
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -56,4 +56,10 @@ class User < ApplicationRecord
|
||||
self.valid?
|
||||
self.errors[attribute_name].blank?
|
||||
end
|
||||
|
||||
def ln_create_invoice(payload)
|
||||
lndhub = Lndhub.new
|
||||
lndhub.authenticate self
|
||||
lndhub.addinvoice payload
|
||||
end
|
||||
end
|
||||
|
||||
57
app/services/lndhub.rb
Normal file
57
app/services/lndhub.rb
Normal file
@@ -0,0 +1,57 @@
|
||||
class Lndhub
|
||||
attr_accessor :auth_token
|
||||
|
||||
def initialize
|
||||
@base_url = ENV["LNDHUB_API_URL"]
|
||||
end
|
||||
|
||||
def post(endpoint, payload)
|
||||
headers = { "Content-Type" => "application/json" }
|
||||
if auth_token
|
||||
headers.merge!({ "Authorization" => "Bearer #{auth_token}" })
|
||||
end
|
||||
|
||||
res = Faraday.post "#{@base_url}/#{endpoint}", payload.to_json, headers
|
||||
|
||||
if res.status != 200
|
||||
Rails.logger.error "[lndhub] API request failed:"
|
||||
Rails.logger.error res.body
|
||||
#TODO add some kind of exception tracking/notifications
|
||||
end
|
||||
|
||||
JSON.parse(res.body)
|
||||
end
|
||||
|
||||
def get(endpoint, auth_token)
|
||||
res = Faraday.get("#{@base_url}/#{endpoint}", {}, {
|
||||
"Content-Type" => "application/json",
|
||||
"Accept" => "application/json",
|
||||
"Authorization" => "Bearer #{auth_token}"
|
||||
})
|
||||
|
||||
JSON.parse(res.body)
|
||||
end
|
||||
|
||||
def create(payload)
|
||||
post "create", payload
|
||||
end
|
||||
|
||||
def authenticate(user)
|
||||
credentials = post "auth?type=auth", { login: user.ln_login, password: user.ln_password }
|
||||
self.auth_token = credentials["access_token"]
|
||||
self.auth_token
|
||||
end
|
||||
|
||||
def balance(user_token)
|
||||
get "balance", user_token || auth_token
|
||||
end
|
||||
|
||||
def addinvoice(payload)
|
||||
invoice = post "addinvoice", {
|
||||
amt: payload[:amount],
|
||||
description_hash: payload[:description_hash]
|
||||
}
|
||||
|
||||
invoice["payment_request"]
|
||||
end
|
||||
end
|
||||
@@ -21,6 +21,9 @@ Rails.application.routes.draw do
|
||||
|
||||
get 'wallet', to: 'wallet#index'
|
||||
|
||||
get 'lnurlpay/:address', to: 'lnurlpay#index', constraints: { address: /[^\/]+/}
|
||||
get 'lnurlpay/:address/invoice', to: 'lnurlpay#invoice', constraints: { address: /[^\/]+/}
|
||||
|
||||
namespace :admin do
|
||||
root to: 'dashboard#index'
|
||||
get 'invitations', to: 'invitations#index'
|
||||
|
||||
15
spec/helpers/lnurlpay_helper_spec.rb
Normal file
15
spec/helpers/lnurlpay_helper_spec.rb
Normal file
@@ -0,0 +1,15 @@
|
||||
require 'rails_helper'
|
||||
|
||||
# Specs in this file have access to a helper object that includes
|
||||
# the LnurlpayHelper. For example:
|
||||
#
|
||||
# describe LnurlpayHelper do
|
||||
# describe "string concat" do
|
||||
# it "concats two strings with spaces" do
|
||||
# expect(helper.concat_strings("this","that")).to eq("this that")
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
RSpec.describe LnurlpayHelper, type: :helper do
|
||||
pending "add some examples to (or delete) #{__FILE__}"
|
||||
end
|
||||
@@ -16,7 +16,7 @@ RSpec.describe CreateLndhubWalletJob, type: :job do
|
||||
perform_enqueued_jobs { job }
|
||||
|
||||
expect(WebMock).to have_requested(:post, "http://10.1.1.163:3023/create")
|
||||
.with { |req| req.body == '{"partnerid":"bluewallet","accounttype":"common"}' }
|
||||
.with { |req| req.body == '{"partnerid":"kosmos.org","accounttype":"user"}' }
|
||||
|
||||
user.reload
|
||||
expect(user.ln_login).to eq("abc123")
|
||||
|
||||
7
spec/requests/lnurlpay_spec.rb
Normal file
7
spec/requests/lnurlpay_spec.rb
Normal file
@@ -0,0 +1,7 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe "Lnurlpays", type: :request do
|
||||
describe "GET /index" do
|
||||
pending "add some examples (or delete) #{__FILE__}"
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user

we should set this to
0because we do not allow the payer to send a comment with the payment.