mirror of https://github.com/bumi/lntip
hello world
This commit is contained in:
commit
bfa5219b02
|
@ -0,0 +1 @@
|
|||
vendor
|
|
@ -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
|
|
@ -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
|
||||
|
|
@ -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"))
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
|
@ -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();
|
||||
})
|
||||
}
|
|
@ -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…
Reference in New Issue