mirror of
https://github.com/bumi/lntip
synced 2025-06-16 17:55:35 +00:00
hello world
This commit is contained in:
commit
bfa5219b02
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
vendor
|
285
Gopkg.lock
generated
Normal file
285
Gopkg.lock
generated
Normal file
@ -0,0 +1,285 @@
|
|||||||
|
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
||||||
|
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
digest = "1:a138d82090d405fcb3cba3d2f5858b01ad3bbd08832ffa6a6db433d9ad597575"
|
||||||
|
name = "github.com/btcsuite/btcwallet"
|
||||||
|
packages = [
|
||||||
|
"internal/zero",
|
||||||
|
"snacl",
|
||||||
|
]
|
||||||
|
pruneopts = "UT"
|
||||||
|
revision = "8ae4afc70174bacc745e8dd89fc6db2d817909bd"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
digest = "1:064d2cebe444ff7907a912c3ee249c70ae7859fd0fb03c25a0240cbcce2adb4c"
|
||||||
|
name = "github.com/btcsuite/golangcrypto"
|
||||||
|
packages = [
|
||||||
|
"nacl/secretbox",
|
||||||
|
"pbkdf2",
|
||||||
|
"poly1305",
|
||||||
|
"salsa20/salsa",
|
||||||
|
"scrypt",
|
||||||
|
]
|
||||||
|
pruneopts = "UT"
|
||||||
|
revision = "53f62d9b43e87a6c56975cf862af7edf33a8d0df"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
digest = "1:e802928326e2d631840a238c372417c3ade751b79ff491c5f0a4755c912643cd"
|
||||||
|
name = "github.com/coreos/bbolt"
|
||||||
|
packages = ["."]
|
||||||
|
pruneopts = "UT"
|
||||||
|
revision = "4f5275f4ebbf6fe7cb772de987fa96ee674460a7"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
digest = "1:76dc72490af7174349349838f2fe118996381b31ea83243812a97e5a0fd5ed55"
|
||||||
|
name = "github.com/dgrijalva/jwt-go"
|
||||||
|
packages = ["."]
|
||||||
|
pruneopts = "UT"
|
||||||
|
revision = "06ea1031745cb8b3dab3f6a236daf2b0aa468b7e"
|
||||||
|
version = "v3.2.0"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
digest = "1:19c3d5be42d5d503b94650e45bb2a01264bb36caff20f708e9cf67a6683b4c04"
|
||||||
|
name = "github.com/golang/protobuf"
|
||||||
|
packages = [
|
||||||
|
"jsonpb",
|
||||||
|
"proto",
|
||||||
|
"protoc-gen-go/descriptor",
|
||||||
|
"ptypes/any",
|
||||||
|
"ptypes/struct",
|
||||||
|
]
|
||||||
|
pruneopts = "UT"
|
||||||
|
revision = "bbd03ef6da3a115852eaf24c8a1c46aeb39aa175"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
digest = "1:983bdae4397d2a7c88316865e7de6b76bbedd8c33342004931829eba0975d9ed"
|
||||||
|
name = "github.com/grpc-ecosystem/grpc-gateway"
|
||||||
|
packages = [
|
||||||
|
"runtime",
|
||||||
|
"runtime/internal",
|
||||||
|
"utilities",
|
||||||
|
]
|
||||||
|
pruneopts = "UT"
|
||||||
|
revision = "f2862b476edcef83412c7af8687c9cd8e4097c0f"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
digest = "1:a330103bc9731260ee9fa14764e9e3fce46e02de19d6aca3eeba1d425badfbf0"
|
||||||
|
name = "github.com/juju/loggo"
|
||||||
|
packages = ["."]
|
||||||
|
pruneopts = "UT"
|
||||||
|
revision = "584905176618da46b895b176c721b02c476b6993"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
digest = "1:9acadb9c8c15b8a9f635a7fa960c9a522b001a3919bc03df92ac26c372476aff"
|
||||||
|
name = "github.com/labstack/echo"
|
||||||
|
packages = [
|
||||||
|
".",
|
||||||
|
"middleware",
|
||||||
|
]
|
||||||
|
pruneopts = "UT"
|
||||||
|
revision = "1abaa3049251d17932e4313c2d6165073fd07fd8"
|
||||||
|
version = "v3.3.6"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
digest = "1:568171fc14a3d819b112c3e219d351ea7b05e8dad7935c4168c6b3373244a686"
|
||||||
|
name = "github.com/labstack/gommon"
|
||||||
|
packages = [
|
||||||
|
"bytes",
|
||||||
|
"color",
|
||||||
|
"log",
|
||||||
|
"random",
|
||||||
|
]
|
||||||
|
pruneopts = "UT"
|
||||||
|
revision = "2a618302b929cc20862dda3aa6f02f64dbe740dd"
|
||||||
|
version = "v0.2.7"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
digest = "1:c2b45ecd1f0a0d7d3820c1c75db58ea2f5f6e89452870f1090b1afae1460c96c"
|
||||||
|
name = "github.com/lightningnetwork/lnd"
|
||||||
|
packages = [
|
||||||
|
"lnrpc",
|
||||||
|
"macaroons",
|
||||||
|
]
|
||||||
|
pruneopts = "UT"
|
||||||
|
revision = "fb95858afce6d7129758e7677bcb1552c5be2c51"
|
||||||
|
version = "v0.5-beta-rc2"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
digest = "1:c658e84ad3916da105a761660dcaeb01e63416c8ec7bc62256a9b411a05fcd67"
|
||||||
|
name = "github.com/mattn/go-colorable"
|
||||||
|
packages = ["."]
|
||||||
|
pruneopts = "UT"
|
||||||
|
revision = "167de6bfdfba052fa6b2d3664c8f5272e23c9072"
|
||||||
|
version = "v0.0.9"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
digest = "1:0981502f9816113c9c8c4ac301583841855c8cf4da8c72f696b3ebedf6d0e4e5"
|
||||||
|
name = "github.com/mattn/go-isatty"
|
||||||
|
packages = ["."]
|
||||||
|
pruneopts = "UT"
|
||||||
|
revision = "6ca4dbf54d38eea1a992b3c722a76a5d1c4cb25c"
|
||||||
|
version = "v0.0.4"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
digest = "1:d673e95129a1107bfd04e093751a5e1267faabc27d218d824fb013f57ac08f55"
|
||||||
|
name = "github.com/rogpeppe/fastuuid"
|
||||||
|
packages = ["."]
|
||||||
|
pruneopts = "UT"
|
||||||
|
revision = "6724a57986aff9bff1a1770e9347036def7c89f6"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
digest = "1:c468422f334a6b46a19448ad59aaffdfc0a36b08fdcc1c749a0b29b6453d7e59"
|
||||||
|
name = "github.com/valyala/bytebufferpool"
|
||||||
|
packages = ["."]
|
||||||
|
pruneopts = "UT"
|
||||||
|
revision = "e746df99fe4a3986f4d4f79e13c1e0117ce9c2f7"
|
||||||
|
version = "v1.0.0"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
digest = "1:268b8bce0064e8c057d7b913605459f9a26dcab864c0886a56d196540fbf003f"
|
||||||
|
name = "github.com/valyala/fasttemplate"
|
||||||
|
packages = ["."]
|
||||||
|
pruneopts = "UT"
|
||||||
|
revision = "dcecefd839c4193db0d35b88ec65b4c12d360ab0"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
digest = "1:a57b670b0726ad104be74e5abc7b4eaea06f04a93f0fd5013d8f2a29b146753f"
|
||||||
|
name = "golang.org/x/crypto"
|
||||||
|
packages = [
|
||||||
|
"acme",
|
||||||
|
"acme/autocert",
|
||||||
|
"curve25519",
|
||||||
|
"internal/subtle",
|
||||||
|
"nacl/box",
|
||||||
|
"nacl/secretbox",
|
||||||
|
"poly1305",
|
||||||
|
"salsa20/salsa",
|
||||||
|
]
|
||||||
|
pruneopts = "UT"
|
||||||
|
revision = "85e1b3f9139abd58575d728a509643924e3b2ebf"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
digest = "1:279284b3cc1429d8ea79402792b17c5dbfadd583ec4963db69f0368762f89be4"
|
||||||
|
name = "golang.org/x/net"
|
||||||
|
packages = [
|
||||||
|
"context",
|
||||||
|
"http2",
|
||||||
|
"http2/hpack",
|
||||||
|
"idna",
|
||||||
|
"internal/timeseries",
|
||||||
|
"lex/httplex",
|
||||||
|
"trace",
|
||||||
|
]
|
||||||
|
pruneopts = "UT"
|
||||||
|
revision = "ae89d30ce0c63142b652837da33d782e2b0a9b25"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
digest = "1:008fb9f84ae9bf7994228b9c78810d44162071ec13caf28291677897dca819ee"
|
||||||
|
name = "golang.org/x/sys"
|
||||||
|
packages = ["unix"]
|
||||||
|
pruneopts = "UT"
|
||||||
|
revision = "d989b31c87461dc8ab2f1cac6792814e27fadea9"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
digest = "1:a2ab62866c75542dd18d2b069fec854577a20211d7c0ea6ae746072a1dccdd18"
|
||||||
|
name = "golang.org/x/text"
|
||||||
|
packages = [
|
||||||
|
"collate",
|
||||||
|
"collate/build",
|
||||||
|
"internal/colltab",
|
||||||
|
"internal/gen",
|
||||||
|
"internal/tag",
|
||||||
|
"internal/triegen",
|
||||||
|
"internal/ucd",
|
||||||
|
"language",
|
||||||
|
"secure/bidirule",
|
||||||
|
"transform",
|
||||||
|
"unicode/bidi",
|
||||||
|
"unicode/cldr",
|
||||||
|
"unicode/norm",
|
||||||
|
"unicode/rangetable",
|
||||||
|
]
|
||||||
|
pruneopts = "UT"
|
||||||
|
revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0"
|
||||||
|
version = "v0.3.0"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
digest = "1:c2dee8dbcc504d1a7858f5dbaed7c8b256c512c5e9e81480158c30185bbd2792"
|
||||||
|
name = "google.golang.org/genproto"
|
||||||
|
packages = [
|
||||||
|
"googleapis/api/annotations",
|
||||||
|
"googleapis/rpc/status",
|
||||||
|
]
|
||||||
|
pruneopts = "UT"
|
||||||
|
revision = "df60624c1e9b9d2973e889c7a1cff73155da81c4"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
digest = "1:8d9ccf0a790b530f94357a5d27e9c86fb53e862b6e0f9133c792269c0b567218"
|
||||||
|
name = "google.golang.org/grpc"
|
||||||
|
packages = [
|
||||||
|
".",
|
||||||
|
"codes",
|
||||||
|
"connectivity",
|
||||||
|
"credentials",
|
||||||
|
"grpclb/grpc_lb_v1",
|
||||||
|
"grpclog",
|
||||||
|
"internal",
|
||||||
|
"keepalive",
|
||||||
|
"metadata",
|
||||||
|
"naming",
|
||||||
|
"peer",
|
||||||
|
"stats",
|
||||||
|
"status",
|
||||||
|
"tap",
|
||||||
|
"transport",
|
||||||
|
]
|
||||||
|
pruneopts = "UT"
|
||||||
|
revision = "b3ddf786825de56a4178401b7e174ee332173b66"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
digest = "1:9f0c81ca4b497d3723d0a66495d8a1efe277068b77ef3ad2d6460e480bf09bb3"
|
||||||
|
name = "gopkg.in/errgo.v1"
|
||||||
|
packages = ["."]
|
||||||
|
pruneopts = "UT"
|
||||||
|
revision = "b20caedf0710d0988e92b5f2d76843ad1f231f2d"
|
||||||
|
version = "v1.0.0"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
digest = "1:f75654fe9e7a52c9df4c13d3c362a02e9dd0ab5e1ef336212ae68964c05ff53f"
|
||||||
|
name = "gopkg.in/macaroon-bakery.v2"
|
||||||
|
packages = [
|
||||||
|
"bakery",
|
||||||
|
"bakery/checkers",
|
||||||
|
"bakery/internal/macaroonpb",
|
||||||
|
]
|
||||||
|
pruneopts = "UT"
|
||||||
|
revision = "94012773d2874a067572bd16d7d11ae02968b47b"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
digest = "1:0622939c131fd6ca6fc9dfb25adda2f195062975045baba7ad94607e6f3c52b7"
|
||||||
|
name = "gopkg.in/macaroon.v2"
|
||||||
|
packages = ["."]
|
||||||
|
pruneopts = "UT"
|
||||||
|
revision = "bed2a428da6e56d950bed5b41fcbae3141e5b0d0"
|
||||||
|
|
||||||
|
[solve-meta]
|
||||||
|
analyzer-name = "dep"
|
||||||
|
analyzer-version = 1
|
||||||
|
input-imports = [
|
||||||
|
"github.com/grpc-ecosystem/grpc-gateway/runtime",
|
||||||
|
"github.com/labstack/echo",
|
||||||
|
"github.com/labstack/echo/middleware",
|
||||||
|
"github.com/lightningnetwork/lnd/lnrpc",
|
||||||
|
"github.com/lightningnetwork/lnd/macaroons",
|
||||||
|
"google.golang.org/grpc",
|
||||||
|
"google.golang.org/grpc/credentials",
|
||||||
|
]
|
||||||
|
solver-name = "gps-cdcl"
|
||||||
|
solver-version = 1
|
31
Gopkg.toml
Normal file
31
Gopkg.toml
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
# Gopkg.toml example
|
||||||
|
#
|
||||||
|
# Refer to https://golang.github.io/dep/docs/Gopkg.toml.html
|
||||||
|
# for detailed Gopkg.toml documentation.
|
||||||
|
#
|
||||||
|
# required = ["github.com/lightningnetwork/lnd", "google.golang.org/grpc"]
|
||||||
|
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
|
||||||
|
#
|
||||||
|
# [[constraint]]
|
||||||
|
# name = "github.com/user/project"
|
||||||
|
# version = "1.0.0"
|
||||||
|
#
|
||||||
|
# [[constraint]]
|
||||||
|
# name = "github.com/user/project2"
|
||||||
|
# branch = "dev"
|
||||||
|
# source = "github.com/myfork/project2"
|
||||||
|
#
|
||||||
|
# [[override]]
|
||||||
|
# name = "github.com/x/y"
|
||||||
|
# version = "2.4.0"
|
||||||
|
#
|
||||||
|
# [prune]
|
||||||
|
# non-go = false
|
||||||
|
# go-tests = true
|
||||||
|
# unused-packages = true
|
||||||
|
|
||||||
|
|
||||||
|
[prune]
|
||||||
|
go-tests = true
|
||||||
|
unused-packages = true
|
||||||
|
|
109
invoices.go
Normal file
109
invoices.go
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"github.com/bumi/lntip/ln"
|
||||||
|
"github.com/labstack/echo"
|
||||||
|
"github.com/labstack/echo/middleware"
|
||||||
|
"html/template"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
var stdOutLogger = log.New(os.Stdout, "", log.LstdFlags)
|
||||||
|
|
||||||
|
type TemplateRenderer struct {
|
||||||
|
templates *template.Template
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TemplateRenderer) Render(w io.Writer, name string, data interface{}, c echo.Context) error {
|
||||||
|
// Add global methods if data is a map
|
||||||
|
if viewContext, isMap := data.(map[string]interface{}); isMap {
|
||||||
|
viewContext["reverse"] = c.Echo().Reverse
|
||||||
|
}
|
||||||
|
|
||||||
|
return t.templates.ExecuteTemplate(w, name, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var indexView = `
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title></title>
|
||||||
|
<script type="text/javascript">
|
||||||
|
function payme(memo, amount) {
|
||||||
|
fetch('/payme?memo=' + memo + '&amount=' + amount)
|
||||||
|
.then(function(response) {
|
||||||
|
console.log(response.json());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
hallo
|
||||||
|
</body>
|
||||||
|
</html>`
|
||||||
|
|
||||||
|
address := flag.String("address", "localhost:10009", "The host and port of the ln gRPC server")
|
||||||
|
certFile := flag.String("cert", "tls.cert", "Path to the lnd tls.cert file")
|
||||||
|
macaroonFile := flag.String("macaroon", "invoice.macaroon", "Path to the lnd macaroon file")
|
||||||
|
viewPath := flag.String("template", "", "Path of a custom HTML template file")
|
||||||
|
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
if *viewPath != "" {
|
||||||
|
content, err := ioutil.ReadFile(*viewPath)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
indexView = string(content)
|
||||||
|
}
|
||||||
|
|
||||||
|
e := echo.New()
|
||||||
|
e.Static("/static", "views/assets")
|
||||||
|
e.Use(middleware.CORS())
|
||||||
|
e.Use(middleware.Recover())
|
||||||
|
renderer := &TemplateRenderer{
|
||||||
|
templates: template.Must(template.New("index").Parse(indexView)),
|
||||||
|
}
|
||||||
|
e.Renderer = renderer
|
||||||
|
|
||||||
|
lndOptions := ln.LNDoptions{
|
||||||
|
Address: *address,
|
||||||
|
CertFile: *certFile,
|
||||||
|
MacaroonFile: *macaroonFile,
|
||||||
|
}
|
||||||
|
lnClient, err := ln.NewLNDclient(lndOptions)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
e.GET("/payme", func(c echo.Context) error {
|
||||||
|
memo := c.FormValue("memo")
|
||||||
|
amount, _ := strconv.ParseInt(c.FormValue("amount"), 10, 64)
|
||||||
|
invoice, err := lnClient.GenerateInvoice(amount, memo)
|
||||||
|
if err != nil {
|
||||||
|
return c.JSON(http.StatusInternalServerError, "error")
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(http.StatusOK, invoice)
|
||||||
|
})
|
||||||
|
|
||||||
|
e.GET("/settled/:invoiceId", func(c echo.Context) error {
|
||||||
|
invoiceId := c.Param("invoiceId")
|
||||||
|
invoice, _ := lnClient.CheckInvoice(invoiceId)
|
||||||
|
return c.JSON(http.StatusOK, invoice)
|
||||||
|
})
|
||||||
|
|
||||||
|
e.GET("/", func(c echo.Context) error {
|
||||||
|
return c.Render(http.StatusOK, "index", map[string]interface{}{})
|
||||||
|
})
|
||||||
|
|
||||||
|
e.Logger.Fatal(e.Start(":1323"))
|
||||||
|
}
|
149
ln/lnd.go
Normal file
149
ln/lnd.go
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
package ln
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/hex"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/lightningnetwork/lnd/lnrpc"
|
||||||
|
"gopkg.in/macaroon.v2"
|
||||||
|
"github.com/lightningnetwork/lnd/macaroons"
|
||||||
|
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
"google.golang.org/grpc/credentials"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
var stdOutLogger = log.New(os.Stdout, "", log.LstdFlags)
|
||||||
|
|
||||||
|
// Invoice is a Lightning Network invoice and contains the typical invoice string and the payment hash.
|
||||||
|
type Invoice struct {
|
||||||
|
ImplDepID string
|
||||||
|
PaymentHash string
|
||||||
|
PaymentRequest string
|
||||||
|
}
|
||||||
|
|
||||||
|
type LNDclient struct {
|
||||||
|
lndClient lnrpc.LightningClient
|
||||||
|
ctx context.Context
|
||||||
|
conn *grpc.ClientConn
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerateInvoice generates an invoice with the given price and memo.
|
||||||
|
func (c LNDclient) GenerateInvoice(amount int64, memo string) (Invoice, error) {
|
||||||
|
result := Invoice{}
|
||||||
|
|
||||||
|
// Create the request and send it
|
||||||
|
invoice := lnrpc.Invoice{
|
||||||
|
Memo: memo,
|
||||||
|
Value: amount,
|
||||||
|
}
|
||||||
|
stdOutLogger.Println("Creating invoice for a new API request")
|
||||||
|
res, err := c.lndClient.AddInvoice(c.ctx, &invoice)
|
||||||
|
if err != nil {
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|
||||||
|
result.ImplDepID = hex.EncodeToString(res.RHash)
|
||||||
|
result.PaymentHash = result.ImplDepID
|
||||||
|
result.PaymentRequest = res.PaymentRequest
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckInvoice takes an invoice ID and checks if the corresponding invoice was settled.
|
||||||
|
// An error is returned if no corresponding invoice was found.
|
||||||
|
// False is returned if the invoice isn't settled.
|
||||||
|
func (c LNDclient) CheckInvoice(id string) (bool, error) {
|
||||||
|
// In the case of lnd, the ID is the hex encoded preimage hash.
|
||||||
|
plainHash, err := hex.DecodeString(id)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
stdOutLogger.Printf("Checking invoice for hash %v\n", id)
|
||||||
|
|
||||||
|
// Get the invoice for that hash
|
||||||
|
paymentHash := lnrpc.PaymentHash{
|
||||||
|
RHash: plainHash,
|
||||||
|
// Hex encoded, must be exactly 32 byte
|
||||||
|
RHashStr: id,
|
||||||
|
}
|
||||||
|
invoice, err := c.lndClient.LookupInvoice(c.ctx, &paymentHash)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if invoice was settled
|
||||||
|
if !invoice.GetSettled() {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c LNDclient) GetURIs() (bool, error) {
|
||||||
|
req := &lnrpc.GetInfoRequest{}
|
||||||
|
info, err := c.lndClient.GetInfo(c.ctx, req)
|
||||||
|
stdOutLogger.Println(info.Uris)
|
||||||
|
return true, err
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// NewLNDclient creates a new LNDclient instance.
|
||||||
|
func NewLNDclient(lndOptions LNDoptions) (LNDclient, error) {
|
||||||
|
result := LNDclient{}
|
||||||
|
|
||||||
|
// Set up a connection to the server.
|
||||||
|
creds, err := credentials.NewClientTLSFromFile(lndOptions.CertFile, "")
|
||||||
|
if err != nil {
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
opts := []grpc.DialOption{
|
||||||
|
grpc.WithTransportCredentials(creds),
|
||||||
|
}
|
||||||
|
|
||||||
|
macaroonData, err := ioutil.ReadFile(lndOptions.MacaroonFile)
|
||||||
|
if err != nil {
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
mac := &macaroon.Macaroon{}
|
||||||
|
if err = mac.UnmarshalBinary(macaroonData); err != nil {
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|
||||||
|
macCred := macaroons.NewMacaroonCredential(mac)
|
||||||
|
opts = append(opts, grpc.WithPerRPCCredentials(macCred))
|
||||||
|
|
||||||
|
conn, err := grpc.Dial(lndOptions.Address, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|
||||||
|
c := lnrpc.NewLightningClient(conn)
|
||||||
|
|
||||||
|
result = LNDclient{
|
||||||
|
conn: conn,
|
||||||
|
ctx: context.Background(),
|
||||||
|
lndClient: c,
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// LNDoptions are the options for the connection to the lnd node.
|
||||||
|
type LNDoptions struct {
|
||||||
|
// Address of your LND node, including the port.
|
||||||
|
// Optional ("localhost:10009" by default).
|
||||||
|
Address string
|
||||||
|
// Path to the "tls.cert" file that your LND node uses.
|
||||||
|
// Optional ("tls.cert" by default).
|
||||||
|
CertFile string
|
||||||
|
// Path to the macaroon file that your LND node uses.
|
||||||
|
// "invoice.macaroon" if you only use the GenerateInvoice() and CheckInvoice() methods
|
||||||
|
// (required by the middleware in the package "wall").
|
||||||
|
// "admin.macaroon" if you use the Pay() method (required by the client in the package "pay").
|
||||||
|
// Optional ("invoice.macaroon" by default).
|
||||||
|
MacaroonFile string
|
||||||
|
}
|
||||||
|
|
24
views/assets/lntip.css
Normal file
24
views/assets/lntip.css
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
@keyframes slideIn{0%{opacity:0;transform:scale(.85)}70%{opacity:1;transform:scale(1.03)}100%{transform:scale(1)}}@keyframes slideOut{from{opacity:1}to{opacity:0}}@keyframes rotate{from{transform:rotate(0)}to{transform:rotate(180deg)}}.jPopup{position:absolute;top:0;right:0;bottom:0;left:0;z-index:9999;max-width:100%;padding:50px 15px 15px;box-sizing:border-box}.jPopup:after{content:'';position:fixed;top:0;right:0;bottom:0;left:0;z-index:9998;background:#fff}.jPopup>.jCloseBtn{position:absolute;right:1rem;top:1rem;z-index:9999;outline:0;border:0;box-sizing:border-box;cursor:pointer;width:5rem;height:5rem;background:#f2f2f2;border-radius:50%}.jPopup>.jCloseBtn>.graphicIcon{width:2rem;height:2rem;position:relative;margin:0 auto}.jPopup>.jCloseBtn>.graphicIcon:after,.jPopup>.jCloseBtn>.graphicIcon:before{position:absolute;left:.9rem;content:'';height:2rem;width:.3rem;background-color:#adadad;border-radius:.5rem}.jPopup>.jCloseBtn>.graphicIcon:before{-ms-transform:rotate(45deg);transform:rotate(45deg)}.jPopup>.jCloseBtn>.graphicIcon:after{-ms-transform:rotate(-45deg);transform:rotate(-45deg)}.jPopup>.jCloseBtn:hover>.graphicIcon{animation:rotate 250ms ease-in}.jPopup>.jCloseBtn:active{-ms-transform:scale(.95);transform:scale(.95)}.jPopup>.content{top:50%;left:1.5rem;right:1.5rem;position:absolute;z-index:9999;-ms-transform:translateY(-50%);transform:translateY(-50%)}@media screen and (min-width:680px){.jPopup{padding:8rem 3rem 3rem}.jPopup>.jCloseBtn{width:6rem;height:6rem;right:2rem;top:2rem}.jPopup>.jCloseBtn:after{content:'(esc)';position:absolute;top:6.5rem;left:50%;font-size:1.1rem;-ms-transform:translateX(-50%);transform:translateX(-50%);color:#adadad;pointer-events:none}.jPopup>.jCloseBtn>.graphicIcon{width:3rem;height:3rem}.jPopup>.jCloseBtn>.graphicIcon:after,.jPopup>.jCloseBtn>.graphicIcon:before{left:1.4rem;height:3rem}.jPopup>.content{left:3rem;right:3rem}}.jPopupOpen,.jPopupOpen body{overflow:hidden}.jPopupOpen .jPopup{animation:slideIn .5s cubic-bezier(.34,.34,.26,.99)}.jPopupClosed .jPopup{animation:slideOut 250ms ease-in}
|
||||||
|
|
||||||
|
.jPopup > .jCloseBtn {
|
||||||
|
z-index:10000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lntip-payment-request {
|
||||||
|
width: 220px;
|
||||||
|
margin: 0 auto;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.lntip-payment-request h1, .lntip-payment-request h2 {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lntip-details {
|
||||||
|
width: 200px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lntip-copy {
|
||||||
|
cursor: pointer;
|
||||||
|
display: inline;
|
||||||
|
}
|
126
views/assets/lntip.js
Normal file
126
views/assets/lntip.js
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
/*! https://github.com/robiveli/jpopup */
|
||||||
|
!function(e,n){void 0===e&&void 0!==window&&(e=window),"function"==typeof define&&define.amd?define([],function(){return e.jPopup=n()}):"object"==typeof module&&module.exports?module.exports=n():e.jPopup=n()}(this,function(){"use strict";var n,o,t=function(){var e=0<arguments.length&&void 0!==arguments[0]?arguments[0]:"";1==(n=0!=e.shouldSetHash)&&(o=void 0!==e.hashtagValue?e.hashtagValue:"#popup"),i(e.content).then(a).then(1==n&&s(!0))},i=function(e){return u.classList.add("jPopupOpen"),Promise.resolve(document.body.insertAdjacentHTML("beforeend",'<div class="jPopup">\n <button type="button" class="jCloseBtn">\n <div class="graphicIcon"></div>\n </button>\n <div class="content">'.concat(e,"</div>\n </div>")))},s=function(e){1==e?window.location.hash=o:window.history.back()},d=function(e){27==e.keyCode&&t.prototype.close(!0)},c=function(){window.location.hash!==o&&t.prototype.close(!1)},a=function(){document.getElementsByClassName("jCloseBtn")[0].addEventListener("click",function(){t.prototype.close(!0)}),window.addEventListener("keydown",d),1==n&&window.addEventListener("hashchange",c)},u=document.querySelector("html");return t.prototype={close:function(e){u.classList.add("jPopupClosed"),1==n&&(e&&s(!1),window.removeEventListener("hashchange",c)),window.removeEventListener("keydown",d),document.getElementsByClassName("jPopup")[0].addEventListener("animationend",function(e){e.target.parentNode.removeChild(this),u.classList.remove("jPopupClosed"),u.classList.remove("jPopupOpen")})},open:function(e){t(e)}},t});
|
||||||
|
|
||||||
|
if (document.currentScript) {
|
||||||
|
window.LNTIP_HOST = document.currentScript.getAttribute('lntip-host');
|
||||||
|
}
|
||||||
|
|
||||||
|
LnTip = function (amount, memo, host) {
|
||||||
|
this.host = host || window.LNTIP_HOST;
|
||||||
|
this.amount = amount;
|
||||||
|
this.memo = memo || '';
|
||||||
|
this.getInvoice();
|
||||||
|
}
|
||||||
|
|
||||||
|
LnTip.prototype.loadStylesheet = function () {
|
||||||
|
if (this.styleLoaded) { return }
|
||||||
|
var head = document.getElementsByTagName('head')[0];
|
||||||
|
var css = document.createElement('link');
|
||||||
|
css.rel = "stylesheet";
|
||||||
|
css.type = "text/css";
|
||||||
|
css.href = `${this.host}/static/lntip.css`;
|
||||||
|
head.appendChild(css);
|
||||||
|
this.styleLoaded = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
LnTip.prototype.closePopup = function () {
|
||||||
|
if (this.popup) {
|
||||||
|
this.popup.close();
|
||||||
|
this.popup = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LnTip.prototype.openPopup = function (content) {
|
||||||
|
this.closePopup();
|
||||||
|
this.loadStylesheet();
|
||||||
|
this.popup = new jPopup({
|
||||||
|
content: content,
|
||||||
|
shouldSetHash: false
|
||||||
|
});
|
||||||
|
return this.popup;
|
||||||
|
}
|
||||||
|
|
||||||
|
LnTip.prototype.thanks = function () {
|
||||||
|
if (window.lntipPopup) { window.lntipPopup.close(); window.lntipPopup = null; }
|
||||||
|
var content = '<div class="lntip-payment-request"><h1 class="lntip-headline">Thank you!</h1></div>';
|
||||||
|
this.openPopup(content);
|
||||||
|
setTimeout(() => {
|
||||||
|
this.closePopup();
|
||||||
|
}, 3000);
|
||||||
|
}
|
||||||
|
|
||||||
|
LnTip.prototype.watchPayment = function () {
|
||||||
|
if (this.paymentWatcher) { window.clearInterval(this.paymentWatcher) }
|
||||||
|
this.paymentWatcher = window.setInterval(() => {
|
||||||
|
this._request(`${this.host}/settled/${this.invoice.ImplDepID}`)
|
||||||
|
.then((settled) => {
|
||||||
|
if (settled) {
|
||||||
|
this.invoice.settled = true;
|
||||||
|
this.thanks();
|
||||||
|
this.stopWatchingPayment();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
LnTip.prototype.stopWatchingPayment = function () {
|
||||||
|
window.clearInterval(this.paymentWatcher);
|
||||||
|
this.paymentWatcher = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
LnTip.prototype.payWithWebln = function () {
|
||||||
|
if (!webln.isEnabled) {
|
||||||
|
webln.enable().then((weblnResponse) => {
|
||||||
|
return webln.sendPayment({ paymentRequest: invoice.PaymentRequest })
|
||||||
|
}).catch((e) => {
|
||||||
|
this.requestPayment();
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
return webln.sendPayment({ paymentRequest: invoice.PaymentRequest })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LnTip.prototype.requestPayment = function () {
|
||||||
|
var content = `<div class="lntip-payment-request">
|
||||||
|
<h1>${this.memo}</h1>
|
||||||
|
<h2>${this.amount} satoshi</h2>
|
||||||
|
<div class="lntip-qr">
|
||||||
|
<img src="https://chart.googleapis.com/chart?cht=qr&chs=200x200&chl=${this.invoice.PaymentRequest}">
|
||||||
|
</div>
|
||||||
|
<div class="lntip-details">
|
||||||
|
<a href="lightning:${this.invoice.PaymentRequest}" class="lntip-invoice">
|
||||||
|
${this.invoice.PaymentRequest}
|
||||||
|
</a>
|
||||||
|
<div class="lntip-copy" id="lntip-copy">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-copy"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>`
|
||||||
|
this.openPopup(content);
|
||||||
|
|
||||||
|
document.getElementById('lntip-copy').onclick = function() {
|
||||||
|
navigator.clipboard.writeText(invoice.PaymentRequest);
|
||||||
|
alert('Copied to clipboad');
|
||||||
|
}
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
LnTip.prototype.getInvoice = function () {
|
||||||
|
return this._request(`${this.host}/payme?memo=${this.memo}&amount=${this.amount}`)
|
||||||
|
.then((invoice) => {
|
||||||
|
this.invoice = invoice;
|
||||||
|
this.watchPayment();
|
||||||
|
|
||||||
|
if (typeof webln !== 'undefined') {
|
||||||
|
this.payWithWebln();
|
||||||
|
} else {
|
||||||
|
this.requestPayment();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
LnTip.prototype._request = function(url) {
|
||||||
|
return fetch(url).then((response) => {
|
||||||
|
return response.json();
|
||||||
|
})
|
||||||
|
}
|
11
views/index.html
Normal file
11
views/index.html
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title></title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<script src="/static/lntip.js" lntip-host="http://localhost:1323"></script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
x
Reference in New Issue
Block a user