-
Notifications
You must be signed in to change notification settings - Fork 44
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* dunno * fix user lookup * lnurl port * fix host * add config example * amount error msg * print stack trace * lnurl_public_server * remove public server ! * smaller * smaller * webhooks * add tag to response * add EncodedMetadata and Status * add success action and empty routes * update invoice memo * description hash * dunno * fix user lookup * lnurl port * fix host * amount error msg * add config example * print stack trace * lnurl_public_server * remove public server ! * smaller * smaller * webhooks * add tag to response * add EncodedMetadata and Status * add success action and empty routes * update invoice memo * description hash Co-authored-by: gohumble <[email protected]>
- Loading branch information
1 parent
f3b3e34
commit 77c3a50
Showing
2 changed files
with
86 additions
and
49 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,8 @@ | ||
package lnurl | ||
|
||
import ( | ||
"crypto/sha256" | ||
"encoding/hex" | ||
"encoding/json" | ||
"fmt" | ||
"net" | ||
|
@@ -61,19 +63,84 @@ func NewServer(lnurlserver string, webhookserver string, bot *tb.Bot, client *ln | |
func (w *Server) newRouter() *mux.Router { | ||
router := mux.NewRouter() | ||
router.HandleFunc(lnurlEndpoint+"/{username}", w.handleLnUrl).Methods(http.MethodGet) | ||
router.HandleFunc("/@{username}", w.handleLnUrl).Methods(http.MethodGet) | ||
//router.HandleFunc("/.well-know/lnurlp/{username}", w.handleLnUrl).Methods(http.MethodGet) | ||
return router | ||
} | ||
|
||
func (w Server) handleLnUrl(writer http.ResponseWriter, request *http.Request) { | ||
if request.URL.RawQuery == "" { | ||
w.createInitialLNURLPayResponse(writer, request) | ||
var err error | ||
err = w.serveLNURLpFirst(writer, request) | ||
if err != nil { | ||
log.Errorln(err) | ||
return | ||
} | ||
} else { | ||
w.createLNURLPayResponse(writer, request) | ||
w.serveLNURLpSecond(writer, request) | ||
} | ||
} | ||
|
||
// descriptionHash is the SHA256 hash of the metadata | ||
func (w Server) descriptionHash(metadata lnurl.Metadata, writer http.ResponseWriter) string { | ||
jsonMeta, err := json.Marshal(metadata) | ||
if err != nil { | ||
writer.WriteHeader(400) | ||
return "" | ||
} | ||
hash := sha256.Sum256([]byte(string(jsonMeta))) | ||
hash_string := hex.EncodeToString(hash[:]) | ||
return hash_string | ||
} | ||
|
||
// metaData returns the metadata that is sent in the first response | ||
// and is used again in the second response to verify the description hash | ||
func (w Server) metaData(request *http.Request) lnurl.Metadata { | ||
vars := mux.Vars(request) | ||
return lnurl.Metadata{{"text/identifier", fmt.Sprintf("%[email protected]", vars["username"])}, {"text/plain", fmt.Sprintf("Pay to %s@%s", vars["username"], w.callbackUrl)}} | ||
} | ||
|
||
// serveLNURLpFirst serves the first part of the LNURLp protocol with the endpoint | ||
// to call and the metadata that matches the description hash of the second response | ||
func (w Server) serveLNURLpFirst(writer http.ResponseWriter, request *http.Request) error { | ||
vars := mux.Vars(request) | ||
|
||
callback := fmt.Sprintf("%s%s/%s", w.callbackUrl, lnurlEndpoint, vars["username"]) | ||
callbackURL, err := url.Parse(callback) | ||
if err != nil { | ||
log.Errorln("callback is not a valid URL") | ||
writer.WriteHeader(400) | ||
return err | ||
} | ||
metadata := w.metaData(request) | ||
jsonMeta, err := json.Marshal(metadata) | ||
if err != nil { | ||
writer.WriteHeader(400) | ||
return err | ||
} | ||
|
||
resp := lnurl.LNURLPayResponse1{ | ||
LNURLResponse: lnurl.LNURLResponse{Status: "OK"}, | ||
Tag: "payRequest", | ||
Callback: fmt.Sprintf("https://%s", callback), | ||
CallbackURL: callbackURL, | ||
MinSendable: minSendable, | ||
MaxSendable: MaxSendable, | ||
CommentAllowed: 512, | ||
EncodedMetadata: string(jsonMeta), | ||
} | ||
jsonResponse, err := json.Marshal(resp) | ||
if err != nil { | ||
writer.WriteHeader(400) | ||
return err | ||
} | ||
writer.WriteHeader(200) | ||
writer.Write(jsonResponse) | ||
return nil | ||
} | ||
|
||
func (w Server) createLNURLPayResponse(writer http.ResponseWriter, request *http.Request) { | ||
// serveLNURLpSecond serves the second LNURL response with the payment request with the correct description hash | ||
func (w Server) serveLNURLpSecond(writer http.ResponseWriter, request *http.Request) { | ||
vars := mux.Vars(request) | ||
user := &lnbits.User{} | ||
tx := w.database.Where("telegram_username = ?", vars["username"]).First(user) | ||
|
@@ -88,7 +155,7 @@ func (w Server) createLNURLPayResponse(writer http.ResponseWriter, request *http | |
stringAmount := request.FormValue("amount") | ||
amount, err := strconv.Atoi(stringAmount) | ||
if err != nil { | ||
errmsg := fmt.Sprintf("[createLNURLPayResponse] Couldn't cast amount to int") | ||
errmsg := fmt.Sprintf("[serveLNURLpSecond] Couldn't cast amount to int") | ||
log.Warnln(errmsg) | ||
return | ||
} | ||
|
@@ -97,22 +164,26 @@ func (w Server) createLNURLPayResponse(writer http.ResponseWriter, request *http | |
|
||
if amount < minSendable || amount > MaxSendable { | ||
// amount is not ok | ||
errmsg := fmt.Sprintf("[createLNURLPayResponse] Amount out of bounds") | ||
errmsg := fmt.Sprintf("[serveLNURLpSecond] Amount out of bounds") | ||
log.Warnln(errmsg) | ||
resp = lnurl.LNURLPayResponse2{ | ||
LNURLResponse: lnurl.LNURLResponse{Status: "ERROR", Reason: fmt.Sprintf("Amount out of bounds (min: %d mSat, max: %d mSat).", minSendable, MaxSendable)}, | ||
} | ||
} else { | ||
// amount is ok | ||
|
||
// the same description_hash needs to be built in the second request | ||
metadata := w.metaData(request) | ||
hash_string := w.descriptionHash(metadata, writer) | ||
invoice, err := user.Wallet.Invoice( | ||
lnbits.InvoiceParams{ | ||
Amount: int64(amount / 1000), | ||
Out: false, | ||
Memo: fmt.Sprintf("Pay to %s@%s", vars["username"], w.callbackUrl), | ||
Webhook: w.WebhookServer}, | ||
Amount: int64(amount / 1000), | ||
Out: false, | ||
DescriptionHash: hash_string, | ||
Webhook: w.WebhookServer}, | ||
*user.Wallet) | ||
if err != nil { | ||
errmsg := fmt.Sprintf("[createLNURLPayResponse] Couldn't create invoice: %s", err.Error()) | ||
errmsg := fmt.Sprintf("[serveLNURLpSecond] Couldn't create invoice: %s", err.Error()) | ||
log.Warnln(errmsg) | ||
resp = lnurl.LNURLPayResponse2{ | ||
LNURLResponse: lnurl.LNURLResponse{Status: "ERROR", Reason: "Couldn't create invoice."}, | ||
|
@@ -134,38 +205,3 @@ func (w Server) createLNURLPayResponse(writer http.ResponseWriter, request *http | |
} | ||
writer.Write(jsonResponse) | ||
} | ||
|
||
func (w Server) createInitialLNURLPayResponse(writer http.ResponseWriter, request *http.Request) { | ||
vars := mux.Vars(request) | ||
|
||
callback := fmt.Sprintf("%s%s/%s", w.callbackUrl, lnurlEndpoint, vars["username"]) | ||
callbackURL, err := url.Parse(callback) | ||
if err != nil { | ||
log.Errorln("callback is not a valid URL") | ||
writer.WriteHeader(400) | ||
return | ||
} | ||
metadata := lnurl.Metadata{{fmt.Sprintf("Pay to %s@%s", vars["username"], w.callbackUrl)}} | ||
jsonMeta, err := json.Marshal(metadata) | ||
if err != nil { | ||
writer.WriteHeader(400) | ||
return | ||
} | ||
resp := lnurl.LNURLPayResponse1{ | ||
LNURLResponse: lnurl.LNURLResponse{Status: "OK"}, | ||
Tag: "payRequest", | ||
Callback: fmt.Sprintf("https://%s", callback), | ||
CallbackURL: callbackURL, | ||
MinSendable: minSendable, | ||
MaxSendable: MaxSendable, | ||
CommentAllowed: 512, | ||
EncodedMetadata: string(jsonMeta), | ||
} | ||
jsonResponse, err := json.Marshal(resp) | ||
if err != nil { | ||
writer.WriteHeader(400) | ||
return | ||
} | ||
writer.WriteHeader(200) | ||
writer.Write(jsonResponse) | ||
} |