From 9a5016225f07daa7a85d4766a0e8926db6ddef55 Mon Sep 17 00:00:00 2001 From: Peter Rounce Date: Sat, 18 Feb 2023 21:09:35 +0000 Subject: [PATCH] lndhub /auth --- go.mod | 1 + go.sum | 2 + lndhub/lndhub.go | 12 +++ lnurlw/lnurlw_callback.go | 216 ++++++++++++++++++++++++-------------- 4 files changed, 155 insertions(+), 76 deletions(-) create mode 100644 lndhub/lndhub.go diff --git a/go.mod b/go.mod index b599a7e..abc8029 100644 --- a/go.mod +++ b/go.mod @@ -90,6 +90,7 @@ require ( github.com/miekg/dns v1.1.50 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/nbd-wtf/ln-decodepay v1.11.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_golang v1.14.0 // indirect github.com/prometheus/client_model v0.3.0 // indirect diff --git a/go.sum b/go.sum index e6fd238..52e950e 100644 --- a/go.sum +++ b/go.sum @@ -535,6 +535,8 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/nbd-wtf/ln-decodepay v1.11.1 h1:MPiT4a4qZ2cKY27Aj0dI8sLFrLz5Ycu72Z3EG1HfPjk= +github.com/nbd-wtf/ln-decodepay v1.11.1/go.mod h1:xzBXPaCj/7oRRaui+iYSIxy5LYUjoPfAyAGq2WCyNKk= github.com/nwaples/rardecode v1.1.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0= github.com/nwaples/rardecode v1.1.2/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= diff --git a/lndhub/lndhub.go b/lndhub/lndhub.go new file mode 100644 index 0000000..e0d6153 --- /dev/null +++ b/lndhub/lndhub.go @@ -0,0 +1,12 @@ +package lndhub + +import ( + log "github.com/sirupsen/logrus" + + "github.com/boltcard/boltcard/db" + "github.com/boltcard/boltcard/email" +) + +func Pay_invoice(card_payment_id int, invoice string) { + +} diff --git a/lnurlw/lnurlw_callback.go b/lnurlw/lnurlw_callback.go index 9f4abab..5d31e27 100644 --- a/lnurlw/lnurlw_callback.go +++ b/lnurlw/lnurlw_callback.go @@ -4,11 +4,143 @@ import ( decodepay "github.com/fiatjaf/ln-decodepay" log "github.com/sirupsen/logrus" "net/http" + "bytes" + "io" "github.com/boltcard/boltcard/db" "github.com/boltcard/boltcard/lnd" "github.com/boltcard/boltcard/resp_err" ) +func lndhub_payment(w http.ResponseWriter, p *db.Payment) { + + //get setting for LNDHUB_URL + lndhub_url := "https://" + db.Get_setting("LNDHUB_URL") + + //get lndhub login details from database + c, err := db.Get_card_from_card_id(p.Card_id) + if err != nil { + log.WithFields(log.Fields{"card_payment_id": p.Card_payment_id}).Warn(err) + resp_err.Write(w) + return + } + + //lndhub.auth API call + //the login JSON is held in the Card_name field + body := []byte(c.Card_name) + + r, err := http.NewRequest("POST", lndhub_url + "/auth", bytes.NewBuffer(body)) + if err != nil { + log.WithFields(log.Fields{"card_payment_id": p.Card_payment_id}).Warn(err) + resp_err.Write(w) + return + } + + r.Header.Add("Access-Control-Allow-Origin", "*") + r.Header.Add("Content-Type", "application/json") + + client := &http.Client{} + res, err := client.Do(r) + if err != nil { + log.WithFields(log.Fields{"card_payment_id": p.Card_payment_id}).Warn(err) + resp_err.Write(w) + return + } + + defer res.Body.Close() + + b, err2 := io.ReadAll(res.Body) + if err2 != nil { + log.WithFields(log.Fields{"card_payment_id": p.Card_payment_id}).Warn(err) + resp_err.Write(w) + return + } + + log.Info(string(b)) + +// fmt.Println(string(b)) + + //lndhub.payinvoice API call +} + +func lnd_payment(w http.ResponseWriter, bolt11 decodepay.Bolt11, p *db.Payment, param_pr string) { + + // check amount limits + invoice_sats := int(bolt11.MSatoshi / 1000) + + day_total_sats, err := db.Get_card_totals(p.Card_id) + if err != nil { + log.WithFields(log.Fields{"card_payment_id": p.Card_payment_id}).Warn(err) + resp_err.Write(w) + return + } + + c, err := db.Get_card_from_card_id(p.Card_id) + if err != nil { + log.WithFields(log.Fields{"card_payment_id": p.Card_payment_id}).Warn(err) + resp_err.Write(w) + return + } + + if invoice_sats > c.Tx_limit_sats { + log.WithFields(log.Fields{"card_payment_id": p.Card_payment_id}).Info("invoice_sats: ", invoice_sats) + log.WithFields(log.Fields{"card_payment_id": p.Card_payment_id}).Info("tx_limit_sats: ", c.Tx_limit_sats) + log.WithFields(log.Fields{"card_payment_id": p.Card_payment_id}).Info("over tx_limit_sats!") + resp_err.Write(w) + return + } + + if day_total_sats+invoice_sats > c.Day_limit_sats { + log.WithFields(log.Fields{"card_payment_id": p.Card_payment_id}).Info("invoice_sats: ", invoice_sats) + log.WithFields(log.Fields{"card_payment_id": p.Card_payment_id}).Info("day_total_sats: ", day_total_sats) + log.WithFields(log.Fields{"card_payment_id": p.Card_payment_id}).Info("day_limit_sats: ", c.Day_limit_sats) + log.WithFields(log.Fields{"card_payment_id": p.Card_payment_id}).Info("over day_limit_sats!") + resp_err.Write(w) + return + } + + // check the card balance if marked as 'must stay above zero' (default) + // i.e. cards.allow_negative_balance == 'N' + if c.Allow_negative_balance != "Y" { + card_total, err := db.Get_card_total_sats(p.Card_id) + if err != nil { + log.WithFields(log.Fields{"card_payment_id": p.Card_payment_id}).Warn(err) + resp_err.Write(w) + return + } + + if card_total-invoice_sats < 0 { + log.WithFields(log.Fields{"card_payment_id": p.Card_payment_id}).Warn("not enough balance") + resp_err.Write(w) + return + } + } + + log.WithFields(log.Fields{"card_payment_id": p.Card_payment_id}).Info("paying invoice") + + // update paid_flag so we only attempt payment once + err = db.Update_payment_paid(p.Card_payment_id) + if err != nil { + log.WithFields(log.Fields{"card_payment_id": p.Card_payment_id}).Warn(err) + resp_err.Write(w) + return + } + + // https://github.com/fiatjaf/lnurl-rfc/blob/luds/03.md + // + // LN SERVICE sends a {"status": "OK"} or + // {"status": "ERROR", "reason": "error details..."} + // JSON response and then attempts to pay the invoices asynchronously. + + go lnd.Pay_invoice(p.Card_payment_id, param_pr) + + log.Debug("sending 'status OK' response") + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + jsonData := []byte(`{"status":"OK"}`) + w.Write(jsonData) +} + func Callback(w http.ResponseWriter, req *http.Request) { env_host_domain := db.Get_setting("HOST_DOMAIN") @@ -87,81 +219,13 @@ func Callback(w http.ResponseWriter, req *http.Request) { return } - // check amount limits - - invoice_sats := int(bolt11.MSatoshi / 1000) - - day_total_sats, err := db.Get_card_totals(p.Card_id) - if err != nil { - log.WithFields(log.Fields{"card_payment_id": p.Card_payment_id}).Warn(err) - resp_err.Write(w) - return + //check if we are using LND or LNDHUB for payment + lndhub := db.Get_setting("FUNCTION_LNDHUB") + if lndhub == "ENABLE" { + log.WithFields(log.Fields{"card_payment_id": p.Card_payment_id}).Info("initiating lndhub payment") + lndhub_payment(w, p) + } else { + log.WithFields(log.Fields{"card_payment_id": p.Card_payment_id}).Info("initiating lnd payment") + lnd_payment(w, bolt11, p, param_pr) } - - c, err := db.Get_card_from_card_id(p.Card_id) - if err != nil { - log.WithFields(log.Fields{"card_payment_id": p.Card_payment_id}).Warn(err) - resp_err.Write(w) - return - } - - if invoice_sats > c.Tx_limit_sats { - log.WithFields(log.Fields{"card_payment_id": p.Card_payment_id}).Info("invoice_sats: ", invoice_sats) - log.WithFields(log.Fields{"card_payment_id": p.Card_payment_id}).Info("tx_limit_sats: ", c.Tx_limit_sats) - log.WithFields(log.Fields{"card_payment_id": p.Card_payment_id}).Info("over tx_limit_sats!") - resp_err.Write(w) - return - } - - if day_total_sats+invoice_sats > c.Day_limit_sats { - log.WithFields(log.Fields{"card_payment_id": p.Card_payment_id}).Info("invoice_sats: ", invoice_sats) - log.WithFields(log.Fields{"card_payment_id": p.Card_payment_id}).Info("day_total_sats: ", day_total_sats) - log.WithFields(log.Fields{"card_payment_id": p.Card_payment_id}).Info("day_limit_sats: ", c.Day_limit_sats) - log.WithFields(log.Fields{"card_payment_id": p.Card_payment_id}).Info("over day_limit_sats!") - resp_err.Write(w) - return - } - - // check the card balance if marked as 'must stay above zero' (default) - // i.e. cards.allow_negative_balance == 'N' - - if c.Allow_negative_balance != "Y" { - card_total, err := db.Get_card_total_sats(p.Card_id) - if err != nil { - log.WithFields(log.Fields{"card_payment_id": p.Card_payment_id}).Warn(err) - resp_err.Write(w) - return - } - - if card_total-invoice_sats < 0 { - log.WithFields(log.Fields{"card_payment_id": p.Card_payment_id}).Warn("not enough balance") - resp_err.Write(w) - return - } - } - - log.WithFields(log.Fields{"card_payment_id": p.Card_payment_id}).Info("paying invoice") - - // update paid_flag so we only attempt payment once - err = db.Update_payment_paid(p.Card_payment_id) - if err != nil { - log.WithFields(log.Fields{"card_payment_id": p.Card_payment_id}).Warn(err) - resp_err.Write(w) - return - } - - // https://github.com/fiatjaf/lnurl-rfc/blob/luds/03.md - // - // LN SERVICE sends a {"status": "OK"} or - // {"status": "ERROR", "reason": "error details..."} - // JSON response and then attempts to pay the invoices asynchronously. - - go lnd.Pay_invoice(p.Card_payment_id, param_pr) - - log.Debug("sending 'status OK' response") - - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - jsonData := []byte(`{"status":"OK"}`) - w.Write(jsonData) }