muun-recovery/vendor/github.com/fiatjaf/go-lnurl/pay.go

203 lines
4.8 KiB
Go

package lnurl
import (
"encoding/base64"
"encoding/json"
"errors"
"net/url"
"strconv"
"strings"
"time"
"github.com/tidwall/gjson"
)
var (
f bool = false
t bool = true
FALSE *bool = &f
TRUE *bool = &t
)
func Action(text string, url string) *SuccessAction {
if url == "" {
return &SuccessAction{
Tag: "message",
Message: text,
}
}
if text == "" {
text = " "
}
return &SuccessAction{
Tag: "url",
Description: text,
URL: url,
}
}
func AESAction(description string, preimage []byte, content string) (*SuccessAction, error) {
plaintext := []byte(content)
ciphertext, iv, err := AESCipher(preimage, plaintext)
if err != nil {
return nil, err
}
return &SuccessAction{
Tag: "aes",
Description: description,
Ciphertext: base64.StdEncoding.EncodeToString(ciphertext),
IV: base64.StdEncoding.EncodeToString(iv),
}, nil
}
type LNURLPayResponse1 struct {
LNURLResponse
Callback string `json:"callback"`
CallbackURL *url.URL `json:"-"`
Tag string `json:"tag"`
MaxSendable int64 `json:"maxSendable"`
MinSendable int64 `json:"minSendable"`
EncodedMetadata string `json:"metadata"`
Metadata Metadata `json:"-"`
CommentAllowed int64 `json:"commentAllowed"`
}
type LNURLPayResponse2 struct {
LNURLResponse
SuccessAction *SuccessAction `json:"successAction"`
Routes [][]RouteInfo `json:"routes"`
PR string `json:"pr"`
Disposable *bool `json:"disposable,omitempty"`
}
type RouteInfo struct {
NodeId string `json:"nodeId"`
ChannelUpdate string `json:"channelUpdate"`
}
type SuccessAction struct {
Tag string `json:"tag"`
Description string `json:"description,omitempty"`
URL string `json:"url,omitempty"`
Message string `json:"message,omitempty"`
Ciphertext string `json:"ciphertext,omitempty"`
IV string `json:"iv,omitempty"`
}
func (sa *SuccessAction) Decipher(preimage []byte) (content string, err error) {
ciphertext, err := base64.StdEncoding.DecodeString(sa.Ciphertext)
if err != nil {
return
}
iv, err := base64.StdEncoding.DecodeString(sa.IV)
if err != nil {
return
}
plaintext, err := AESDecipher(preimage, ciphertext, iv)
if err != nil {
return
}
return string(plaintext), nil
}
func (_ LNURLPayResponse1) LNURLKind() string { return "lnurl-pay" }
func HandlePay(j gjson.Result) (LNURLParams, error) {
strmetadata := j.Get("metadata").String()
var metadata Metadata
err := json.Unmarshal([]byte(strmetadata), &metadata)
if err != nil {
return nil, err
}
callback := j.Get("callback").String()
// parse url
callbackURL, err := url.Parse(callback)
if err != nil {
return nil, errors.New("callback is not a valid URL")
}
// add random nonce to avoid caches
qs := callbackURL.Query()
qs.Set("nonce", strconv.FormatInt(time.Now().Unix(), 10))
callbackURL.RawQuery = qs.Encode()
return LNURLPayResponse1{
Tag: "payRequest",
Callback: callback,
CallbackURL: callbackURL,
EncodedMetadata: strmetadata,
Metadata: metadata,
MaxSendable: j.Get("maxSendable").Int(),
MinSendable: j.Get("minSendable").Int(),
CommentAllowed: j.Get("commentAllowed").Int(),
}, nil
}
type Metadata [][]string
// Description returns the content of text/plain metadata entry.
func (m Metadata) Description() string {
for _, entry := range m {
if len(entry) == 2 && entry[0] == "text/plain" {
return entry[1]
}
}
return ""
}
// ImageDataURI returns image in the form data:image/type;base64,... if an image exists
// or an empty string if not.
func (m Metadata) ImageDataURI() string {
for _, entry := range m {
if len(entry) == 2 && strings.Split(entry[0], "/")[0] == "image" {
return "data:" + entry[0] + "," + entry[1]
}
}
return ""
}
// ImageBytes returns image as bytes, decoded from base64 if an image exists
// or nil if not.
func (m Metadata) ImageBytes() []byte {
for _, entry := range m {
if len(entry) == 2 && strings.Split(entry[0], "/")[0] == "image" {
if decoded, err := base64.StdEncoding.DecodeString(entry[1]); err == nil {
return decoded
}
}
}
return nil
}
// ImageExtension returns the file extension for the image, either "png" or "jpeg"
func (m Metadata) ImageExtension() string {
for _, entry := range m {
if len(entry) == 2 && strings.Split(entry[0], "/")[0] == "image" {
spl := strings.Split(entry[0], "/")
if len(spl) == 2 {
return strings.Split(spl[1], ";")[0]
}
}
}
return ""
}
// Entry returns an arbitrary entry from the metadata array.
// eg.: "video/mp4" or "application/vnd.some-specific-thing-from-a-specific-app".
func (m Metadata) Entry(key string) string {
for _, entry := range m {
if len(entry) == 2 && entry[0] == key {
return entry[1]
}
}
return ""
}