hello world

This commit is contained in:
bumi 2019-01-07 01:35:26 +01:00
commit bfa5219b02
8 changed files with 736 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
vendor

285
Gopkg.lock generated Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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>