Add LndHub service, lnurl-pay endpoints
Some checks failed
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is failing

Enables the lnurl-pay payment workflow
This commit is contained in:
Basti 2021-11-22 15:41:05 -06:00
parent 884070a3cb
commit dbc811b840
Signed by untrusted user: basti
GPG Key ID: 9F88009D31D99C72
10 changed files with 158 additions and 23 deletions

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
}
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({
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
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