Add LndHub service, lnurl-pay endpoints
Enables the lnurl-pay payment workflow
This commit is contained in:
parent
884070a3cb
commit
dbc811b840
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({
|
||||||
|
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
|
@ -27,27 +27,18 @@ class WalletController < ApplicationController
|
|||||||
if session["ln_auth_token"].present?
|
if session["ln_auth_token"].present?
|
||||||
@ln_auth_token = session["ln_auth_token"]
|
@ln_auth_token = session["ln_auth_token"]
|
||||||
else
|
else
|
||||||
res = Faraday.post("#{ENV["LNDHUB_API_URL"]}/auth?type=auth",
|
lndhub = Lndhub.new
|
||||||
{ login: current_user.ln_login, password: current_user.ln_password }.to_json,
|
auth_token = lndhub.authenticate(current_user)
|
||||||
"Content-Type" => "application/json",
|
session["ln_auth_token"] = auth_token
|
||||||
"Accept" => "application/json")
|
@ln_auth_token = auth_token
|
||||||
|
|
||||||
credentials = JSON.parse(res.body)
|
|
||||||
session["ln_auth_token"] = credentials["access_token"]
|
|
||||||
@ln_auth_token = credentials["access_token"]
|
|
||||||
end
|
end
|
||||||
rescue
|
rescue
|
||||||
# TODO add exception tracking
|
# TODO add exception tracking
|
||||||
end
|
end
|
||||||
|
|
||||||
def fetch_balance
|
def fetch_balance
|
||||||
res = Faraday.get("#{ENV["LNDHUB_API_URL"]}/balance", {}, {
|
lndhub = Lndhub.new
|
||||||
"Content-Type" => "application/json",
|
data = lndhub.balance @ln_auth_token
|
||||||
"Accept" => "application/json",
|
|
||||||
"Authorization" => "Bearer #{@ln_auth_token}"
|
|
||||||
})
|
|
||||||
|
|
||||||
data = JSON.parse(res.body)
|
|
||||||
data["BTC"]["AvailableBalance"]
|
data["BTC"]["AvailableBalance"]
|
||||||
end
|
end
|
||||||
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
|
queue_as :default
|
||||||
|
|
||||||
def perform(user)
|
def perform(user)
|
||||||
res = Faraday.post("#{ENV["LNDHUB_API_URL"]}/create",
|
lndhub = Lndhub.new
|
||||||
{ partnerid: "bluewallet", accounttype: "common" }.to_json,
|
credentials = lndhub.create({ partnerid: user.ou, accounttype: "user" })
|
||||||
"Content-Type" => "application/json")
|
|
||||||
|
|
||||||
credentials = JSON.parse(res.body)
|
|
||||||
|
|
||||||
user.update! ln_login: credentials["login"],
|
user.update! ln_login: credentials["login"],
|
||||||
ln_password: credentials["password"]
|
ln_password: credentials["password"]
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
@ -56,4 +56,10 @@ class User < ApplicationRecord
|
|||||||
self.valid?
|
self.valid?
|
||||||
self.errors[attribute_name].blank?
|
self.errors[attribute_name].blank?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def ln_create_invoice(payload)
|
||||||
|
lndhub = Lndhub.new
|
||||||
|
lndhub.authenticate self
|
||||||
|
lndhub.addinvoice payload
|
||||||
|
end
|
||||||
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 'wallet', to: 'wallet#index'
|
||||||
|
|
||||||
|
get 'lnurlpay/:address', to: 'lnurlpay#index', constraints: { address: /[^\/]+/}
|
||||||
|
get 'lnurlpay/:address/invoice', to: 'lnurlpay#invoice', constraints: { address: /[^\/]+/}
|
||||||
|
|
||||||
namespace :admin do
|
namespace :admin do
|
||||||
root to: 'dashboard#index'
|
root to: 'dashboard#index'
|
||||||
get 'invitations', to: 'invitations#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 }
|
perform_enqueued_jobs { job }
|
||||||
|
|
||||||
expect(WebMock).to have_requested(:post, "http://10.1.1.163:3023/create")
|
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
|
user.reload
|
||||||
expect(user.ln_login).to eq("abc123")
|
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
|
Loading…
x
Reference in New Issue
Block a user