Add LndHub wallets #33

Merged
raucao merged 13 commits from feature/lndhub into master 2021-11-22 23:04:19 +00:00
10 changed files with 158 additions and 23 deletions
Showing only changes of commit dbc811b840 - Show all commits

View 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
Outdated
Review

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

we should set this to `0` because we do not allow the payer to send a comment with the payment.
}
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({
Review

this must be amount/1000 - amount is in msats but we create invoices for sats.

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
Review

Shouldn't this also give an error response code? Maybe 422?

Shouldn't this also give an error response code? Maybe 422?
Review

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. ;)
Review

WHUT?

WHUT? ![](https://storage.5apps.com/galfert/public/shares/170629-1354-lebowski.gif)
Review

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

View File

@@ -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

View File

@@ -0,0 +1,2 @@
module LnurlpayHelper
end

View File

@@ -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

View File

@@ -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
View 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

View File

@@ -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'

View 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

View File

@@ -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")

View 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