forked from fiatjaf/bridgeaddr
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathlnurl.go
118 lines (100 loc) · 3.06 KB
/
lnurl.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
package main
import (
"bytes"
"encoding/json"
"fmt"
"net"
"net/http"
"net/url"
"strconv"
"time"
"github.com/fiatjaf/go-lnurl"
"github.com/gorilla/mux"
nostr "github.com/nbd-wtf/go-nostr"
decodepay "github.com/nbd-wtf/ln-decodepay"
"github.com/tidwall/sjson"
)
func handleLNURL(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", "*")
username := mux.Vars(r)["username"]
domain := r.Host
var nostr_pubkey string
if v, err := net.LookupTXT("_nostr_pubkey." + domain); err == nil && len(v) > 0 {
nostr_pubkey = v[0]
}
if err != nil {
log.Info().Err(err).Msg("Failed to retrieve nostr pubkey")
return
}
log.Info().Str("username", username).Str("domain", domain).
Msg("got lnurl request")
if amount := r.URL.Query().Get("amount"); amount == "" {
// check if the receiver accepts comments
var commentLength int64 = 0
if v, err := net.LookupTXT("_webhook." + domain); err == nil && len(v) > 0 {
commentLength = 500
}
json.NewEncoder(w).Encode(lnurl.LNURLPayParams{
LNURLResponse: lnurl.LNURLResponse{Status: "OK"},
Callback: fmt.Sprintf("https://%s/.well-known/lnurlp/%s", domain, username),
MinSendable: 1000,
MaxSendable: 100000000,
EncodedMetadata: makeMetadata(username, domain),
CommentAllowed: commentLength,
Tag: "payRequest",
AllowsNostr: nostr_pubkey != "",
NostrPubkey: nostr_pubkey,
})
} else {
msat, err := strconv.Atoi(amount)
if err != nil {
json.NewEncoder(w).Encode(lnurl.ErrorResponse("amount is not integer"))
return
}
zapReqStr, _ := url.QueryUnescape(r.URL.Query().Get("nostr"))
// TODO: better zap validation
var zapReq nostr.Event
if err := json.Unmarshal([]byte(zapReqStr), &zapReq); err != nil {
log.Warn().Err(err).Msg("Failed to unmarshal zap request")
return
}
valid, err := zapReq.CheckSignature()
if !valid {
log.Info().Msg("Zap request signature invalid")
return
}
log.Info().Interface("zap request", zapReq).Msg("Parsed zap request")
bolt11, err := makeInvoice(username, domain, msat, zapReq.String())
if err != nil {
json.NewEncoder(w).Encode(
lnurl.ErrorResponse("failed to create invoice: " + err.Error()))
return
}
json.NewEncoder(w).Encode(lnurl.LNURLPayValues{
LNURLResponse: lnurl.LNURLResponse{Status: "OK"},
PR: bolt11,
Routes: make([][]interface{}, 0),
Disposable: lnurl.FALSE,
SuccessAction: lnurl.Action("Payment received!", ""),
})
go func() {
inv, err := decodepay.Decodepay(bolt11)
if err != nil {
return
}
WaitForZap(inv.PaymentHash, domain, zapReq)
}()
// send webhook
go func() {
if v, err := net.LookupTXT("_webhook." + domain); err == nil && len(v) > 0 {
body, _ := sjson.Set("{}", "pr", bolt11)
body, _ = sjson.Set(body, "amount", msat)
if comment := r.URL.Query().Get("comment"); comment != "" {
body, _ = sjson.Set(body, "comment", comment)
}
(&http.Client{Timeout: 5 * time.Second}).
Post(v[0], "application/json", bytes.NewBufferString(body))
}
}()
}
}