2023-09-09 22:52:51 +02:00

125 lines
3.3 KiB
Go

package main
import (
"context"
"database/sql"
"encoding/hex"
"log"
"net/http"
"strings"
"time"
"github.com/joho/godotenv"
"github.com/labstack/echo/v4"
"github.com/lightninglabs/lndclient"
"github.com/lightningnetwork/lnd/lnrpc"
"github.com/namsral/flag"
)
var (
LndCert string
LndMacaroonDir string
LndHost string
lnd *LndClient
)
type LndClient struct {
lnrpc.LightningClient
}
func init() {
err := godotenv.Load()
if err != nil {
log.Println("Error loading .env file")
}
flag.StringVar(&LndCert, "LND_CERT", "", "Path to LND TLS certificate")
flag.StringVar(&LndMacaroonDir, "LND_MACAROON_DIR", "", "LND macaroon directory")
flag.StringVar(&LndHost, "LND_HOST", "localhost:10001", "LND gRPC server address")
flag.Parse()
rpcClient, err := lndclient.NewBasicClient(LndHost, LndCert, LndMacaroonDir, "regtest")
if err != nil {
panic(err)
}
lnd = &LndClient{LightningClient: rpcClient}
if info, err := lnd.GetInfo(context.TODO(), &lnrpc.GetInfoRequest{}); err != nil {
panic(err)
} else {
version := strings.Split(info.Version, " ")[0]
log.Printf("Connected to %s running LND v%s", LndHost, version)
}
}
func (lnd *LndClient) CreateInvoice(pubkey string, msats int) (*Invoice, error) {
addInvoiceResponse, err := lnd.AddInvoice(context.TODO(), &lnrpc.Invoice{
ValueMsat: int64(msats),
Expiry: 3600,
})
if err != nil {
return nil, err
}
lnInvoice, err := lnd.LookupInvoice(context.TODO(), &lnrpc.PaymentHash{RHash: addInvoiceResponse.RHash})
if err != nil {
return nil, err
}
dbInvoice := Invoice{
Session: Session{pubkey},
Msats: msats,
Preimage: hex.EncodeToString(lnInvoice.RPreimage),
PaymentRequest: lnInvoice.PaymentRequest,
PaymentHash: hex.EncodeToString(lnInvoice.RHash),
CreatedAt: time.Unix(lnInvoice.CreationDate, 0),
ExpiresAt: time.Unix(lnInvoice.CreationDate+lnInvoice.Expiry, 0),
}
if err := db.CreateInvoice(&dbInvoice); err != nil {
return nil, err
}
return &dbInvoice, nil
}
func (lnd *LndClient) CheckInvoice(hash string) {
for {
log.Printf("lookup invoice: hash=%s", hash)
invoice, err := lnd.LookupInvoice(context.TODO(), &lnrpc.PaymentHash{RHashStr: hash})
if err != nil {
panic(err)
}
if time.Now().After(time.Unix(invoice.CreationDate+invoice.Expiry, 0)) {
log.Printf("invoice expired: hash=%s", hash)
break
}
if invoice.SettleDate != 0 && invoice.AmtPaidMsat > 0 {
if err := db.ConfirmInvoice(hash, time.Unix(invoice.SettleDate, 0), int(invoice.AmtPaidMsat)); err != nil {
panic(err)
}
log.Printf("invoice confirmed: hash=%s", hash)
break
}
time.Sleep(5 * time.Second)
}
}
func invoice(c echo.Context) error {
invoiceId := c.Param("id")
var invoice Invoice
if err := db.FetchInvoice(invoiceId, &invoice); err == sql.ErrNoRows {
return echo.NewHTTPError(http.StatusNotFound, "Not Found")
} else if err != nil {
return err
}
session := c.Get("session").(Session)
if invoice.Pubkey != session.Pubkey {
return echo.NewHTTPError(http.StatusUnauthorized, "Unauthorized")
}
qr, err := ToQR(invoice.PaymentRequest)
if err != nil {
return err
}
data := map[string]any{
"session": c.Get("session"),
"Invoice": invoice,
"lnurl": invoice.PaymentRequest,
"qr": qr,
}
return c.Render(http.StatusOK, "invoice.html", data)
}