class FaradayLnPaywall::Middleware

Public Class Methods

new(app, options = {}) click to toggle source
Calls superclass method
# File lib/faraday_ln_paywall/middleware.rb, line 9
def initialize(app, options = {})
  super(app)
  @options = options
  @options[:timeout] ||= 30
  @lnd_client = Lnrpc::Client.new(@options)
end

Public Instance Methods

call(request_env) click to toggle source
# File lib/faraday_ln_paywall/middleware.rb, line 40
def call(request_env)
  original_call = request_env.dup
  response = @app.call(request_env)
  response.on_complete do |response_env|
    if payment_requested?(response_env)
      log(:info, "payment requested")
      payment = pay(response_env)
      if payment && payment.payment_error == ""
        preimage = payment.payment_preimage.each_byte.map { |b| b.to_s(16).rjust(2, '0') }.join # .unpack("H*")
        log(:info, "paid preimage: #{preimage}")
        original_call[:request_headers].merge!('X-Preimage' => preimage)
        log(:info, "sending original request with preimage header")
        response = @app.call(original_call)
      else
        log(:error, "payment error #{payment.payment_error}")
        raise PaymentError, payment.payment_error
      end
    end
  end
  response
end
invoice_amount_in_satoshi(invoice) click to toggle source

todo move to Lightning/invoice gem

# File lib/faraday_ln_paywall/middleware.rb, line 63
def invoice_amount_in_satoshi(invoice)
  return if invoice.amount.nil?
  multi = {
    'm' => 0.001,
    'u' => 0.000001,
    'n' => 0.000000001,
    'p' => 0.000000000001
  }[invoice.multiplier]

  (invoice.amount * multi * 100000000).to_i # amount in bitcoin * 100000000
end
log(level, message) click to toggle source
# File lib/faraday_ln_paywall/middleware.rb, line 75
def log(level, message)
  @options[:logger].send(level, message) if @options[:logger]
end
pay(env) click to toggle source
# File lib/faraday_ln_paywall/middleware.rb, line 29
def pay(env)
  invoice = Lightning::Invoice.parse(env.body)
  log(:info, "amount: #{invoice_amount_in_satoshi(invoice)} description: #{invoice.description} payment_hash: #{invoice.payment_hash}")
  validate_invoice!(invoice)

  Timeout::timeout(@options[:timeout], PaymentError, "payment execution expired") do
    log(:info, "sending payment")
    @lnd_client.send_payment_sync(payment_request: env.body)
  end
end
payment_requested?(env) click to toggle source
# File lib/faraday_ln_paywall/middleware.rb, line 16
def payment_requested?(env)
  env[:status] == 402 && env[:response_headers]['Content-Type'] == "application/vnd.lightning.bolt11"
end
validate_invoice!(invoice) click to toggle source
# File lib/faraday_ln_paywall/middleware.rb, line 20
def validate_invoice!(invoice)
  if !@options[:max_amount].nil? && @options[:max_amount] < invoice_amount_in_satoshi(invoice)
    raise PaymentError, "invoice amount greater than expected maximum of #{@options[:max_amount]}"
  end
  if !invoice.expiry.nil? && Time.now.to_i > invoice.timestamp + invoice.expiry.to_i
    raise PaymentError, "invoice expired"
  end
end